feat(serial): support disconnected Joybus configuration and local registers

This commit is contained in:
0xrsydn 2026-10-07 06:36:54 +07:00
commit 7546d17630
15 changed files with 462 additions and 62 deletions

View file

@ -75,11 +75,13 @@ The repository is a Cargo workspace with three crates:
| `crates/gba-core/tests/pulse_sound/channel2.rs` | Channel 2 mapping, register gaps, independent state, shared clocks, and sum-before-rounding mixing |
| `crates/gba-core/tests/direct_sound.rs` | FIFO lanes/reset, signed mixing, timer selection, DMA refill, and HALT/STOP |
| `crates/gba-core/src/memory/audio_step_tests.rs` | Audio access phases, shadow validation, and CPU/DMA failure rollback |
| `crates/gba-core/src/io/serial.rs` | Disconnected normal serial shifter, internal clocks, external waiting, idle multiplayer registers, GPIO latches, and live-write validation |
| `crates/gba-core/src/io/serial.rs` | Disconnected normal serial shifter, internal clocks, external waiting, idle multiplayer and local Joybus registers, GPIO latches, and live-write validation |
| `crates/gba-core/tests/inactive_devices.rs` | Device masks, wave RAM, GPIO pull-ups, serial data, and failed CPU/DMA write isolation |
| `crates/gba-core/tests/serial_control.rs` | RCNT mode gating, inactive bits, byte/halfword lanes, CPU/DMA diagnostics, and interrupt isolation |
| `crates/gba-core/tests/serial_external.rs` | External-clock waiting, cancellation, HALT/STOP, ARM/Thumb/DMA starts, and atomic diagnostics |
| `crates/gba-core/tests/serial_internal.rs` | Nominal bit edges, widths/rates, cancellation, completion IRQs, HALT/STOP, and live-write limits |
| `crates/gba-core/tests/serial_joybus.rs` | Local Joybus masks/data, pending status, inactive IRQs, mode gating, HALT/STOP, DMA, and padding diagnostics |
| `crates/gba-core/src/memory/joybus_step_tests.rs` | Joybus ARM/Thumb stores, staged reads, normal completion boundaries, and failed block-store isolation |
| `crates/gba-core/tests/serial_multiplayer.rs` | Disconnected child status, receive/send lanes, mode aliases, ignored child start, HALT/STOP, and ARM/Thumb/DMA access |
| `crates/gba-core/src/memory/multiplayer_step_tests.rs` | Multiplayer mode changes at normal completion, block-transfer preflight, staged reads, and atomic diagnostics |
| `crates/gba-core/src/memory/serial_step_tests.rs` | Serial bus-phase observations, preflight validation, DMA, IF ordering, capture isolation, and rollback |

View file

