feat(memory): support ARM unused-memory open-bus reads

Snapshot mapped PC+8 bytes for ARM data reads in the documented unused ranges. Preserve strict host/fetch/Thumb/DMA diagnostics, missing-ROM errors, existing write rules, and nominal timing. Add original regressions and document the bounded model.

The unmodified pinned jsmolka ARM ROM now reaches its verified checkpoint with r12=0 in debug and release: 1344 steps and 11257 nominal cycles. No ROM, manifest, or assertion changes.

Validated on Darwin arm64: 842 workspace tests in debug and release; 773 core/demo tests separately; Python adapter tests; native ROM windows; all ten demo window smoke tests; fmt, clippy, and rustdoc.
This commit is contained in:
0xrsydn 2026-10-05 20:25:18 +07:00
commit a9493d4cc6
11 changed files with 418 additions and 26 deletions

View file

@ -68,6 +68,36 @@ On ARM7, an empty register list transfers PC but uses a 64-byte span for address
With writeback enabled, `STM` stores the old base only when that register is first in the list.
Otherwise, `STM` stores the updated base.
## ARM unused-memory data reads
During ARM execution, data reads from `0x00004000..0x01ffffff` and `0x10000000..0xffffffff` return an open-bus value.
Open bus means the bus retains previously driven data. GBATEK identifies this ARM value as the instruction word at PC+8.
These high addresses do not mirror BIOS, RAM, or cartridge addresses.
The interpreter snapshots mapped bytes at PC+8 before executing each ARM instruction.
All unused-memory reads within that instruction use the same snapshot.
Byte and halfword reads select their addressed lanes; the CPU applies normal rotation and sign extension.
Base writeback, register aliases, block loads, PC loads, and flags retain their existing instruction semantics.
Each data access pays the nominal unused-region cost of one cycle, plus normal code and internal costs.
Snapshot reads add no data accesses or device cycles. This is not a simulated prefetch pipeline or Game Pak prefetch.
The snapshot uses strict mapped reads, without recursive open-bus fallback.
If any snapshot byte is unavailable, an unused-memory data read retains its `Unmapped` diagnostic.
Missing lookahead does not fail ordinary instructions, mapped loads, or skipped conditional loads.
The CPU clears the access context after each instruction, including errors.
Host inspection, ROM-suite memory assertions, instruction fetches, and DMA do not inherit this context.
Limits remain explicit:
- Thumb open bus depends on instruction region, alignment, and prior bus data; it remains unsupported.
- BIOS-protected reads, unused/write-only I/O reads, DMA latches, and disabled-RAM reads are not modeled here.
- Missing BIOS, truncated ROM, unsupported I/O, and save-memory accesses retain their existing diagnostics.
- Unused-memory writes remain diagnostics rather than ignored hardware writes. Swaps cannot silently discard their write.
- No persistent pipeline or bus history exists. Self-modifying code and DMA-to-CPU bus transitions are not hardware-accurate.
This subset lets public ARM test 362 complete. That test does not assert the loaded data value.
Original regressions check the values separately. See [the public ARM result](../public-arm-tests.md).
## Processor status and exceptions
User and System share registers. Each exception mode has its own stack pointer, link register, and SPSR.
@ -144,7 +174,7 @@ The new settings apply to subsequent code accesses.
Important timing limits:
- Code S/N counts follow instruction summaries, not a simulated fetch pipeline.
- Ordinary code costs use the current instruction address. No speculative PC+8 fetch or startup pipeline fill is performed.
- Ordinary code costs use the current instruction address. The ARM open-bus PC+8 snapshot has no fetch timing or startup pipeline fill.
- PC writes use destination-region costs and the restored instruction width for nominal refill accesses.
- Refill cost calculation does not read target bytes. An invalid branch target fails on the following instruction fetch.
- Game Pak prefetch is not implemented. WAITCNT bit 14 is stored but does not accelerate execution.

View file

