fix(memory): return the Game Pak address pattern beyond the ROM chip

CPU and DMA reads past the supplied image returned Unmapped and stopped the
machine; a second game's boot code reads 0x09fe20f8 past a 16 MiB image. The
Game Pak shares data lines with the low address lines, so where no chip drives
the bus a read returns the halfword address, (A >> 1) & 0xffff, per lane
(GBATEK; mGBA uses the same pattern).

The chip is the image size rounded up to a power of two. Bytes between a
trimmed image's end and that boundary are unknown and stay diagnostic, as do
host reads and instruction fetches. Two truncation fixtures move into that gap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhfHfHEuzv2AUTd6erW1gU
This commit is contained in:
0xrsydn 2026-10-14 11:58:36 +07:00
commit 4e4dde742d
11 changed files with 120 additions and 11 deletions

View file

@ -51,6 +51,7 @@ The repository is a Cargo workspace with three crates:
| `crates/gba-core/src/cartridge.rs` | Explicit GPIO/RTC selection, ROM read overlay, pin directions, caller-clock routing, and bounded diagnostics |
| `crates/gba-core/src/cartridge/flash.rs` | Explicit save devices, Macronix IDs/banks, delayed program/erase, bounded DQ7 polling, and staged mutations |
| `crates/gba-core/src/cartridge/sram.rs` | 32 KiB byte-wide SRAM, mirrored offsets, one staged byte per step, and its save-state section |
| `crates/gba-core/tests/rom_bus.rs` | Game Pak reads beyond the ROM chip in every window, trimmed-image gaps, strict host/fetch reads, and DMA sources |
| `crates/gba-core/tests/sram.rs` | SRAM selection, mirroring, byte stores/loads from ROM code, SWPB, width/DMA diagnostics, and save states |
| `crates/gba-core/tests/flash.rs` | Erased/supplied images, chip IDs, all bank addresses, invalid commands, reset limits, DMA rejection, and RTC independence |
| `crates/gba-core/tests/flash_write.rs` | Program values, sector/chip scopes, delays, polling, cancellation, bit transitions, and HALT/STOP |

View file

@ -31,7 +31,8 @@ Bytes are not padded, patched, or written back.
The loader does not validate a cartridge header, logo, or checksum.
It does not decode archives or ELF executables. Supply a raw binary with an ARM instruction at offset zero.
Headerless original programs are accepted.
Reads past supplied cartridge bytes remain explicit memory errors, not open-bus values.
CPU and DMA reads beyond the ROM chip, the image size rounded up to a power of two, return the halfword address (`(A >> 1) & 0xffff`) on each lane, as no device drives the Game Pak bus there.
Bytes between a trimmed image's end and that chip boundary are unknown and remain explicit memory errors, as do host reads and instruction fetches beyond the image.
All three Game Pak ROM windows use the same supplied bytes and existing wait-state rules.
Ordinary cartridge bytes remain read-only. CPU and DMA writes to the `0x08`–`0x0c` ROM windows pay their bus cost and are ignored, as on hardware.
Writes to the `0x0d` window remain `ReadOnly` diagnostics until EEPROM is modeled there. Host `Memory::write*` calls stay strict everywhere.

View file

@ -6,6 +6,8 @@ Hardware references used for the core:
- [GBATEK ARM instruction cycle times](https://problemkaputt.de/gbatek-arm-cpu-instruction-cycle-times.htm).
- [GBATEK GBA system control and WAITCNT](https://problemkaputt.de/gbatek-gba-system-control.htm), including HALTCNT power-mode selection.
- [GBATEK GBA unpredictable things](https://problemkaputt.de/gbatek-gba-unpredictable-things.htm): with no cartridge, the ROM area reads as `Address/2 AND FFFFh`, because the Game Pak shares its data lines with the low address lines. The emulator applies the same undriven-bus pattern beyond the ROM chip.
- [mGBA `src/gba/memory.c`](https://github.com/mgba-emu/mgba/blob/master/src/gba/memory.c): out-of-bounds ROM loads return `(address >> 1) & 0xFFFF` per halfword. mGBA applies it from the image size; this emulator keeps bytes up to the power-of-two chip boundary diagnostic.
- [GBATEK BIOS reset functions](https://problemkaputt.de/gbatek-bios-reset-functions.htm), for SoftReset state and restart selection, plus RegisterRamReset flags, RAM bounds, and forced blank.
- [GBATEK BIOS halt functions](https://problemkaputt.de/gbatek-bios-halt-functions.htm), for wait contracts, STOP clock gating, allowed wake sources, and the clock-off IF note.
- [GBATEK BIOS function calling conventions](https://problemkaputt.de/gbatek-bios-functions.htm).

View file

@ -92,7 +92,8 @@ a deliberate policy decision against the explicit-error convention. See
[the usability roadmap](../usability-roadmap.md) for sequencing.
Update: emulated writes to the `0x08`–`0x0c` ROM windows are now ignored, which removes the
`0x09fe2ffe` store diagnostic. Reads past the end of the image remain explicit errors.
`0x09fe2ffe` store diagnostic. CPU reads beyond the 16 MiB chip, such as `0x09fe20f8`, now return the
Game Pak halfword-address pattern instead of faulting.
## References

View file

@ -230,7 +230,7 @@ BIOS protection uses separate retained BIOS fetch snapshots as described in [BIO
Host inspection and instruction fetches remain strict. Other unmapped reads return errors.
CPU and DMA writes to unused I/O addresses are ignored, as on hardware; host writes there stay strict.
The unmodeled internal memory control register at `0x04xx0800` and all unused I/O reads remain diagnostics.
Reads beyond the supplied cartridge bytes also return errors.
CPU and DMA reads beyond the power-of-two ROM chip return the Game Pak halfword-address pattern; reads inside a trimmed image's chip, host reads, and instruction fetches past the image return errors.
Direct memory-bus halfword and word accesses require alignment.
CPU load/store instructions apply ARM7TDMI alignment and rotation rules before accessing the bus.
CPU and DMA writes to the `0x08`–`0x0c` ROM windows are ignored; the `0x0d` window stays diagnostic until EEPROM exists. Explicitly selected cartridge GPIO and supported Flash command writes are separate from ROM storage.

View file

@ -65,7 +65,7 @@ A reproducible source build or an accessible upstream ROM is needed before recor
Next implementation steps:
1. Visible startup/menu progress is established: attract mode reaches the start menu and Birch dialogue, and sparse taps reach the in-game start menu by 12,000 frames ([probe](research/emerald-visible-progress.md)).
The first demonstrated second-game failure is a boot-init halfword store to ROM mirror `0x09fe2ffe`. ROM-window writes are now ignored; reads past the image end remain diagnostics.
The first demonstrated second-game failure is a boot-init halfword store to ROM mirror `0x09fe2ffe`. ROM-window writes are now ignored, and reads beyond the ROM chip return the Game Pak address pattern.
Use original regressions for any hardware correction; do not invent connected serial devices.
2. Validate an external game's completed save/restart/load cycle using a disposable `--save-file` path.
Refine nominal Flash timing and polling only with independent evidence and original regressions.