fix(bios): preserve documented protected-read words at firmware exits

Place skipped compatibility data at boot, SoftReset, SWI, and IRQ exit PC+8 locations. Preserve the real IRQ callback return instruction as its readback word. Keep CPU and memory behavior image-derived, including caller-supplied images.

Add original boundary, load-lane, caller-state, wait, supplied-image, and builder-fixup regressions. Document the historical failures, research decision, passing public BIOS checkpoint, and remaining fetch-history limits.

Validated on Darwin arm64: 868 workspace tests in debug and release; 799 core/demo tests separately in both profiles; 39 preparation tests; native ROM window tests in both profiles; ten graphics/window smoke modes; fmt, clippy, and rustdoc. Hash-verified public ARM, Thumb, memory, and BIOS suites pass unchanged with identical debug/release reports. BIOS: r12=0, 17606 steps, 197456 nominal cycles, one IRQ entry.
This commit is contained in:
0xrsydn 2026-10-06 00:37:20 +07:00
commit efdc288db6
15 changed files with 387 additions and 34 deletions

View file

@ -125,6 +125,7 @@ The repository is a Cargo workspace with three crates:
| `crates/gba-core/tests/bios/stop.rs` | ARM/Thumb Stop calls, caller preservation, keypad wake, and stopped frame running |
| `crates/gba-core/tests/halt.rs` | HALT wake masks, idle timing, BIOS-only power writes, DMA progress, and validation |
| `crates/gba-core/tests/bios.rs` | ARM/Thumb service calls, copy/fill boundaries, wait races, callback contracts, and boot |
| `crates/gba-core/tests/bios/readback.rs` | Image-derived boot/reset/SWI/IRQ compatibility words, load lanes, caller state, and supplied-image isolation |
| `crates/gba-core/tests/bios/ram_reset.rs` | Selective RAM boundaries, flag combinations, I/O reset, display rendering, DMA/timer latches, and diagnostics |
| `crates/gba-core/tests/bios/reset.rs` | All restart flags, exact RAM boundaries, CPU banks, restart execution, device continuity, and IRQ masking |
| `crates/gba-core/tests/bios/arithmetic.rs` | Arithmetic boundaries, wide-integer references, status restoration, and zero-division diagnostics |

View file

@ -1,9 +1,9 @@
# BIOS replacement
The original, Nintendo-free BIOS image built by `crates/gba-core/src/bios/` and the services it implements.
The original BIOS replacement built by `crates/gba-core/src/bios/` and the services it implements.
`crates/gba-core/src/bios.rs` builds a deterministic 16 KiB image from original ARM instructions and mathematically generated data.
No Nintendo firmware bytes are included.
No Nintendo firmware routines or dumped image are included. Documented protected-read words are explicit compatibility data.
A compile-time integer calculation generates the 512-byte sine table at `0x3e00..0x3fff`.
The image builder checks that code and literal pools do not overlap this table.
Literal pools follow unconditional branches and stay within each load instruction's address range.
@ -108,8 +108,21 @@ The current interpreter implements a bounded ARM prefetch model:
The initial history is unknown. Direct cartridge startup with `Cpu::new` does not manufacture boot prefetch data.
Use BIOS execution, such as `bios::boot`, to establish supported history.
All retained words come from the supplied image. No Nintendo opcode constants are substituted.
The original replacement's boot and return paths therefore produce different values from Nintendo firmware.
All retained words come from the supplied image. The bus never substitutes a firmware-specific constant.
Our generated image places documented compatibility data at PC+8 of its non-fallthrough exits:
| Boundary | Retained word |
| --- | --- |
| Boot and SoftReset | `0xe129f000` |
| SWI return | `0xe3a02004` |
| IRQ callback entry | `0xe25ef004` |
| IRQ return | `0xe55ec002` |
Boot, SoftReset, SWI return, and IRQ return skip a diagnostic trap at PC+4 and the data word at PC+8.
The callback branch instead has the real `SUBS pc,lr,#4` return instruction at PC+8.
Builder fixups preserve this layout without adding executed instructions or host-side service hooks.
Other supplied BIOS images retain their own values; their data is not rewritten.
See the [root-cause research](../research/bios-readback.md) for evidence and the firmware-versus-bus distinction.
Host/debug reads and ROM-suite memory assertions still inspect raw mapped bytes and do not initialize or change the history.
Instruction fetches remain strict mapped reads outside the CPU data-access context.
@ -118,7 +131,8 @@ Missing BIOS images remain unmapped. The unused-memory ARM open-bus snapshot is
This models neither a persistent fetch pipeline nor exact bus history across Thumb BIOS code and DMA.
Original regressions cover synthetic images, boot, exception returns, access widths, alignment, modes, and diagnostics.
The [public BIOS ROM](../public-bios-tests.md) still fails its firmware-specific test 1; this feature is not a public BIOS pass.
The [public BIOS ROM](../public-bios-tests.md) passes its unchanged protected-read assertions in debug and release builds.
This result covers these firmware boundaries, not full BIOS service compatibility.
## Stop