@ -2,7 +2,8 @@
The headless runner now executes the unmodified ARM ROM from [jsmolka/gba-tests](https://github.com/jsmolka/gba-tests).
This is independent public test code, not a complete ARM7TDMI conformance suite or a new hardware measurement.
The full public ARM test still fails. Compare/status and load/writeback alias errors are fixed; the next memory-access failure is recorded below.
The pinned public ARM ROM now passes its result checkpoint on Darwin arm64 in debug and release builds.
Compare/status, load/writeback aliases, and ARM unused-memory reads are fixed. Earlier failures are retained below.
## Prepare and run
@ -61,7 +62,7 @@ The instruction at the checkpoint is checked during preparation, in addition to
The case budget is 1,000,000 successful machine steps, including our original BIOS boot.
Timeouts, emulator diagnostics, and nonzero r12 all remain failures under the [normal suite rules](rom-tests.md).
Preparation does not mean the test passed. The current run exits nonzero and writes a failed JSON report.
Preparation does not mean the test passed. Check the runner's exit status and JSON assertions separately.
## Observed results on Darwin arm64
@ -85,7 +86,7 @@ After the compare/status fix, before load/writeback alias support:
- This is `LDR r0, [r0, #4]!` in upstream test 360, with base and destination equal.
- The CPU is in System/ARM state, and r12 remains zero. The final completion checkpoint has not been reached.
After load/writeback alias support:
After load/writeback alias support, before ARM unused-memory reads:
- Tests 360 and 361 complete without taking their failure paths.
- After 877 successful steps, the runner reports `unmapped memory at 0x80000000`.
@ -95,9 +96,20 @@ After load/writeback alias support:
Single-load aliases now retain the loaded value rather than the updated base.
Original regressions also cover byte, halfword, and signed loads, every non-PC register bank, index modes, offsets, alignment, timing, and failures.
The public halfword alias tests later in the ROM have not yet been reached; their source and mGBA's load ordering support the same rule.
The next compatibility task is to verify high-address read behavior and implement the appropriate memory mapping or open-bus behavior.
Do not bypass test 362, invent a return value, or weaken the result assertion.
After ARM unused-memory open-bus support:
- Test 362 completes. The read returns the ARM word at the executing PC plus eight, not a high-address mirror.
- Execution also reaches the later halfword alias and block-transfer tests without taking their failure exits.
- The runner reaches `eval` at `0x08001d4c` after 1,344 successful steps, including original BIOS boot.
- The CPU is in System/ARM state, with CPSR `0x6000001f`, r12 = 0, and 11,257 nominal cycles.
- Debug and release runs exit zero, with `reason: checkpoint`, no error, and the result assertion passing.
- The pinned ROM, manifest checkpoint, budget, and expected result are unchanged.
Test 362 checks shifted addressing, writeback, and carry, but does not check the loaded value.
Original regressions separately check the returned word, byte lanes, rotation, sign extension, timing, and retained diagnostics.
See the bounded [ARM open-bus implementation](hardware/cpu.md#arm-unused-memory-data-reads).
This is a passing result for this pinned ARM ROM, not proof of complete instruction, memory, or timing compatibility.
No public Thumb, timing, memory, BIOS, graphics, or unsafe suite has been claimed as passing.
These results do not establish Pokémon Emerald compatibility.

View file

@ -50,7 +50,9 @@ Hardware references used for the core:
- [NanoBoyAdvance keypad implementation](https://github.com/nba-emu/NanoBoyAdvance/blob/master/src/nba/src/hw/keypad/keypad.cc), compared for control access widths; its AND/retrigger behavior differs from the chosen polling model.
- [mGBA keypad timing issue](https://github.com/mgba-emu/mgba/issues/2490), for frontend frame-based input timing limits.
- [Tonc hardware interrupts](https://gbadev.net/tonc/interrupts.html), for keypad source enable and IF/IE bit 12.
- [GBATEK memory mirrors and video byte writes](https://problemkaputt.de/gbatek-gba-unpredictable-things.htm).
- [GBATEK memory mirrors, video byte writes, and unused-memory reads](https://problemkaputt.de/gbatek-gba-unpredictable-things.htm), for unused address ranges, ARM PC+8 open bus, byte lanes, and the distinct Thumb region/history rules.
- [nocash GBA open-bus discussion](https://www.ngemu.com/threads/gba-open-bus.170809/), for the distinction between ARM prefetch and region-dependent Thumb values.
- [mGBA memory implementation](https://github.com/mgba-emu/mgba/blob/master/src/gba/memory.c), reviewed for `GBALoadBad`, ARM prefetch sourcing, byte/halfword lane selection, and separate DMA/history behavior.
- [GBATEK GBA memory map and bus widths](https://problemkaputt.de/gbatek-gba-memory-map.htm).
- [GBATEK GBA DMA transfers](https://problemkaputt.de/gbatek-gba-dma-transfers.htm).
- [mGBA DMA implementation](https://github.com/mgba-emu/mgba/blob/master/src/gba/dma.c), used to cross-check masks, repeat behavior, and Game Pak source increments.

View file

@ -130,8 +130,9 @@ They do not hash ROM contents; record source revisions and file checksums alongs
## Next validation stage
The first public integration is [jsmolka/gba-tests ARM](public-arm-tests.md), with a verified result-register checkpoint and a known failing baseline.
Investigate its next unsupported high-address read at test 362 before claiming the full ARM ROM passes.
The first public integration is [jsmolka/gba-tests ARM](public-arm-tests.md), with a verified result-register checkpoint.
The pinned ARM ROM now passes this checkpoint on Darwin arm64. Historical failures remain documented.
Next, integrate a pinned public Thumb suite with a verified result protocol.
For further public ARM7TDMI/GBA tests, inspect their source, license, entry assumptions, and result protocol.
Pin source revisions and build instructions before comparing emulator changes.
Adapt suites to verified completion addresses and result locations; do not guess them or treat timeouts as passes.

View file

@ -67,8 +67,9 @@ Update this file when a feature lands or a limit is removed.
- 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.
- Headless JSON ROM suites with per-case checkpoints, register/CPSR/memory assertions, bounded budgets, and structured failure reports.
- Hash-pinned preparation of the public `jsmolka/gba-tests` ARM ROM, with an explicit failing compatibility baseline.
- Hash-pinned preparation of the public `jsmolka/gba-tests` ARM ROM, passing its result checkpoint on Darwin arm64.
- ARM test/compare Rd=15 status restoration, without a PC result write or nominal refill.
- ARM unused-memory data reads return a PC+8 word snapshot, with normal byte lanes and load rotation.
- Little-endian byte, halfword, and word reads/writes.
- ARM7TDMI word-load rotation, aligned stores, and odd-address halfword-load behavior.
- Explicit errors for unsupported instructions when their condition passes, and for unsupported memory accesses.
@ -114,10 +115,12 @@ ROM windows have no demo-specific startup checks. Input is sampled between bound
Audio, saves, and external BIOS loading remain unavailable. ROM loading does not establish commercial-game compatibility.
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 public ARM ROM now gets past its compare/status and word-load alias tests but stops at test 362 on an unmapped read from `0x80000000`.
It requires known completion addresses and has no debug-port protocol, scripted input, or rendered-image assertions.
The pinned public ARM ROM passes its result checkpoint in debug and release builds. Other public suites remain unverified.
The runner requires known completion addresses and has no debug-port protocol, scripted input, or rendered-image assertions.
Unmapped reads return errors instead of hardware open-bus values.
ARM data reads from unused address ranges use a bounded [PC+8 open-bus snapshot](hardware/cpu.md#arm-unused-memory-data-reads).
This is not a fetch pipeline. Thumb, DMA, BIOS-protected reads, and unused/write-only I/O open bus remain unmodeled.
Host inspection and instruction fetches remain strict. Other unmapped reads return errors.
Reads beyond the supplied cartridge bytes also 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.
@ -125,8 +128,8 @@ Writes to cartridge addresses return errors instead of modeling cartridge hardwa
## Next steps
1. Verify high-address read behavior exposed by public ARM test 362; implement the appropriate memory mapping or open-bus behavior and rerun the pinned suite.
Add further public suites only with verified result protocols before claiming instruction compatibility.
1. Add a pinned public Thumb suite with a verified result protocol, then address its first compatibility failure.
Extend open-bus support with verified Thumb region/alignment rules and bus history, rather than a blanket unmapped-read fallback.
2. Refine nominal timing with a fetch pipeline, Game Pak prefetch, per-access device updates, and verified timer/IRQ delays.
3. Validate keypad retrigger behavior with hardware tests and add remaining device registers; extend BIOS reset coverage as sound/serial support becomes available.
Validate STOP entry/wake edges and add external wake sources and remaining DMA device modes.

View file

@ -49,7 +49,7 @@ Do not set a Linux cross-compilation target for this validation.
The [headless ROM suite runner](rom-tests.md) provides explicit checkpoint and assertion results through `--test-suite PATH.json`.
Its tests verify manifest limits, whole-suite validation, exact step boundaries, ARM/Thumb checkpoints, and register/CPSR/memory checks.
Additional tests cover DMA/IRQ accounting, HALT/STOP, diagnostic state, case isolation, repeatable JSON output, and failure exit status.
These original tests validate the runner. The [public ARM adapter](public-arm-tests.md) is a separate, currently failing compatibility baseline.
These original tests validate the runner. The [public ARM adapter](public-arm-tests.md) provides a separate passing checkpoint result for its pinned ROM.
Its preparation tests use only synthetic original bytes and no network:
```sh
@ -60,7 +60,10 @@ They verify pinned URLs, hashes, sizes, checkpoint validation, offline copies, d
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.
They verify loaded-value precedence, preserved flags, unchanged stores, nominal timing, I/O reads before device progress, deferred IRQ delivery, and atomic errors.
The public ARM ROM progresses past tests 360–361 but still fails on the high-address read in test 362.
ARM open-bus regressions cover unused-range boundaries, PC+8 source regions, byte lanes, signed/unaligned loads, writeback, and block loads.
They check branch-target resampling, missing lookahead, skipped conditions, nominal timing, device progress, and retained host/fetch/Thumb/DMA diagnostics.
The pinned public ARM ROM now reaches its completion checkpoint with r12 = 0 in debug and release builds.
This does not establish public Thumb, timing, graphics, BIOS, or commercial-game compatibility.
ROM-runner tests use temporary files containing original instructions, without Nintendo assets or a desktop session.
They check file-size boundaries, bounded reads, short reads, I/O errors, paths, symlinks, and read-only loading.