From 7d53992841b6c873ada03908963e758837c4676b Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Tue, 12 May 2026 03:48:03 -0600 Subject: [PATCH] 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). --- src/content/docs/reference/program-format.mdx | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/content/docs/reference/program-format.mdx b/src/content/docs/reference/program-format.mdx index ba1d432..6b5c257 100644 --- a/src/content/docs/reference/program-format.mdx +++ b/src/content/docs/reference/program-format.mdx @@ -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` (traditional) or as `OP` + `Arg1_ArgType` (structured). -:::caution[Open: disk byte order for `Arg1_IX` / `Arg2_IX` / `CompConst`] -The C# `clsProgram.Read` reads u16 fields **little-endian** via -`clsPcaCryptFileStream.ReadUInt16`, but the `Arg1_IX` / -`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 -00 00 00 00 00`) is byte-symmetric (all zero except byte 4 = `0x01`) -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` — -the first non-symmetric example will pin it down. +:::note[Disk byte order: BE for AND records (verified)] +Authored an `AND IF ZONE 5 SECURE` capture and diffed it against an +`AND IF NEVER`: + +``` +NEVER: 08 00 00 00 01 ... +ZONE 5 SECURE: 08 04 00 00 05 ... + ↑ ↑ + byte 1 byte 4 +``` + +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