feat(memory): retain aligned Thumb BIOS prefetch words

Snapshot the full word at (executing PC+4)&~3 for Thumb BIOS execution, using strict image reads and incoming instruction state. Keep ARM PC+8 open bus separate and unchanged. Commit snapshots only after successful steps; preserve diagnostic behavior for unavailable lookahead.

Add original regression coverage for both code alignments, load widths and lanes, signed and unaligned values, processor modes, sequential fetches, ARM/Thumb transitions, SWI, RAM-backed POP exits, BIOS boundaries, failure retention, and nominal device progression. Record source evidence and explicit pipeline/hardware-validation limits.

Darwin arm64 validation: 878 workspace tests and 809 core/demo tests in each of debug and release; 39 Python tests; native ROM window tests in both profiles; ten window/graphics smoke modes; fmt, clippy, and rustdoc. Public ARM, Thumb, memory, and BIOS reports match prior passing reports and match between profiles. No physical-hardware validation claimed.
This commit is contained in:
0xrsydn 2026-10-06 00:57:49 +07:00
commit cb4670c245
12 changed files with 427 additions and 25 deletions

View file

@ -28,6 +28,7 @@ The repository is a Cargo workspace with three crates:
| `crates/gba-core/src/cpu/exception.rs` | Reset state, exception entry, and interrupt sampling |
| `crates/gba-core/src/cpu/load_alias_tests.rs` | Original single-load alias regressions for indexing, banks, widths, alignment, I/O, timing, and diagnostics |
| `crates/gba-core/src/cpu/bios_access_tests.rs` | Original CPU BIOS protection regressions for retained history, modes, widths, exception returns, and diagnostics |
| `crates/gba-core/src/cpu/thumb_bios_access_tests.rs` | Thumb BIOS aligned-word snapshots, transitions, load lanes, boundaries, diagnostics, and nominal timing |
| `crates/gba-core/src/cpu/compare_psr_tests.rs` | Original Rd=15 test/compare status restoration, flags, bank, and timing regressions |
| `crates/gba-core/src/cpu/status_tests.rs`, `crates/gba-core/src/cpu/exception_tests.rs` | Status, banking, exception, and return tests |
| `crates/gba-core/src/cpu/thumb_tests.rs` | Thumb instructions, state switching, and decoder checks |

View file

@ -97,13 +97,15 @@ While executing inside BIOS, ARM and Thumb code can read the mapped image direct
While executing outside BIOS, both instruction sets read byte lanes from retained BIOS prefetch data instead.
Normal word rotation, odd-halfword behavior, sign extension, and load timing still apply.
The current interpreter implements a bounded ARM prefetch model:
The current interpreter implements bounded ARM and Thumb BIOS prefetch snapshots:
- Before an ARM BIOS instruction executes, sample its mapped PC+8 word without charging another data access.
- Before a Thumb BIOS instruction executes, sample the full mapped word at `(PC+4) & ~3`.
BIOS drives a 32-bit word even for a halfword fetch; both distinct lanes are retained.
- After successful execution, retain that snapshot for later protected reads. Skipped conditional instructions count as successful execution.
- Instructions outside BIOS do not replace the retained value. Re-entering ARM BIOS code refreshes it.
- Instructions outside BIOS do not replace the retained value. Re-entering ARM or Thumb BIOS code refreshes it.
- Diagnostic failures preserve the previous value and clear the CPU access context.
- Successful Thumb BIOS execution or unavailable PC+8 bytes invalidate the known value. Their fetch history is not yet modeled.
- Unavailable lookahead bytes invalidate the known value after a successful BIOS instruction.
- A protected read without known history returns `MemoryError::Unmapped` at the requested bus address, rather than exposing raw BIOS bytes.
The initial history is unknown. Direct cartridge startup with `Cpu::new` does not manufacture boot prefetch data.
@ -129,7 +131,10 @@ Instruction fetches remain strict mapped reads outside the CPU data-access conte
BIOS writes remain read-only diagnostics. DMA BIOS sources retain their existing unsupported-source diagnostic.
Missing BIOS images remain unmapped. The unused-memory ARM open-bus snapshot is separate from this retained BIOS value.
This models neither a persistent fetch pipeline nor exact bus history across Thumb BIOS code and DMA.
The Thumb rule follows documented BIOS bus lanes and a source comparison with NanoBoyAdvance.
See [Thumb BIOS prefetch research](../research/thumb-bios-prefetch.md) for evidence, emulator differences, and test scope.
This models neither a persistent fetch pipeline nor exact refill, BIOS data-access, or DMA bus history.
The boundary diagnostic policy and precise bus timing remain hardware-unverified.
Original regressions cover synthetic images, boot, exception returns, access widths, alignment, modes, and diagnostics.
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.

