Previously every command register except 0x02/0x03 read 0x00 and dropped
writes. Real software will hit them, so they are now implemented, enumerated
from libogc2's gcif.c rather than guessed at random (Swiss's patch driver
touches only 0x02 and 0x03):
0x01 read revision ID -> priv->revid
0x02 write interrupt mask (IRMASKALL / IRMASKNONE)
0x03 read / w1c interrupt status
0x04 write 2 bytes priv->devid = 0xD107
0x05 write priv->acstart = 0x4E
0x08 read 4 bytes challenge | device-initiated path,
0x09 write 4 bytes response | gated on status bit 0x10
0x0B read challenge/response status
0x0F read (slow) in __bba_reset(), result DISCARDED
0x01 returns 0x00 and is read-only. That is a real-hardware value, not a
guess: the BBA never received a functional firmware revision that incremented
it. libogc2's priv->revid = 0xf0 is its PRE-INIT PLACEHOLDER, overwritten by
whatever this read returns, so it must not be mistaken for the device value.
revid/devid/acstart feed only bba_calc_response(), whose path is
device-initiated, so they are functionally inert for us.
0x04 and up are plain read/write storage, which IS a guess: both drivers treat
them as write-only and never read them back, so read-what-you-wrote is the
least surprising behaviour and lets our tests confirm the write landed.
Ignoring them (the old behaviour) is strictly worse -- the write vanished.
Registers above 0x0F still read 0x00 so a stray high register cannot alias onto
a real one.
Noted for later: bba_cmd_in8_slow(0x0F) sits in __bba_reset() between two 10 ms
delays, holds CS for 200 us and discards the result. A read whose value is
thrown away but whose timing is deliberate looks like a reset strobe; we treat
it as an ordinary register, and the NCRA reset follows immediately in that
sequence, so ignoring the side effect costs nothing today.
T14 covers the whole table and is mutation-tested: making 0x01 plain storage
fails it, and dropping the 0x04+ writes fails it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>