feat(cpu): retain IWRAM bus history across execution states

This commit is contained in:
0xrsydn 2026-10-06 14:30:38 +07:00
commit f2c532f8f6
24 changed files with 522 additions and 359 deletions

View file

@ -30,10 +30,11 @@ The repository is a Cargo workspace with three crates:
| `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/thumb_open_bus_tests.rs` | Region-dependent Thumb open bus, mirrors, widths, transfers, source boundaries, isolation, and nominal timing |
| `crates/gba-core/src/cpu/iwram_open_bus_tests.rs` | Sequential IWRAM lane history, access widths, isolation, errors, refills, IRQ invalidation, and nominal timing |
| `crates/gba-core/src/cpu/iwram_dma_tests.rs` | Bounded DMA continuation lanes, access ordering, channel isolation, diagnostics, and resume snapshots |
| `crates/gba-core/src/cpu/iwram_open_bus_tests.rs` | Persistent IWRAM lanes, cold fills, access widths, isolation, errors, refills, IRQ preservation, and nominal timing |
| `crates/gba-core/src/cpu/iwram_dma_tests.rs` | DMA lane updates, cold/resumed fetch ordering, channel isolation, diagnostics, and IRQ preservation |
| `crates/gba-core/src/cpu/iwram_refill_tests.rs` | Thumb IWRAM target-pair refills, state changes, exception returns, boundaries, isolation, and nominal costs |
| `crates/gba-core/src/memory/iwram_bus.rs` | Transactional known-lane history for Thumb IWRAM execution and target-pair refills |
| `crates/gba-core/src/memory/iwram_bus.rs` | Transactional persistent IWRAM lanes for ARM/Thumb fetches, CPU data accesses, and DMA |
| `crates/gba-core/src/memory/iwram_history_tests.rs` | Cross-state latch ownership, ARM accesses/refills, nonlocal code, pre-start DMA, IRQ preservation, and rollback |
| `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

@ -136,7 +136,7 @@ Host inspection, ROM-suite memory assertions, instruction fetches, and DMA do no
Limits remain explicit:
- Thumb open bus uses the supported region rules below, including bounded sequential internal-RAM lane history.
- Thumb open bus uses the supported region rules below, including persistent internal-RAM lane history.
- [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, general DMA-to-CPU bus handoff, and disabled-RAM reads are not modeled here.
[DMA channel data](dma.md#retained-channel-data) is separate and never overrides a CPU snapshot.
@ -144,7 +144,7 @@ Limits remain explicit:
- Unused-memory writes remain diagnostics rather than ignored hardware writes. Swaps cannot silently discard their write.
- ARM/Thumb instruction buffering is persistent, but these data snapshots remain a separate bounded model.
Thumb IWRAM bus-history samples do not share storage with retained instructions.
DMA updates existing IWRAM continuation lanes at instruction boundaries, not during CPU accesses.
DMA updates IWRAM lanes at instruction boundaries, not during CPU accesses.
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).
@ -158,7 +158,7 @@ The snapshot depends on the executing code region. P below is the executing inst
| --- | --- |
| External work RAM, palette RAM, video RAM, all three ROM windows | Halfword at P+4, repeated in both word lanes |
| BIOS and object attribute memory (OAM) | Full word at `(P+4) & ~3` |
| Internal work RAM (IWRAM) | PC+4 replaces one halfword in known sequential IWRAM history; the other lanes retain their values |
| Internal work RAM (IWRAM) | PC+4 replaces one halfword in the persistent IWRAM latch; the other lanes retain their values |
| Other code regions | Unsupported |
For BIOS/OAM, aligned instructions expose halfwords at P+4 and P+6.
@ -184,77 +184,75 @@ Do not substitute P+2 for that history: it is only the usual case, not a general
The original Nintendo DS also differs from GBA-family IWRAM behavior; this core targets GBA.
Bus history remains bounded despite sharing samples with the persistent instruction buffer.
Refill timing, general DMA-to-CPU transitions, cross-state IWRAM history, unused/write-only I/O, and disabled RAM remain incomplete.
Refill timing, general DMA-to-CPU transitions, unused/write-only I/O, and disabled RAM remain incomplete.
Original regressions validate the documented formulas. No physical-hardware or independent public Thumb open-bus pass is claimed.
### Sequential IWRAM history
For consecutive Thumb instructions in IWRAM, the emulator retains a separate lane value and known-bit mask.
The PC+4 halfword fetch updates its addressed halfword before execution.
IWRAM data reads and writes then update only their addressed byte, halfword, or word lanes.
The emulator retains a separate IWRAM lane value and known-bit mask, independent of CPU state or executing region.
Every successful ARM IWRAM fetch drives a word; every Thumb IWRAM fetch drives its addressed halfword.
IWRAM data reads and writes update their addressed byte, halfword, or word lanes, including accesses from non-IWRAM code.
The recorded value is raw aligned bus data, before CPU rotation or sign extension.
Accesses to other regions do not replace the IWRAM latch.
Accesses to other regions, state changes, and exception entry do not erase this local latch.
See [the evidence and implementation scope](../research/iwram-bus-history.md).
History starts unknown. An unused-memory load requires all word lanes to be known, including for byte/halfword loads.
Ordinary instructions and mapped loads still execute with incomplete history.
Consecutive fetches can establish both halves. An IWRAM word read or write establishes the entire word.
Cold pipeline filling samples the current instruction, next instruction, and lookahead in order.
A mapped Thumb IWRAM fill therefore establishes both halves before the first data read.
A retained pipeline advances only its new lookahead; old instruction slots do not drive the bus again.
Debugger invalidation does not erase the latch, but the following cold fill updates it from current mapped bytes.
Host inspection and setup writes do not seed or change this emulated history.
Each instruction stages its fetch and data-access changes.
Success commits history only for the next sequential Thumb IWRAM PC. Failure preserves the previous committed history.
Each instruction stages all new fetches and data-access changes.
Success commits history regardless of the resulting PC, instruction state, or region. Failure preserves the previous committed history.
Block-transfer data accesses stage updates in order; unused-memory reads still use one entry snapshot per instruction.
This preserves the existing atomic diagnostic policy without adding bus or device cycles.
Taken branches and PC-writing refills end the old continuation history, even when their target is the fallthrough address.
A successful refill into Thumb IWRAM establishes new history from its target pair, as described below.
Untaken conditional branches keep sequential history.
Other execution states/regions and accepted machine IRQs invalidate continuation history.
Successful DMA units update existing continuation lanes as described below.
A later exception return into Thumb IWRAM can establish new history through its refill.
Failed DMA units leave history unchanged. Discontinuous entry without an executed refill starts unknown.
Missing or region-crossing Thumb lookahead still leaves that instruction's unused-memory snapshot unsupported.
It does not erase independently known IWRAM lanes.
### Thumb IWRAM refill history
After a successful refill instruction, the emulator uses the resulting PC and instruction state.
If the destination is Thumb IWRAM, it samples two halfwords in order: target T, then T+2.
Both accesses must remain in IWRAM. Normal physical RAM mirrors apply.
These samples establish both word lanes for execution at T. The first instruction then samples T+4 as usual.
Thumb target fetches occur at T and T+2; ARM target fetches occur at T and T+4.
Each successful IWRAM fetch drives its own lanes, in order. Other-region or unavailable fetches do not erase local history.
Normal physical RAM mirrors apply.
A complete Thumb IWRAM target pair establishes both word lanes; the first target instruction then samples T+4.
An ARM IWRAM target pair leaves the second fetched word in the latch.
This applies to ARM/Thumb BX, taken Thumb branches, BL suffixes, Thumb PC writes, and ARM status-restoring returns.
Saved Thumb state, not the target's low bit alone, controls exception-return sampling.
This applies to BX, taken branches, BL suffixes, PC writes, PC loads, and status-restoring returns.
Saved instruction state, not the target's low bit alone, controls exception-return sampling.
Stack and block-load returns finish their data accesses before the target samples.
Failed instructions do not sample a target or replace committed history.
Target samples use strict mapped reads and add no nominal data or device cycles.
The same captured target pair fills the CPU buffer and updates supported IWRAM history; no second target read occurs.
Host inspection does not change captured lanes. A failed target fetch remains a later instruction diagnostic.
Cold direct startup, unsupported target states/regions, and region-crossing lookahead still have no inferred refill history.
DMA between refill and arrival updates this continuation's actual IWRAM access lanes before the target+4 sample.
The same captured target pair fills the CPU buffer and updates IWRAM history; no second target read occurs.
A failed target fetch remains a later instruction diagnostic.
IRQ entry samples its ARM BIOS vector pair without erasing IWRAM lanes.
A BIOS handler can still change those lanes through actual IWRAM data accesses.
The [refill evidence and scope](../research/iwram-bus-history.md#thumb-iwram-refill-extension) distinguish these samples from a full pipeline.
ARM-target refill history, sub-instruction DMA ordering, and exact refill timing remain incomplete.
The [refill evidence and scope](../research/iwram-bus-history.md#thumb-iwram-refill-extension) distinguish these samples from a fully timed pipeline.
Sub-instruction DMA ordering and exact refill timing remain incomplete.
### DMA effects on Thumb IWRAM continuations
Within the instruction-boundary scheduler, DMA finishes before the next CPU PC+4 sample.
For an existing sequential or refill continuation, each successful DMA unit updates IWRAM source lanes, then IWRAM destination lanes.
Within the instruction-boundary scheduler, DMA finishes before the next CPU fetch.
Each successful DMA unit updates IWRAM source lanes, then IWRAM destination lanes, even before the first CPU instruction.
Word accesses replace all lanes. Halfword accesses replace only the addressed halfword.
Other memory regions do not replace the local IWRAM value, even when DMA's channel data changes.
The resumed PC+4 fetch then replaces one halfword before the instruction's unused-memory snapshot is taken.
A resumed Thumb PC+4 fetch then replaces one halfword before the instruction's unused-memory snapshot is taken.
A cold fill instead drives all its newly fetched instructions.
Channel halfword duplication is not a full-word IWRAM access.
A blocked source below work RAM does not drive IWRAM; an IWRAM destination still drives the halfword or word actually written.
A blocked source below work RAM does not drive IWRAM; an IWRAM destination drives the actual halfword or word written.
Multiple units and channel preemption update lanes in access order, not channel-number order.
Host inspection/setup cannot change them.
History changes commit only after a unit succeeds. A failed unit retains the previous successful unit's history.
A failed resumed instruction discards its staged changes, not the completed DMA history.
DMA does not establish an expected PC at cold direct entry. Existing partially known continuations can gain known lanes.
PC discontinuities, unsupported state changes, IRQ entry, and region-crossing lookahead keep their existing conservative rules.
No expected PC or Thumb continuation is needed to record DMA activity.
This implements local lane effects, not a general last-DMA-value override or a persistent pipeline.
Sub-instruction arbitration, DMA during CPU internal cycles, cold/cross-state IWRAM history, and exact resume timing remain incomplete.
This implements local lane effects, not a general last-DMA-value override.
Sub-instruction arbitration, DMA during CPU internal cycles, and exact resume timing remain incomplete.
See [the source evidence and limits](../research/iwram-bus-history.md#dma-continuation-extension).
## Processor status and exceptions
@ -354,7 +352,7 @@ Important timing limits:
- Code S/N counts follow instruction summaries, not a simulated fetch pipeline.
- 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. ARM instruction-buffer and Thumb IWRAM bus-history samples add no extra cycles.
- Refill cost calculation does not read target bytes. ARM/Thumb instruction-buffer and local IWRAM bus-history samples add no extra cycles.
- Target-pair sampling cannot fail the branch early. An invalid branch target still fails on the following instruction fetch.
- Game Pak prefetch is not implemented. WAITCNT bit 14 is stored but does not accelerate execution.
- PHI and SRAM wait fields are stored; PHI output and SRAM mapping are not implemented.

View file

@ -58,10 +58,12 @@ DMA writes to DMA registers return `DmaError::RegisterDestination`; self-modifyi
Other accesses use the existing memory map, alignment checks, mirrors, and I/O write rules.
Other unmapped reads and cartridge writes remain diagnostics, not general DMA open-bus behavior.
A failed unit changes no memory, device state, or clock. Earlier units remain committed; the failed unit remains pending.
Successful units update existing Thumb IWRAM continuation lanes for their actual IWRAM reads and writes, in that order.
Successful units update local IWRAM lanes for their actual IWRAM reads and writes, in that order.
No CPU continuation is required; DMA can establish lanes before the first CPU instruction.
Accesses elsewhere leave those local lanes unchanged. Failed units preserve history.
The resumed CPU's PC+4 fetch then updates its addressed halfword. No universal DMA-value override is used.
See [bounded IWRAM DMA history](cpu.md#dma-effects-on-thumb-iwram-continuations) for the scope.
A resumed Thumb PC+4 fetch updates its addressed halfword; a cold pipeline fill drives all its new samples.
No universal DMA-value override is used.
See [local IWRAM DMA history](cpu.md#dma-effects-on-thumb-iwram-continuations) for the scope.
## Retained channel data
@ -88,5 +90,5 @@ Earlier successful units remain committed. Normal destination validation, nomina
This error policy is not a model of hardware data aborts.
See [the latch evidence and scope](../research/dma-data-latches.md).
General DMA open-bus reads, cross-state IWRAM history, and sub-instruction bus ownership remain unimplemented.
General DMA open-bus reads and sub-instruction bus ownership remain unimplemented.
In particular, the known channel value does not supply unused/write-only I/O reads or CPU open-bus snapshots.

View file

@ -50,7 +50,7 @@ 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, 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, the distinct Thumb region/history rules, and PC-dependent BIOS read protection. The Thumb implementation follows its 16-bit-region and BIOS/OAM formulas. IWRAM has bounded sequential lane history and Thumb target-pair refill samples; region transitions remain unsupported.
- [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, the distinct Thumb region/history rules, and PC-dependent BIOS read protection. The Thumb implementation follows its 16-bit-region and BIOS/OAM formulas. IWRAM has persistent local lanes across ARM/Thumb fetches, data accesses, and DMA; region-crossing snapshots remain unsupported.
- [nocash GBA open-bus discussion](https://www.ngemu.com/threads/gba-open-bus.170809/), for ARM prefetch, repeated Thumb halfwords in 16-bit regions, aligned BIOS/OAM words, and history-dependent IWRAM lanes. Its data-load overwrite note prevents treating P+2 as a universal IWRAM rule.
- [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. Also reviewed its BIOS region-exit prefetch retention and protected-load lane selection.
- [GBATEK GBA memory map and bus widths](https://problemkaputt.de/gbatek-gba-memory-map.htm).
@ -110,7 +110,10 @@ IWRAM lane-history research:
- [ares IWRAM accesses](https://github.com/ares-emulator/ares/blob/6f6786e04f0822a3475463df284f313ab8518d51/ares/gba/cpu/memory.cpp) and [bus dispatch](https://github.com/ares-emulator/ares/blob/6f6786e04f0822a3475463df284f313ab8518d51/ares/gba/cpu/bus.cpp), for separate byte-lane retention and debugger isolation.
- [jgenesis issue 676](https://github.com/jsgroth/jgenesis/issues/676) and [its correction](https://github.com/jsgroth/jgenesis/commit/fab6e2ccc60e492dd68b7f1e927b0829a6d80195), for distinguishing the IWRAM latch from reads to other regions.
See [IWRAM Thumb bus history](research/iwram-bus-history.md). These were source reviews, not local hardware or external-emulator test runs.
See [persistent IWRAM bus history](research/iwram-bus-history.md).
The pinned ares helpers also support state-independent local ownership: they do not gate updates on the executing PC or CPU state.
The pinned NanoBoyAdvance loop/refill helpers below support driving actual new samples, without replaying retained instruction slots.
These were source reviews, not local hardware or external-emulator test runs.
IWRAM refill research:
@ -136,7 +139,7 @@ DMA IWRAM continuation research:
Its [DMA controller](https://github.com/jsgroth/jgenesis/blob/fab6e2ccc60e492dd68b7f1e927b0829a6d80195/backend/gba-core/src/dma.rs) and [memory storage](https://github.com/jsgroth/jgenesis/blob/fab6e2ccc60e492dd68b7f1e927b0829a6d80195/backend/gba-core/src/memory.rs) distinguish channel data from local bus lanes.
- The pinned ares DMA, bus dispatch, and IWRAM helpers above independently use the same local lane ownership.
See [the bounded DMA continuation extension](research/iwram-bus-history.md#dma-continuation-extension).
See [the DMA local-lane extension](research/iwram-bus-history.md#dma-continuation-extension).
These source reviews do not establish sub-instruction arbitration, exact CPU resume timing, or hardware conformance.
DMA resume-timing research:

View file

@ -75,7 +75,8 @@ The current instruction address still selects nominal code cost rather than a se
ARM PC+8 open bus and protected BIOS readback remain separate snapshots.
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.
ARM IWRAM target samples now drive the [persistent local latch](iwram-bus-history.md).
Complete BIOS and general-bus history remain unimplemented.
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

@ -56,7 +56,7 @@ Stores and 128 KiB ROM boundaries already use N and receive no duplicate cost.
Game Pak prefetch remains unimplemented; WAITCNT bit 14 does not alter this nominal timing path.
This change does not select a DMA value for CPU open-bus reads.
DMA channel data, bounded IWRAM lane history, and protected BIOS history remain separate.
DMA channel data, persistent local IWRAM lanes, and protected BIOS history remain separate.
General bus-data ownership and exact resume timing still need independent per-access validation.
## Original regression coverage

View file

@ -1,11 +1,12 @@
# IWRAM Thumb bus history
# Persistent IWRAM bus history
## Finding
Internal work RAM (IWRAM) retains its own bus lanes. A previous CPU load result is not a universal replacement.
Internal work RAM (IWRAM) retains its own bus lanes, independently of the CPU's instruction state and executing region.
An IWRAM byte, halfword, or word access updates only the addressed lanes of that latch.
A Thumb fetch updates one halfword. Reads and writes elsewhere do not replace the IWRAM latch.
This distinction matters when Thumb code reads another memory region before an unused-memory read.
ARM instruction fetches drive words. Thumb instruction fetches drive halfwords.
Reads and writes elsewhere do not replace the IWRAM latch.
Neither a prior CPU destination register nor a DMA channel value is a universal replacement.
For sequential Thumb execution at P, the fetch at P+4 updates:
@ -21,127 +22,107 @@ Using current bytes at P+2 would lose observed history and ignore data-access ch
- [GBATEK](https://problemkaputt.de/gbatek-gba-unpredictable-things.htm) documents the two Thumb IWRAM lanes and warns about overwritten history.
- [mGBA issue 1575](https://github.com/mgba-emu/mgba/issues/1575) reports hardware-based alignment expectations and read/write effects on IWRAM open bus.
- [ares IWRAM access implementation](https://github.com/ares-emulator/ares/blob/6f6786e04f0822a3475463df284f313ab8518d51/ares/gba/cpu/memory.cpp) retains separate IWRAM lanes for byte, halfword, and word reads and writes.
Its [bus implementation](https://github.com/ares-emulator/ares/blob/6f6786e04f0822a3475463df284f313ab8518d51/ares/gba/cpu/bus.cpp) distinguishes this latch from the general data bus and debugger access.
Its helpers update the same latch without testing CPU state, executing PC, or sequential continuation.
Its [bus implementation](https://github.com/ares-emulator/ares/blob/6f6786e04f0822a3475463df284f313ab8518d51/ares/gba/cpu/bus.cpp) selects those helpers for CPU and DMA accesses and isolates debugger reads.
- [jgenesis issue 676](https://github.com/jsgroth/jgenesis/issues/676) identifies failures after byte/halfword reads from other regions.
The [correction at fab6e2cc](https://github.com/jsgroth/jgenesis/commit/fab6e2ccc60e492dd68b7f1e927b0829a6d80195) adds a separate IWRAM latch.
The commit reports that its openbuster tests pass. That report is upstream evidence, not a test run by this project.
- [NanoBoyAdvance's CPU loop and refill helpers](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/arm/arm7tdmi.hh) fetch the new instruction before execution and sample target pairs in instruction-width order.
These sources support a separate lane-retaining latch, rather than a copy of the last CPU destination register.
These sources support local bus ownership rather than a latch restricted to consecutive Thumb instructions.
Our implementation and synthetic programs are original. No external source code or test ROM was imported.
No physical-hardware measurement or external emulator differential run was performed here.
## Bounded implementation
## Implementation and cold fills
`memory/iwram_bus.rs` records lane values and a known-bit mask during consecutive Thumb instructions in IWRAM.
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.
`memory/iwram_bus.rs` stores persistent lane values and a known-bit mask.
There is no expected PC or CPU-state tag on this latch. The instruction buffer retains its own PC/state tags.
Every instruction stages changes from the previously committed lanes.
The [shared fetch samples](shared-fetch-samples.md) drive successful IWRAM fetches at their actual address and instruction width.
Successful IWRAM data accesses then drive raw bus values before load rotation or sign extension.
CPU code can run in BIOS, ROM, EWRAM, or IWRAM without changing that rule.
A completed sequential instruction commits the staged history for the next exact virtual PC.
A failed instruction discards all staged changes and preserves the last committed history.
The existing whole-instruction diagnostic policy still applies; this is not hardware data-abort behavior.
All unused-memory reads within one instruction use its entry snapshot.
A retained pipeline drives only its newly fetched lookahead. It never replays current/decode bus samples.
A cold or discontinuous pipeline supplies current, next, and lookahead samples in order.
A valid Thumb IWRAM cold fill therefore establishes both lanes before the first data read.
This follows actual samples already performed by the interpreter; it is not an invented previous P+2 memory read.
Debugger invalidation clears only instruction retention, but the following cold fill drives its new samples.
The model starts with unknown lanes. An unused-memory load requires a complete known word, even for a narrower load.
Ordinary instructions and mapped data accesses can establish history without requiring a prior complete word.
Consecutive halfword fetches or an IWRAM word access can make every lane known.
Missing lookahead and 16 MiB region-crossing fetches retain diagnostics.
A successful instruction commits its staged lanes regardless of its resulting state, PC, or region.
A failed instruction discards every staged change, including cold-fill samples.
Successful PC-writing instructions then apply target fetches after their data accesses.
Other-region execution, BIOS IRQ entry, and unavailable targets do not erase known local lanes.
Host inspection and setup never drive the latch.
The initial sequential-only implementation invalidated history after all refill instructions.
The extension below now establishes target-pair history for successful refills into Thumb IWRAM.
The existing ARM/Thumb timing classifiers identify these refills for both timed and untimed CPU stepping.
Execution outside Thumb IWRAM still ends a sequential history sequence.
An accepted machine IRQ invalidates history. Failed DMA units preserve it.
The DMA extension below updates existing continuation lanes from successful IWRAM accesses.
This is not a full IWRAM bus model across all CPU states. ARM-target history and DMA-to-CPU ordering remain incomplete.
It is not a persistent instruction pipeline. Exact per-access device timing remains unverified.
Initial lanes remain unknown until observed accesses establish them.
Byte/halfword accesses may establish only part of the word; a word access establishes all lanes.
All unused-memory reads within an instruction still use one entry snapshot.
A Thumb fetch crossing a 16 MiB region boundary leaves that instruction's unused-memory snapshot unsupported.
Successful IWRAM accesses still update the local latch independently of this conservative snapshot policy.
## Thumb IWRAM refill extension
The [ARM7TDMI branch sequence](https://support.arm.com/documentation/ddi0029/g/instruction-cycle-timings/branch-and-branch-with-link) fetches the destination, then destination plus instruction width.
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.
Thumb destinations use T and T+2; ARM destinations use T and T+4.
The same captured samples fill the instruction buffer and drive local IWRAM lanes, with no duplicate memory reads.
Each successful IWRAM sample drives its lanes independently. Other-region or unavailable samples leave them unchanged.
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.
A complete Thumb IWRAM target pair replaces both halfwords.
The first target instruction fetches T+4, replacing one halfword while retaining the captured T+2 lane.
An ARM IWRAM target pair leaves T+4's word in the latch.
Data loads and status restoration finish first, so saved instruction state determines the refill width.
This covers actual ARM/Thumb BX, taken Thumb branches, BL suffixes, PC-writing Thumb operations, and ARM status-restoring returns.
This covers BX, taken branches, BL suffixes, PC writes/loads, and status-restoring returns.
The Thumb BL prefix alone does not refill.
Failed instructions retain the previous committed history. An unsupported or incomplete target sample does not fail the branch early.
Unmapped instruction targets still fail on the following fetch.
Cold direct startup and arbitrary PC changes do not manufacture a refill.
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.
The refill-only implementation invalidated history on successful DMA, including between refill and arrival.
The DMA extension below replaces that invalidation within existing continuations.
ARM-target refill history, exact pipeline timing, and sub-instruction DMA ordering remain incomplete.
No external emulator differential run or physical-hardware test was performed.
A branch with an unavailable target can complete; the later instruction fetch retains its strict diagnostic.
A failed branch or return does not commit data or target lane changes.
IRQ vector samples in BIOS leave IWRAM unchanged, but an IRQ handler's IWRAM data access updates it normally.
## DMA continuation extension
Two independent implementations route DMA through the same local IWRAM lane updates as CPU accesses:
- [ares bus dispatch](https://github.com/ares-emulator/ares/blob/6f6786e04f0822a3475463df284f313ab8518d51/ares/gba/cpu/bus.cpp) selects its IWRAM helpers for both CPU and DMA.
[Its DMA controller](https://github.com/ares-emulator/ares/blob/6f6786e04f0822a3475463df284f313ab8518d51/ares/gba/cpu/dma.cpp) performs a source read before its destination write.
- [ares DMA](https://github.com/ares-emulator/ares/blob/6f6786e04f0822a3475463df284f313ab8518d51/ares/gba/cpu/dma.cpp) performs a source read before its destination write through the shared bus dispatch above.
- [jgenesis bus at fab6e2cc](https://github.com/jsgroth/jgenesis/blob/fab6e2ccc60e492dd68b7f1e927b0829a6d80195/backend/gba-core/src/bus.rs) routes `AccessCtx::DMA` through `read_iwram` and `write_iwram`.
Both call `iwram_update_open_bus`, which changes only addressed lanes and drives the general bus from the local word.
Its `try_progress_dma` performs source reads and destination writes in order.
Both call `iwram_update_open_bus`. Its DMA controller performs source reads and destination writes in order.
Both run pending DMA before a CPU bus access. The access that resumes determines the subsequent bus value.
These source reviews support local lane effects, not a universal first-instruction DMA override.
They do not determine every hardware arbitration edge or validate our instruction-boundary scheduling.
Our instruction-boundary scheduler commits source lanes, then destination lanes, only after a complete unit succeeds.
A blocked source below work RAM drives no IWRAM read lanes.
A halfword destination drives its actual selected half, not both halves of a duplicated channel word.
DMA can establish local lanes before any CPU instruction, without an expected continuation PC.
Failed DMA units preserve the previous committed latch.
Our bounded model applies those effects only when an existing Thumb IWRAM continuation has an expected next PC.
After a successful unit, the source and destination independently update their addressed IWRAM lanes.
DMA elsewhere leaves those lanes unchanged. A blocked source drives no IWRAM read lanes.
The destination sees the actual selected halfword, not an assumed duplicated full word.
The next PC+4 sample updates one halfword before the resumed instruction takes its snapshot.
This also applies between a completed refill and its first target instruction.
A retained Thumb pipeline resumes with one PC+4 sample; a cold pipeline resumes with its full fill sequence.
These actual fetches determine how much DMA history remains visible.
DMA channel values never overwrite CPU instruction-buffer slots or protected BIOS history automatically.
Sub-instruction arbitration and DMA during CPU internal cycles remain unmodeled.
Only complete successful units commit history, preserving the project's atomic diagnostic policy.
Cold direct entry still has no expected continuation PC. DMA does not manufacture one, even after a word access.
An existing partially known continuation can gain lanes. IRQ entry and CPU discontinuities retain their invalidation rules.
General DMA open-bus reads, cross-state history, sub-instruction arbitration, and DMA during CPU internal cycles remain outside this subset.
This extension models bus lanes, not self-modifying instruction execution.
The later [ARM](arm-instruction-buffer.md) and [Thumb](thumb-instruction-buffer.md) instruction buffers separately retain instructions across DMA.
## Limits and original coverage
No physical-hardware measurements or external emulator differential runs were performed.
All regression programs are original, with a separate byte-array lane reference in the DMA matrix tests.
This is persistent local IWRAM history, not a complete general-bus implementation or cycle-accurate pipeline.
ARM unused-memory reads still use their supported PC+8 snapshot.
BIOS protection remains separately retained and image-derived.
General DMA open bus, unused/write-only I/O, disabled RAM, exact region-crossing snapshots, and per-access timing remain incomplete.
Nominal CPU/DMA costs do not change.
## Original regression coverage
Original tests cover:
- Both code alignments, IWRAM mirrors, and physical RAM wrap.
- Captured halfwords that remain unchanged by host inspection or later host writes.
- Byte/halfword/word data reads and writes, sign extension, load rotation, and all processor modes.
- Other-region accesses that leave IWRAM lanes unchanged.
- Multiple-register transfers, final-word retention, and writeback.
- Unknown lanes, establishment of known history, staged-access rollback, and retry.
- Taken and untaken branches, PC writes to fallthrough, discontinuities, IRQ entry, and DMA success/failure.
- ARM/Thumb source regions, both target alignments, refill sample ordering, stack/block returns, and saved User/System banks.
- Actual SWI/IRQ return handlers, failed refills, unmapped targets, separate BIOS history, and DMA between refill and arrival.
- DMA source/destination regions and alignments, word/halfword widths, mirrors, blocked-source lane selection, and other-region isolation.
- Channel preemption, completed-unit order, DMA/instruction failure retention, partial/cold continuations, and unchanged other CPU snapshot rules.
- Equal timed/untimed CPU results and unchanged nominal data/device costs.
- Cold fills, both Thumb alignments, mirrors, physical RAM wrap, and entry snapshots.
- ARM word fetches and ARM/Thumb target pairs, without replaying retained instruction slots.
- ARM byte/halfword/word reads and stores from EWRAM, plus Thumb data access from EWRAM.
- Preserved lanes through state changes, non-IWRAM execution, IRQ entry, and BIOS handler fetches.
- DMA before CPU startup, source/destination ordering, channel preemption, and blocked-source lane selection.
- Failed sequential instructions, failed cold fills, failed refills, and failed DMA units.
- Separate BIOS values, host isolation, debugger refill effects, timed/untimed equality, and unchanged nominal costs.
The persistent-history regressions failed under the previous Thumb-continuation-only implementation.
Existing cold-entry tests now assert captured lane values instead of the removed unsupported-history diagnostic.
Region-crossing and strict unmapped-fetch diagnostics remain covered.
## Validation result
The sequential-history regressions reproduced the old unsupported-read failures before implementation.
The refill regressions also failed on the prior invalidation-only path, then passed with target-pair sampling.
The DMA continuation regressions reproduced unsupported-read failures under unconditional DMA invalidation, then passed with local lane updates.
Workspace and core/demo tests pass on Darwin arm64 in debug and release builds.
Formatting, lint checks, rustdoc, preparation tests, native ROM windows, and graphics smoke modes also pass.
Public ARM, Thumb, memory, and BIOS reports match their previous passing results exactly.
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 prior shared-fetch reports exactly, including nominal cycle counts.
Debug and release reports also match for the same fixture paths.
Those public suites remain regression checks, not independent IWRAM-history conformance tests.
These public suites remain regression checks, not independent IWRAM-history or hardware-timing conformance tests.

View file

@ -33,11 +33,12 @@ 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`.
For retained pipelines, the CPU passes only its new P+8 or P+4 sample to `begin_cpu_access`.
The later persistent IWRAM extension also passes newly sampled current/decode entries for cold fills.
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.
After successful control flow, the same target samples fill the instruction buffer and update local IWRAM history in either state.
Machine IRQ entry uses this refill path; BIOS vector fetches leave IWRAM lanes unchanged.
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.
@ -46,11 +47,10 @@ A retry takes a new lookahead sample while retaining the previously committed in
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
## Current scope and limits
- Cold pipeline filling does not establish previously unknown IWRAM history.
- Cold fills and cross-state local IWRAM history are now implemented in the [persistent-latch extension](iwram-bus-history.md).
- 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.