@ -1,6 +1,6 @@
# Disconnected serial subset
The core supports disconnected normal-mode transfers, external-clock waiting, idle multiplayer configuration, and general-purpose pins.
The core supports disconnected normal-mode transfers, external-clock waiting, idle multiplayer, local Joybus registers, and general-purpose pins.
Internal clocks shift pulled-high input at nominal GBA rates. No link partner is emulated.
External clock edges, connected multiplayer transfers, UART, Joybus communication, and GPIO interrupts remain unsupported.
Unsupported activity remains diagnostic rather than reporting a fabricated completion.
@ -16,13 +16,12 @@ Unsupported activity remains diagnostic rather than reporting a fabricated compl
Completion or software cancellation clears start. Multiplayer uses the separate format described below; UART remains diagnostic.
- `RCNT` at `0x04000134` retains mode bits, interrupt configuration, output latches, and pin directions with mask `0xc1ff`.
With bit 15 clear, bits 8 and 14 are writable latches without GPIO interrupt or Joybus effects.
With bit 15 set, bit 14 selects Joybus; otherwise bit 8 enables GPIO interrupts. Both active configurations remain diagnostic.
With bit 15 set, bit 14 selects Joybus; bit 8 is unused in that mode.
With bit 15 set and bit 14 clear, bit 8 enables GPIO interrupts, which remain diagnostic.
In general-purpose mode, each input reads high through its pull-up. Each output reads its output latch.
`RCNT=0x8000` therefore reads back as `0x800f`, not `0x8000`.
Normal and multiplayer low-byte pin readback remains unmapped rather than guessed.
- `JOYCNT` permits clearing already-empty status flags. Joybus interrupt enable is unsupported.
- `JOY_RECV` and `JOY_TRANS` permit only zero writes and zero readback for reset initialization.
Nonzero writes return an explicit error. Joybus status and data-transfer side effects are not implemented.
Normal, multiplayer, and Joybus low-byte pin readback remains unmapped rather than guessed.
- `JOYCNT`, `JOY_RECV`, `JOY_TRANS`, and `JOYSTAT` support the disconnected configuration described below.
GPIO means general-purpose input/output. UART means universal asynchronous receiver/transmitter.
No external serial device is connected. Only an enabled internal serial clock can shift data or generate a completion IRQ.
@ -86,7 +85,7 @@ The actual transfers then execute once from the original state. Other IF sources
### Bounded behavior
After any bit has shifted, changes to width, clock selection/rate, or the selected data register remain diagnostic while start stays set.
Selecting GPIO during an internally clocked request also remains diagnostic. Clear start before these operations.
Selecting GPIO or Joybus during an internally clocked request also remains diagnostic. Clear start before these operations.
IRQ-enable and idle SO configuration writes remain supported without restarting progress.
These limits also apply to BIOS services that write serial registers; cancel active transfers before requesting serial reset.
Unverified live reconfiguration is not silently approximated.
@ -141,11 +140,52 @@ Original tests cover control masks, all baud selections, register lanes, aliases
Bus-phase tests cover normal completion, retryable diagnostics, block loads/stores, and mode-dependent staged reads.
There is no parent, cable topology, external clock input, multiplayer timeout, or linked-game compatibility claim.
## Disconnected Joybus configuration
RCNT bits 14–15 both set select Joybus. The GBA is a slave; an external master must initiate communication.
This subset has no master, cable, commands, or external clock input. Selecting the mode does not start communication.
RCNT bit 8 remains a writable inactive latch, not a GPIO interrupt enable.
RCNT low-byte pin samples remain unmapped: GPIO output latches must not substitute for unverified Joybus pin levels.
SIOCNT is unused in Joybus. Its existing format latches remain readable without synthetic SI/SD status or internal shifting.
UART-format writes still return the existing diagnostic, including while Joybus is selected.
- `JOYCNT` at `0x04000140` retains interrupt enable in bit 6. Unused bits read zero.
Reset, receive-complete, and send-complete flags in bits 0–2 are write-one-to-clear.
They remain zero because no master can produce those events. Enabling interrupts cannot itself request IF bit 7.
- `JOY_RECV` at `0x04000150` and `JOY_TRANS` at `0x04000154` retain independent 32-bit data latches.
Byte, halfword, and word accesses preserve other lanes. Synthetic initial data is zero, not received peer data.
Local receive writes do not set receive status or a completion flag.
- `JOYSTAT` at `0x04000158` retains software flags in bits 4–5.
Every local transmit-data write sets bit 1, including a zero write or a write to an upper lane.
This means a reply is available for a master; it does not mean transmission completed.
Software cannot directly set or clear hardware status bits 1/3 by writing JOYSTAT.
Receive-pending bit 3 remains zero. Receive reads therefore have no observable read-clear effect in this disconnected subset.
Transmit pending survives elapsed time, mode changes, status writes, and empty-event acknowledgements.
No Joybus event wakes HALT or STOP. Joybus selection disables the normal serial clock even if SIOCNT retains start.
The existing live normal-transfer limits still apply when entering Joybus. Clear start or wait for normal completion first.
A normal completion at the write's bus phase remains pending in IF after the mode change.
Registers participate in serial transactions, including failed CPU/DMA access rollback and block-store preflight.
Word accesses to JOYCNT and JOYSTAT reject unmapped upper padding without partial writes.
Local Joybus registers remain accessible in other modes under this bounded latch model.
Receive writes follow GBATEK's R/W description; the pinned mGBA implementation leaves them unhandled rather than storing them.
mGBA corroborates JOYCNT/JOYSTAT masks in Joybus and sets transmit pending from either halfword outside mode dispatch.
Cross-mode masks, CPU receive writes, synthetic reset values, and byte-lane behavior lack physical-hardware validation.
These choices do not establish GameCube communication, hardware reset accuracy, or linked-game compatibility.
Original tests cover masks, lanes, pending status, mode gating, inactive IRQ bits, HALT/STOP, and ARM/Thumb/DMA accesses.
Staged tests cover completion boundaries, local readback, rollback, and later block-store failures.
Original desktop programs also check status/data through terminal and native-window execution.
## BIOS reset
RegisterRamReset always clears the low halfword of SIODATA32, even without bit 5.
The upper halfword remains unchanged in this functional subset.
With bit 5 selected, the firmware clears SIOCNT and SIODATA8, selects general-purpose inputs, and clears the supported Joybus reset registers.
With bit 5 selected, the firmware clears SIOCNT and SIODATA8 and selects general-purpose inputs.
It clears JOYCNT interrupt enable and both Joybus data latches.
Its zero write to JOY_TRANS sets transmit pending. It does not write JOYSTAT; software flags remain unchanged.
This is the replacement's functional sequence, not a claim about complete hardware BIOS reset side effects.
It uses ordinary ARM bus stores, not a host-side reset bypass.
Clearing SIOCNT cancels a pending external-clock request. Unselected serial reset leaves that request pending.
The firmware writes SIODATA8 after selecting normal format. This does not clear the retained upper multiplayer SEND byte.
@ -163,6 +203,8 @@ Inactive configuration bits do not wake HALT. RCNT word accesses still reject th
- [GBATEK normal serial mode](https://problemkaputt.de/gbatek-sio-normal-mode.htm): data widths, MSB-first shifting, rates, pulled-high SI, start/completion, IRQ selection, and external-clock waiting.
- [GBATEK multiplayer mode](https://problemkaputt.de/gbatek-sio-multi-player-mode.htm): child start restrictions, SI/SD status, undefined pre-transfer ID, register widths, and R/W receive registers.
- [LinkRawCable at c61bf351](https://github.com/afska/gba-link-connection/blob/c61bf351f68ad2d6e1c9d72d70e21bec19adfc0b/lib/LinkRawCable.hpp): public API usage for mode selection, SEND, receive words, and separate role/readiness/ID checks. Its disconnected-cable warning is not a pin-level measurement.
- [GBATEK Joybus mode](https://problemkaputt.de/gbatek-sio-joy-bus-mode.htm): slave-only communication, RCNT/SIOCNT roles, local register masks, R/W data, and status side effects.
- [mGBA I/O implementation at 3a5e34be](https://github.com/mgba-emu/mgba/blob/3a5e34be33dc7f8f707e5bc9db69e8a430046f21/src/gba/io.c): transmit-pending writes and receive-status read clearing; compare with masks and unhandled receive writes in its serial implementation below.
- [GBATEK mode summary](https://problemkaputt.de/gbatek-sio-control-registers-summary.htm): RCNT/SIOCNT mode selection.
- [mGBA serial implementation at 3a5e34be](https://github.com/mgba-emu/mgba/blob/3a5e34be33dc7f8f707e5bc9db69e8a430046f21/src/gba/sio.c): cross-checked normal transfer-duration arithmetic and completion IRQ handling. Its no-driver scheduled completion and zero receive data are not copied; they do not establish disconnected hardware behavior. Multiplayer comparison covers SI/SD defaults, receive-write disagreement, and uncertain floating SC timing.
- [GBATEK](https://mgba-emu.github.io/gbatek/): GBA general-purpose pin directions, internal pull-ups, SI falling-edge interrupts, and the SIO mode-selection table.

View file

@ -26,7 +26,7 @@ Use a local ROM that you may lawfully use:
```sh
direnv exec . cargo run --locked --release -- --rom roms/pokemon-emerald.gba --rtc --save-type flash128 --steps 3000000
direnv exec . cargo run --locked --release -- --rom roms/pokemon-emerald.gba --rtc --save-type flash128 --window --frames 60
direnv exec . cargo run --locked --release -- --rom roms/pokemon-emerald.gba --rtc --save-type flash128 --window --frames 600
```
The earlier reset-only implementation stopped at master sound enable (`0x04000084`, PC `0x082e0518`).
@ -36,7 +36,8 @@ Both pulse channels then reached the noise trigger (`0x0400007d`, PC `0x082e03c4
Noise channel 4 support then reached an incorrectly rejected RCNT write: `0x0100` at `0x04000134`, PC `0x082e2aac`.
RCNT bit 15 was clear, so bit 8 did not enable general-purpose input/output (GPIO) interrupts.
GBATEK documents bits 8 and 14 as writable but unused in normal mode.
The serial model now retains these inactive bits. Effective GPIO interrupt enable and Joybus selection still return explicit errors.
The serial model now retains these inactive bits. Effective GPIO interrupt enable still returns an explicit error.
Joybus configuration was added later, as described below.
That correction reached `read-only memory at 0x00000000` inside the original BIOS replacement, PC `0x00000378`.
A bounded local trace found a Thumb `CpuSet` call with source `0x02022a74`, destination zero, and count `0x1c` halfwords.
@ -77,19 +78,34 @@ Select `--save-type flash128` for this bounded run; the device begins erased and
A later redundant `0xf0` write occurred after returning to idle array mode.
The subset accepts that no-op without claiming direct Flash128 ID exit or busy-command cancellation.
Both terminal and native runs with these options now reach Joybus mode selection:
Flash identification then reached Joybus selection (`RCNT=0xc000`, PC `0x082deeca`) after sixteen captured frames.
The [disconnected Joybus subset](../hardware/serial.md#disconnected-joybus-configuration) now accepts local configuration and status/data accesses.
No master, command, remote receive word, or completion IRQ is fabricated.
The terminal run reaches its requested three-million-step limit with exit status zero:
```text
Steps: 1389249; instructions: 1386088; IRQ entries: 89; DMA units: 3072; HALT idle: 0
Nominal cycles: 4688039
PC=0x082deeca
unsupported Joybus serial mode: write 0xc0 at 0x04000135
Result: step limit reached
Steps: 3000000; instructions: 2985620; IRQ entries: 124; DMA units: 14256; HALT idle: 0
Nominal cycles: 9626104
PC=0x080008cc
```
The native run captures sixteen startup frames. These do not establish a working title screen or gameplay.
Both 60-frame and 600-frame native runs now reach their frame limits with exit status zero.
The longer run reports:
```text
Result: window frame limit reached
Captured ROM frames: 600
Steps: 58456876; instructions: 58087737; IRQ entries: 1251; DMA units: 367888; HALT idle: 0
Nominal cycles: 168453824
PC=0x080008ca
```
These runs establish bounded execution without a diagnostic, not visible menu progress or gameplay.
No scripted input or pixel assertions were used. Repeated frames can still come from a waiting loop.
Audio-device behavior is validated by original tests, not by audible game output.
The window run does not reach its requested frame limit and correctly exits with failure.
Terminal execution is also diagnostic, not a compatibility pass.
Host RTC values can change between runs; these totals are observations, not deterministic golden assertions.
Results apply to this local file; no cartridge revision or checksum was inferred from its filename.
## Validation and next requirement
@ -98,8 +114,9 @@ Workspace debug/release tests, strict Clippy, public ARM/Thumb/memory/BIOS check
Native ROM-window tests and graphics smoke modes pass on Darwin arm64.
The pinned Pong debug/release reports match.
The next startup requirement is disconnected Joybus configuration, beginning with `RCNT=0xc000`.
Research idle registers, absent-device status, and mode transitions before implementing them. Do not fabricate a connected peer.
Next, inspect visible startup/menu progress and exercise button presses and releases in a bounded run.
Identify any waiting loop or new diagnostic before selecting another compatibility correction.
Do not treat frame-limit success as a title-screen or gameplay pass.
Flash programming, erase, busy behavior, and safe persistence remain required before claiming save compatibility.
RTC persistence and cartridge IRQ behavior remain separate missing features.
Ordinary ROM writes remain read-only. Without `--rtc`, the earlier GPIO diagnostic is still expected.

View file

@ -85,6 +85,7 @@ Update this file when a feature lands or a limit is removed.
- Raw ROM-file loading with original BIOS boot, terminal diagnostics, and final CPU/step reports.
- Firmware boot initializes BG2/BG3 identity scales for bitmap and affine programs that do not program their own matrices.
- RegisterRamReset supports disabled sound and disconnected serial initialization, including the common `r0=0xff` request.
- Disconnected Joybus selection, local data/status registers, and transmit-pending state without invented remote events or IRQs.
- Direct Sound A/B support timer-driven FIFO playback, stereo digital levels, and DMA1/A or DMA2/B refill blocks.
- PSG pulse channels 1 and 2 support independent nominal frequency/duty playback, length, envelope, status, and stereo mixing. Channel 1 also supports sweep.
- Noise channel 4 supports nominal 7/15-bit generation, divider clocks, shared length/envelope behavior, status, and stereo mixing.
@ -157,8 +158,9 @@ The [serial subset](hardware/serial.md) supports disconnected normal 8/32-bit tr
Internal clocks shift high input at nominal rates, clear busy, and request completion IRQs. External requests retain busy without clock edges.
Disconnected multiplayer exposes child/ready status and 16-bit send/receive registers without inventing transfers or completion IRQs.
Pre-transfer ID is a zero placeholder; receive-write behavior and reset/alias details lack physical-hardware validation.
Connected links, multiplayer transfers, UART, Joybus, GPIO interrupts, and unverified mid-transfer reconfiguration remain unsupported.
RCNT retains inactive bits 8/14 in normal mode. Effective GPIO interrupt enable and Joybus selection remain diagnostic.
Joybus supports local configuration, data latches, and transmit-pending status, without an external master or completion IRQs.
Connected links, multiplayer transfers, UART, Joybus commands, GPIO interrupts, and unverified mid-transfer reconfiguration remain unsupported.
RCNT retains inactive bits 8/14 in normal mode and bit 8 in Joybus. Effective GPIO interrupt enable remains diagnostic.
Mapped I/O covers DMA, timers, interrupts, WAITCNT, display control/status, background control/scroll/affine registers, window/effect registers, KEYINPUT/KEYCNT, POSTFLG, and HALTCNT.
Keypad IRQs use a documented polling model; hardware retrigger edges and asynchronous input timing remain unverified.
STOP implements keypad wake only. Serial/Game Pak wake sources, oscillator restart delays, and exact wake timing remain unimplemented or unverified.
@ -174,7 +176,9 @@ It also accepts an external-clock serial request without inventing clock edges o
It now also completes internally clocked normal transfers and selects idle multiplayer mode.
With explicit `--rtc` selection, it also passes Game Pak GPIO configuration and RTC calendar reads.
With `--save-type flash128`, it also passes Flash identification and reaches a later startup check.
It next stops at Joybus mode selection (`0x04000135`, PC `0x082deeca`). Flash writing and persistence remain unimplemented.
It now passes disconnected Joybus setup and completes a bounded 600-frame native run without a diagnostic error.
This run uses no scripted input or pixel assertions; menu progress and gameplay remain unverified.
Flash writing and persistence remain unimplemented.
Without `--rtc`, cartridge GPIO writes still return read-only diagnostics.
No title-screen, gameplay, or audible-game compatibility is established.
See [the startup result](research/emerald-reset.md) for the bounded run and next investigation.

View file

@ -311,6 +311,9 @@ Bus-phase tests cover start/load timing, IF acknowledgement order, CPU/DMA rollb
Disconnected multiplayer tests cover every control value in that format, all baud settings, send/receive lanes, and normal-mode aliases.
They verify idle child status, ignored start, no completion IRQ or HALT wake, GPIO sampling, and ARM/Thumb/DMA stores.
Staged tests check mode changes at normal completion, block loads/stores, rollback, and reads after leaving multiplayer format.
Joybus tests cover mode selection, local control/data/status lanes, transmit pending, and absence of remote events or IRQs.
They check HALT/STOP, BIOS reset stores, unmapped padding, ARM/Thumb/DMA accesses, staged reads, and failed block-store rollback.
Original desktop programs check Joybus status and reply data in both terminal and native-window execution.
These tests validate the documented bounded serial model, not physical pin timing, receive-write behavior, or linked-game compatibility.
Timer/IRQ tests verify that changing WAITCNT changes when an interrupt is sampled.
Timer tests cover all prescalers, reload changes, start/stop behavior, byte writes, large clock advances, and cascades.

View file

@ -42,10 +42,11 @@ Frame-based scenarios and the preparation adapter now make the result repeatable
A local Emerald run now passes its initial all-device reset, master sound enable, and pulse/noise triggers.
It also passes normal serial transfers and idle multiplayer configuration.
With `--rtc --save-type flash128`, it passes Game Pak GPIO setup, RTC reads, and Flash identification.
It next stops at Joybus serial-mode selection.
It also passes disconnected Joybus configuration and completes a bounded 600-frame native run without a diagnostic error.
That run does not verify screen contents, button responses, or gameplay.
The core has tested Direct Sound, both pulse channels, and noise channel 4.
Wave channel 3, a continuous sample stream, and desktop audio output remain missing.
See [the runtime result](research/emerald-reset.md). Disconnected Joybus configuration is the next demonstrated startup requirement.
See [the runtime result](research/emerald-reset.md). Next, verify visible menu progress and button responses rather than treating frame counts as playability.
The separate save requirement includes Flash programming/erase and safe host persistence. Identification/read support is not successful saving.
SRAM is byte-addressable battery-backed save memory. Flash and EEPROM remain separate protocols.
@ -57,7 +58,8 @@ A reproducible source build or an accessible upstream ROM is needed before recor
Next implementation steps:
1. Research the demonstrated Joybus configuration failure without inventing a connected device.
1. Inspect visible startup/menu progress and exercise input in the bounded local game run. Record the first demonstrated failure.
Use original regressions for any hardware correction; do not invent connected serial devices.
2. Extend explicit Flash selection with verified programming, erase, and busy behavior. Keep absent devices and unsupported commands diagnostic.
Add SRAM separately when the independent candidate requires it; verify its widths, mirroring, and wait behavior first.
3. Add bounded save loading and safe persistence in the desktop crate. Do not put file operations in the core.