gameboy-rust/docs/research/iwram-bus-history.md

9.3 KiB
Raw Permalink Blame History

Persistent IWRAM bus history

Finding

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. 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:

  • Bits 0–15 when P is word-aligned.
  • Bits 16–31 when P has only halfword alignment.

The other halfword retains its previous IWRAM value. Without intervening IWRAM data accesses, consecutive fetches produce GBATEK's usual P+2/P+4 combination. Using current bytes at P+2 would lose observed history and ignore data-access changes.

Evidence

  • GBATEK documents the two Thumb IWRAM lanes and warns about overwritten history.
  • mGBA issue 1575 reports hardware-based alignment expectations and read/write effects on IWRAM open bus.
  • ares IWRAM access implementation retains separate IWRAM lanes for byte, halfword, and word reads and writes. Its helpers update the same latch without testing CPU state, executing PC, or sequential continuation. Its bus implementation selects those helpers for CPU and DMA accesses and isolates debugger reads.
  • jgenesis issue 676 identifies failures after byte/halfword reads from other regions. The correction at fab6e2cc 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 fetch the new instruction before execution and sample target pairs in instruction-width order.

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.

Implementation and cold fills

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 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 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.

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 vector fetches, and unavailable targets do not erase known local lanes. The later IRQ fetch extension updates addressed IWRAM lanes through its discarded old-state source fetch. Host inspection and setup never drive the latch.

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. The boundary extension selects the bus observation from the actual new fetch region. An EWRAM-to-IWRAM lookahead uses the resulting local lanes; incomplete lanes still leave that instruction's snapshot unknown.

Thumb IWRAM refill extension

The ARM7TDMI branch sequence fetches the destination, then destination plus instruction width. 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.

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 BX, taken branches, BL suffixes, PC writes/loads, and status-restoring returns. The Thumb BL prefix alone does not refill. 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 DMA performs a source read before its destination write through the shared bus dispatch above.
  • jgenesis bus at fab6e2cc routes AccessCtx::DMA through read_iwram and write_iwram. Both call iwram_update_open_bus. Its DMA controller performs source reads and destination writes in order.

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.

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.

Limits and original coverage

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, unsupported-region snapshots, and per-access timing remain incomplete. Nominal CPU/DMA costs do not change.

Original tests cover:

  • 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. Unknown-lane boundary cases and strict unmapped-fetch diagnostics remain covered.

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 prior shared-fetch reports exactly, including nominal cycle counts. Debug and release reports also match for the same fixture paths. These public suites remain regression checks, not independent IWRAM-history or hardware-timing conformance tests.