View file

@ -63,7 +63,7 @@ Original tests in `crates/gba-core/src/cpu/thumb_bios_access_tests.rs` cover:
- 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, retained cold-IWRAM Thumb open-bus diagnostics, nominal costs, and device progression.
- Separate host reads, independent cold-IWRAM fetch history, nominal costs, and device progression.
## Validation result

View file

@ -50,8 +50,8 @@ CPU clones own independent buffers; full CPU equality includes them.
## Separation from bus history and timing
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.
Cold instruction-buffer filling now drives its actual IWRAM samples under the [persistent-latch model](iwram-bus-history.md).
Successful DMA can establish or update IWRAM bus lanes without changing retained CPU instructions.
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.

View file

@ -28,7 +28,7 @@ Update this file when a feature lands or a limit is removed.
- Original ARM graphics demo that uses DMA, BIOS VBlank waiting, and a ROM-side IRQ callback.
- Sixteen active 32-bit CPU registers, with ARM7 mode-specific register banks.
- Persistent ARM/Thumb instruction buffering, sequential fetch retention, branch/vector target pairs, and deferred fetch diagnostics.
- Shared mapped fetch samples for instruction retention, supported open-bus values, BIOS snapshots, and Thumb IWRAM refill history.
- Shared mapped fetch samples for instruction retention, supported open-bus values, BIOS snapshots, and IWRAM refill history.
- User, System, Supervisor, IRQ, FIQ, Abort, and Undefined modes.
- Current and saved program status registers (`CPSR`/`SPSR`), with `MRS`/`MSR` transfers.
- ARM/Thumb software interrupts (`SWI`), exception vectors, and status-restoring returns.
@ -77,8 +77,8 @@ Update this file when a feature lands or a limit is removed.
- 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.
- Thumb unused-memory reads use repeated PC+4 halfwords in 16-bit code regions and aligned PC+4 words in BIOS/OAM.
- Sequential Thumb IWRAM history retains fetched and data-access lanes, with known-bit tracking and diagnostic rollback.
- Successful refills into Thumb IWRAM establish bus history from the target and target+2 halfwords, including exception returns.
- Persistent IWRAM lanes track ARM/Thumb fetches, CPU data accesses, and successful DMA, with known-bit tracking and diagnostic rollback.
- Cold fills and ARM/Thumb target pairs update local history; state changes and BIOS IRQ entry do not erase it.
- 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.
@ -137,11 +137,11 @@ Other public suites remain unverified. These results do not establish full CPU o
The runner requires known completion addresses and has no debug-port protocol, scripted input, or rendered-image assertions.
ARM data reads from unused address ranges use a bounded [PC+8 open-bus snapshot](hardware/cpu.md#arm-unused-memory-data-reads).
Thumb reads use [region-dependent snapshots](hardware/cpu.md#thumb-unused-memory-data-reads) and bounded sequential IWRAM lane history.
Cold or invalidated IWRAM history remains diagnostic until all lanes are known.
Refills into Thumb IWRAM establish new target-pair history after the instruction succeeds.
Accepted machine IRQs, unsupported refills, and other execution states/regions invalidate continuation history.
Successful DMA updates existing Thumb IWRAM continuation lanes for actual IWRAM accesses, before the resumed PC+4 sample.
Thumb reads use [region-dependent snapshots](hardware/cpu.md#thumb-unused-memory-data-reads) and persistent local IWRAM lanes.
Only actual IWRAM fetches and data accesses drive those lanes, independently of CPU state and executing region.
Cold fills and target pairs establish both lanes when all required samples are mapped.
State changes, other-region execution, and BIOS IRQ entry preserve local history; failed instructions discard staged updates.
Successful DMA can establish lanes before CPU startup. Resumed fetches then update their addressed lanes.
ARM/Thumb instruction buffering retains sequential instructions and refill target pairs.
Instruction buffering and supported bus history now consume the same mapped fetch samples, without sharing retained storage.
Complete ARM bus history, exact region-crossing bus behavior, general DMA handoff, and unused/write-only I/O open bus remain incomplete.
@ -155,7 +155,7 @@ Writes to cartridge addresses return errors instead of modeling cartridge hardwa
## Next steps
1. Extend the shared fetch-sample model to verified cross-state bus history and region crossings.
1. Extend shared fetch samples to verified general-bus history and region-crossing snapshots.
General DMA open-bus reads and sub-instruction arbitration remain separate from retained channel data and local IWRAM lanes.
Keep unknown history diagnostic until fetch ordering and latch ownership have independent coverage.
Validate BIOS snapshots with independent hardware tests before claiming exact refill or data-access history.

View file

@ -65,10 +65,11 @@ Compare/status CPU regressions verify saved flags/masks, bank switching, sequent
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.
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/cold-IWRAM/DMA diagnostics.
They check branch-target resampling, missing lookahead, skipped conditions, nominal timing, device progress, and retained host/fetch/DMA diagnostics.
Thumb open-bus tests cover supported code regions, both instruction alignments, ROM windows, mirrors, and physical memory wrap.
They check widths, sign extension, rotation, block/stack/PC loads, aliases, modes, state preservation, and sequential/branch resampling.
Short-ROM tests require only the fetched halfword in 16-bit regions. Cold IWRAM and region-crossing tests retain explicit diagnostics.
Short-ROM tests require only the fetched halfword in 16-bit regions. Region-crossing tests retain explicit diagnostics.
Cold IWRAM tests now assert actual current/decode/lookahead lane updates before the first data read.
Sequential IWRAM regressions check retained lanes across fetches, reads, writes, mirrors, and physical RAM wrap.
They cover raw bus values before load rotation/sign extension, all modes, block transfers, and isolation from other memory regions.
Known-bit tests reject incomplete words. Transaction tests verify discarded partial updates and preserved history after errors.
@ -77,8 +78,9 @@ Refill tests cover ARM/Thumb BX, Thumb branches and BL, high-register PC writes,
They check both target alignments, mirrors/wrap, captured target+2 versus later target+4, widths, sign extension, and rotation.
Original SWI/IRQ handlers verify return-time history. Failed returns, cold entry, region boundaries, DMA lane effects, and BIOS isolation retain checks.
DMA tests compare source/destination lane updates against a byte-array reference, then apply the resumed PC+4 fetch.
They cover channel preemption, blocked sources, failures, mirrors, partial/cold continuations, and other-region snapshot isolation.
IRQ and PC-discontinuity tests retain conservative invalidation; timed/untimed tests verify CPU results and nominal clock costs.
They cover channel preemption, blocked sources, failures, mirrors, pre-start lane establishment, cold fills, and other-region snapshot isolation.
IRQ tests verify local-latch preservation; PC discontinuities verify new pipeline-fill samples.
Timed/untimed tests verify CPU results and nominal clock costs.
Host/fetch isolation, separate BIOS history, unsupported stores/data regions, nominal device progression, and DMA diagnostics also have checks.
ARM instruction-buffer tests verify retained P+4/P+8 words across CPU stores, DMA transfers, and host writes.
They cover refill target pairs, PC writes to fallthrough, conditional branches, ARM/Thumb transitions, exception entry/return, and deferred fetch errors.
@ -88,6 +90,9 @@ The display-runner error test allows an already buffered branch to complete befo
Thumb instruction-buffer tests verify retained P+2/P+4 halfwords, BL prefix/suffix behavior, and state-tagged target pairs.
They also cover stores, host/DMA writes, saved-state returns, deferred errors, mirrors, debugger repair, and nominal timing.
Bus-history repair tests explicitly invalidate buffered code or repair a data address; failure checks retain full CPU equality.
Debugger invalidation itself preserves local lanes, but the next cold fill drives current mapped instruction bytes.
Persistent-latch tests cover ARM data accesses, ARM/Thumb target pairs, non-IWRAM code, IRQ handlers, and pre-start DMA.
They also verify partial lanes and rollback of both cold-fill and resumed-instruction samples.
Shared fetch-sample tests mutate memory between internal sampling and consumption to reject duplicate reads.
They cover ARM words, Thumb narrow/wide buses, IWRAM lanes, target pairs, BIOS retention, and retry after failure.
This test-only split is not a DMA scheduling feature. Separate tests verify strict fetch errors and no timing/history side effects.
@ -257,7 +262,8 @@ They cover all processor modes, load widths, lanes, rotation, both Thumb code al
They verify direct reads while executing inside BIOS, re-entry, SWI/IRQ returns, host-inspection isolation, and original-boot provenance.
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.
Unknown Thumb IWRAM history remains diagnostic. Separate tests cover sequential/refill history, general Thumb snapshots, and retained BIOS history.
Unknown IWRAM lanes remain diagnostic until observed accesses establish them; mapped cold pipeline fills now establish both halves.
Separate tests cover sequential/refill history, general Thumb snapshots, and retained BIOS history.
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.