From 88d05988b54b5141d93db13801a38b357d6bf46d Mon Sep 17 00:00:00 2001 From: Dennis Brentjes Date: Sat, 10 Oct 2026 11:04:02 +0200 Subject: [PATCH] Document that the command decoder is one mechanism, and its real limit Records why supporting both command-space forms is a single decoder rather than two paths, and where that abstraction genuinely stops. The unification: Swiss's "4-byte transfer" is not a second protocol. From the device's side it is the same 2-byte header followed by a data phase that happens to be 2 bytes (00, val) instead of 1 (val). Command space was the last place still assuming a fixed transfer length, which register space already avoids. Both rules are length-agnostic -- last data byte wins, fill the first two data slots -- so there is no driver detection, no mode flag, and with comments stripped the words "Swiss" and "libogc" do not appear in the RTL. The limit, which is real but inert: a 2-byte data phase means "pad + value" (Swiss) or "two consecutive registers" (libogc2), and those cannot be told apart on the wire. libogc2 does do multi-byte command writes -- bba_cmd_outs(0x04, &priv->devid, 2) runs unconditionally in its init -- so last byte wins is wrong for those and auto-increment is wrong for Swiss. It does not matter today because the only command registers implemented are 0x02 (IMR) and 0x03 (IR), which both drivers access one byte at a time, while libogc2's multi-byte accesses target 0x04/0x05 and 0x08/0x09, none of which are decoded. If 0x04/0x05 are ever implemented the fix is per-register width knowledge, not a driver mode -- noted so nobody resolves it by sniffing transfer length. Also records that the challenge/response handshake must NOT be implemented: libogc2 reads a 4-byte challenge from command reg 0x08 and writes the response to 0x09, but only when the DEVICE raises status bit 0x10 in command reg 0x03. It is device-initiated, we never set that bit, so the path cannot be entered. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 9152577..83da879 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1110,6 +1110,48 @@ a fast path independent of this FSM. Tests: `bba_register_file` **T10** (libogc form) and **T12** (Swiss form, asserting bytes 2 AND 3), both mutation-tested — re-adding the auto-increment fails T12, and pushing the response once fails T12's byte 3. + +**This is ONE decoder, not two.** There is no driver detection, no mode flag +and no conditional on which driver is talking — grep the RTL with comments +stripped and the words "Swiss" and "libogc" do not appear. The unification is +that **Swiss's "4-byte transfer" is not a second protocol**: from the device's +side it is the same 2-byte header followed by a data phase that happens to be +2 bytes (`00, val`) rather than 1 (`val`). Command space was simply the last +place that still assumed a fixed transfer length — the same mistake register +space already avoids ("no length field, the data phase runs until CS"). Both +rules are therefore length-agnostic: last data byte wins, fill the first two +data slots. + +⚠️ **The residual ambiguity, which is real but currently inert.** A 2-byte +data phase has two meanings that cannot be told apart on the wire: + +``` +[0x42, 0x00, 0x00, val] Swiss -> cmd reg 2 = val (pad + value) +[0x42, 0x00, b0, b1 ] libogc2 -> cmd reg 2 = b0, 3 = b1 (two registers) +``` + +libogc2 really does multi-byte command writes — `bba_cmd_outs(0x04, +&priv->devid, 2)` runs unconditionally in its init. "Last byte wins" is wrong +for those, and auto-increment is wrong for Swiss. It does not matter today +because: +- the only command registers implemented are **0x02** (IMR) and **0x03** (IR), + and BOTH drivers access those a single byte at a time; and +- libogc2's multi-byte command accesses target `0x04`/`0x05` and + `0x08`/`0x09`, none of which are decoded — they fall through the `Switch` + and are ignored. + +So the decoder is correct for every register it implements. **If `0x04`/`0x05` +are ever implemented, the fix is per-register width knowledge (single-byte +registers take the last byte; multi-byte registers auto-increment), NOT a +driver mode.** Do not "resolve" the ambiguity by sniffing the transfer length. + +⚠️ **Do NOT implement the challenge/response handshake.** libogc2 reads a +4-byte challenge from command reg `0x08`, computes `bba_calc_response()` and +writes it to `0x09` — but only when the DEVICE raises status bit `0x10` in +command reg `0x03`. It is device-initiated, we never set that bit, so the path +never runs. Likewise `bba_cmd_out8(0x05, priv->acstart)` and the `0x0B` status +read only appear inside those handlers. Emulating any of it would be work for +a path that cannot be entered. NCRA, IR, NWAYS, RWP, RRP, PAR… all live in the REGISTER space (`bba_in8`/`bba_out8`/`bba_ins`/`bba_outs`). Only the device-ID query uses a short header — the single case where the two formats coincide.