gameboy-rust/docs/research/thumb-bios-prefetch.md

4.9 KiB

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 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 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 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 was also reviewed. Its region-exit path saves cpu->prefetch[1], while its Thumb execution loop 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 now consumes the pipeline's new fetch sample, using the incoming instruction state. The shared sampler captures strict mapped bytes before execution. 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, or DMA bus transitions. A subsequent Thumb open-bus subset adds region-dependent snapshots outside protected BIOS reads. 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, independent cold-IWRAM fetch history, 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.