View file

@ -98,7 +98,7 @@ Host inspection, ROM-suite memory assertions, instruction fetches, and DMA do no
Limits remain explicit:
- Thumb open bus depends on instruction region, alignment, and prior bus data; it remains unsupported.
- [BIOS-protected reads](bios.md#cpu-bios-read-protection) use separate retained ARM BIOS snapshots.
- [BIOS-protected reads](bios.md#cpu-bios-read-protection) use separate retained ARM PC+8 or aligned Thumb PC+4 word snapshots.
- 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.

View file

@ -90,7 +90,8 @@ See the [root-cause research](research/bios-readback.md) and [CPU BIOS read-prot
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.
Separate original tests cover bounded [Thumb BIOS snapshots](research/thumb-bios-prefetch.md); this public ROM does not validate that path.
Full pipeline behavior, physical-hardware Thumb readback, and exact firmware timing remain unverified.
## References

View file

@ -95,6 +95,15 @@ BIOS protected-read root-cause research:
See [the root-cause analysis and implementation decision](research/bios-readback.md).
Thumb BIOS fetch research:
- [NanoBoyAdvance bus at revision 55b5cf0a](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/bus/bus.cc), for full aligned BIOS latch updates on halfword reads.
- [NanoBoyAdvance ARM7TDMI loop at the same revision](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/arm/arm7tdmi.hh), for Thumb PC+4 fetch order.
- [mGBA CPU loop](https://github.com/mgba-emu/mgba/blob/master/src/arm/arm.c), compared with its region-exit retention path; not evidence of exact full-word Thumb BIOS readback.
- GBATEK's BIOS/OAM Thumb lane table above supports aligned-word sampling, but describes general unused-memory reads.
See [Thumb BIOS prefetch snapshots](research/thumb-bios-prefetch.md) for the inference, implementation limits, and original regressions.
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.
The ignore file excludes `roms/`, common game ROM extensions, and save files.

View file

@ -93,6 +93,7 @@ Workspace tests, preparation tests, lint checks, rustdoc, native ROM windows, an
- Keep synthetic caller-supplied images image-derived, including images with different exit words.
- Rerun public ARM, Thumb, and memory suites and native Darwin regressions.
This change does not implement a persistent instruction pipeline, Thumb BIOS fetch history, or exact firmware timing.
The firmware-layout fix did not implement a persistent instruction pipeline, Thumb BIOS fetch history, or exact firmware timing.
A subsequent [Thumb BIOS snapshot change](thumb-bios-prefetch.md) adds bounded aligned-word history, with separate tests and explicit limits.
It does not prove complete BIOS compatibility or commercial-game support.
The public BIOS ROM checks these protected-read values; it is not a comprehensive BIOS service suite.

View file

@ -0,0 +1,75 @@
# Thumb BIOS prefetch snapshots
## Decision
Retain the full mapped word at `(executing PC + 4) & !3` after a successful Thumb BIOS instruction.
Keep ARM's existing PC+8 rule. Keep this BIOS history separate from general unused-memory open bus.
BIOS has a 32-bit bus. A Thumb instruction fetch selects a halfword, but the BIOS bus drives both word lanes.
This gives the following snapshot for an instruction at address P:
| Executing address | Low halfword | High halfword |
| --- | --- | --- |
| P aligned to four bytes | BIOS[P+4] | BIOS[P+6] |
| P aligned to two bytes only | BIOS[P+2] | BIOS[P+4] |
The two halfwords need not match. Do not repeat the fetched halfword, zero-extend it, or read an unaligned word at P+4.
Do not use ARM's PC+8 offset for Thumb execution.
## Evidence and confidence
[GBATEK](https://problemkaputt.de/gbatek-gba-unpredictable-things.htm) documents two related behaviors:
- Protected BIOS reads return the most recently fetched BIOS opcode.
- Thumb open bus while executing in BIOS exposes the two aligned word lanes shown above.
Its Thumb table describes unused-memory reads, not a dedicated Thumb protected-BIOS test.
The retained-word rule combines that bus-width evidence with the documented BIOS latch behavior.
We have not measured it on physical hardware.
[NanoBoyAdvance's bus implementation](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/bus/bus.cc) corroborates the full-word latch.
`ReadBIOS` aligns a permitted address down to four bytes and updates its BIOS latch with a full word.
It then selects the requested lanes. A halfword access does not reduce the retained latch to 16 bits.
Its [ARM7TDMI execution loop](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/arm/arm7tdmi.hh) fetches the Thumb PC+4 halfword before dispatching the instruction.
This is a source comparison, not a differential emulator run. No implementation code was copied.
[mGBA's memory implementation](https://github.com/mgba-emu/mgba/blob/master/src/gba/memory.c) was also reviewed.
Its region-exit path saves `cpu->prefetch[1]`, while its [Thumb execution loop](https://github.com/mgba-emu/mgba/blob/master/src/arm/arm.c) fetches a halfword.
That path does not independently establish both retained word lanes. Its general Thumb BIOS open-bus path also notes an alignment approximation.
Do not treat agreement between all emulators, or exact hardware conformance, as established.
## Implementation scope
`Memory::begin_cpu_access` samples before execution, using the incoming instruction state and strict mapped bytes.
`end_cpu_access` commits BIOS history only after success. Failed instructions preserve previous history.
Thumb execution outside BIOS neither initializes nor replaces BIOS history.
Host reads remain raw inspection. CPU data loads outside BIOS select lanes from retained history as before.
Snapshots add no data accesses or nominal cycles.
The snapshot does not create a persistent instruction pipeline.
It does not model refill-time latches, BIOS data-access ordering within an instruction, DMA bus transitions, or general Thumb open bus.
At BIOS boundaries, unavailable lookahead invalidates history under the existing diagnostic policy.
A branch can still succeed; a later protected read reports unknown history rather than exposing stale bytes.
This boundary policy is conservative, not a hardware claim.
## Regression coverage
Original tests in `crates/gba-core/src/cpu/thumb_bios_access_tests.rs` cover:
- Both Thumb instruction alignments and both output instruction states.
- Full words, byte lanes, halfwords, sign extension, rotation, and all processor modes.
- Sequential history, ARM/Thumb transitions, SWI entry, and POP-to-PC exits with RAM stack data.
- Raw reads inside BIOS and protected reads outside it.
- The last complete fetch word, unavailable lookahead, and failure retention.
- Separate host reads, unchanged general Thumb open-bus diagnostics, nominal costs, and device progression.
## Validation result
The Thumb boundary-read regressions failed on the old history-invalidation path and pass with aligned-word snapshots.
Darwin arm64 workspace and core/demo tests pass in debug and release builds.
Preparation tests, formatting, lint checks, rustdoc, native ROM windows, and graphics smoke tests also pass.
The pinned public ARM, Thumb, memory, and BIOS reports match their previous passing reports exactly.
Debug and release reports also match each other for the same fixture paths.
The pinned public BIOS ROM uses ARM firmware paths. Its unchanged pass is regression evidence, not independent Thumb BIOS coverage.
Full pipeline timing and commercial-game compatibility remain outside this change.

View file

@ -43,7 +43,7 @@ Update this file when a feature lands or a limit is removed.
- CPU/device stepping with nominal ARM7 instruction costs and memory wait states.
- Game Pak wait-state control (`WAITCNT`) for all three ROM windows.
- Sequential/non-sequential data accesses, branch refill costs, and variable multiply timing.
- Optional caller-supplied 16 KiB BIOS mapping, with CPU read protection based on retained ARM BIOS PC+8 snapshots.
- Optional caller-supplied 16 KiB BIOS mapping, with CPU read protection based on retained ARM PC+8 or aligned Thumb PC+4 words.
- Optional original BIOS replacement: minimal boot, SoftReset, selective RegisterRamReset, IRQ dispatch, interrupt waits, memory copy/fill, integer/fixed-point arithmetic, affine matrices, bit unpacking, LZ77/run-length/Huffman decompression, and differential filters.
- All 16 ARM data-processing operations, with immediate and shifted-register operands.
- Logical operations: `AND`, `EOR`, `TST`, `TEQ`, `ORR`, `MOV`, `BIC`, and `MVN`.
@ -94,8 +94,10 @@ The reserved ARM condition code `0xF` and Thumb condition code `0xE` return erro
ARMv5+ extensions such as `BLX`, `BKPT`, and Thumb-2 instructions are not supported.
The ARMv4T high-register format requires at least one high register; low-to-low forms in that format return errors.
`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.
CPU BIOS reads are protected by the executing address using bounded ARM and Thumb prefetch snapshots.
Thumb BIOS execution retains the full word at `(PC+4) & ~3`, not a repeated halfword.
Missing lookahead invalidates that history; protected reads with unknown history remain diagnostics.
Exact refill and BIOS data-access ordering remain unmodeled. The Thumb rule has source-based, not physical-hardware validation.
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.
@ -129,7 +131,7 @@ The runner requires known completion addresses and has no debug-port protocol, s
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. General Thumb, DMA, and unused/write-only I/O open bus remain unmodeled.
BIOS protection uses separate retained ARM BIOS history as described in [BIOS behavior](hardware/bios.md#cpu-bios-read-protection).
BIOS protection uses separate retained BIOS fetch snapshots as described in [BIOS behavior](hardware/bios.md#cpu-bios-read-protection).
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.
@ -138,9 +140,9 @@ Writes to cartridge addresses return errors instead of modeling cartridge hardwa
## Next steps
1. Extend BIOS prefetch history to Thumb execution and validate broader fetch-history behavior with independent tests.
1. Extend general open-bus support with documented Thumb region/alignment rules and explicit limits for history-dependent cases.
Validate BIOS snapshots with independent hardware tests before claiming exact refill or data-access history.
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.
Validate STOP entry/wake edges and add external wake sources and remaining DMA device modes.

View file

@ -219,9 +219,12 @@ Status tests cover all modes, flag combinations, interrupt masks, field selectio
Exception tests cover vectors, ARM/Thumb return addresses, nested interrupts, mask priority, and return-state restoration.
User-bank transfers and failed returns are checked for unintended changes to active and hidden registers.
Integration tests execute original vector handlers and check optional BIOS mapping boundaries and write protection.
Original BIOS-access regressions check retained ARM PC+8 data for ARM/Thumb callers, all processor modes, load widths, lanes, and rotation.
Original BIOS-access regressions check retained ARM PC+8 and aligned Thumb PC+4 words for ARM/Thumb callers.
They cover all processor modes, load widths, lanes, rotation, both Thumb code alignments, and distinct halfwords.
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.
Thumb tests cover sequential snapshots, instruction-state transitions, SWI entry, and RAM-backed POP exits.
Unknown history, last valid fetch words, missing lookahead, and diagnostic-step retention have explicit checks.
General Thumb unused-memory reads remain diagnostics; supporting Thumb BIOS snapshots does not enable general open bus.
Protected loads keep normal nominal costs and advance devices only through machine stepping.
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.