From 1dfd052ec23f1c2b20a63abbc02507aa65a1b446 Mon Sep 17 00:00:00 2001 From: DoubleGate Date: Tue, 25 Aug 2026 00:19:05 -0400 Subject: [PATCH 1/2] docs(accuracy): NROM provides PRG-RAM where the board has none Records a correctness finding in `docs/accuracy-ledger.md`, and updates the MiSTer task board for v2.6.3's rung-5 progress. ## The finding `crates/rustynes-mappers/src/m000_nrom.rs` allocates 8 KiB of PRG-RAM unconditionally, with a comment saying it is so accesses to $6000-$7FFF "don't fall off the edge". A read of that window therefore returns $00. On hardware an NROM board decodes nothing there. The read floats, and returns OPEN BUS -- the last value the data bus held. This is the emulator default `nesdev_wiki/Open_bus_behavior.xhtml` names as a problem, in its own words: "A few games read the region $6000-7FFF but have no WRAM present here. This can be a problem for emulators, as the original iNES file format had no way to specify a lack of WRAM, leaving the emulator to provide WRAM behavior in that region by default. The NES 2.0 format corrects this with a WRAM size field." The same page names titles that break on the WRAM answer: Low G Man (a crash in Chapter 1 Scene 3B, or boomerang graphical glitches) and Battletoads & Double Dragon (a $00 read at the end of level 1 crashes it). ## Not fixed here, and the reason is scope rather than doubt The fix changes shipped behaviour on every iNES-header NROM cartridge. It needs the NES 2.0 WRAM-size field honoured where present, a decision about what to do when the header cannot say, the per-game database consulted, and the full accuracy battery plus the commercial-ROM oracle re-run. That is the same shape as the header-override hazard that froze Wizards & Warriors (ADR 0031), and it is not a drive-by edit inside a co-simulation step. Recorded with a citation and named symptoms so it is a tracked item rather than something quietly carried. ## How it was found is the point The v2.6.3 co-simulation step wrote a `cpu_bus` module in SystemVerilog FROM THE WIKI and ran it against this emulator. The DUT and the oracle agreed on $4016, $4017, $5000 and $5C34, and disagreed only in $6000-$7FFF. This is the FIRST time the verification ladder has found the oracle wrong rather than the device under test. The v2.5.0 plan listed it in advance as risk 6: "the oracle can be wrong. 141/141 is not matches silicon." The mitigation it named -- label every rung by whether it has an INDEPENDENT oracle -- is what made the disagreement legible instead of looking like a DUT defect. ## Task board v2.6.3 now shows four rung-5 pieces delivered and gated in the sibling repository (NROM cartridge, work RAM, CPU bus, controller ports; 50 gates green), and names the two that remain: DMA sequencing, which is still testbench code, and `nes_top` itself, which is still a shell. Documentation only. No crate changes, so AccuracyCoin and nestest are untouched by construction. --- docs/accuracy-ledger.md | 33 +++++++++++++++++++++++++++++++++ to-dos/mister/TASKS.md | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/docs/accuracy-ledger.md b/docs/accuracy-ledger.md index babb88f2..f70c3e36 100644 --- a/docs/accuracy-ledger.md +++ b/docs/accuracy-ledger.md @@ -104,6 +104,39 @@ disposition under the v2.1.0 "Fathom" accuracy-remediation line `palette_ram`, work RAM) is already serialized, and the selections themselves are config re-applied on load like `region`. +- **NROM provides PRG-RAM at `$6000-$7FFF` where the board has none, and the + MiSTer co-simulation DUT found it (2026-08-25, v2.6.3 rung 5).** + `crates/rustynes-mappers/src/m000_nrom.rs` allocates 8 KiB of PRG-RAM + unconditionally so accesses "don't fall off the edge", so a read of that + window returns `$00`. On hardware an NROM board decodes nothing there and the + read floats — it returns **open bus**, the last value the data bus held. + + This is the emulator default `nesdev_wiki/Open_bus_behavior.xhtml` names as a + problem in its own words: *"A few games read the region $6000-7FFF but have no + WRAM present here. This can be a problem for emulators, as the original iNES + file format had no way to specify a lack of WRAM, leaving the emulator to + provide WRAM behavior in that region by default. The NES 2.0 format corrects + this with a WRAM size field."* The page then names titles that break on the + WRAM answer — **Low G Man** (a crash in Chapter 1 Scene 3B, or boomerang + graphical glitches) and **Battletoads & Double Dragon** (a `$00` read at the + end of level 1 crashes it). + + **Not fixed here, and the reason is scope rather than doubt.** The fix is a + behaviour change to the shipped emulator on every iNES-header NROM cartridge, + so it needs the NES 2.0 WRAM-size field honoured where present, a decision + about iNES-header defaults, the per-game database consulted, and the full + accuracy battery plus the commercial-ROM oracle re-run — the same shape as the + header-override hazard that froze *Wizards & Warriors* (ADR 0031). It is a + correctness item with a citation and named symptoms, recorded rather than + quietly carried. + + **How it was found is the point.** The v2.6.3 co-simulation step wrote a + `cpu_bus` module from the wiki and ran it against this emulator; the DUT and + the oracle agreed on `$4016`, `$4017`, `$5000` and `$5C34` and disagreed only + in `$6000-$7FFF`. This is the first time the verification ladder has found the + **oracle** wrong rather than the DUT, which the v2.5.0 plan listed in advance + as risk 6: *"the oracle can be wrong. 141/141 is not 'matches silicon'."* + ## Ignored-test dispositions (all 20) Every `#[ignore]`'d test in the workspace, with its disposition. **None is an diff --git a/to-dos/mister/TASKS.md b/to-dos/mister/TASKS.md index 2ad78a3d..ca648b52 100644 --- a/to-dos/mister/TASKS.md +++ b/to-dos/mister/TASKS.md @@ -61,7 +61,41 @@ Legend: `[ ]` open · `[~]` in progress · `[x]` done ## v2.6.3 – v2.6.4 — rung 5, NROM + AccuracyCoin -- [ ] v2.6.3 NROM cartridge; first end-to-end AccuracyCoin run +- [~] v2.6.3 NROM cartridge; first end-to-end AccuracyCoin run + - [x] `rtl/cart/cart_nrom.sv` — written from `nesdev_wiki/NROM.xhtml`, + instantiated beneath the existing gates and cross-checked per access + rather than trusted. Runtime write port, not `$readmemh`: on MiSTer the + image arrives from `hps_io` at runtime, so the port that ships is the + port that is tested + - [x] `rtl/wram.sv` — 2 KiB and the `$0000-$1FFF` decode. **50 gates green**, + 5 mutations CAUGHT. Its mirroring was invisible to the entire prior + corpus: all 48 ROMs stay inside `$0000-$07FF`, so three mirror-only + mutations came back **NOT CAUGHT** against `blargg03`'s 893,410 cycles + and CAUGHT against the new `wrammirror044`. A stimulus NOT CAUGHT — the + fourth distinct meaning that verdict has had here + - [x] The harness's own memory model was wrong first: it wrote `mem[addr]` + unmirrored, so a correctly-mirrored RAM would have been reported as the + defect. Fixed before the RTL was trusted + - [x] `rtl/cpu_bus.sv` — the address decode, read mux and open-bus latch. + **The flat 64 KiB array no longer answers the CPU**; the RTL bus does, + and the harness's array survives only as a model scoped to the windows + it can independently speak for. The testbench's hardcoded `$40` for a + `$4016` read is now DERIVED from the latch + - [x] `rtl/controller.sv` — the standard pad's shift register. `cpu_bus` + first returned a bare 0 in D0, which is not a model of "no controller" + but a model of nothing, and diverged from the oracle on the NINTH read + - [x] **Found the ORACLE wrong, not the DUT** — NROM provides PRG-RAM at + `$6000-$7FFF` where the board has none. Recorded in + `docs/accuracy-ledger.md` with the wiki citation and the games the wiki + names as breaking on it. First time the ladder has caught the oracle; + risk 6 in the v2.5.0 plan + - [ ] **DMA is still `tb/cpu_main.cpp`.** On stalled cycles the bus mux is + not the requester, which is also why two mutations are provably inert + today and should flip to CAUGHT when it moves + - [ ] **`rtl/nes_top.sv` is still a shell.** The chips are tied together by + the co-simulation wrapper rather than by the core's own top level + - [ ] First end-to-end AccuracyCoin run — blocked on the above, and NOT + attempted. Producing a status vector is v2.6.3; matching it is v2.6.4 - [ ] v2.6.4 status vector identical **entry-for-entry**, including `Skipped` and `NotRun` — **rung 5 closes**. State a floor, not a target From 9e1151492034ef9620d2fd87ca806356468a1da4 Mon Sep 17 00:00:00 2001 From: DoubleGate Date: Tue, 25 Aug 2026 00:30:32 -0400 Subject: [PATCH 2/2] docs(accuracy): scope the $00 claim, and cite the public wiki pages Two review findings, both correct and both verified against the code before the fix was written. ## The $00 claim was too broad The entry said a read of $6000-$7FFF "returns $00". Checked: `cpu_read` returns `self.prg_ram[addr - 0x6000]` and `cpu_write` STORES into the same array, so the window behaves as real, writable RAM. $00 is what an UNWRITTEN location reads, because the array is zero-filled at construction -- a written one reads back what was stored. That distinction matters for the finding itself rather than being a prose nit: the divergence from hardware is not "the oracle returns a constant", it is "the oracle provides RAM at all". A game that writes there and reads it back gets its own data from the emulator and open bus from the board, which is a larger difference than the original wording described, not a smaller one. ## Public citations Both documents cited repository-relative wiki snapshots with no public reference. Added, so a reader outside this tree can check the quotations: nesdev.org/wiki/Open_bus_behavior in the ledger, nesdev.org/wiki/NROM in the task board. Documentation only. --- docs/accuracy-ledger.md | 13 +++++++++---- to-dos/mister/TASKS.md | 3 ++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/accuracy-ledger.md b/docs/accuracy-ledger.md index f70c3e36..59c049c3 100644 --- a/docs/accuracy-ledger.md +++ b/docs/accuracy-ledger.md @@ -107,12 +107,17 @@ disposition under the v2.1.0 "Fathom" accuracy-remediation line - **NROM provides PRG-RAM at `$6000-$7FFF` where the board has none, and the MiSTer co-simulation DUT found it (2026-08-25, v2.6.3 rung 5).** `crates/rustynes-mappers/src/m000_nrom.rs` allocates 8 KiB of PRG-RAM - unconditionally so accesses "don't fall off the edge", so a read of that - window returns `$00`. On hardware an NROM board decodes nothing there and the - read floats — it returns **open bus**, the last value the data bus held. + unconditionally so accesses "don't fall off the edge". `cpu_read` returns + `self.prg_ram[addr - 0x6000]` and `cpu_write` stores into it, so the window + behaves as **real, writable RAM**: an *unwritten* location reads `$00` + (the array is zero-filled at construction) and a written one reads back what + was stored. On hardware an NROM board decodes nothing there and the read + floats — it returns **open bus**, the last value the data bus held, whether + or not the program has written to that address. This is the emulator default `nesdev_wiki/Open_bus_behavior.xhtml` names as a - problem in its own words: *"A few games read the region $6000-7FFF but have no + problem in its own words (public source: + ): *"A few games read the region $6000-7FFF but have no WRAM present here. This can be a problem for emulators, as the original iNES file format had no way to specify a lack of WRAM, leaving the emulator to provide WRAM behavior in that region by default. The NES 2.0 format corrects diff --git a/to-dos/mister/TASKS.md b/to-dos/mister/TASKS.md index ca648b52..2cd154e7 100644 --- a/to-dos/mister/TASKS.md +++ b/to-dos/mister/TASKS.md @@ -62,7 +62,8 @@ Legend: `[ ]` open · `[~]` in progress · `[x]` done ## v2.6.3 – v2.6.4 — rung 5, NROM + AccuracyCoin - [~] v2.6.3 NROM cartridge; first end-to-end AccuracyCoin run - - [x] `rtl/cart/cart_nrom.sv` — written from `nesdev_wiki/NROM.xhtml`, + - [x] `rtl/cart/cart_nrom.sv` — written from `nesdev_wiki/NROM.xhtml` + (public source: ), instantiated beneath the existing gates and cross-checked per access rather than trusted. Runtime write port, not `$readmemh`: on MiSTer the image arrives from `hps_io` at runtime, so the port that ships is the