refactor(cpu): share instruction fetch samples with bus history

This commit is contained in:
0xrsydn 2026-10-06 14:12:03 +07:00
commit e630d7a0ea
18 changed files with 538 additions and 117 deletions

View file

@ -73,7 +73,8 @@ The existing destination-based refill summaries and one-shot DMA resume timing r
The current instruction address still selects nominal code cost rather than a separately scheduled fetch address.
ARM PC+8 open bus and protected BIOS readback remain separate snapshots.
The instruction-buffer sample and those snapshots see mapped memory before instruction effects, but no unified per-access bus model exists yet.
The [shared fetch sample](shared-fetch-samples.md) supplies the instruction-buffer entry and supported bus snapshots without duplicate reads.
No unified per-access bus model exists yet.
The target samples do not establish complete ARM IWRAM or BIOS bus history.
Instruction-state-changing compare quirks, Game Pak prefetch, and DMA arbitration remain incomplete.
DMA still runs between whole instructions, not during their data accesses or internal cycles.

View file

@ -33,7 +33,8 @@ No physical-hardware measurement or external emulator differential run was perfo
## Bounded implementation
`memory/iwram_bus.rs` records lane values and a known-bit mask during consecutive Thumb instructions in IWRAM.
Before execution, the mapped PC+4 halfword updates its addressed lanes.
Before execution, the pipeline's new mapped PC+4 fetch updates its addressed lanes.
The bus-history consumer does not reread memory or reuse the current/decode instruction.
During execution, successful IWRAM bus reads and writes update the staged lanes at their actual aligned addresses and widths.
The latch sees raw bus data before CPU load rotation and sign extension.
Other-region accesses and host inspection do not change it.
@ -65,7 +66,8 @@ For Thumb destinations, those addresses are T and T+2.
[NanoBoyAdvance's refill helpers](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/arm/arm7tdmi.hh) use that order.
Its execution loop then fetches T+4 before executing the first Thumb instruction.
After a successful refill instruction, we sample the two mapped destination halfwords if both remain in Thumb IWRAM.
After a successful refill instruction, the pipeline samples the two mapped destination instructions.
The same captured halfwords update bus history if both remain in Thumb IWRAM.
The samples replace the old lane history and establish the next expected PC as T.
The next instruction's normal PC+4 sample updates one lane while the other retains the captured T+2 halfword.
The samples occur after data accesses and status restoration, so loaded PC values and saved Thumb state select the destination.
@ -76,7 +78,7 @@ Failed instructions retain the previous committed history. An unsupported or inc
Unmapped instruction targets still fail on the following fetch.
Cold direct startup and arbitrary PC changes do not manufacture a refill.
These are bus-history samples, not instruction-buffer contents or extra emulated cycles.
These captured values now supply both bus history and instruction-buffer entries, without extra emulated cycles.
ARM and Thumb execution now use a separate instruction buffer. Cold buffer filling does not seed this bus history.
Changing T+2 after the branch does not replace its captured bus value; changing T+4 before arrival affects the later sample.
Those original tests verify sample ordering, not hardware-accurate self-modifying instruction execution.

View file

@ -0,0 +1,78 @@
# Shared instruction-fetch samples
## Finding and evidence
Persistent ARM/Thumb instruction buffering exposed duplicate memory reads in the interpreter.
The pipeline sampled the new lookahead instruction, then `Memory::begin_cpu_access` reread those bytes for bus history.
Thumb IWRAM refills also read their target pair once for history and again for instruction buffering.
With instruction-boundary scheduling, those reads normally saw identical bytes. They still represented the same fetch twice.
This refactor gives instruction buffering and supported bus-history updates a common captured sample.
It does not introduce new hardware formulas or change nominal cycle costs.
Previously reviewed evidence supports this separation:
- [NanoBoyAdvance at 55b5cf0a](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/arm/arm7tdmi.hh): `Run` fetches the next instruction before execution; refill helpers fetch the target pair.
- [ares at 6f6786e0](https://github.com/ares-emulator/ares/blob/6f6786e04f0822a3475463df284f313ab8518d51/ares/gba/cpu/memory.cpp): IWRAM updates local addressed lanes; EWRAM halfword reads drive duplicated halves.
- [Thumb BIOS research](thumb-bios-prefetch.md) and [Thumb open-bus rules](../hardware/cpu.md#thumb-unused-memory-data-reads): BIOS/OAM bus observations use an aligned word, not a universally duplicated halfword.
These sources were reviewed, not copied into the emulator.
No physical-hardware measurements or external-emulator differential runs were performed.
## Sample and consumers
`memory/fetch.rs` returns an `InstructionFetch` with:
- Fetch address and instruction state.
- Strict instruction result or deferred mapped-read error.
- Supported bus-word observation, when it does not require retained IWRAM lanes.
ARM samples use the fetched word for both instruction bits and bus observation.
Thumb samples retain only their selected halfword as instruction bits.
Supported 16-bit regions duplicate that halfword for bus observation.
BIOS/OAM samples read the adjacent halfword too, without rereading the instruction bytes.
Thumb IWRAM supplies the fetched halfword to the existing lane-retaining transaction instead of creating a full bus word.
The CPU passes only its newly sampled P+8 or P+4 observation to `begin_cpu_access`.
It does not replay the bus observations of retained current/decode instructions.
The consumer performs no mapped memory read.
After successful control flow, the same target samples fill the instruction buffer and supported Thumb IWRAM refill history.
Machine IRQ entry uses this refill path; the ARM target still invalidates Thumb IWRAM continuation history.
Instruction buffers and BIOS/IWRAM latches remain separate retained state.
Sampling alone changes no CPU access context, latch, device clock, DMA resume marker, or data-timing trace.
Instruction errors discard staged history and speculative buffer advances.
A retry takes a new lookahead sample while retaining the previously committed instruction pair.
Unavailable lookahead remains diagnostic only when consumed by an instruction or a data read that needs it.
Strict instruction reads never use protected BIOS or unused-memory data fallback.
## Deliberately unchanged limits
- Cold pipeline filling does not establish previously unknown IWRAM history.
- Thumb 16 MiB region crossings remain unsupported for bus snapshots.
- ARM-wide local IWRAM history and cross-state latch ownership remain incomplete.
- BIOS history commits after successful BIOS instruction execution; target refills do not establish additional BIOS history.
- All unused-memory data reads within an instruction still use its entry snapshot.
- DMA remains scheduled between whole instructions. Sampling does not add an interleaving point.
- Current-instruction and destination-based nominal timing remain unchanged.
- Per-access device updates, Game Pak prefetch, and exact refill/arbitration timing remain incomplete.
## Original tests
CPU tests split the existing internal fetch/execute helpers and mutate lookahead bytes between them.
ARM, narrow Thumb, wide Thumb, and IWRAM tests then require the originally captured values.
Those consumption tests failed with the previous duplicate-read path.
The split is a test-only ownership check, not a hardware DMA experiment.
Memory tests use explicit expected lane values across mapped regions and both Thumb alignments.
They also check target-pair consumption after mutation, BIOS snapshot retention, strict errors, short ROMs, and data-context isolation.
Tests verify that sampling adds no timing or history effects and that failed execution preserves committed history.
Existing instruction-buffer, bus-history, exception, DMA, and nominal timing tests remain regression coverage.
## Validation result
Workspace and core/demo tests pass in debug and release on Darwin arm64.
Formatting, lint checks, rustdoc, preparation tests, native ROM windows, and graphics smoke modes pass.
Public ARM, Thumb, memory, and BIOS reports match the previous Thumb-buffer reports exactly, including nominal cycle counts.
Debug and release reports also match for the same fixture paths.
These checks validate the refactor against existing behavior, not complete hardware bus or timing conformance.

View file

@ -40,7 +40,8 @@ Do not treat agreement between all emulators, or exact hardware conformance, as
## Implementation scope
`Memory::begin_cpu_access` samples before execution, using the incoming instruction state and strict mapped bytes.
`Memory::begin_cpu_access` now consumes the pipeline's new fetch sample, using the incoming instruction state.
The [shared sampler](shared-fetch-samples.md) 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.

View file

@ -36,7 +36,8 @@ ARM-to-Thumb BX and saved-state exception returns therefore capture T and T+2, n
Thumb-to-ARM transitions and SWI/IRQ vectors capture ARM pairs.
`Memory::fetch_instruction` uses strict mapped reads with the selected instruction width.
A Thumb fetch reads exactly two bytes, even in a region whose separate bus snapshot uses a full word.
Its instruction result needs only two bytes in Thumb state.
The later [shared fetch sampler](shared-fetch-samples.md) also captures the adjacent halfword for BIOS/OAM bus observations.
Memory mirrors apply. Unmapped and misaligned instruction reads retain diagnostics rather than using data open bus.
Lookahead errors remain deferred until their slot executes; a branch can discard them.
@ -51,12 +52,12 @@ CPU clones own independent buffers; full CPU equality includes them.
Instruction retention and data-bus snapshots remain separate models.
Cold instruction-buffer filling does not make unknown IWRAM bus lanes known.
Successful DMA can update existing IWRAM bus lanes without changing retained CPU instructions.
Refill bus-history sampling and instruction sampling currently perform separate strict mapped reads.
Refill bus history and instruction buffering now consume the same captured target pair without a second mapped read.
Buffer samples add no nominal cycles or data accesses.
Timed and untimed execution use the same buffer logic.
Destination-based refill summaries and one-shot non-sequential CPU resume costs remain unchanged.
Unified fetch-driven bus history, exact region crossings, per-access timing, Game Pak prefetch, and sub-instruction DMA arbitration remain incomplete.
Complete fetch-driven bus history, exact region crossings, per-access timing, Game Pak prefetch, and sub-instruction DMA arbitration remain incomplete.
Instruction-state-changing compare quirks also remain hardware-unverified.
## Original regression coverage