program-format: AND-record u16s are BE on disk (verified empirically)

Authored AND IF ZONE 5 SECURE in PC Access and diffed against AND IF
NEVER. The zone number 5 lands at byte 4 (high-offset of the u16),
confirming that Arg1_IX / Arg2_IX / CompConst in AND records use
big-endian byte order on disk. This is the opposite of compact-form
cond / cond2 / pr2 which are LE. Different record families use
different byte orders.

Convert the earlier "open question" caution to a confirmation note,
and add a separate caution for the remaining open question about
byte 1's semantic role (OP vs family code).
This commit is contained in:
Ryan Malloy 2026-05-12 03:48:03 -06:00
parent 7a766d69ba
commit 7d53992841

View File

@ -426,16 +426,37 @@ AND record's condition is rendered from the **`Cond` u16 at bytes
`OP > 0`. So the same byte positions serve double duty: as `Cond` `OP > 0`. So the same byte positions serve double duty: as `Cond`
(traditional) or as `OP` + `Arg1_ArgType` (structured). (traditional) or as `OP` + `Arg1_ArgType` (structured).
:::caution[Open: disk byte order for `Arg1_IX` / `Arg2_IX` / `CompConst`] :::note[Disk byte order: BE for AND records (verified)]
The C# `clsProgram.Read` reads u16 fields **little-endian** via Authored an `AND IF ZONE 5 SECURE` capture and diffed it against an
`clsPcaCryptFileStream.ReadUInt16`, but the `Arg1_IX` / `AND IF NEVER`:
`Arg2_IX` / `CompConst` accessors index `Data[]` as **big-endian**.
This implies an implicit byte swap somewhere we haven't fully traced. ```
Our one captured `AND IF NEVER` record (`08 00 00 00 01 00 00 00 00 NEVER: 08 00 00 00 01 ...
00 00 00 00 00`) is byte-symmetric (all zero except byte 4 = `0x01`) ZONE 5 SECURE: 08 04 00 00 05 ...
so it doesn't disambiguate. Resolving the disk byte order needs a ↑ ↑
controlled capture of `AND IF ZONE 5 SECURE` or `AND IF UNIT 7 ON` — byte 1 byte 4
the first non-symmetric example will pin it down. ```
The zone number `5` lands in **byte 4** (high-offset byte of the
positions 3-4 u16). If disk were LE the `5` would be in byte 3. So
**`Arg1_IX` / `Arg2_IX` / `CompConst` are big-endian on disk for
AND records** — opposite of compact-form `cond` / `cond2` / `pr2`
which are LE. The two record families use different byte orders;
the C# encoder writes AND records' u16s directly in BE matching
the in-memory `Data[]` layout. Compact-form fields go through
`Cond`/`Cond2`/`Pr2` setters that swap to LE on the wire.
:::
:::caution[Still open: byte 1's semantic role]
For `AND IF ZONE 5 SECURE`, byte 1 = `0x04`. The C# accessor at
`clsProgram.cs:326` says `OP = Data[1]` and `0x04 = Arg1_GT_Arg2` —
which makes no sense for a zone-state check. But `0x04` *also* matches
the compact-form `ProgramCond.ZONE` family code. So byte 1 is
probably the **condition family code** (re-using compact-form
semantics) when `OP == Arg1_Traditional` (the implicit case), and
only acts as the structured `OP` when the user has actually picked
a comparison operator. Resolving this needs a capture of an
explicitly-structured AND like `AND IF TEMPERATURE > 70`.
::: :::
A 5-record block from our test fixture (one WHEN, three ANDs, one A 5-record block from our test fixture (one WHEN, three ANDs, one