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

View file

@ -54,6 +54,8 @@ Use `--test-suite suite.json` for [repeatable headless ROM assertions](docs/rom-
| [docs/hardware/bios.md](docs/hardware/bios.md) | The original BIOS replacement and each supported service | | [docs/hardware/bios.md](docs/hardware/bios.md) | The original BIOS replacement and each supported service |
| [docs/hardware/timers-irq.md](docs/hardware/timers-irq.md) | Timers, interrupt registers, HALT/STOP, and power control | | [docs/hardware/timers-irq.md](docs/hardware/timers-irq.md) | Timers, interrupt registers, HALT/STOP, and power control |
| [docs/hardware/dma.md](docs/hardware/dma.md) | DMA channels, triggers, and timing | | [docs/hardware/dma.md](docs/hardware/dma.md) | DMA channels, triggers, and timing |
| [docs/hardware/audio.md](docs/hardware/audio.md) | Disabled sound initialization and remaining audio work |
| [docs/hardware/serial.md](docs/hardware/serial.md) | Disconnected serial initialization and transfer limits |
| [docs/hardware/display-timing.md](docs/hardware/display-timing.md) | Display clock, status, IRQs, and scanline capture | | [docs/hardware/display-timing.md](docs/hardware/display-timing.md) | Display clock, status, IRQs, and scanline capture |
| [docs/hardware/video.md](docs/hardware/video.md) | Backgrounds, sprites, mosaic, windows, color effects, and input | | [docs/hardware/video.md](docs/hardware/video.md) | Backgrounds, sprites, mosaic, windows, color effects, and input |
| [docs/references.md](docs/references.md) | External hardware references and emulator sources consulted | | [docs/references.md](docs/references.md) | External hardware references and emulator sources consulted |

View file

@ -52,8 +52,9 @@ pub fn boot(rom: Vec<u8>) -> Result<Machine, MemoryError> {
/// and registers other than each arithmetic service's documented outputs. /// and registers other than each arithmetic service's documented outputs.
/// SoftReset instead clears BIOS work RAM, resets specified CPU banks, and enters /// SoftReset instead clears BIOS work RAM, resets specified CPU banks, and enters
/// ROM or RAM in ARM System mode with IRQ masked. It does not reset devices. /// ROM or RAM in ARM System mode with IRQ masked. It does not reset devices.
/// RegisterRamReset clears selected RAM and supported I/O registers. Serial/sound /// RegisterRamReset clears selected RAM and supported I/O registers, including
/// reset flags reach a diagnostic before display or reset writes. /// disabled sound and disconnected serial reset state. Active audio/link operations
/// remain diagnostic in the device model.
/// IRQ callbacks must preserve r4–r11, acknowledge IF, update IRQ_FLAGS for wait /// IRQ callbacks must preserve r4–r11, acknowledge IF, update IRQ_FLAGS for wait
/// services, and return with BX lr. Nested IRQs and SWIs from callbacks are unsupported. /// services, and return with BX lr. Nested IRQs and SWIs from callbacks are unsupported.
/// Service routines use the Supervisor stack; exact firmware stack layout and /// Service routines use the Supervisor stack; exact firmware stack layout and

View file

@ -1,10 +1,13 @@
//! Original ARM SoftReset and selective RegisterRamReset services. //! Original ARM SoftReset and selective RegisterRamReset services.
//! Serial/sound reset requests are diagnostics until those devices are modeled. //! Sound and serial reset use the disabled/disconnected I/O subset.
use super::{ArmImage, IRQ_STACK, ROM_START, SVC_STACK, SYSTEM_STACK}; use super::{ArmImage, IRQ_STACK, ROM_START, SVC_STACK, SYSTEM_STACK};
use crate::{ use crate::{
dma::DMA_BASE, dma::DMA_BASE,
io::{DISPCNT, IE, IME, TIMER_BASE}, io::{
DISPCNT, IE, IME, JOYCNT, JOY_RECV, JOY_TRANS, RCNT, SIOCNT, SIODATA32, SIODATA8,
SOUNDBIAS, SOUNDCNT_H, SOUNDCNT_X, TIMER_BASE, WAVE_RAM,
},
}; };
pub(super) fn emit(a: &mut ArmImage) { pub(super) fn emit(a: &mut ArmImage) {
@ -47,14 +50,15 @@ pub(super) fn emit(a: &mut ArmImage) {
fn emit_ram_reset(a: &mut ArmImage) { fn emit_ram_reset(a: &mut ArmImage) {
a.label("register_ram_reset"); a.label("register_ram_reset");
// Validate unsupported devices before forced blank or any reset writes.
// Higher flag bits are ignored, as in the documented low-byte flag format. // Higher flag bits are ignored, as in the documented low-byte flag format.
a.emit(0xe310_0060); // TST r0,#serial|sound
a.branch(1, "invalid_argument");
a.literal(1, DISPCNT); a.literal(1, DISPCNT);
a.emit(0xe3a0_2080); // MOV r2,#forced blank (also clear other DISPCNT bits) a.emit(0xe3a0_2080); // MOV r2,#forced blank (also clear other DISPCNT bits)
a.emit(0xe1c1_20b0); // STRH r2,[r1] a.emit(0xe1c1_20b0); // STRH r2,[r1]
a.emit(0xe3a0_3000); // MOV r3,#0 (shared fill value) a.emit(0xe3a0_3000); // MOV r3,#0 (shared fill value)
// Documented BIOS side effect occurs even when serial reset is not selected.
a.literal(1, SIODATA32);
store_halfword(a, 0);
for (flag, start, bytes, next) in [ for (flag, start, bytes, next) in [
(1, 0x0200_0000, 0x40000, "ram_reset_iwram"), (1, 0x0200_0000, 0x40000, "ram_reset_iwram"),
(2, 0x0300_0000, 0x7e00, "ram_reset_palette"), (2, 0x0300_0000, 0x7e00, "ram_reset_palette"),
@ -67,6 +71,35 @@ fn emit_ram_reset(a: &mut ArmImage) {
clear_words(a, start, bytes); clear_words(a, start, bytes);
a.label(next); a.label(next);
} }
a.emit(0xe310_0020); // TST r0,#serial
a.branch(0, "ram_reset_sound");
a.literal(1, SIOCNT);
store_halfword(a, 0);
a.literal(1, SIODATA8);
store_halfword(a, 0);
a.literal(1, RCNT);
a.emit(0xe3a0_3902); // MOV r3,#0x8000 (GPIO, all pins inputs)
store_halfword(a, 0);
a.emit(0xe3a0_3000); // MOV r3,#0
a.literal(1, JOYCNT);
store_halfword(a, 0);
clear_words(a, JOY_RECV, 4);
clear_words(a, JOY_TRANS, 4);
a.label("ram_reset_sound");
a.emit(0xe310_0040); // TST r0,#sound
a.branch(0, "ram_reset_other");
a.literal(1, SOUNDCNT_X);
store_halfword(a, 0); // Disable sound; PSG register state is held in reset.
a.literal(1, SOUNDCNT_H);
store_halfword(a, 0);
a.literal(1, SOUNDBIAS);
a.emit(0xe3a0_3c02); // MOV r3,#0x200 (bias default)
store_halfword(a, 0);
a.emit(0xe3a0_3000); // MOV r3,#0
clear_words(a, WAVE_RAM, 16); // Accessible inactive bank; other bank cannot be enabled yet.
a.label("ram_reset_other");
a.emit(0xe310_0080); // TST r0,#other registers a.emit(0xe310_0080); // TST r0,#other registers
a.branch(0, "return"); a.branch(0, "return");

View file

@ -1,6 +1,5 @@
use super::*; use super::*;
use gba_core::{ use gba_core::{
bios::INVALID_ARGUMENT_TRAP,
dma::{DmaError, DMA_BASE, DMA_STRIDE}, dma::{DmaError, DMA_BASE, DMA_STRIDE},
input::Buttons, input::Buttons,
io::{ io::{
@ -185,34 +184,57 @@ fn internal_ram_clear_preserves_bios_work_area_except_common_swi_frame() {
} }
#[test] #[test]
fn all_serial_sound_requests_fail_before_forced_blank_or_memory_clear() { fn serial_sound_reset_flags_are_selective_and_all_devices_return() {
use gba_core::io::{
RCNT, SIOCNT, SIODATA32, SIODATA8, SOUNDBIAS, SOUNDCNT_H, SOUNDCNT_X, WAVE_RAM,
};
for thumb in [false, true] { for thumb in [false, true] {
for flags in (0..256).filter(|flags| flags & 0x60 != 0) { for flags in [
let (mut m, _) = prepare(flags, thumb, 0x1f); 0, 0x20, 0x40, 0x60, 0x80, 0xff, 0xffffff20, 0xffffff40, 0xffffffff,
] {
let (mut m, pc) = prepare(flags, thumb, 0x1f);
seed_samples(&mut m); seed_samples(&mut m);
m.memory_mut().write16(DISPCNT, 0x0403).unwrap(); let bus = m.memory_mut();
for _ in 0..1000 { bus.write32(SIODATA32, MARKER).unwrap();
let before = m.cpu().clone(); bus.write16(SIODATA8, 0x55).unwrap();
let cycles = m.cycles(); bus.write16(SIOCNT, 0x500b).unwrap();
let timing = m.last_timing(); bus.write16(RCNT, 0x80fa).unwrap();
if let Err(error) = m.step() { bus.write16(SOUNDCNT_H, 0xffff).unwrap();
assert!(matches!( bus.write16(SOUNDBIAS, 0xffff).unwrap();
error, for offset in 0..16 {
MachineError::Cpu(CpuError::UnsupportedInstruction { bus.write8(WAVE_RAM + offset, 0x5a).unwrap();
instruction: INVALID_ARGUMENT_TRAP, }
.. complete(&mut m, pc);
}) check_samples(&m, flags);
)); let bus = m.memory();
assert_eq!(m.cpu(), &before); assert_eq!(bus.read32(SIODATA32).unwrap(), MARKER & 0xffff0000); // Unconditional low-halfword clear.
assert_eq!(m.cycles(), cycles); assert_eq!(
assert_eq!(m.last_timing(), timing); bus.read16(SIODATA8).unwrap(),
assert_eq!(m.step(), Err(error)); if flags & 0x20 != 0 { 0 } else { 0x55 }
break; );
} assert_eq!(
bus.read16(SIOCNT).unwrap(),
if flags & 0x20 != 0 { 4 } else { 0x500b }
);
assert_eq!(
bus.read16(RCNT).unwrap(),
if flags & 0x20 != 0 { 0x800f } else { 0x80fa }
);
assert_eq!(
bus.read16(SOUNDCNT_H).unwrap(),
if flags & 0x40 != 0 { 0 } else { 0x770f }
);
assert_eq!(
bus.read16(SOUNDBIAS).unwrap(),
if flags & 0x40 != 0 { 0x200 } else { 0xc3fe }
);
assert_eq!(bus.read16(SOUNDCNT_X).unwrap(), 0);
for offset in 0..16 {
assert_eq!(
bus.read8(WAVE_RAM + offset).unwrap(),
if flags & 0x40 != 0 { 0 } else { 0x5a }
);
} }
assert!(m.step().is_err());
assert_eq!(m.memory().read16(DISPCNT).unwrap(), 0x0403);
check_samples(&m, 0);
} }
} }
} }

View file

@ -50,6 +50,8 @@ The repository is a Cargo workspace with three crates:
| `crates/gba-core/src/cpu/fetch_history_tests.rs` | Shared sample consumption, no execution-entry rereads, and failed-step retry | | `crates/gba-core/src/cpu/fetch_history_tests.rs` | Shared sample consumption, no execution-entry rereads, and failed-step retry |
| `crates/gba-core/src/cpu/fetch_boundary_tests.rs` | Actual fetch-region snapshots, split refills, IWRAM lane requirements, ROM-window boundaries, and nominal timing | | `crates/gba-core/src/cpu/fetch_boundary_tests.rs` | Actual fetch-region snapshots, split refills, IWRAM lane requirements, ROM-window boundaries, and nominal timing |
| `crates/gba-core/src/io.rs` | I/O registers, timers, HALT/STOP wake-up, next-event bounds, and IRQ latches | | `crates/gba-core/src/io.rs` | I/O registers, timers, HALT/STOP wake-up, next-event bounds, and IRQ latches |
| `crates/gba-core/src/io/inactive.rs` | Disabled sound and disconnected serial initialization with explicit unsupported-activity 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/src/io/timer_step.rs` | Staged timer bank, shared divider phase, and timer IF bits at ordered CPU/DMA bus phases | | `crates/gba-core/src/io/timer_step.rs` | Staged timer bank, shared divider phase, and timer IF bits at ordered CPU/DMA bus phases |
| `crates/gba-core/src/memory/timer_step_tests.rs` | Timer access phases, coherent lanes, cascades, IF ordering, rollback, DMA, idle, and clock ownership | | `crates/gba-core/src/memory/timer_step_tests.rs` | Timer access phases, coherent lanes, cascades, IF ordering, rollback, DMA, idle, and clock ownership |
| `crates/gba-core/src/display.rs` | Display clock, scanline status, comparison edges, and display IRQ events | | `crates/gba-core/src/display.rs` | Display clock, scanline status, comparison edges, and display IRQ events |

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 | | Number | Service | Behavior |
| --- | --- | --- | | --- | --- | --- |
| `0x00` | SoftReset | Clear BIOS work RAM and reset CPU registers/stacks; restart in ROM or RAM without returning | | `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 | | `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 | | `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 | | `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` | | 2 | Clear palette RAM: `0x05000000..0x050003ff` |
| 3 | Clear all 96 KiB of video RAM: `0x06000000..0x06017fff` | | 3 | Clear all 96 KiB of video RAM: `0x06000000..0x06017fff` |
| 4 | Clear sprite attribute memory (OAM): `0x07000000..0x070003ff` | | 4 | Clear sprite attribute memory (OAM): `0x07000000..0x070003ff` |
| 5 | Serial reset: unsupported | | 5 | Reset supported idle serial state and select general-purpose inputs |
| 6 | Sound reset: unsupported | | 6 | Reset disabled sound state, mixing control, bias, and accessible wave RAM |
| 7 | Reset supported display, DMA, timer, and interrupt registers as described below | | 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`. All low-byte flag combinations, including `r0=0xff`, are accepted within the supported device subset.
Validation occurs before forced blank or RAM/I/O reset writes, but after the common SWI stack save. This is not full audio or serial support. [Sound activation](audio.md) and [serial transfers](serial.md) still fail explicitly.
This includes the common all-devices request `r0=0xff`; it is not silently treated as a successful full reset. The firmware always clears the low halfword of SIODATA32, even when bit 5 is clear; its upper halfword is preserved.
Serial and audio devices remain unimplemented. The firmware's unconditional serial-data side effect is not reproduced.
Every supported request first writes `DISPCNT=0x0080`, including requests with no selected flags. Every supported request first writes `DISPCNT=0x0080`, including requests with no selected flags.
This forces a white screen and clears other display-control bits. This forces a white screen and clears other display-control bits.
RAM clears then run in bit order, using ordinary word stores. 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. 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 IME.
- Disable all four DMA channels, then zero their source, destination, count, and control registers. - 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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 ## 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.

View file

@ -239,6 +239,13 @@ General gameplay and boot graphics:
- [tolik518/GBA_Pong](https://github.com/tolik518/GBA_Pong/tree/422e22a5d1f7d11710e03ffc19d5bff6ac95240f): a future homebrew candidate found during research. It has not been built or tested here. - [tolik518/GBA_Pong](https://github.com/tolik518/GBA_Pong/tree/422e22a5d1f7d11710e03ffc19d5bff6ac95240f): a future homebrew candidate found during research. It has not been built or tested here.
- [Paperdomo101/2048-GBA at ae36800d](https://github.com/Paperdomo101/2048-GBA/tree/ae36800dfa1314f96c4c8785b5c2d077fb731aff): reviewed its CC0 license, README, `main.c`, `global.h`, and `storage.c` for SRAM byte accesses and Maxmod initialization. This is a source-level requirement, not a runtime result. The [linked release page](https://basil-termini.itch.io/2048-advance) returned HTTP 522 during download research. - [Paperdomo101/2048-GBA at ae36800d](https://github.com/Paperdomo101/2048-GBA/tree/ae36800dfa1314f96c4c8785b5c2d077fb731aff): reviewed its CC0 license, README, `main.c`, `global.h`, and `storage.c` for SRAM byte accesses and Maxmod initialization. This is a source-level requirement, not a runtime result. The [linked release page](https://basil-termini.itch.io/2048-advance) returned HTTP 522 during download research.
Disabled sound and disconnected serial reset:
- [GBATEK BIOS reset functions](https://problemkaputt.de/gbatek-bios-reset-functions.htm), for flag selection and unconditional SIODATA32 low-halfword destruction.
- [GBATEK sound control](https://problemkaputt.de/gbatek-gba-sound-control-registers.htm), for disabled PSG state, master enable, mixing masks, and bias.
- [GBATEK normal serial mode](https://problemkaputt.de/gbatek-sio-normal-mode.htm) and its [general-purpose register chapter](https://mgba-emu.github.io/gbatek/), for data widths, control fields, and disconnected input pull-ups.
- [mGBA BIOS implementation at 3a5e34be](https://github.com/mgba-emu/mgba/blob/3a5e34be33dc7f8f707e5bc9db69e8a430046f21/src/gba/bios.c), for functional serial and sound reset defaults. Exact firmware ordering and timing remain unverified.
Use [GBATEK](https://problemkaputt.de/gbatek.htm) and ARM7TDMI documentation for further hardware work. Use [GBATEK](https://problemkaputt.de/gbatek.htm) and ARM7TDMI documentation for further hardware work.
Only use game ROMs that you may lawfully use. Do not commit game ROMs, BIOS files, or game assets. Only use game ROMs that you may lawfully use. Do not commit game ROMs, BIOS files, or game assets.
The ignore file excludes `roms/`, common game ROM extensions, and save files. The ignore file excludes `roms/`, common game ROM extensions, and save files.

View file

@ -0,0 +1,54 @@
# Local Emerald startup: device reset
## Initial failure
A user-supplied local Emerald ROM stopped in the original BIOS replacement during `RegisterRamReset(0xff)`.
The BIOS deliberately rejected serial/sound flags and executed `INVALID_ARGUMENT_TRAP` (`0xe7f000f1`).
This was an unsupported service argument, not an ARM instruction-decoder failure.
No ROM bytes, extracted code, or assets were added to the repository.
## General correction
The core now provides a bounded [disabled sound](../hardware/audio.md) and [disconnected serial](../hardware/serial.md) initialization model.
The BIOS performs reset through original ARM stores against that model.
Bit 5 selects general-purpose input pins and clears supported serial reset registers.
Bit 6 disables sound, clears mixing configuration and accessible wave RAM, and sets the functional bias default.
The low halfword of SIODATA32 is cleared unconditionally, matching the documented BIOS side effect.
This is not a game-specific bypass. Original ARM and Thumb callers exercise selected flags and the all-device request.
Tests verify caller preservation, RAM selection, unselected device state, readback masks, and rejected device activity.
Byte, halfword, word, block-store, and DMA validation retain explicit errors without partial register writes.
## Reproduction and current result
Use a local ROM that you may lawfully use:
```sh
direnv exec . cargo run --locked --release -- --rom roms/pokemon-emerald.gba --steps 1000000
direnv exec . cargo run --locked --release -- --rom roms/pokemon-emerald.gba --window --frames 120
```
Both runs now pass the BIOS reset and stop at the game's sound activation write:
```text
PC=0x082e0518
unsupported sound master enable (audio engine not implemented): write 0x8f at 0x04000084
```
The native run captures startup frames before this diagnostic. These are not evidence of a working title screen or gameplay.
The window run does not reach its requested frame limit and correctly exits with failure.
Terminal execution is also diagnostic, not a compatibility pass.
Results apply to this local file; no cartridge revision or checksum was inferred from its filename.
## Validation and next requirement
Workspace debug/release tests, strict Clippy, public ARM/Thumb/memory/BIOS checkpoints, and pinned Pong scenarios pass.
Native ROM-window tests and graphics smoke modes pass on Darwin arm64.
The pinned Pong debug/release reports match.
The next observed startup requirement is active audio-device behavior, starting at SOUNDCNT_X master enable.
Implement channel/FIFO clocks, state, DMA requests, and an output interface before claiming sound support.
Do not make enable succeed solely to suppress this diagnostic.
Cartridge saves remain a separate required feature. SRAM alone will not support Emerald's Flash save protocol.
Hardware and emulator references are recorded in the linked subsystem documents.

View file

@ -77,6 +77,8 @@ Update this file when a feature lands or a limit is removed.
- Read-only cartridge bytes in the three GBA cartridge windows. - Read-only cartridge bytes in the three GBA cartridge windows.
- Raw ROM-file loading with original BIOS boot, terminal diagnostics, and final CPU/step reports. - 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. - 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.
- Sound activation and unsupported serial operations produce explicit address/value diagnostics with whole-access and block-store rollback.
- ROM windows with scanline capture, keyboard input, focus-loss release, bounded execution slices, and interactive keypad wake from STOP. - ROM windows with scanline capture, keyboard input, focus-loss release, bounded execution slices, and interactive keypad wake from STOP.
- Optional ROM-window frame limits and an original input-test ROM generator. - Optional ROM-window frame limits and an original input-test ROM generator.
- Headless JSON ROM suites with PC or VBlank completion, register/CPSR/memory/pixel assertions, scheduled buttons, bounded budgets, and structured failure reports. - Headless JSON ROM suites with PC or VBlank completion, register/CPSR/memory/pixel assertions, scheduled buttons, bounded budgets, and structured failure reports.
@ -132,7 +134,9 @@ Internal affine origins, vertical window flags, and vertical mosaic counters tra
Horizontal window comparators retain four-cycle event history. Horizontal window comparators retain four-cycle event history.
Sprite rows prepare one line ahead with a whole-row cycle40 sample and nominal per-row work limits. Sprite rows prepare one line ahead with a whole-row cycle40 sample and nominal per-row work limits.
Individual fetch timing, hardware-accurate work cutoffs, video-bus contention, and per-pixel color composition remain unimplemented. Individual fetch timing, hardware-accurate work cutoffs, video-bus contention, and per-pixel color composition remain unimplemented.
There is no sound or cartridge save support. There is no sound synthesis, host audio output, or cartridge save support.
The [sound initialization subset](hardware/audio.md) supports disabled register state and wave RAM; master sound enable remains diagnostic.
The [serial initialization subset](hardware/serial.md) supports idle normal data and disconnected general-purpose pins, not active transfers.
Mapped I/O covers DMA, timers, interrupts, WAITCNT, display control/status, background control/scroll/affine registers, window/effect registers, KEYINPUT/KEYCNT, POSTFLG, and HALTCNT. 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. 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. STOP implements keypad wake only. Serial/Game Pak wake sources, oscillator restart delays, and exact wake timing remain unimplemented or unverified.
@ -141,6 +145,8 @@ All named graphics demos connect desktop input and rendering to the emulated CPU
ROM files can run in bounded terminal mode or a window with the original BIOS replacement. ROM files can run in bounded terminal mode or a window with the original BIOS replacement.
ROM windows have no demo-specific startup checks. Input is sampled between bounded execution slices. ROM windows have no demo-specific startup checks. Input is sampled between bounded execution slices.
Audio, saves, and external BIOS loading remain unavailable. ROM loading does not establish commercial-game compatibility. Audio, saves, and external BIOS loading remain unavailable. ROM loading does not establish commercial-game compatibility.
A local Emerald run now completes its all-device reset and reaches sound activation at `0x04000084`, where it stops explicitly.
Its captured startup frames do not establish title-screen or gameplay compatibility. See [the startup result](research/emerald-reset.md).
See [cartridge loading](hardware/cartridge.md) for file limits, step budgets, and exit status. See [cartridge loading](hardware/cartridge.md) for file limits, step budgets, and exit status.
The [ROM suite runner](rom-tests.md) has original regressions and a [pinned public ARM baseline](public-arm-tests.md). The [ROM suite runner](rom-tests.md) has original regressions and a [pinned public ARM baseline](public-arm-tests.md).
The pinned public ARM ROM passes its result checkpoint in debug and release builds. The pinned public ARM ROM passes its result checkpoint in debug and release builds.

View file

@ -64,6 +64,9 @@ direnv exec . python3 -B -m unittest discover -s tools -p 'test_*.py' -v
They verify suite selection, pinned URLs, hashes, sizes, checkpoint validation, offline copies, destination safety, and non-overwriting output. They verify suite selection, pinned URLs, hashes, sizes, checkpoint validation, offline copies, destination safety, and non-overwriting output.
The [Pong adapter](public-pong.md) has additional offline tests for its pinned downloads, retained license, and bounded gameplay scenarios. The [Pong adapter](public-pong.md) has additional offline tests for its pinned downloads, retained license, and bounded gameplay scenarios.
Run the external Pong suite separately; ordinary Cargo and Python tests require no public ROM or network access. Run the external Pong suite separately; ordinary Cargo and Python tests require no public ROM or network access.
Device initialization regressions use original instructions to check disabled sound, wave RAM, disconnected serial pins, and reset flags.
They verify explicit activation errors and atomic byte/word/block-store/DMA failure behavior.
The optional [local Emerald startup check](research/emerald-reset.md) now reaches sound activation but still fails deliberately there.
Thumb cases also verify the return-to-ARM bridge. Command-line tests cover default ARM selection, explicit Thumb/memory/BIOS selection, and errors. Thumb cases also verify the return-to-ARM bridge. Command-line tests cover default ARM selection, explicit Thumb/memory/BIOS selection, and errors.
Compare/status CPU regressions verify saved flags/masks, bank switching, sequential PC/timing, User/System fallback, and atomic diagnostics. Compare/status CPU regressions verify saved flags/masks, bank switching, sequential PC/timing, User/System fallback, and atomic diagnostics.
Load-alias regressions cover widths, signed/unaligned values, immediate/register offsets, pre/post-indexing, every non-PC register bank, and address wrapping. Load-alias regressions cover widths, signed/unaligned values, immediate/register offsets, pre/post-indexing, every non-PC register bank, and address wrapping.
@ -204,7 +207,8 @@ Tile tests cover both color depths, palette banks, flips, all map sizes, scrolli
They check errors for unsupported features and out-of-range fetches without changing the output image. They check errors for unsupported features and out-of-range fetches without changing the output image.
The CPU tile demo test verifies asset copies, every pixel, wrapping, opposite directions, reset, and VBlank synchronization. The CPU tile demo test verifies asset copies, every pixel, wrapping, opposite directions, reset, and VBlank synchronization.
RegisterRamReset tests check full memory regions and all memory-flag combinations from ARM and Thumb. RegisterRamReset tests check full memory regions and all memory-flag combinations from ARM and Thumb.
They verify forced blank, reserved flag bits, caller preservation, protected BIOS RAM, and serial/sound diagnostics. They verify forced blank, reserved flag bits, caller preservation, protected BIOS RAM, and selective serial/sound initialization.
All-device reset returns from ARM and Thumb; active sound and serial operations remain separately tested diagnostics.
Rendering tests check write-only display state, both identity matrices, and the effect of zeroed sprite attributes. Rendering tests check write-only display state, both identity matrices, and the effect of zeroed sprite attributes.
Device tests check DMA address/count latches, stopped timer counters, cleared reloads, and IRQ acknowledgement. Device tests check DMA address/count latches, stopped timer counters, cleared reloads, and IRQ acknowledgement.
Other checks cover partial clears, preserved input/device state, and callers whose own RAM code is erased. Other checks cover partial clears, preserved input/device state, and callers whose own RAM code is erased.

View file

@ -39,7 +39,9 @@ The [pinned Pong homebrew](public-pong.md) passes scripted menus, ball movement,
The native Darwin window also starts and presents frames without a diagnostic. The native Darwin window also starts and presents frames without a diagnostic.
This exposed and corrected missing firmware affine-scale initialization, without a game-specific patch. This exposed and corrected missing firmware affine-scale initialization, without a game-specific patch.
Frame-based scenarios and the preparation adapter now make the result repeatable. Frame-based scenarios and the preparation adapter now make the result repeatable.
The next selected requirement is cartridge SRAM, followed by safe host save persistence. A local Emerald run now passes its initial all-device reset and stops at sound master enable.
See [the runtime result](research/emerald-reset.md). Active audio-device behavior is the next demonstrated startup requirement.
The separate save requirement remains cartridge SRAM, followed by safe host save persistence.
SRAM is byte-addressable battery-backed save memory. Flash and EEPROM remain separate protocols. SRAM is byte-addressable battery-backed save memory. Flash and EEPROM remain separate protocols.
[Paperdomo101/2048-GBA at ae36800d](https://github.com/Paperdomo101/2048-GBA/tree/ae36800dfa1314f96c4c8785b5c2d077fb731aff) is the next independent candidate. [Paperdomo101/2048-GBA at ae36800d](https://github.com/Paperdomo101/2048-GBA/tree/ae36800dfa1314f96c4c8785b5c2d077fb731aff) is the next independent candidate.