Replaces the invented 2-byte header (addr split across both bytes, 2-bit
length field) with the protocol libogc and Swiss actually speak:
command (bit7=0, 6-bit reg) [reg, 00, 00, data] one 4-byte transfer
register (bit7=1, 16-bit reg) [0x80|w<<6, ah, al, 00] + data until CS
This was not an optimisation: the old decoder read Swiss's NWAYS read
(80 00 31 00) as 'write 0x31 to NCRA', and bit 0 of 0x31 is RESET, so every
register read would have reset the device.
Changes:
* 16-bit address; no length field -- register reads and writes now stream
until CS, like the SPRAM path already did
* RHDR_LO starts the SPRAM fetch as soon as the address completes, using
the trailing header byte as the prefetch window the protocol provides
* command space serves EXI regs 0x02/0x03 (interrupt mask/status), the only
two Swiss uses
* EXI_GetID gets its own path: it offers just ONE header byte of warning, so
the first ID byte is pushed the instant byte 0 identifies the query
Two timing subtleties, both found by simulation and commented in place:
* MISO is driven LIVE from the tx FIFO head, so a byte pushed while the pad
byte is on the wire appears in the pad slot. A filler byte is parked
there (MISO is don't-care through the header) so the first real response
lands in data slot 0.
* the prefetch must be one-shot; re-firing while waiting for the pad byte
consumed the register stream early and shifted reads by a byte.
All testbenches updated to the real format and passing: bba_register_file
(7), bba_top (8 incl. the full W5100->SPRAM->GC loop and TX), exi_j4,
exi_capture, spi_mode3_slave, tx_frame_drain, spram_arbiter,
rx_frame_assembler, w5100_parallel_master. gc_bba_test rebuilt against the
new protocol.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
re-bba-rb — on-console test suite (GameCube)
A .dol you run from Swiss that drives the adapter in Serial Port 1 and
reports pass/fail per test on the TV.
Build
Needs devkitPro's gamecube-dev group (devkitPPC + libogc). The devcontainer
installs it — if DEVKITPPC is unset, rebuild the container, since the
toolchain is added in .devcontainer/Dockerfile.
make # -> gc_bba_test.dol
Copy the .dol to your SD card (SD Gecko / SD2SP2) and launch it from Swiss.
Controls
| Button | |
|---|---|
| D-pad up/down | move |
| A | open a suite / rerun the selected test |
| X | run the selected suite |
| START | run everything |
| Y | clear all results |
| B | back |
What it tests
| Suite | Proves |
|---|---|
| Identity | device enumerates: 04 02 02 00, HIPR, NWAYS |
| Registers | PAR0–5, IMR writable and read back; IR is write-1-clear |
| Reset/control | NCRA RESET self-clears, SR holds |
| EXI integrity | 200 reads with no bit errors; which EXI clock indices work |
| Ethernet | MAC readback, 60-byte ARP transmit (TI), frame receive (RWP/RI) |
| SPRAM/DMA | ring descriptor is well-formed; reads ≥ 0x100 stream until CS |
Things that will bite you
The device ID is a ONE-SHOT. Address 0 returns 04 02 02 00 only until
the first NCRA reset clears id_phase in the gateware; after that it reads
back NCRA. That is correct — it matches the GC's own init order — but it means
the Identity test can only pass once per console power cycle. Run it before
the Reset suite. On a re-run the app reports SKIP with an explanation rather
than a misleading failure.
TI is not proof of transmission. It only means TXFrameDrain handed the
frame to the W5100. A frame can set TI and never reach the wire. The TX test
says so; to confirm the wire, capture on a PC or watch the RX test pick up a
reply.
The RX test needs traffic. It waits ~2 s for any frame. On an isolated
link with nothing talking it will fail legitimately — give the PC an address on
the same subnet (the ARP the TX test sends is aimed at 10.0.0.50, so
sudo ip addr add 10.0.0.50/24 dev <nic> makes the PC answer it and both tests
pass together).
Never over-clock a register read. Asking for N bytes and clocking more
leaves stale prefetched bytes queued and desynchronises the next transaction
— a deterministic every-Nth-read corruption that looks like a signal-integrity
fault. bba_read() clocks exactly len; don't "round up" for convenience.
TXDATA takes one byte per transaction. The register file's effective
address auto-increments per data byte, so a 4-byte write to 0x48 would spill
into 0x49–0x4B. bba_tx_bytes() does it correctly.
Build status
Builds clean (no warnings) with devkitPPC 16.1.0 / GCC 16 in the devcontainer:
gc_bba_test.dol, ~172 KB, entry 0x80003100.
.gitignore excludes the build products — run make to produce the .dol.