feat(bios): support sound and serial register reset

This commit is contained in:
0xrsydn 2026-10-07 00:44:46 +07:00
commit d6a47a3e12
14 changed files with 278 additions and 47 deletions

49
docs/hardware/audio.md Normal file
View file

@ -0,0 +1,49 @@
# Sound initialization subset
Sound synthesis and host audio output are not implemented.
The core now models disabled sound-register state so firmware can perform a functional sound reset.
This is not a silent-audio compatibility mode. Enabling sound returns an explicit diagnostic.
## Supported state
| Address | Behavior |
| --- | --- |
| `0x04000060..0x04000081` | Programmable sound generator (PSG) registers remain zero; writes have no effect while master sound is disabled |
| `0x04000082` (`SOUNDCNT_H`) | Retains mixing/routing/timer configuration with readback mask `0x770f` |
| `0x04000084` (`SOUNDCNT_X`) | Reads zero; status flags are read-only; a write setting bit 7 fails |
| `0x04000088` (`SOUNDBIAS`) | Retains bias/resolution configuration with mask `0xc3fe`; no analog output is generated |
| `0x04000090..0x0400009f` | Read/write wave RAM in the CPU-accessible inactive bank |
Unused mapped bytes through `0x0400008b` read zero and ignore writes.
The FIFO reset strobes in `SOUNDCNT_H` do not latch. No FIFO data can be queued in this subset.
FIFO addresses and the remaining sound-region holes stay unmapped.
All sound configuration starts at zero in raw memory. RegisterRamReset bit 6 sets the functional bias default to `0x0200`.
With master sound disabled, `SOUND3CNT_L` stays zero. The CPU accesses wave bank 1.
Bank 0 cannot be selected or played and remains zero. Disabling master sound does not erase wave RAM.
The BIOS reset explicitly clears the accessible wave RAM through ordinary stores.
Future bank switching and channel playback need a complete sound engine, not a larger register array.
## Diagnostics and atomicity
A write setting `SOUNDCNT_X` bit 7 returns `MemoryError::UnsupportedIo`, including the address, byte value, and operation.
Status bits and unused bits alone do not enable sound.
Validation occurs before any bytes in the access are written.
Block stores validate every register value before committing earlier writes. Failed CPU and DMA steps remain retryable.
Original tests cover disabled-register writes, masks, byte lanes, wave retention, and BIOS reset selection.
They also check rejected sound activation through byte/word accesses, ARM block stores, and DMA without partial state changes.
## Remaining work
Implement channel clocks, length/envelope/sweep state, wave banking, FIFO consumption, timer-triggered sound DMA, and mixing.
Add a host-independent sample interface before connecting a desktop audio backend.
Do not acknowledge active sound requests without implementing their device behavior.
## References
- [GBATEK sound control registers](https://problemkaputt.de/gbatek-gba-sound-control-registers.htm): disabled PSG behavior, master enable, masks, FIFO reset strobes, and bias.
- [GBATEK](https://mgba-emu.github.io/gbatek/): channel 3 wave-bank access and register layout.
- [mGBA BIOS service implementation at 3a5e34be](https://github.com/mgba-emu/mgba/blob/3a5e34be33dc7f8f707e5bc9db69e8a430046f21/src/gba/bios.c): functional reset defaults. No upstream implementation was copied.
Reset values are source-backed. Exact Nintendo firmware ordering, timing, and analog behavior are not established.

View file

@ -40,7 +40,7 @@ Supported software interrupt services:
| Number | Service | Behavior |
| --- | --- | --- |
| `0x00` | SoftReset | Clear BIOS work RAM and reset CPU registers/stacks; restart in ROM or RAM without returning |
| `0x01` | RegisterRamReset | r0 selects RAM and supported I/O resets; force blank; reject serial/sound flags |
| `0x01` | RegisterRamReset | r0 selects RAM and supported I/O resets, including disabled sound and disconnected serial state |
| `0x02` | Halt | Wait for `IE & IF`; preserve IME and caller registers |
| `0x03` | Stop | Freeze system clocks until an enabled live keypad condition wakes the system; preserve caller state |
| `0x04` | IntrWait | `r0`: discard old selected flags when nonzero; `r1`: flags to wait for |
@ -215,21 +215,26 @@ Supported flags can be combined:
| 2 | Clear palette RAM: `0x05000000..0x050003ff` |
| 3 | Clear all 96 KiB of video RAM: `0x06000000..0x06017fff` |
| 4 | Clear sprite attribute memory (OAM): `0x07000000..0x070003ff` |
| 5 | Serial reset: unsupported |
| 6 | Sound reset: unsupported |
| 5 | Reset supported idle serial state and select general-purpose inputs |
| 6 | Reset disabled sound state, mixing control, bias, and accessible wave RAM |
| 7 | Reset supported display, DMA, timer, and interrupt registers as described below |
**Unsupported flags:** any request containing bit 5 or 6 reaches `bios::INVALID_ARGUMENT_TRAP`.
Validation occurs before forced blank or RAM/I/O reset writes, but after the common SWI stack save.
This includes the common all-devices request `r0=0xff`; it is not silently treated as a successful full reset.
Serial and audio devices remain unimplemented. The firmware's unconditional serial-data side effect is not reproduced.
All low-byte flag combinations, including `r0=0xff`, are accepted within the supported device subset.
This is not full audio or serial support. [Sound activation](audio.md) and [serial transfers](serial.md) still fail explicitly.
The firmware always clears the low halfword of SIODATA32, even when bit 5 is clear; its upper halfword is preserved.
Every supported request first writes `DISPCNT=0x0080`, including requests with no selected flags.
This forces a white screen and clears other display-control bits.
RAM clears then run in bit order, using ordinary word stores.
Zeroed OAM contains regular sprites at the origin, not disabled sprites; software must configure them before enabling objects.
Bit 7 runs after the selected RAM clears:
After the RAM clears, bit 5 clears SIOCNT and SIODATA8, writes RCNT=0x8000, and clears supported Joybus reset registers.
Disconnected general-purpose inputs read high, so RCNT reads back as 0x800f.
Bit 6 writes SOUNDCNT_X=0, SOUNDCNT_H=0, and SOUNDBIAS=0x0200, then clears the accessible wave RAM bank.
Disabled PSG registers remain zero. The other wave bank cannot be enabled in the current subset and remains zero.
Reset does not produce audio, fabricate serial transfers, or bypass normal bus accesses.
Bit 7 runs after the selected RAM and sound/serial resets:
- Disable IME.
- Disable all four DMA channels, then zero their source, destination, count, and control registers.
@ -244,7 +249,7 @@ Read-only display status and VCOUNT continue to reflect the advancing display cl
GREENSWAP, KEYINPUT/button state, KEYCNT, POSTFLG, and BIOS IRQ communication words are not reset.
Bit 7 covers only the listed registers. A later matching input sample can request keypad IRQ again after IF acknowledgement.
Without bit 7, device configuration remains unchanged except for DISPCNT.
Without their corresponding reset flags, device configuration remains unchanged except for DISPCNT and the SIODATA32 low halfword.
IRQ delivery stays CPU-masked during the call. The service restores the caller's mask on return.
Timers and DMA continue until their reset writes, if selected; this service does not stop clocks or replace frame-capture history.
Pending IRQs remain pending without bit 7 and can be delivered after return.
@ -504,5 +509,5 @@ Unsupported SWIs, unsupported exception vectors, and null/misaligned IRQ callbac
The CPU reports `CpuError::UnsupportedInstruction` with `bios::UNSUPPORTED_TRAP`, rather than silently treating a service as a no-op.
Invalid arithmetic/decompression arguments use the same CPU error type with the distinct `bios::INVALID_ARGUMENT_TRAP` instruction.
Prior boot/service steps remain committed on failure.
RegisterRamReset serial/sound flags, HardReset, and serial/Game Pak STOP wake-up remain unimplemented.
Active sound, serial transfers, HardReset, and serial/Game Pak STOP wake-up remain unimplemented.
This subset is not sufficient for Pokémon Emerald compatibility.

View file

@ -38,7 +38,8 @@ Cartridge writes, save devices, and cartridge peripherals remain unsupported.
Both modes call `bios::boot` with the loaded bytes.
The [original BIOS replacement](bios.md) executes its minimal boot before entering `0x08000000` in ARM System mode.
There is no command-line option for external BIOS files.
Unsupported services remain diagnostics. In particular, RegisterRamReset sound/serial flags, including `r0=0xff`, still fail.
Unsupported services remain diagnostics. RegisterRamReset now accepts sound/serial reset flags, including `r0=0xff`, within the initialization subset.
Active sound and serial transfers still fail with device-specific diagnostics; accepting reset does not establish game compatibility.
## Terminal execution limits and results

43
docs/hardware/serial.md Normal file
View file

@ -0,0 +1,43 @@
# Disconnected serial initialization subset
The core supports idle normal-mode data registers and disconnected general-purpose pins.
It does not support link transfers, multiplayer, UART, Joybus communication, or external serial interrupts.
Unsupported activity remains diagnostic rather than reporting a fabricated completion.
## Registers
- `SIODATA32` at `0x04000120` is a four-byte latch in the supported normal-mode subset.
- `SIODATA8` at `0x0400012a` retains its low byte; its upper byte reads zero.
- `SIOCNT` at `0x04000128` retains normal 8/32-bit mode, IRQ configuration, clock selection, and the output-data bit.
The retained mask is `0x500b`. The disconnected serial input is high, except when a general-purpose output drives it low.
Transfer-start bit 7 and multiplayer/UART mode bit 13 return explicit errors.
- `RCNT` at `0x04000134` retains general-purpose mode, output latches, and pin directions with mask `0x80ff`.
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`.
Joybus mode and GPIO interrupt enable return errors. Normal-mode 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.
GPIO means general-purpose input/output. UART means universal asynchronous receiver/transmitter.
No external serial device is connected. Elapsed cycles alone do not change serial data or generate an IRQ.
Configuration that hardware accepts in other modes can still be rejected by this intentionally limited subset.
## 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.
It uses ordinary ARM bus stores, not a host-side reset bypass.
Original tests cover independent byte lanes, input pull-ups, output directions, reset flag selection, and explicit unsupported operations.
Whole-access and block-store validation prevent a rejected control value from committing preceding bytes or registers.
## References
- [GBATEK reset functions](https://problemkaputt.de/gbatek-bios-reset-functions.htm): reset flags, general-purpose selection, and the unconditional SIODATA32 side effect.
- [GBATEK normal serial mode](https://problemkaputt.de/gbatek-sio-normal-mode.htm): data widths, input status, and control bits.
- [GBATEK](https://mgba-emu.github.io/gbatek/): general-purpose pin directions and internal pull-ups.
- [mGBA BIOS implementation at 3a5e34be](https://github.com/mgba-emu/mgba/blob/3a5e34be33dc7f8f707e5bc9db69e8a430046f21/src/gba/bios.c): functional serial reset defaults, not copied source.
Exact firmware write ordering and hardware pin timing remain unverified.