View file

@ -112,7 +112,8 @@ See the bounded [ARM open-bus implementation](hardware/cpu.md#arm-unused-memory-
This is a passing result for this pinned ARM ROM, not proof of complete instruction, memory, or timing compatibility.
The [pinned public Thumb ROM](public-thumb-tests.md) also passes its verified checkpoint.
The [pinned public memory ROM](public-memory-tests.md) passes its limited mirror and video-byte checks.
No public timing, BIOS, graphics, save, or unsafe suite has been claimed as passing.
The [public BIOS read-protection ROM](public-bios-tests.md) has a separate bounded passing result.
No public timing, graphics, save, or unsafe suite has been claimed as passing.
These results do not establish Pokémon Emerald compatibility.
## References

View file

@ -1,7 +1,7 @@
# Pinned public BIOS read-protection test
The headless runner executes the unmodified BIOS test ROM from [jsmolka/gba-tests](https://github.com/jsmolka/gba-tests).
It currently fails test 1 on Darwin arm64 in debug and release builds.
It passes its unchanged protected-read assertions on Darwin arm64 in debug and release builds.
This ROM tests protected BIOS reads against Nintendo firmware opcode values, not the complete BIOS service set.
Our runner uses the original replacement BIOS. It neither supplies nor loads Nintendo firmware.
@ -25,7 +25,7 @@ direnv exec . python3 tools/prepare_gba_tests.py /tmp/gba-public-bios-offline --
```
ARM remains the default suite. The Thumb and memory selections remain unchanged.
Preparation is not a passing result. The BIOS run currently exits nonzero and writes a failed assertion report.
Preparation is not a passing result. A successful BIOS run exits zero and reports `r12 == 0` at the verified checkpoint.
## Provenance and checkpoint
@ -51,6 +51,8 @@ Timeouts, diagnostics, STOP, and nonzero r12 remain failures under the [normal r
## Observed result on Darwin arm64
### Historical failures
Before BIOS read protection:
- Both builds reach `eval` after 64 successful steps and 543 nominal cycles.
@ -68,12 +70,27 @@ After the bounded ARM BIOS read-protection implementation:
- The test still expects `0xe129f000`. No assertion, checkpoint, ROM byte, or returned constant was changed to force a pass.
- Tests 2–4 remain unreached. Their SWI and IRQ cases have not passed independently.
The initial failure exposed both a missing bus-protection rule and a firmware-specific expectation.
The supported protection subset now derives data from the actual supplied image.
The original BIOS has a different layout, so its retained prefetch data differs from Nintendo firmware.
See [CPU BIOS read protection and its limits](hardware/bios.md#cpu-bios-read-protection).
No public BIOS pass is claimed. The ARM, Thumb, and memory suites remain separate passing results.
Exact fetch-pipeline behavior and full BIOS compatibility remain unverified.
### Current passing result
After correcting the replacement firmware's exit layout:
- Both builds reach `eval` at `0x08000248` after 17,606 successful steps and 197,456 nominal cycles.
- The CPU is in System/ARM state, with CPSR `0x6000001f` and r12 = 0.
- The report says `reason: checkpoint`, with no emulator diagnostic and one IRQ entry.
- Boot, returning Sqrt, external IRQ callback, and IRQ return checks all execute and pass.
- The ROM, checkpoint, r12 assertion, and step budget remain unchanged.
- Debug and release reports match exactly for the same fixture path.
The initial failure exposed both a missing bus-protection rule and a firmware layout difference.
The protected-read bus still derives its retained word from the actual supplied image.
Our generated firmware now places documented compatibility words at its exit instructions' PC+8 locations.
These words are skipped data, not copied firmware routines. No bus override or ROM-specific condition was added.
The IRQ callback uses its existing real return instruction as the readback word.
See the [root-cause research](research/bios-readback.md) and [CPU BIOS read-protection limits](hardware/bios.md#cpu-bios-read-protection).
The ARM, Thumb, and memory suites remain separate passing results.
This BIOS result covers boundary readback values, not complete BIOS services, exact fetch history, or commercial-game compatibility.
Thumb BIOS execution, full pipeline behavior, and exact firmware timing remain unverified.
## References

View file

@ -81,7 +81,8 @@ Adjacent halfwords must remain unchanged. Ignored stores still pay their nominal
Halfword and word stores still modify object video RAM and object attribute memory.
These are implementation regressions based on documented rules, not new hardware measurements.
No public BIOS, timing, graphics, save, or unsafe suite is claimed as passing.
The [public BIOS read-protection ROM](public-bios-tests.md) has a separate bounded passing result.
No public timing, graphics, save, or unsafe suite is claimed as passing.
Pokémon Emerald compatibility remains unverified.
## References

View file

@ -82,7 +82,8 @@ Existing empty-list expectations that incorrectly required PC+4 were corrected;
See [CPU transfer semantics](hardware/cpu.md#instruction-demo-and-execution-rules).
This proves only the pinned suites' checkpoints and assertions, not complete CPU, bus, or timing compatibility.
The [pinned public memory ROM](public-memory-tests.md) also passes, with documented video-byte coverage limits.
No public timing, BIOS, graphics, save, or unsafe suite is claimed as passing.
The [public BIOS read-protection ROM](public-bios-tests.md) has a separate bounded passing result.
No public timing, graphics, save, or unsafe suite is claimed as passing.
These results do not establish Pokémon Emerald compatibility.
## References

View file

@ -2,12 +2,12 @@
## Finding
The remaining public BIOS failure comes from the replacement firmware's exit layout, not its protected-load byte lanes.
The memory bus correctly returns the replacement image's retained ARM PC+8 word.
That word differs from the documented value left by the standard GBA firmware.
The public BIOS failure came from the replacement firmware's exit layout, not its protected-load byte lanes.
The memory bus correctly returned the replacement image's retained ARM PC+8 word.
Before the layout fix, that word differed from the documented value left by the standard GBA firmware.
Matching the bus mechanism alone does not make replacement firmware's observable outputs compatible.
At the initial failing checkpoint:
Before the firmware-layout fix, at the initial failing checkpoint:
- The unmodified public ROM expects `0xe129f000` after boot.
- Our replacement's final boot instruction is at `0x60`; its PC+8 word at `0x68` is `0xe59f1320`.
@ -68,6 +68,22 @@ This corrects the earlier overly broad claim that reproducing any documented opc
Arbitrarily overriding a read to satisfy an assertion would be wrong.
Reproducing documented firmware boundary outputs for all callers is a legitimate compatibility feature, provided its scope is explicit.
## Implemented result
`ArmImage::prefetch_tail` adds skipped compatibility data after boot, SoftReset, SWI return, and IRQ return.
The change adds 32 image bytes without adding normally executed instructions.
The callback path retains its actual `SUBS pc,lr,#4` at the branch's PC+8.
No CPU or memory-bus implementation changed.
Original regressions in `crates/gba-core/tests/bios/readback.rs` reproduced the boundary failures before the fix.
They now pass for ARM/Thumb callers, load lanes and widths, User/System state, both reset targets, and interrupt waits.
A supplied-image variant keeps its own changed word, proving that no compatibility constant overrides image data.
Builder regressions verify branch and literal relocation around the skipped data.
The [unchanged public BIOS ROM](../public-bios-tests.md) passes in debug and release on Darwin arm64.
It reaches its checkpoint with r12 = 0 and one IRQ entry. Public ARM, Thumb, and memory results remain passing.
Workspace tests, preparation tests, lint checks, rustdoc, native ROM windows, and graphics smoke tests also pass.
## Validation requirements and limits
- Run the pinned BIOS ROM unchanged, with its original budget, checkpoint, and r12 assertion.

View file

@ -4,7 +4,7 @@ Use `--test-suite PATH.json` to run repeatable ROM checks without opening a wind
Each case has a machine-step budget, a completion address, and explicit assertions.
A timeout never counts as a pass. Existing `--rom ... --steps ...` remains a diagnostic mode, not a test verdict.
Original programs cover the runner itself. Pinned public [ARM](public-arm-tests.md), [Thumb](public-thumb-tests.md), and [memory](public-memory-tests.md) ROMs provide independent compatibility results.
Original programs cover the runner itself. Pinned public [ARM](public-arm-tests.md), [Thumb](public-thumb-tests.md), [memory](public-memory-tests.md), and [BIOS](public-bios-tests.md) ROMs provide independent compatibility results.
These pinned ROMs pass their checkpoints on Darwin arm64 in debug and release builds.
The memory ROM has documented assertion and mode-selection limits; a pass is not exhaustive memory validation.
Passing the runner's unit tests does not mean an external suite passes.
@ -137,8 +137,9 @@ The first public integration is [jsmolka/gba-tests ARM](public-arm-tests.md), wi
The pinned ARM ROM now passes this checkpoint on Darwin arm64. Historical failures remain documented.
The [pinned Thumb ROM](public-thumb-tests.md) also passes its verified r7 checkpoint after the empty-list store correction.
The [pinned memory ROM](public-memory-tests.md) also passes, without a core change and with explicit coverage limits.
The [pinned BIOS read-protection ROM](public-bios-tests.md) reaches its checkpoint but fails test 1 with the original replacement BIOS.
Its expected values depend on Nintendo firmware. Record that failure rather than changing the assertion or returning guessed constants.
The [pinned BIOS read-protection ROM](public-bios-tests.md) also passes with the original replacement BIOS.
Its documented boundary values come from compatibility data in our firmware's exit layout, not a bus override.
The ROM, assertions, checkpoint, and budget remain unchanged. This is not a complete BIOS service test.
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,9 +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 public `jsmolka/gba-tests` ARM, Thumb, and memory ROMs, with verified result-register checkpoints.
- Hash-pinned preparation of public `jsmolka/gba-tests` ARM, Thumb, memory, and BIOS ROMs, with verified result-register checkpoints.
- These pinned ROMs pass their checkpoints on Darwin arm64 in debug and release builds; coverage limits remain documented.
- A pinned public BIOS read-protection ROM has a verified checkpoint and an explicit failing baseline with the original BIOS.
- The original BIOS exposes documented boot, SoftReset, SWI, and IRQ protected-read words through its actual exit layout.
- 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.
@ -96,7 +96,7 @@ The ARMv4T high-register format requires at least one high register; low-to-low
`LDRT`/`STRT` single-transfer variants and invalid register combinations return errors.
CPU BIOS reads are protected by the executing address using a bounded ARM prefetch snapshot.
Thumb BIOS execution and missing lookahead invalidate that history; protected reads with unknown history remain diagnostics.
This is not a full fetch pipeline, and the original BIOS does not reproduce Nintendo's opcode-specific retained values.
This is not a full fetch pipeline. The original BIOS reproduces documented boundary readback words, not all firmware side effects.
Multiply flags N/Z are implemented. Unspecified multiply C/V outputs are preserved deterministically, not hardware-verified.
Instruction and memory errors are development diagnostics, not emulated CPU exceptions.
Block transfers validate all accesses before committing register, RAM, or I/O changes.
@ -122,7 +122,8 @@ The [ROM suite runner](rom-tests.md) has original regressions and a [pinned publ
The pinned public ARM ROM passes its result checkpoint in debug and release builds.
The [pinned public Thumb ROM](public-thumb-tests.md) also passes after correcting its empty-list store PC value.
The [pinned public memory ROM](public-memory-tests.md) passes without a core change; its video-byte assertions have documented limits.
The [pinned BIOS read-protection ROM](public-bios-tests.md) fails test 1; it expects Nintendo firmware opcode values.
The [pinned BIOS read-protection ROM](public-bios-tests.md) passes after correcting the replacement firmware's exit layout.
The bus still derives retained words from the actual image; no test-specific override or ROM patch is used.
Other public suites remain unverified. These results do not establish full CPU or memory compatibility.
The runner requires known completion addresses and has no debug-port protocol, scripted input, or rendered-image assertions.
@ -137,8 +138,8 @@ Writes to cartridge addresses return errors instead of modeling cartridge hardwa
## Next steps
1. Extend BIOS prefetch history to Thumb execution and validate firmware-specific reads with a lawfully supplied BIOS.
The desktop runner still uses only the original BIOS. Do not force the public test to pass with guessed opcode constants.
1. Extend BIOS prefetch history to Thumb execution and validate broader fetch-history behavior with independent tests.
The desktop runner still uses only the original BIOS. Keep protected reads image-derived for all supplied images.
Extend general open-bus support with verified Thumb region/alignment rules and bus history.
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.

View file

@ -52,7 +52,7 @@ Additional tests cover DMA/IRQ accounting, HALT/STOP, diagnostic state, case iso
These original tests validate the runner. The [public ARM adapter](public-arm-tests.md) provides a separate passing checkpoint result for its pinned ROM.
The [public Thumb adapter](public-thumb-tests.md) uses a separate lock and r7 assertion after its return to ARM state.
The [public memory adapter](public-memory-tests.md) uses its own lock and an ARM-state r12 checkpoint.
The [public BIOS adapter](public-bios-tests.md) also uses an ARM-state r12 checkpoint, but currently reports test 1 as failed.
The [public BIOS adapter](public-bios-tests.md) also passes its ARM-state r12 checkpoint in debug and release builds.
Preparation tests use only synthetic original bytes and no network:
```sh
@ -76,7 +76,7 @@ Separate original CPU tests check exact video-byte results across supported mode
They require unchanged nonzero sentinels for ignored writes and unchanged adjacent halfwords for duplicated writes.
They also check forced blank, full source-value preservation, CPU halfword readback, nominal data costs, and device progress.
Halfword/word stores remain writable in object regions. No emulator behavior change was needed for these tests.
These results do not establish public timing, graphics, BIOS, save, or commercial-game compatibility.
These results do not establish full BIOS, timing, graphics, save, 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.
@ -223,7 +223,11 @@ Original BIOS-access regressions check retained ARM PC+8 data for ARM/Thumb call
They verify direct reads while executing inside BIOS, re-entry, SWI/IRQ returns, host-inspection isolation, and original-boot provenance.
Unknown history, Thumb BIOS execution, missing lookahead, and diagnostic-step retention have explicit checks.
Protected loads keep normal nominal costs and advance devices only through machine stepping.
The pinned public BIOS ROM remains a failed assertion, not a conformance pass.
Firmware readback regressions separately verify documented boot, SoftReset, returning SWI, IRQ callback, and IRQ return words.
They execute ARM/Thumb loads across widths and lanes, check caller state, and trace actual exits to their image data.
Interrupt waits replace callback readback with SWI readback on return. Builder tests check branch/literal relocation around exit data.
A modified original image verifies that the bus retains supplied data rather than forcing compatibility constants.
The pinned public BIOS ROM passes unchanged; this is a bounded protected-read result, not complete BIOS conformance.
Display tests cover color conversion, framebuffer bounds, cursor movement, pause behavior, and animation wraparound.
Input regression tests check focused arrow keys, focused Space presses, and ignored input when unfocused.
Command-line tests cover mode selection and invalid arguments.