From 99ca8aa5d03f4c20581e6a8f174a2d416ce7ce4f Mon Sep 17 00:00:00 2001
From: DoubleGate
Date: Thu, 8 Oct 2026 04:58:10 -0400
Subject: [PATCH 1/4] review(v3.1.0): slice B, docs and test data (do not
merge)
Review-only slice of #594 for CodeRabbit, which skips PRs over 100 files.
Every doc and test-data path the release changes (54), each equal to the
release head 5f7eac66. Closed unmerged.
Co-Authored-By: Claude Opus 5.5 (1M context)
Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
---
AGENTS.md | 4 +-
ARCHITECTURE.md | 2 +-
CHANGELOG.md | 151 ++++++++++++++++++
OVERVIEW.md | 10 +-
README.md | 12 +-
ROADMAP.md | 4 +-
SECURITY.md | 2 +-
SUPPORT.md | 4 +-
VERSION-PLAN.md | 8 +-
.../rustynes-libretro/rustynes_libretro.info | 2 +-
.../golden/epoch_fingerprint.tsv | 15 ++
...er_146_Sachen_NINA_Millionaire_Sachen.snap | 2 +-
docs/STATUS.md | 12 +-
docs/accuracy-ledger.md | 4 +-
...0032-vs-dualsystem-desktop-presentation.md | 13 ++
...and-netplay-carry-the-emulation-options.md | 18 +++
...-timing-epoch-guards-movies-and-netplay.md | 16 ++
docs/agents/ci-and-release.md | 2 +-
docs/agents/review-bots.md | 2 +-
docs/agents/tooling-traps.md | 1 +
docs/apu-2a03.md | 19 +++
docs/compatibility.md | 41 +++--
docs/dev/TESTING.md | 2 +-
docs/frontend.md | 58 +++++--
docs/ios-v1.9.9-readiness.md | 6 +
docs/libretro/UPSTREAM_SYNC.md | 13 +-
docs/mappers.md | 38 ++++-
docs/mobile-v2.9.3-run-sheet.md | 36 +++++
docs/nesdev-hardware-emulation-checklist.md | 20 +--
docs/netplay-webrtc.md | 3 +-
docs/performance.md | 19 +++
docs/ppu-2c02.md | 70 +++++++-
docs/scheduler.md | 37 ++++-
docs/user-guide/compatibility.md | 5 +-
...r-core-contribution-requirements-update.md | 125 +++++++++++++++
tests/roms/AccuracyCoin/README.md | 21 ++-
tests/roms/AccuracyCoin/SOURCE_CATALOG.tsv | 18 ++-
.../mirror/AccuracyCoin-mirror.nes | Bin 40976 -> 40976 bytes
tests/roms/AccuracyCoin/mirror/README.md | 10 +-
.../sub-tests/BUILD-PROVENANCE.tsv | 27 +++-
.../sub-tests/dmc-reload-timing.nes | Bin 0 -> 40976 bytes
.../sub-tests/sprite-eval-misaligned-oam.nes | Bin 40976 -> 40976 bytes
tests/roms/LICENSES.md | 2 +-
tests/roms/accuracycoin/AccuracyCoin.nes | Bin 40976 -> 40976 bytes
tests/roms/accuracycoin/RUNTIME.md | 24 ++-
to-dos/DEFERRED-AND-CARRYOVER-FEATURES.md | 15 ++
to-dos/README.md | 4 +-
to-dos/ROADMAP.md | 28 ++--
to-dos/mister/IMPLEMENTATION_PLAN.md | 2 +-
to-dos/mister/TASKS.md | 18 ++-
to-dos/mister/contribution-checklist.md | 62 +++++--
.../plans/v2.0.x-mobile-finalization-plan.md | 6 +-
to-dos/plans/v3.1.0-plan.md | 30 +++-
to-dos/v1.8.x-on-device-verification.md | 101 ------------
54 files changed, 899 insertions(+), 245 deletions(-)
create mode 100644 crates/rustynes-test-harness/golden/epoch_fingerprint.tsv
create mode 100644 ref-docs/2026-10-07-mister-core-contribution-requirements-update.md
create mode 100644 tests/roms/AccuracyCoin/sub-tests/dmc-reload-timing.nes
delete mode 100644 to-dos/v1.8.x-on-device-verification.md
diff --git a/AGENTS.md b/AGENTS.md
index e6fd5ca59..e4d74e11a 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -44,7 +44,7 @@ Enforcement lives alongside the prose: `/ref-proj/` is gitignored/`.dockerignore
RustyNES is a cycle-accurate Nintendo Entertainment System emulator written in pure Rust. The accuracy bar is Mesen2 / higan / ares: tight lockstep scheduling at PPU-dot resolution on a master-clock-precise timebase, sub-instruction PPU events visible to subsequent CPU code, and a lookup-table non-linear audio mixer with band-limited synthesis. The frontend is pure Rust (`winit` + `wgpu` + `cpal` + `egui`).
-**Current release: v3.0.1 "Mortar"** (2026-10-07) — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** (2026-10-06) — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core ([ADR 0043](docs/adr/0043-v3-is-the-api-major-and-a-release-candidate-core.md)). **No hardware has run any MiSTer bitstream**: hardware verification moves to a later v3.x release (ADR 0043 supersedes [ADR 0041](docs/adr/0041-hardware-release-is-v3.0.0.md)'s hardware-verified v3.0.0), and the SDRAM pin constraints stay provisional until the SuperStation One's memory is read. Suite counts, mapper matrix and per-release detail live in `docs/STATUS.md`, `CHANGELOG.md` and the GitHub releases; they are deliberately not duplicated here. The earlier release-by-release narrative and other condensed paragraphs are archived verbatim in `docs/history/AGENTS-archive.md`.
+**Current release: v3.1.0 "Bellwether"** (2026-10-08) — the AccuracyCoin re-sync and an honest 146/146, the CPU overclock and the sprite-limit option in movies and netplay, PAL emphasis, the alternate MMC3, rewind and run-ahead on the Vs. cabinet, and the MiSTer core matched to all of it. Built on **v3.0.1 "Mortar"** (2026-10-07) — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** (2026-10-06) — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core ([ADR 0043](docs/adr/0043-v3-is-the-api-major-and-a-release-candidate-core.md)). **No hardware has run any MiSTer bitstream**: hardware verification moves to a later v3.x release (ADR 0043 supersedes [ADR 0041](docs/adr/0041-hardware-release-is-v3.0.0.md)'s hardware-verified v3.0.0), and the SDRAM pin constraints stay provisional until the SuperStation One's memory is read. Suite counts, mapper matrix and per-release detail live in `docs/STATUS.md`, `CHANGELOG.md` and the GitHub releases; they are deliberately not duplicated here. The earlier release-by-release narrative and other condensed paragraphs are archived verbatim in `docs/history/AGENTS-archive.md`.
- **Native Android app** — the **v1.8.0 → v1.8.9 "Android"** train (`crates/rustynes-mobile` UniFFI bridge + `crates/rustynes-android` JNI/NDK host + a Jetpack Compose app, ADR 0024): full on-device emulation, multi-touch + P1–P4 hardware controllers, wgpu `SurfaceView` rendering + the shared WGSL shader stack, save-states / battery SRAM, Lua, RetroAchievements, direct-IP + CGNAT/TURN room-code netplay, a box-art ROM library, and platform polish (adaptive / foldable / TV, Material You, capture / PiP / home-screen widget). Distributed as **GitHub-Releases sideload**; a free store listing is an unversioned later step with no monetization (ADR 0035, which superseded ADR 0025's v2.3.0 date; the `foss`/`play` flavour split remains).
- **Native iOS / iPadOS app** — the **v1.9.0 → v1.9.9 "iOS" TestFlight train** (`crates/rustynes-ios` Metal + CoreAudio shim reusing `rustynes-mobile` verbatim → UniFFI-generated Swift, ADR 0026): a native SwiftUI shell over wgpu→Metal, multi-touch + GameController, the shader stack, TAS / HD-pack / palettes / per-game DB, Lua + RetroAchievements, LAN + room-code netplay, CloudKit save-state sync, accessibility + EN/ES i18n + ReplayKit + Game Center, and the v1.9.9 creator tools (Cheats, a FOSS-gated read-only debugger, a touch TAStudio piano-roll, foreign movie import, a host audio-depth DSP). Ships to **TestFlight**; a free App Store listing is an unversioned later step with no monetization (ADR 0035, which superseded ADR 0027's v2.3.0 date). Mobile ROM loading was iNES / NES 2.0 only until v2.9.7, which adds FDS (host-supplied BIOS), NSF and the Vs. DualSystem cabinet to the bridge (the Swift half uncompiled; run-sheet rows T1-T12). Readiness record: `docs/ios-v1.9.9-readiness.md`.
@@ -236,7 +236,7 @@ that is a reason to add a tenth — not a reason to grow this section back.
- `ref-docs/` is immutable. Research updates go in dated supplemental files.
- ADRs go in `docs/adr/` (Michael Nygard format).
- `rustynes-core` re-exports the public types from the chip crates; downstream consumers (`rustynes-frontend`, `rustynes-test-harness`) should depend on `rustynes-core` rather than the chip crates directly.
-- When relabeling old engine "v2.x" narrative for users, present it as upstream lineage/history — **never as a current RustyNES release version.** The current release is **v3.0.1 "Mortar"** (2026-10-07). **Never claim any version *later* than v3.0.1 is released.** Two distinct "v2.0"s exist and must not be conflated: the **engine-lineage v2.0** master-clock work shipped as the **v1.0.0** production core (2026-06-13) and was the only scheduler through v1.10.0; RustyNES's own **v2.0.0 "Timebase"** (2026-07-03) is a different milestone that REPLACES that dot-lockstep scheduler with the one-clock, every-cycle-bus-access model (ADR 0002 / 0028 / 0029) and broke byte-identity and save-state compatibility, by design (v2.9.8 later broke save, movie and API compatibility again, ahead of v3.0.0). The per-release narrative that used to be inlined here is in `CHANGELOG.md`, the per-release notes under `.github/release-notes/`, and the published GitHub releases — three places that are maintained, against one copy here that was not.
+- When relabeling old engine "v2.x" narrative for users, present it as upstream lineage/history — **never as a current RustyNES release version.** The current release is **v3.1.0 "Bellwether"** (2026-10-08). **Never claim any version *later* than v3.1.0 is released.** Two distinct "v2.0"s exist and must not be conflated: the **engine-lineage v2.0** master-clock work shipped as the **v1.0.0** production core (2026-06-13) and was the only scheduler through v1.10.0; RustyNES's own **v2.0.0 "Timebase"** (2026-07-03) is a different milestone that REPLACES that dot-lockstep scheduler with the one-clock, every-cycle-bus-access model (ADR 0002 / 0028 / 0029) and broke byte-identity and save-state compatibility, by design (v2.9.8 later broke save, movie and API compatibility again, ahead of v3.0.0). The per-release narrative that used to be inlined here is in `CHANGELOG.md`, the per-release notes under `.github/release-notes/`, and the published GitHub releases — three places that are maintained, against one copy here that was not.
- **Forward plans + roadmap live in `to-dos/`.** `to-dos/ROADMAP.md` (updated in #129) is the planning entry point and frames the release line + "the path to v2.0.0 and beyond"; `to-dos/plans/` holds the per-release plan docs (through `v1.7.0-forge-plan.md` on `main`, plus the staged-forward `v1.8.0-android-plan.md` / `v1.9.0-ios-plan.md` / `v2.0.0-master-clock-plan.md`) + the `to-dos/plans/engine-lineage/` history archive + a `to-dos/plans/research/` reference-mining archive.
- The v1.0.0 release + GitHub Pages/CI + post-release record is in `docs/v1.0.0-synthesis-handoff-2026-06-13.md` — read it before touching CI, Pages, or release tooling. Full per-release history is in `CHANGELOG.md`.
- **Markdownlint is a CI gate** (pre-commit, pinned `markdownlint-cli v0.49.1`). The pin was v0.39.0 until the v2.6.3 dependency refresh, held because the newer local binary reported rules the pin lacked — chiefly **MD060** (`table-column-style`), which was therefore NOT gated. That is now measured and resolved: MD060's inferred default reads this corpus as style `compact` and reports **1,936 findings across 122 files** and nothing else, so `.markdownlint.json` pins `MD060` to the style actually in use (`leading_and_trailing`), which measures **zero** and rewrites no document. It IS a gate now. Still verify with `pre-commit run markdownlint --all-files` rather than the bare binary — the pin and the local build can drift apart again. `.markdownlint.json` also keeps `MD013`/`MD033`/`MD041` disabled by design (long technical tables, the README HTML banner/`
`, the HTML-led README). `.markdownlintignore` exempts `ref-docs/`, `ref-proj/` (the reference-emulator clone, now removed from disk but kept in the ignore lists as a firewall guard so it can never re-enter the tree — see the MOST IMPORTANT RULE section above), the vendored `tricnes/` + upstream READMEs, and the frozen `docs/archive/` + `to-dos/archive/` trees — don't lint or reformat those.
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index c23c54915..7a9984cee 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -2,7 +2,7 @@
**Document Version:** 2.1.0
**Last Updated:** 2026-10-07
-**Applies to:** RustyNES v3.0.1 (the scheduling model is v2.0.0 "Timebase" onward)
+**Applies to:** RustyNES v3.1.0 (the scheduling model is v2.0.0 "Timebase" onward)
This document fixes the high-level architecture of RustyNES. The per-subsystem specs under `docs/` (`cpu-6502.md`, `ppu-2c02.md`, `apu-2a03.md`, `mappers.md`, `scheduler.md`) take these decisions as given and elaborate one chip each. After reading this you should know the workspace shape, the scheduling model, the public boundary, and the load-bearing invariants. The canonical, always-current architecture spec is [`docs/architecture.md`](docs/architecture.md); this file is the top-level companion.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3c0046cd6..b6d68f4d1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,6 +26,157 @@ cycle-accurate core later replaced.
## [Unreleased]
+## [3.1.0] - 2026-10-08 - "Bellwether" (the AccuracyCoin re-sync and an honest 146/146, the CPU overclock and the sprite-limit option in movies and netplay, PAL emphasis, the alternate MMC3, rewind and run-ahead on the Vs. cabinet, and the MiSTer core matched to all of it)
+
+The first release of the v3.1 to v4.0 line. AccuracyCoin is re-synced to its
+newest upstream, which showed that every earlier 100% score included a failure
+the old ROM hid; the emulator now passes all 146 for real, and so does the
+MiSTer core. Two long-shown options work and travel with movies and netplay
+(a CPU overclock, and the sprite-limit switch), PAL emphasis is right, the
+alternate MMC3 is selectable and correct, and the Vs. cabinet gets rewind and
+run-ahead. **Save states, movies and netplay from v3.0.1 are refused.**
+
+### Breaking
+
+- **Movies and netplay from v3.0.1 are refused, and so are its save states.**
+ `EMULATION_EPOCH` rises from 2 to 3 (the two accuracy fixes below change bus
+ cycles and sprite evaluation). Save states: BUS section version 3 (the DMC
+ latch and the overclock's position) and `PPU_SNAPSHOT_VERSION` 13 (the
+ sprite-limit option's pending sprites). Movies: `.rnm` format 6 (the options
+ record gains the two options below). Netplay: protocol 7, magic `"RNE7"`; a
+ v3.0.x peer is refused as another emulator version, naming its epoch.
+
+### Fixed
+
+- **AccuracyCoin re-synced to upstream `f5f41dc2`: 146 of 146.** The catalog
+ grows to 151 rows / 146 scored with `DMA Landing on Write` and `DMC Reload
+ Timing`. The new ROM found two defects:
+ - **A DMC load DMA refused by a write took three cycles, not four.** It now
+ enters on the next read whichever half that is (`DMA Landing on Write`
+ test 9), found by a per-cycle comparison with TriCNES's output.
+ - **Misaligned sprite evaluation.** Evaluation now takes its start from
+ OAMADDR at dot 65 (it used dot 0), copies four bytes from a start at the
+ last byte of a slot (it copied one), and stays misaligned after an
+ in-range X (it always realigned). The old ROM recorded these failures as
+ a pass: its fail path returned into the test without popping the return
+ address, fixed upstream in `adacbc23`.
+ - **So the earlier 100% scores were overstated.** Every release that
+ reported AccuracyCoin 144/144 (and 141/141 before it) failed parts of
+ `Misaligned OAM behavior` as the fixed ROM scores it; the old ROM's bug
+ recorded those failures as a pass. v3.1.0 is the first release whose 100% does not
+ include that masked failure.
+- **PAL and Dendy games that use colour emphasis show the right tint.** On the
+ PAL 2C07 and the Dendy, PPUMASK bits 5 and 6 swap meaning (green and red);
+ every PAL or Dendy game that set emphasis was tinted the wrong way.
+- **The alternate MMC3 IRQ revision fires on a `$C001` reload to 0**, as the
+ MMC3A and non-Sharp MMC3B do (NES 2.0 submapper 4, and mapper 12). Sharp,
+ the default, is unchanged.
+- **The AccuracyCoin tooling reads upstream's new row macros.**
+ `extract_catalog.py` returned 85 of 151 rows and exited 0 when upstream
+ compressed the unofficial-opcode rows into `tblf1` / `tblf2`; it now
+ rebuilds their names from the ROM's own string table and aborts on any row
+ it cannot read. `derive_indices.py` shares that grammar, and no longer
+ refuses to run: it read the provenance TSV's columns by position, which went
+ stale when an address column was added. The eight `Unofficial Immediates`
+ rows are now spelled `immediate`, as the ROM prints them.
+- **The AccuracyCoin mirror ROM is rebuilt from `f5f41dc2`.**
+- **Opening the pattern viewer, or using an HD pack, could change the game on
+ MMC2 / MMC4 boards** (Punch-Out!!, Fire Emblem). Their "side-effect-free" CHR
+ read went through the cartridge's normal read, which flips a CHR latch on
+ tiles `$FD`/`$FE`; the same held on three other boards. Those reads now
+ leave the cartridge exactly as it was.
+
+### Added
+
+- **CPU overclock** (`T-CPU-OVERCLOCK`, Settings > Enhancements): the CPU runs
+ 2 to 4 times faster against the same picture and sound, removing slowdown.
+ The APU, mapper IRQ counters and PPU timers stay at the stock rate, so pitch,
+ tempo and raster effects are unchanged. Unlike the extra-scanline overclock,
+ movies record it and replay with it, and netplay players must match. The
+ multiplier is exact on every region: a CPU cycle is 12 master clocks on NTSC
+ but 16 on PAL and 15 on Dendy, which do not divide by 3 or 4, so the
+ overclocked cycle lengths alternate to keep the average exact (a review of
+ the release PR found PAL x3 running at x3.2 and Dendy x4 at x5).
+- **"Disable 8-sprite-per-scanline limit" now works** (`T-SPRITE-LIMIT`; it was
+ shown and saved but inert). It draws the dropped sprites behind the eight the
+ console shows, and changes nothing the game can see: the overflow flag, the
+ sprite fetches and every CPU cycle stay exact. It is skipped on the five
+ boards whose pattern reads change the cartridge (MMC2, MMC4, the J.Y. ASIC,
+ Bandai 96, Nanjing 163).
+- **Differential phase distortion in the raw NTSC signal decode**
+ (`T-COMPOSITE-ARTIFACTS`): a new "Differential phase" slider rotates brighter
+ colours' hue as the NES PPU does (about 2.5° per palette row on a 2C02E, 5° on
+ a 2C02G). Off by default; display only.
+- **MMC3 IRQ revision setting** (`T-MMC3-NEC-OVERRIDE`, Settings > Emulation):
+ run any mapper-4 game on the Sharp or the alternate (MMC3A / NEC) chip, for
+ dumps whose header cannot say which. blargg's `mmc3_test_2/6-MMC3_alt` passes
+ under the alternate setting. Carried in movies and netplay.
+- **Rewind and run-ahead on the Vs. DualSystem cabinet** (`T-PS-dual-runahead`,
+ ADR 0032 amended): both work in two-screen mode, on the whole cabinet, so
+ the two consoles never fall out of step. A step back restores both screens
+ exactly, and run-ahead shows the same frames a run without it would, a
+ frame or more sooner. Netplay, movies, the debugger and HD packs stay
+ single-console.
+- **A test now enforces the emulation-epoch rule** (`T-EPOCH-FINGERPRINT`).
+ It fingerprints seven test ROMs (frames, audio, RAM, CPU cycles) and fails
+ when that output moves while `EMULATION_EPOCH` still equals the last
+ release's, refusing a re-bless in that state. Until now the rule was kept
+ by hand.
+
+### The MiSTer core (`RustyNES_MiSTer`)
+
+- **It matches the re-synced AccuracyCoin, all 146 entries.** The new ROM found
+ the core's version of both emulator defects. A misaligned sprite evaluation
+ now masks the address after an out-of-range X byte, as the ROM's comments
+ state; and a `$4010` write on the DMC timer's reload edge now sets that
+ reload's period. Before them the core differed on two entries; after them the
+ status vector is identical.
+- **A `$2006` write that meets the PPU's address pipeline matches the
+ emulator** (RTL-1). A new generated ROM lands the copy on every dot of a
+ rendering line; the core diverged on 28,129 of 331,838 background fetches,
+ for three reasons measured from the emulator's per-dot trace, and now matches
+ on all of them. One of the three, a copy delay that depends on where the
+ write lands, is the emulator's rule and not a documented one (NESdev says a
+ constant "1 to 1.5 dots"). It is recorded as provisional until a board can
+ settle it.
+- **Ten more AccuracyCoin sub-test ROMs gate the core** (RTL-10), plus the new
+ `dmc-reload-timing`: 30 of 34 now, the other four out for stated reasons. The
+ `sprite-eval-misaligned-oam` sub-test ROM is rebuilt from the new source: the
+ old build's fail path fell through to a pass, so that gate read green every
+ release while the core failed it.
+- **The oracle pin moves to v3.1.0**; every golden that changed is attributed
+ (AccuracyCoin, the rebuilt sub-test, two new stems) and the other 646
+ artifacts are byte-identical.
+- **A self-hosted ladder workflow** (`ladder.yml`, manual dispatch only) runs
+ the whole ladder on the maintainer's runner. CI's Verilator is recorded
+ (5.032).
+- **No hardware has run any bitstream.**
+
+### Records
+
+- **The MiSTer contribution page was rewritten in September**, dropping the
+ "evidence of quality and accuracy testing" sentence the submission case
+ answered and adding reviewer questions about the developer. The new page and
+ its consequences are recorded in `ref-docs/`, the checklist is re-scoped, and
+ `submission-case.md` is refreshed. Two decisions are the maintainer's: how to
+ show reviewers the code is understood and maintained, and whether the
+ sibling repository becomes public.
+- **Nine stale documents corrected against the code** (DOC-01..09), and the
+ v1.8.x Android checklist folded into the mobile run sheet.
+
+### Verification
+
+- `cargo test --workspace --features test-roms --release`: **3,262 passed, 0
+ failed, 11 ignored**. The epoch fingerprint gate passes at epoch 3, now
+ recorded as the released epoch.
+- AccuracyCoin **146/146** at upstream `f5f41dc2`; `nestest` 0-diff.
+- The local commercial suites: `external_real_games` 60/0, `external_extended`
+ 137/0, `external_coverage` 6/0 over 744 staged ROMs. One baseline moved:
+ *Millionaire* (Sachen, mapper 146), which the game database marks PAL, at one
+ checkpoint, from the PAL emphasis fix. It was attributed by running that ROM
+ alone on each v3.1.0 commit, and re-blessed.
+- The MiSTer core: LADDER-FILL.
+
## [3.0.1] - 2026-10-07 - "Mortar" (the open items closed, one game's graphics fixed, the last MMC3 rule exception tested in the MiSTer core, Rust 1.99 everywhere, every unanswered bot review answered, and a roadmap to v4.0.0)
A maintenance release on v3.0.0. It closes the items v3.0.0 left open:
diff --git a/OVERVIEW.md b/OVERVIEW.md
index 3bc8b90c0..e9a3cddfa 100644
--- a/OVERVIEW.md
+++ b/OVERVIEW.md
@@ -2,7 +2,7 @@
**Document Version:** 2.1.0
**Last Updated:** 2026-10-06
-**Applies to:** RustyNES v3.0.1
+**Applies to:** RustyNES v3.1.0
---
@@ -22,9 +22,9 @@
RustyNES is the **definitive NES emulator for the modern era** — combining cycle-perfect accuracy with a complete contemporary feature set and the safety guarantees of Rust. It is more than an emulator: it is a platform for NES preservation, competitive online play, tool-assisted speedrunning, and homebrew development.
-As of **v1.0.0**, that vision was realized: RustyNES clears the Mesen2 / higan / ares accuracy bar, ships a polished desktop application and a browser build, and supports the full platform surface — netplay, achievements, TAS movies, a debugger, FDS, and arcade (Vs. / PlayChoice-10) hardware. Since then the additive v1.x line added three more platforms (native Android, iOS / iPadOS, and a Libretro / RetroArch core), **v2.0.0 "Timebase"** replaced the scheduler substrate with the one-clock / every-cycle-bus-access model (ADR 0029 — the first designated breaking release), and the v2.1.x → v2.3.x lines deepened accuracy, presentation, and analysis tooling. The current release is **v3.0.1 "Mortar"** — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core. Built on **v2.9.9 "Ballast"** — the release candidate for v3.0.0: the audits re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it. Built on **v2.9.8 "Vanguard"** — the preparation release for v3.0.0: v3.0.0's breaking changes landed early (a save identity that ignores the header, old states and movies refused, movies and netplay that record the machine, the API removals), every staged game was booted and the defects found were fixed, and the game database's corrections reach every platform. Built on **v2.9.7 "Tandem"** — the desktop's features on the web and on phones, the release binaries built with every native feature, and a PPU A12 fix found by real games: Acclaim's MC-ACC games, the J.Y. ASIC and mapper 91 now count at their documented rates. Built on **v2.9.6 "Roster"** — seventeen mapper families written from their NESdev pages (174 → 191), GTROM promoted to Curated with a modelled flash chip whose saves persist, mapper 4's NES 2.0 submappers corrected (MMC6, NEC, MC-ACC, T9552), and the local commercial suites re-baselined after drifting unread since about v2.0.0. Built on **v2.9.5 "Caliper"** — every open accuracy item measured, then fixed or closed: four fixes red first (the `apu_test` frame-counter coincidence, the composite 2C02 scanline-0 sprite glitch, OAM DMA filling the PPU I/O latch, KS7032 at `$6000`), 49 unreferenced test ROMs gated, the MMC3 M2-edge filter lever tried and refuted, and a save-state epoch (`PPU_SNAPSHOT_VERSION` 11). Built on **v2.9.4 "Plumb"** — the records made true, and CI made to run what it only linted: v3.0.0 decided as the API major with a release-candidate core (ADR 0043), CI now running 63 feature-gated tests it never ran, the eight fuzz targets and a 70% line-coverage floor, the mapper tiers, store status and deferred-features catalogue corrected against the code, and the OAM-decay model recorded as derived from Mesen2. Built on **v2.9.3 "Handset"** — the old review threads closed and the mobile run prepared: every dependency moved to its newest release (egui 0.36 with wgpu 30, rcheevos 12.5.0), all 244 review threads left unanswered on PRs #7-#97 answered and the ten findings that still held fixed (Action 53 multicarts rebuilt to the NESdev spec, and a ROM header editor that no longer rewrites bytes you did not edit or saves mappers from 16 up as the wrong mapper), and the Android unit tests and the iOS renderer added to CI. Built on **v2.9.2 "Candidate"** — the full audit acted on, and the release-candidate pair: all 32 findings of a fifth audit have a verdict and 16 are fixed, save states keep the cartridge RAM of twelve board families they used to drop, the MiSTer core no longer loses an NMI raised inside a DMA, and both bitstreams are cut for the SuperStation One session. Built on **v2.9.1 "Hone"** — what the optimisation bars measure, and what clears them: the A/B tool had been timing the old code on both sides of every code comparison and is fixed, a two-screen Vs. cabinet saves about 9x faster, the off-die MiSTer build keeps CHR in its own SDRAM bank, and both bitstreams are pinned at fitter seed 2 and rebuild byte-identically. Built on **v2.9.0 "Survey"** — every audit re-checked, and the SuperStation One surveyed: a Power Cycle no longer erases your save, the off-die MiSTer build boots without the menu core, and 39 new audit findings are fixed or dispositioned. Built on **v2.8.4 "Tether"** — the MiSTer core's SDRAM build, made trustworthy: its controller now reads data on the edge the memory presents it (every off-die read would have been wrong on hardware, and only the new SDRAM timing constraints could see it), the power-up sequence and CAS-latency-3 reads follow the datasheet, the arbiter can no longer return the wrong byte or lose a write, the off-die bitstream builds from a script, both builds are swept and pinned at fitter seed 5, and the co-simulation ladder runs all 165 gates from a clean checkout. Built on **v2.8.3 "Rivet"** — the MiSTer core's reset, area and comments, measured: every reset is released on the clock that uses it and the timing analysis now checks each release, the CPU is about 4% smaller by two exact rewrites the fit report confirmed, four false comments are corrected, and the co-simulation ladder runs from a fresh checkout (164 of its 165 gates; the last needs a hand-built ROM no generator produces). Built on **v2.8.2 "Solder"** — the MiSTer core's on-die RTL, corrected against the oracle and the wiki: an MMC3 IRQ acknowledge is no longer lost to a same-edge counter clock, SNROM's battery RAM obeys its CHR-line enable, the triangle and noise drop a reload landing on a length clock, a `$2002` read leaves the byte it returned on the data bus, and the emulator's MMC1 no longer ignores a reset written on the cycle after another write. Built on **v2.8.1 "Gasket"** — the libretro core fits the frontends around it: four-player games work through a Four Score option, a controller works again after its port leaves the Zapper, RetroArch no longer reads past the core's input-descriptor list, expansion audio no longer clips, the core declares UNIF images, and the Makefile honours PREFIX, platform=win, DEBUG and CARGO_TARGET_DIR. Built on **v2.8.0 "Bulkhead"** — the libretro core stops a fault at its own boundary: an internal error no longer closes RetroArch, save states survive plugging in a Zapper, closing a game withdraws its memory maps, the core loads from any libretro frontend, and the save state now carries the 2A03 internal data bus. Built on **v2.7.6 "Recount"** — the v2.7.5 deletions measured one at a time: the six performance proposals v2.7.5 bounded only together were each measured alone, where the benchmarks reach them: five are zero, and the sixth, the pulse sweep-mute check, bounds at about 0.2%, and the cheap byte-identical way to take it measured slower; the fast render path now asserts a rendering-history invariant it used to re-write; and the libretro buildbot builds macOS again and gains 32-bit Windows, 32-bit Linux and webOS targets. Built on **v2.7.5 "Tally"** — every audit claim closed with a measurement or a reason: the core audit's twelve performance proposals were closed, eleven of them by measurement, and one adopted (the audio buffer keeps its capacity between frames); 18 dead bus methods and the unused ApuBus trait are deprecated; and the core and frontend ledgers have no open row. Built on **v2.7.4 "Pocket"** — the mobile apps survive what a phone does to them: an internal error no longer closes the Android or iOS app, battery saves persist on both, the apps pause and give up audio when they should, and saves are written so a dying phone keeps the last good one. Built on **v2.7.3 "Hearth"** — the desktop and web frontends keep what they are given: battery saves persist on the desktop, a Lua script can no longer hang or exhaust the emulator, script HTTP cannot reach local services by default, and audio survives a device change. Built on **v2.7.2 "Bankroll"** — cartridge memory, every bank a cartridge has and nothing it has not: MMC1 reaches SUROM / SXROM, MMC5 banks its PRG-RAM, Namco 163 selects nametables, and `$6000-$7FFF` reads open bus where a board has nothing there. Built on **v2.7.1 "Keepsake"** — six cartridge boards now hand RetroArch their battery save instead of an empty one, every user file the frontend writes is written atomically, and three mapper files are now recorded as derived from Mesen2 and puNES. Built on **v2.7.0 "Palisade"** — a corrupt or hand-edited save state now fails at restore with a typed error instead of crashing the emulator one tick later, pulse 1 no longer mutes on the `$4001 = $08` sweep idiom, and the save-state fuzz target can finally reach what it exists to find. Built on **v2.6.23 "Pulse"** — the access does not increment, it pulses the load already there: the CHR-during-rendering gate closes at 61,440 of 61,440 pixels, and **no hardware has run any bitstream** — built on **v2.6.22 "Rigging"**, the instruments for the board, built before the board: AccuracyCoin reads back from hardware as bytes rather than as a photograph, the catalog turns out to carry 149 rows and 144 results so the headline no longer depends on when the run was sampled (at the same 144/144), and a `rom_sha256` recorded in every golden manifest and compared to nothing is now rung 0 — built on **v2.6.21 "Steward"**, which brought battery saves, the CHR-during-rendering gate RED at 32,861 of 61,440 pixels, and the deploy loop the runbook prescribed and **v2.6.20 "Telltale"**, itself on **v2.6.19 "Accession"** and **v2.6.18 "Errata"**, built on **v2.6.17 "Terminus"**. The never-tagged v2.4.0 "Concordance" shipped inside **v2.4.1 "Fabric"** — this sentence had attached that fact to whichever release was current, carried forward by three mechanical version bumps, and said it of v2.4.2, v2.4.3 and v2.4.4 in turn.
+As of **v1.0.0**, that vision was realized: RustyNES clears the Mesen2 / higan / ares accuracy bar, ships a polished desktop application and a browser build, and supports the full platform surface — netplay, achievements, TAS movies, a debugger, FDS, and arcade (Vs. / PlayChoice-10) hardware. Since then the additive v1.x line added three more platforms (native Android, iOS / iPadOS, and a Libretro / RetroArch core), **v2.0.0 "Timebase"** replaced the scheduler substrate with the one-clock / every-cycle-bus-access model (ADR 0029 — the first designated breaking release), and the v2.1.x → v2.3.x lines deepened accuracy, presentation, and analysis tooling. The current release is **v3.1.0 "Bellwether"** — the AccuracyCoin re-sync and an honest 146/146, the CPU overclock and the sprite-limit option in movies and netplay, PAL emphasis, the alternate MMC3, rewind and run-ahead on the Vs. cabinet, and the MiSTer core matched to all of it. Built on **v3.0.1 "Mortar"** — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core. Built on **v2.9.9 "Ballast"** — the release candidate for v3.0.0: the audits re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it. Built on **v2.9.8 "Vanguard"** — the preparation release for v3.0.0: v3.0.0's breaking changes landed early (a save identity that ignores the header, old states and movies refused, movies and netplay that record the machine, the API removals), every staged game was booted and the defects found were fixed, and the game database's corrections reach every platform. Built on **v2.9.7 "Tandem"** — the desktop's features on the web and on phones, the release binaries built with every native feature, and a PPU A12 fix found by real games: Acclaim's MC-ACC games, the J.Y. ASIC and mapper 91 now count at their documented rates. Built on **v2.9.6 "Roster"** — seventeen mapper families written from their NESdev pages (174 → 191), GTROM promoted to Curated with a modelled flash chip whose saves persist, mapper 4's NES 2.0 submappers corrected (MMC6, NEC, MC-ACC, T9552), and the local commercial suites re-baselined after drifting unread since about v2.0.0. Built on **v2.9.5 "Caliper"** — every open accuracy item measured, then fixed or closed: four fixes red first (the `apu_test` frame-counter coincidence, the composite 2C02 scanline-0 sprite glitch, OAM DMA filling the PPU I/O latch, KS7032 at `$6000`), 49 unreferenced test ROMs gated, the MMC3 M2-edge filter lever tried and refuted, and a save-state epoch (`PPU_SNAPSHOT_VERSION` 11). Built on **v2.9.4 "Plumb"** — the records made true, and CI made to run what it only linted: v3.0.0 decided as the API major with a release-candidate core (ADR 0043), CI now running 63 feature-gated tests it never ran, the eight fuzz targets and a 70% line-coverage floor, the mapper tiers, store status and deferred-features catalogue corrected against the code, and the OAM-decay model recorded as derived from Mesen2. Built on **v2.9.3 "Handset"** — the old review threads closed and the mobile run prepared: every dependency moved to its newest release (egui 0.36 with wgpu 30, rcheevos 12.5.0), all 244 review threads left unanswered on PRs #7-#97 answered and the ten findings that still held fixed (Action 53 multicarts rebuilt to the NESdev spec, and a ROM header editor that no longer rewrites bytes you did not edit or saves mappers from 16 up as the wrong mapper), and the Android unit tests and the iOS renderer added to CI. Built on **v2.9.2 "Candidate"** — the full audit acted on, and the release-candidate pair: all 32 findings of a fifth audit have a verdict and 16 are fixed, save states keep the cartridge RAM of twelve board families they used to drop, the MiSTer core no longer loses an NMI raised inside a DMA, and both bitstreams are cut for the SuperStation One session. Built on **v2.9.1 "Hone"** — what the optimisation bars measure, and what clears them: the A/B tool had been timing the old code on both sides of every code comparison and is fixed, a two-screen Vs. cabinet saves about 9x faster, the off-die MiSTer build keeps CHR in its own SDRAM bank, and both bitstreams are pinned at fitter seed 2 and rebuild byte-identically. Built on **v2.9.0 "Survey"** — every audit re-checked, and the SuperStation One surveyed: a Power Cycle no longer erases your save, the off-die MiSTer build boots without the menu core, and 39 new audit findings are fixed or dispositioned. Built on **v2.8.4 "Tether"** — the MiSTer core's SDRAM build, made trustworthy: its controller now reads data on the edge the memory presents it (every off-die read would have been wrong on hardware, and only the new SDRAM timing constraints could see it), the power-up sequence and CAS-latency-3 reads follow the datasheet, the arbiter can no longer return the wrong byte or lose a write, the off-die bitstream builds from a script, both builds are swept and pinned at fitter seed 5, and the co-simulation ladder runs all 165 gates from a clean checkout. Built on **v2.8.3 "Rivet"** — the MiSTer core's reset, area and comments, measured: every reset is released on the clock that uses it and the timing analysis now checks each release, the CPU is about 4% smaller by two exact rewrites the fit report confirmed, four false comments are corrected, and the co-simulation ladder runs from a fresh checkout (164 of its 165 gates; the last needs a hand-built ROM no generator produces). Built on **v2.8.2 "Solder"** — the MiSTer core's on-die RTL, corrected against the oracle and the wiki: an MMC3 IRQ acknowledge is no longer lost to a same-edge counter clock, SNROM's battery RAM obeys its CHR-line enable, the triangle and noise drop a reload landing on a length clock, a `$2002` read leaves the byte it returned on the data bus, and the emulator's MMC1 no longer ignores a reset written on the cycle after another write. Built on **v2.8.1 "Gasket"** — the libretro core fits the frontends around it: four-player games work through a Four Score option, a controller works again after its port leaves the Zapper, RetroArch no longer reads past the core's input-descriptor list, expansion audio no longer clips, the core declares UNIF images, and the Makefile honours PREFIX, platform=win, DEBUG and CARGO_TARGET_DIR. Built on **v2.8.0 "Bulkhead"** — the libretro core stops a fault at its own boundary: an internal error no longer closes RetroArch, save states survive plugging in a Zapper, closing a game withdraws its memory maps, the core loads from any libretro frontend, and the save state now carries the 2A03 internal data bus. Built on **v2.7.6 "Recount"** — the v2.7.5 deletions measured one at a time: the six performance proposals v2.7.5 bounded only together were each measured alone, where the benchmarks reach them: five are zero, and the sixth, the pulse sweep-mute check, bounds at about 0.2%, and the cheap byte-identical way to take it measured slower; the fast render path now asserts a rendering-history invariant it used to re-write; and the libretro buildbot builds macOS again and gains 32-bit Windows, 32-bit Linux and webOS targets. Built on **v2.7.5 "Tally"** — every audit claim closed with a measurement or a reason: the core audit's twelve performance proposals were closed, eleven of them by measurement, and one adopted (the audio buffer keeps its capacity between frames); 18 dead bus methods and the unused ApuBus trait are deprecated; and the core and frontend ledgers have no open row. Built on **v2.7.4 "Pocket"** — the mobile apps survive what a phone does to them: an internal error no longer closes the Android or iOS app, battery saves persist on both, the apps pause and give up audio when they should, and saves are written so a dying phone keeps the last good one. Built on **v2.7.3 "Hearth"** — the desktop and web frontends keep what they are given: battery saves persist on the desktop, a Lua script can no longer hang or exhaust the emulator, script HTTP cannot reach local services by default, and audio survives a device change. Built on **v2.7.2 "Bankroll"** — cartridge memory, every bank a cartridge has and nothing it has not: MMC1 reaches SUROM / SXROM, MMC5 banks its PRG-RAM, Namco 163 selects nametables, and `$6000-$7FFF` reads open bus where a board has nothing there. Built on **v2.7.1 "Keepsake"** — six cartridge boards now hand RetroArch their battery save instead of an empty one, every user file the frontend writes is written atomically, and three mapper files are now recorded as derived from Mesen2 and puNES. Built on **v2.7.0 "Palisade"** — a corrupt or hand-edited save state now fails at restore with a typed error instead of crashing the emulator one tick later, pulse 1 no longer mutes on the `$4001 = $08` sweep idiom, and the save-state fuzz target can finally reach what it exists to find. Built on **v2.6.23 "Pulse"** — the access does not increment, it pulses the load already there: the CHR-during-rendering gate closes at 61,440 of 61,440 pixels, and **no hardware has run any bitstream** — built on **v2.6.22 "Rigging"**, the instruments for the board, built before the board: AccuracyCoin reads back from hardware as bytes rather than as a photograph, the catalog turns out to carry 149 rows and 144 results so the headline no longer depends on when the run was sampled (at the same 144/144), and a `rom_sha256` recorded in every golden manifest and compared to nothing is now rung 0 — built on **v2.6.21 "Steward"**, which brought battery saves, the CHR-during-rendering gate RED at 32,861 of 61,440 pixels, and the deploy loop the runbook prescribed and **v2.6.20 "Telltale"**, itself on **v2.6.19 "Accession"** and **v2.6.18 "Errata"**, built on **v2.6.17 "Terminus"**. The never-tagged v2.4.0 "Concordance" shipped inside **v2.4.1 "Fabric"** — this sentence had attached that fact to whichever release was current, carried forward by three mechanical version bumps, and said it of v2.4.2, v2.4.3 and v2.4.4 in turn.
-> RustyNES's emulation core descends from an extensively-documented accuracy program. Where this and related docs reference deep "v1.x"/"v2.x" engine narrative, read it as upstream engine lineage (engineering history), not as RustyNES release versions. Two distinct "v2.0"s exist and must not be conflated: the engine-lineage v2.0 master-clock work shipped as RustyNES **v1.0.0**, while RustyNES's own **v2.0.0 "Timebase"** (2026-07-03) is the later release that *replaced* that same scheduler. The current release is **v3.0.1**.
+> RustyNES's emulation core descends from an extensively-documented accuracy program. Where this and related docs reference deep "v1.x"/"v2.x" engine narrative, read it as upstream engine lineage (engineering history), not as RustyNES release versions. Two distinct "v2.0"s exist and must not be conflated: the engine-lineage v2.0 master-clock work shipped as RustyNES **v1.0.0**, while RustyNES's own **v2.0.0 "Timebase"** (2026-07-03) is the later release that *replaced* that same scheduler. The current release is **v3.1.0**.
---
@@ -56,7 +56,7 @@ A one-directional crate graph keeps each chip (`rustynes-cpu`, `rustynes-ppu`, `
| Test | Result |
|------|--------|
-| **AccuracyCoin** | **100.00% (144/144)** (RAM-direct decoder) — the 2026-09 upstream re-sync grew the battery to 144 assigned tests and v2.6.18 closed the last one (`Advanced Sprite Evaluation :: Frozen OAM2 Increment`), so `KNOWN_FAILING` is empty; the two newest upstream PPU tests ("ALE + Read", "Hybrid Addresses") were closed by the v2.0.3 2-cycle-ALE promotion |
+| **AccuracyCoin** | **100.00% (146/146)** (RAM-direct decoder) at upstream `f5f41dc2` since v3.1.0, whose re-sync showed that the 144/144 reported from v2.6.18 to v3.0.1 was overstated, because the older ROM's `Misaligned OAM behavior` fail path fell through to a pass and hid a real failure. Before it: the 2026-09 upstream re-sync grew the battery to 144 assigned tests and v2.6.18 closed the last one (`Advanced Sprite Evaluation :: Frozen OAM2 Increment`), so `KNOWN_FAILING` is empty; the two newest upstream PPU tests ("ALE + Read", "Hybrid Addresses") were closed by the v2.0.3 2-cycle-ALE promotion |
| **`nestest`** | **0-diff** against the Nintendulator golden log |
| **blargg / kevtris / `mmc3_test_2`** | Green |
| **Commercial-ROM oracle** | 60-ROM byte-identical regression gate + extended visual survey |
@@ -88,7 +88,7 @@ RustyNES uses **cycle-accurate** emulation rather than scanline-based shortcuts.
| Area | What ships today |
|------|----------------------|
-| **Accuracy** | One-clock scheduler (v2.0.0 "Timebase"), master-clock timebase, AccuracyCoin **144/144 (100.00%)** on a battery that grew to 144 assigned tests at the 2026-09 re-sync, `nestest` 0-diff |
+| **Accuracy** | One-clock scheduler (v2.0.0 "Timebase"), master-clock timebase, AccuracyCoin **146/146 (100.00%)** on the battery as re-synced to upstream `f5f41dc2` in v3.1.0 (the 144/144 before it hid a masked failure), `nestest` 0-diff |
| **Cartridges** | **191** mapper families incl. expansion audio (VRC6/VRC7-OPLL/Sunsoft 5B/N163/MMC5) |
| **Platforms** | iNES / NES 2.0, Famicom Disk System (real-BIOS boot, read/write, multi-side), Vs. System / PlayChoice-10 RGB |
| **Online** | Rollback netplay, UDP (native) + WebRTC (browser), 2–4 players |
diff --git a/README.md b/README.md
index d09cd2c62..97a755a11 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
-

+


@@ -18,7 +18,7 @@
pure Rust.** It aims at the Mesen2 / higan / ares accuracy bar: one master clock,
every CPU cycle a real bus access, and the PPU caught up to each half of it, so a
sprite-zero hit, a mid-scanline scroll write or an MMC3 IRQ lands on the exact dot
-without a per-game patch. It passes **AccuracyCoin 144/144** and matches the
+without a per-game patch. It passes **AccuracyCoin 146/146** and matches the
Nintendulator `nestest` log with **zero diff**.
Around that core sits a complete, modern platform: 191 mapper families, the
@@ -72,7 +72,7 @@ audience: players, RetroArch, mobile and the Rust crates.
| | |
| --- | --- |
-| **Cycle-accurate** | CPU, PPU and APU on one master clock: AccuracyCoin 144/144, `nestest` 0-diff, blargg's CPU, APU and PAL suites |
+| **Cycle-accurate** | CPU, PPU and APU on one master clock: AccuracyCoin 146/146, `nestest` 0-diff, blargg's CPU, APU and PAL suites |
| **191 mapper families** | NROM through MMC5, the whole VRC line, Sunsoft FME-7, Namco 163, Taito, J.Y. Company, the MMC3 and MMC1 multicarts, Waixing and Nanjing boards, homebrew flash boards with working saves, and a UNIF (`.unf`) loader. Each is classified Core, Curated or BestEffort by the evidence behind it |
| **Famicom Disk System** | Real-BIOS boot, writable disks, side swapping, a timed disk-head model and 2C33 wavetable audio |
| **Vs. / PlayChoice-10** | Arcade boards in true 2C03 / 2C04 / 2C05 RGB, per-game DIP presets, and Vs. DualSystem two-screen cabinets |
@@ -261,7 +261,7 @@ to player 1 automatically.
| Suite | Result |
| --- | --- |
-| **AccuracyCoin** | **144/144 (100.00%)**, read from RAM rather than the screen |
+| **AccuracyCoin** | **146/146 (100.00%)** at upstream `f5f41dc2`, read from RAM rather than the screen. The 144/144 reported before v3.1.0 was overstated: the older ROM's `Misaligned OAM behavior` fail path fell through to a pass, hiding a real failure the re-sync found and fixed |
| `nestest` | 0-diff against the Nintendulator log |
| blargg `cpu_interrupts_v2` | 5/5, and the unstable-store tests 6/6 |
| blargg APU (NTSC and PAL) | 11/11 and 10/10 |
@@ -357,7 +357,7 @@ detailed in [`docs/architecture.md`](docs/architecture.md) and
## Current release
-RustyNES's current release is **v3.0.1 "Mortar"** (2026-10-07) — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** (2026-10-06) — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core. Built on **v2.9.9 "Ballast"** (2026-10-04) — the release candidate for v3.0.0: the audits re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it. Built on **v2.9.8 "Vanguard"** (2026-10-02) — the preparation release for v3.0.0: v3.0.0's breaking changes landed early (a save identity that ignores the header, old states and movies refused, movies and netplay that record the machine, the API removals), every staged game was booted and the defects found were fixed, and the game database's corrections reach every platform. Built on **v2.9.7 "Tandem"** (2026-09-30) — the desktop's features on the web and on phones, the release binaries built with every native feature, and a PPU A12 fix found by real games: Acclaim's MC-ACC games, the J.Y. ASIC and mapper 91 now count at their documented rates. Built on **v2.9.6 "Roster"** (2026-09-30) — seventeen mapper families written from their NESdev pages (174 → 191), GTROM promoted to Curated with a modelled flash chip whose saves persist, mapper 4's NES 2.0 submappers corrected (MMC6, NEC, MC-ACC, T9552), and the local commercial suites re-baselined after drifting unread since about v2.0.0.
+RustyNES's current release is **v3.1.0 "Bellwether"** (2026-10-08) — the AccuracyCoin re-sync and an honest 146/146, the CPU overclock and the sprite-limit option in movies and netplay, PAL emphasis, the alternate MMC3, rewind and run-ahead on the Vs. cabinet, and the MiSTer core matched to all of it. Built on **v3.0.1 "Mortar"** (2026-10-07) — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** (2026-10-06) — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core. Built on **v2.9.9 "Ballast"** (2026-10-04) — the release candidate for v3.0.0: the audits re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it. Built on **v2.9.8 "Vanguard"** (2026-10-02) — the preparation release for v3.0.0: v3.0.0's breaking changes landed early (a save identity that ignores the header, old states and movies refused, movies and netplay that record the machine, the API removals), every staged game was booted and the defects found were fixed, and the game database's corrections reach every platform. Built on **v2.9.7 "Tandem"** (2026-09-30) — the desktop's features on the web and on phones, the release binaries built with every native feature, and a PPU A12 fix found by real games: Acclaim's MC-ACC games, the J.Y. ASIC and mapper 91 now count at their documented rates. Built on **v2.9.6 "Roster"** (2026-09-30) — seventeen mapper families written from their NESdev pages (174 → 191), GTROM promoted to Curated with a modelled flash chip whose saves persist, mapper 4's NES 2.0 submappers corrected (MMC6, NEC, MC-ACC, T9552), and the local commercial suites re-baselined after drifting unread since about v2.0.0.
**v3.0.0 is the API major** ([ADR 0043](docs/adr/0043-v3-is-the-api-major-and-a-release-candidate-core.md)).
It gathers every breaking change since v2.x, most of them made early in v2.9.8 and
@@ -489,7 +489,7 @@ Full attribution is in [`NOTICE`](NOTICE).
title = {RustyNES: A Cycle-Accurate NES Emulator in Rust},
year = {2026},
url = {https://github.com/doublegate/RustyNES},
- note = {Cycle-accurate NES emulator; AccuracyCoin 144/144, nestest 0-diff}
+ note = {Cycle-accurate NES emulator; AccuracyCoin 146/146, nestest 0-diff}
}
```
diff --git a/ROADMAP.md b/ROADMAP.md
index 4df8a813b..781b361d2 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -2,13 +2,13 @@
**Document Version:** 2.0.4
**Last Updated:** 2026-10-06
-**Project Status:** v3.0.1 "Mortar" released — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** (the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core) and **v2.9.9 "Ballast"** (the release candidate for v3.0.0: all four audit scopes re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it with the release-candidate bitstream pair; the tenth release of the v2.9.x line and the sixth of the line to v3.0.0 (ADR 0043, amended)) and **v2.9.8 "Vanguard"** (v3.0.0's breaking changes landed early) and **v2.9.7 "Tandem"** (the desktop's features on the web and on phones). **No hardware has run any bitstream**; the mobile device runs and the SuperStation One board session move after v3.0.0 (maintainer, 2026-09-29).
+**Project Status:** v3.1.0 "Bellwether" released — the AccuracyCoin re-sync and an honest 146/146, the CPU overclock and the sprite-limit option in movies and netplay, PAL emphasis, the alternate MMC3, rewind and run-ahead on the Vs. cabinet, and the MiSTer core matched to all of it. Built on **v3.0.1 "Mortar"** (a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0) and **v3.0.0 "Cornerstone"** (the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core) and **v2.9.9 "Ballast"** (the release candidate for v3.0.0: all four audit scopes re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it with the release-candidate bitstream pair; the tenth release of the v2.9.x line and the sixth of the line to v3.0.0 (ADR 0043, amended)) and **v2.9.8 "Vanguard"** (v3.0.0's breaking changes landed early) and **v2.9.7 "Tandem"** (the desktop's features on the web and on phones). **No hardware has run any bitstream**; the mobile device runs and the SuperStation One board session move after v3.0.0 (maintainer, 2026-09-29).
---
## Where we are
-RustyNES is well past v1.0.0. The current release is **v3.0.1 "Mortar"** — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core. Built on **v2.9.9 "Ballast"** — the release candidate for v3.0.0: the audits re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it. Built on **v2.9.8 "Vanguard"** — the preparation release for v3.0.0: v3.0.0's breaking changes landed early (a save identity that ignores the header, old states and movies refused, movies and netplay that record the machine, the API removals), every staged game was booted and the defects found were fixed, and the game database's corrections reach every platform. Built on **v2.9.7 "Tandem"** — the desktop's features on the web and on phones, the release binaries built with every native feature, and a PPU A12 fix found by real games: Acclaim's MC-ACC games, the J.Y. ASIC and mapper 91 now count at their documented rates. Built on **v2.9.6 "Roster"** — seventeen mapper families written from their NESdev pages (174 → 191), GTROM promoted to Curated with a modelled flash chip whose saves persist, mapper 4's NES 2.0 submappers corrected (MMC6, NEC, MC-ACC, T9552), and the local commercial suites re-baselined after drifting unread since about v2.0.0. Built on **v2.9.5 "Caliper"** — every open accuracy item measured, then fixed or closed: four fixes red first (the `apu_test` frame-counter coincidence, the composite 2C02 scanline-0 sprite glitch, OAM DMA filling the PPU I/O latch, KS7032 at `$6000`), 49 unreferenced test ROMs gated, the MMC3 M2-edge filter lever tried and refuted, and a save-state epoch (`PPU_SNAPSHOT_VERSION` 11). Built on **v2.9.4 "Plumb"** — the records made true, and CI made to run what it only linted: v3.0.0 decided as the API major with a release-candidate core (ADR 0043), CI now running 63 feature-gated tests it never ran, the eight fuzz targets and a 70% line-coverage floor, the mapper tiers, store status and deferred-features catalogue corrected against the code, and the OAM-decay model recorded as derived from Mesen2. Built on **v2.9.3 "Handset"** — the old review threads closed and the mobile run prepared: every dependency moved to its newest release (egui 0.36 with wgpu 30, rcheevos 12.5.0), all 244 review threads left unanswered on PRs #7-#97 answered and the ten findings that still held fixed (Action 53 multicarts rebuilt to the NESdev spec, and a ROM header editor that no longer rewrites bytes you did not edit or saves mappers from 16 up as the wrong mapper), and the Android unit tests and the iOS renderer added to CI. Built on **v2.9.2 "Candidate"** — the full audit acted on, and the release-candidate pair: all 32 findings of a fifth audit have a verdict and 16 are fixed, save states keep the cartridge RAM of twelve board families they used to drop, the MiSTer core no longer loses an NMI raised inside a DMA, and both bitstreams are cut for the SuperStation One session. Built on **v2.9.1 "Hone"** — what the optimisation bars measure, and what clears them: the A/B tool had been timing the old code on both sides of every code comparison and is fixed, a two-screen Vs. cabinet saves about 9x faster, the off-die MiSTer build keeps CHR in its own SDRAM bank, and both bitstreams are pinned at fitter seed 2 and rebuild byte-identically. Built on **v2.9.0 "Survey"** — every audit re-checked, and the SuperStation One surveyed: a Power Cycle no longer erases your save, the off-die MiSTer build boots without the menu core, and 39 new audit findings are fixed or dispositioned. Built on **v2.8.4 "Tether"** — the MiSTer core's SDRAM build, made trustworthy: its controller now reads data on the edge the memory presents it (every off-die read would have been wrong on hardware, and only the new SDRAM timing constraints could see it), the power-up sequence and CAS-latency-3 reads follow the datasheet, the arbiter can no longer return the wrong byte or lose a write, the off-die bitstream builds from a script, both builds are swept and pinned at fitter seed 5, and the co-simulation ladder runs all 165 gates from a clean checkout. Built on **v2.8.3 "Rivet"** — the MiSTer core's reset, area and comments, measured: every reset is released on the clock that uses it and the timing analysis now checks each release, the CPU is about 4% smaller by two exact rewrites the fit report confirmed, four false comments are corrected, and the co-simulation ladder runs from a fresh checkout (164 of its 165 gates; the last needs a hand-built ROM no generator produces). **No hardware has run any bitstream.**
+RustyNES is well past v1.0.0. The current release is **v3.1.0 "Bellwether"** — the AccuracyCoin re-sync and an honest 146/146, the CPU overclock and the sprite-limit option in movies and netplay, PAL emphasis, the alternate MMC3, rewind and run-ahead on the Vs. cabinet, and the MiSTer core matched to all of it. Built on **v3.0.1 "Mortar"** — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core. Built on **v2.9.9 "Ballast"** — the release candidate for v3.0.0: the audits re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it. Built on **v2.9.8 "Vanguard"** — the preparation release for v3.0.0: v3.0.0's breaking changes landed early (a save identity that ignores the header, old states and movies refused, movies and netplay that record the machine, the API removals), every staged game was booted and the defects found were fixed, and the game database's corrections reach every platform. Built on **v2.9.7 "Tandem"** — the desktop's features on the web and on phones, the release binaries built with every native feature, and a PPU A12 fix found by real games: Acclaim's MC-ACC games, the J.Y. ASIC and mapper 91 now count at their documented rates. Built on **v2.9.6 "Roster"** — seventeen mapper families written from their NESdev pages (174 → 191), GTROM promoted to Curated with a modelled flash chip whose saves persist, mapper 4's NES 2.0 submappers corrected (MMC6, NEC, MC-ACC, T9552), and the local commercial suites re-baselined after drifting unread since about v2.0.0. Built on **v2.9.5 "Caliper"** — every open accuracy item measured, then fixed or closed: four fixes red first (the `apu_test` frame-counter coincidence, the composite 2C02 scanline-0 sprite glitch, OAM DMA filling the PPU I/O latch, KS7032 at `$6000`), 49 unreferenced test ROMs gated, the MMC3 M2-edge filter lever tried and refuted, and a save-state epoch (`PPU_SNAPSHOT_VERSION` 11). Built on **v2.9.4 "Plumb"** — the records made true, and CI made to run what it only linted: v3.0.0 decided as the API major with a release-candidate core (ADR 0043), CI now running 63 feature-gated tests it never ran, the eight fuzz targets and a 70% line-coverage floor, the mapper tiers, store status and deferred-features catalogue corrected against the code, and the OAM-decay model recorded as derived from Mesen2. Built on **v2.9.3 "Handset"** — the old review threads closed and the mobile run prepared: every dependency moved to its newest release (egui 0.36 with wgpu 30, rcheevos 12.5.0), all 244 review threads left unanswered on PRs #7-#97 answered and the ten findings that still held fixed (Action 53 multicarts rebuilt to the NESdev spec, and a ROM header editor that no longer rewrites bytes you did not edit or saves mappers from 16 up as the wrong mapper), and the Android unit tests and the iOS renderer added to CI. Built on **v2.9.2 "Candidate"** — the full audit acted on, and the release-candidate pair: all 32 findings of a fifth audit have a verdict and 16 are fixed, save states keep the cartridge RAM of twelve board families they used to drop, the MiSTer core no longer loses an NMI raised inside a DMA, and both bitstreams are cut for the SuperStation One session. Built on **v2.9.1 "Hone"** — what the optimisation bars measure, and what clears them: the A/B tool had been timing the old code on both sides of every code comparison and is fixed, a two-screen Vs. cabinet saves about 9x faster, the off-die MiSTer build keeps CHR in its own SDRAM bank, and both bitstreams are pinned at fitter seed 2 and rebuild byte-identically. Built on **v2.9.0 "Survey"** — every audit re-checked, and the SuperStation One surveyed: a Power Cycle no longer erases your save, the off-die MiSTer build boots without the menu core, and 39 new audit findings are fixed or dispositioned. Built on **v2.8.4 "Tether"** — the MiSTer core's SDRAM build, made trustworthy: its controller now reads data on the edge the memory presents it (every off-die read would have been wrong on hardware, and only the new SDRAM timing constraints could see it), the power-up sequence and CAS-latency-3 reads follow the datasheet, the arbiter can no longer return the wrong byte or lose a write, the off-die bitstream builds from a script, both builds are swept and pinned at fitter seed 5, and the co-simulation ladder runs all 165 gates from a clean checkout. Built on **v2.8.3 "Rivet"** — the MiSTer core's reset, area and comments, measured: every reset is released on the clock that uses it and the timing analysis now checks each release, the CPU is about 4% smaller by two exact rewrites the fit report confirmed, four false comments are corrected, and the co-simulation ladder runs from a fresh checkout (164 of its 165 gates; the last needs a hand-built ROM no generator produces). **No hardware has run any bitstream.**
**This root ROADMAP is a historical snapshot of the v1.0.0 cut.** For the authoritative, current forward roadmap see **[`to-dos/ROADMAP.md`](to-dos/ROADMAP.md)**; for the authoritative current-state pass counts and platform matrix see **[`docs/STATUS.md`](docs/STATUS.md)**; for the full per-release history see **[`CHANGELOG.md`](CHANGELOG.md)**. Many of the "post-1.0 directions" listed further down (mobile, Lua scripting, TAS editor, Vs. DualSystem, HD packs, hosted netplay) have since shipped — the tables below record what was **done at v1.0.0**, not the current feature set.
diff --git a/SECURITY.md b/SECURITY.md
index 09903d496..970347976 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -2,7 +2,7 @@
## Supported Versions
-The current release is **v3.0.1 "Mortar"**. Built on **v3.0.0 "Cornerstone"** and **v2.9.9 "Ballast"** and **v2.9.8 "Vanguard"** and **v2.9.7 "Tandem"** and **v2.9.6 "Roster"** and **v2.9.5 "Caliper"** and **v2.9.4 "Plumb"** and **v2.9.3 "Handset"** and **v2.9.2 "Candidate"** and **v2.9.1 "Hone"** and **v2.9.0 "Survey"** and **v2.8.4 "Tether"** and **v2.8.3 "Rivet"** and **v2.8.2 "Solder"** and **v2.8.1 "Gasket"** and **v2.8.0 "Bulkhead"** and **v2.7.6 "Recount"** and **v2.7.5 "Tally"** and **v2.7.4 "Pocket"** and **v2.7.3 "Hearth"** and **v2.7.2 "Bankroll"** and **v2.7.1 "Keepsake"** and **v2.7.0 "Palisade"** and **v2.6.23 "Pulse"** and **v2.6.22 "Rigging"** and **v2.6.21 "Steward"** and **v2.6.20 "Telltale"** and **v2.6.19 "Accession"** and **v2.6.18 "Errata"** and **v2.6.17 "Terminus"** and **v2.6.16 "Interlock"** and **v2.6.15 "Warrant"** and **v2.6.14 "Docket"** and **v2.6.13 "Slack"** and **v2.6.12 "Groundwork"** and **v2.6.11 "Exposure"** and **v2.6.10 "Inference"** and **v2.6.9 "Abeyance"** and **v2.6.8 "Arrears"** and **v2.6.7 "Detent"** and **v2.6.6 "Chassis"** and **v2.6.5 "Muster"** and **v2.6.4 "Rubric"** and **v2.6.3 "Mainspring"** and **v2.6.2 "Witness"** and **v2.6.1 "Interleave"** and **v2.6.0 "Assay"** and **v2.5.9 "Overture"** and **v2.5.8 "Blanking"** and **v2.5.7 "Collimation"** and **v2.5.6 "Vestige"** and **v2.5.5 "Raster"** and **v2.5.4 "Escapement"** and **v2.5.3 "Hysteresis"** and **v2.5.2 "Dormant"** and **v2.5.1 "Retrace"** and **v2.5.0 "Rungwork"** and **v2.4.9 "Plumbline II"** and **v2.4.8 "Palimpsest"** and **v2.4.7 "Keystone"** and **v2.4.6 "Abacus"** and **v2.4.5 "Compass"** and **v2.4.4 "Ignition"** and **v2.4.3 "Touchstone"** and **v2.4.2 "Cairn"** and **v2.4.1 "Fabric"**, which also carries the never-tagged v2.4.0 "Concordance". RustyNES ships from `main` on a
+The current release is **v3.1.0 "Bellwether"**. Built on **v3.0.1 "Mortar"** and **v3.0.0 "Cornerstone"** and **v2.9.9 "Ballast"** and **v2.9.8 "Vanguard"** and **v2.9.7 "Tandem"** and **v2.9.6 "Roster"** and **v2.9.5 "Caliper"** and **v2.9.4 "Plumb"** and **v2.9.3 "Handset"** and **v2.9.2 "Candidate"** and **v2.9.1 "Hone"** and **v2.9.0 "Survey"** and **v2.8.4 "Tether"** and **v2.8.3 "Rivet"** and **v2.8.2 "Solder"** and **v2.8.1 "Gasket"** and **v2.8.0 "Bulkhead"** and **v2.7.6 "Recount"** and **v2.7.5 "Tally"** and **v2.7.4 "Pocket"** and **v2.7.3 "Hearth"** and **v2.7.2 "Bankroll"** and **v2.7.1 "Keepsake"** and **v2.7.0 "Palisade"** and **v2.6.23 "Pulse"** and **v2.6.22 "Rigging"** and **v2.6.21 "Steward"** and **v2.6.20 "Telltale"** and **v2.6.19 "Accession"** and **v2.6.18 "Errata"** and **v2.6.17 "Terminus"** and **v2.6.16 "Interlock"** and **v2.6.15 "Warrant"** and **v2.6.14 "Docket"** and **v2.6.13 "Slack"** and **v2.6.12 "Groundwork"** and **v2.6.11 "Exposure"** and **v2.6.10 "Inference"** and **v2.6.9 "Abeyance"** and **v2.6.8 "Arrears"** and **v2.6.7 "Detent"** and **v2.6.6 "Chassis"** and **v2.6.5 "Muster"** and **v2.6.4 "Rubric"** and **v2.6.3 "Mainspring"** and **v2.6.2 "Witness"** and **v2.6.1 "Interleave"** and **v2.6.0 "Assay"** and **v2.5.9 "Overture"** and **v2.5.8 "Blanking"** and **v2.5.7 "Collimation"** and **v2.5.6 "Vestige"** and **v2.5.5 "Raster"** and **v2.5.4 "Escapement"** and **v2.5.3 "Hysteresis"** and **v2.5.2 "Dormant"** and **v2.5.1 "Retrace"** and **v2.5.0 "Rungwork"** and **v2.4.9 "Plumbline II"** and **v2.4.8 "Palimpsest"** and **v2.4.7 "Keystone"** and **v2.4.6 "Abacus"** and **v2.4.5 "Compass"** and **v2.4.4 "Ignition"** and **v2.4.3 "Touchstone"** and **v2.4.2 "Cairn"** and **v2.4.1 "Fabric"**, which also carries the never-tagged v2.4.0 "Concordance". RustyNES ships from `main` on a
rolling patch cadence rather than maintaining long-lived release branches, so
security fixes land in the next patch release rather than being backported.
Report against the latest release or `main`.
diff --git a/SUPPORT.md b/SUPPORT.md
index ee55055e3..452be26ba 100644
--- a/SUPPORT.md
+++ b/SUPPORT.md
@@ -94,11 +94,11 @@ A: RustyNES is a cycle-accurate NES emulator written in pure Rust, clearing the
**Q: Can I use RustyNES now?**
-A: Yes. RustyNES is well past its first stable release — the current release is **v3.0.1 "Mortar"** — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core. Built on **v2.9.9 "Ballast"** — the release candidate for v3.0.0: the audits re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it. Built on **v2.9.8 "Vanguard"** — the preparation release for v3.0.0: v3.0.0's breaking changes landed early (a save identity that ignores the header, old states and movies refused, movies and netplay that record the machine, the API removals), every staged game was booted and the defects found were fixed, and the game database's corrections reach every platform. Built on **v2.9.7 "Tandem"** — the desktop's features on the web and on phones, the release binaries built with every native feature, and a PPU A12 fix found by real games: Acclaim's MC-ACC games, the J.Y. ASIC and mapper 91 now count at their documented rates. Built on **v2.9.6 "Roster"** — seventeen mapper families written from their NESdev pages (174 → 191), GTROM promoted to Curated with a modelled flash chip whose saves persist, mapper 4's NES 2.0 submappers corrected (MMC6, NEC, MC-ACC, T9552), and the local commercial suites re-baselined after drifting unread since about v2.0.0. Built on **v2.9.5 "Caliper"** — every open accuracy item measured, then fixed or closed: four fixes red first (the `apu_test` frame-counter coincidence, the composite 2C02 scanline-0 sprite glitch, OAM DMA filling the PPU I/O latch, KS7032 at `$6000`), 49 unreferenced test ROMs gated, the MMC3 M2-edge filter lever tried and refuted, and a save-state epoch (`PPU_SNAPSHOT_VERSION` 11). Built on **v2.9.4 "Plumb"** — the records made true, and CI made to run what it only linted: v3.0.0 decided as the API major with a release-candidate core (ADR 0043), CI now running 63 feature-gated tests it never ran, the eight fuzz targets and a 70% line-coverage floor, the mapper tiers, store status and deferred-features catalogue corrected against the code, and the OAM-decay model recorded as derived from Mesen2. Built on **v2.9.3 "Handset"** — the old review threads closed and the mobile run prepared: every dependency moved to its newest release (egui 0.36 with wgpu 30, rcheevos 12.5.0), all 244 review threads left unanswered on PRs #7-#97 answered and the ten findings that still held fixed (Action 53 multicarts rebuilt to the NESdev spec, and a ROM header editor that no longer rewrites bytes you did not edit or saves mappers from 16 up as the wrong mapper), and the Android unit tests and the iOS renderer added to CI. Built on **v2.9.2 "Candidate"** — the full audit acted on, and the release-candidate pair: all 32 findings of a fifth audit have a verdict and 16 are fixed, save states keep the cartridge RAM of twelve board families they used to drop, the MiSTer core no longer loses an NMI raised inside a DMA, and both bitstreams are cut for the SuperStation One session. Built on **v2.9.1 "Hone"** — what the optimisation bars measure, and what clears them: the A/B tool had been timing the old code on both sides of every code comparison and is fixed, a two-screen Vs. cabinet saves about 9x faster, the off-die MiSTer build keeps CHR in its own SDRAM bank, and both bitstreams are pinned at fitter seed 2 and rebuild byte-identically. Built on **v2.9.0 "Survey"** — every audit re-checked, and the SuperStation One surveyed: a Power Cycle no longer erases your save, the off-die MiSTer build boots without the menu core, and 39 new audit findings are fixed or dispositioned. Built on **v2.8.4 "Tether"** — the MiSTer core's SDRAM build, made trustworthy: its controller now reads data on the edge the memory presents it (every off-die read would have been wrong on hardware, and only the new SDRAM timing constraints could see it), the power-up sequence and CAS-latency-3 reads follow the datasheet, the arbiter can no longer return the wrong byte or lose a write, the off-die bitstream builds from a script, both builds are swept and pinned at fitter seed 5, and the co-simulation ladder runs all 165 gates from a clean checkout. Built on **v2.8.3 "Rivet"** — the MiSTer core's reset, area and comments, measured: every reset is released on the clock that uses it and the timing analysis now checks each release, the CPU is about 4% smaller by two exact rewrites the fit report confirmed, four false comments are corrected, and the co-simulation ladder runs from a fresh checkout (164 of its 165 gates; the last needs a hand-built ROM no generator produces). **No hardware has run any bitstream.**
+A: Yes. RustyNES is well past its first stable release — the current release is **v3.1.0 "Bellwether"** — the AccuracyCoin re-sync and an honest 146/146, the CPU overclock and the sprite-limit option in movies and netplay, PAL emphasis, the alternate MMC3, rewind and run-ahead on the Vs. cabinet, and the MiSTer core matched to all of it. Built on **v3.0.1 "Mortar"** — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core. Built on **v2.9.9 "Ballast"** — the release candidate for v3.0.0: the audits re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it. Built on **v2.9.8 "Vanguard"** — the preparation release for v3.0.0: v3.0.0's breaking changes landed early (a save identity that ignores the header, old states and movies refused, movies and netplay that record the machine, the API removals), every staged game was booted and the defects found were fixed, and the game database's corrections reach every platform. Built on **v2.9.7 "Tandem"** — the desktop's features on the web and on phones, the release binaries built with every native feature, and a PPU A12 fix found by real games: Acclaim's MC-ACC games, the J.Y. ASIC and mapper 91 now count at their documented rates. Built on **v2.9.6 "Roster"** — seventeen mapper families written from their NESdev pages (174 → 191), GTROM promoted to Curated with a modelled flash chip whose saves persist, mapper 4's NES 2.0 submappers corrected (MMC6, NEC, MC-ACC, T9552), and the local commercial suites re-baselined after drifting unread since about v2.0.0. Built on **v2.9.5 "Caliper"** — every open accuracy item measured, then fixed or closed: four fixes red first (the `apu_test` frame-counter coincidence, the composite 2C02 scanline-0 sprite glitch, OAM DMA filling the PPU I/O latch, KS7032 at `$6000`), 49 unreferenced test ROMs gated, the MMC3 M2-edge filter lever tried and refuted, and a save-state epoch (`PPU_SNAPSHOT_VERSION` 11). Built on **v2.9.4 "Plumb"** — the records made true, and CI made to run what it only linted: v3.0.0 decided as the API major with a release-candidate core (ADR 0043), CI now running 63 feature-gated tests it never ran, the eight fuzz targets and a 70% line-coverage floor, the mapper tiers, store status and deferred-features catalogue corrected against the code, and the OAM-decay model recorded as derived from Mesen2. Built on **v2.9.3 "Handset"** — the old review threads closed and the mobile run prepared: every dependency moved to its newest release (egui 0.36 with wgpu 30, rcheevos 12.5.0), all 244 review threads left unanswered on PRs #7-#97 answered and the ten findings that still held fixed (Action 53 multicarts rebuilt to the NESdev spec, and a ROM header editor that no longer rewrites bytes you did not edit or saves mappers from 16 up as the wrong mapper), and the Android unit tests and the iOS renderer added to CI. Built on **v2.9.2 "Candidate"** — the full audit acted on, and the release-candidate pair: all 32 findings of a fifth audit have a verdict and 16 are fixed, save states keep the cartridge RAM of twelve board families they used to drop, the MiSTer core no longer loses an NMI raised inside a DMA, and both bitstreams are cut for the SuperStation One session. Built on **v2.9.1 "Hone"** — what the optimisation bars measure, and what clears them: the A/B tool had been timing the old code on both sides of every code comparison and is fixed, a two-screen Vs. cabinet saves about 9x faster, the off-die MiSTer build keeps CHR in its own SDRAM bank, and both bitstreams are pinned at fitter seed 2 and rebuild byte-identically. Built on **v2.9.0 "Survey"** — every audit re-checked, and the SuperStation One surveyed: a Power Cycle no longer erases your save, the off-die MiSTer build boots without the menu core, and 39 new audit findings are fixed or dispositioned. Built on **v2.8.4 "Tether"** — the MiSTer core's SDRAM build, made trustworthy: its controller now reads data on the edge the memory presents it (every off-die read would have been wrong on hardware, and only the new SDRAM timing constraints could see it), the power-up sequence and CAS-latency-3 reads follow the datasheet, the arbiter can no longer return the wrong byte or lose a write, the off-die bitstream builds from a script, both builds are swept and pinned at fitter seed 5, and the co-simulation ladder runs all 165 gates from a clean checkout. Built on **v2.8.3 "Rivet"** — the MiSTer core's reset, area and comments, measured: every reset is released on the clock that uses it and the timing analysis now checks each release, the CPU is about 4% smaller by two exact rewrites the fit report confirmed, four false comments are corrected, and the co-simulation ladder runs from a fresh checkout (164 of its 165 gates; the last needs a hand-built ROM no generator produces). **No hardware has run any bitstream.**
**Q: How accurate is RustyNES?**
-A: AccuracyCoin 100.00% (144/144). The 2026-09 upstream re-sync grew the battery from 141 to 144 assigned tests, and v2.6.18 closed the last outstanding entry (`Advanced Sprite Evaluation :: Frozen OAM2 Increment`). That includes the two older upstream PPU tests ("ALE + Read", "Hybrid Addresses"), which the v2.0.3 2-cycle-ALE PPU-fetch promotion closed — `nestest` 0-diff, and the blargg / kevtris suites green, validated by a byte-identical commercial-ROM regression oracle. See [docs/STATUS.md](docs/STATUS.md) for the authoritative pass-count matrix.
+A: AccuracyCoin 100.00% (146/146) at upstream `f5f41dc2`, since v3.1.0. That re-sync also showed that the 144/144 reported from v2.6.18 to v3.0.1 was overstated, because the older ROM's `Misaligned OAM behavior` fail path fell through to a pass and hid a real failure; v3.1.0 fixed it. Before it, the 2026-09 upstream re-sync grew the battery from 141 to 144 assigned tests, and v2.6.18 closed the last outstanding entry (`Advanced Sprite Evaluation :: Frozen OAM2 Increment`). That includes the two older upstream PPU tests ("ALE + Read", "Hybrid Addresses"), which the v2.0.3 2-cycle-ALE PPU-fetch promotion closed — `nestest` 0-diff, and the blargg / kevtris suites green, validated by a byte-identical commercial-ROM regression oracle. See [docs/STATUS.md](docs/STATUS.md) for the authoritative pass-count matrix.
**Q: How can I contribute?**
diff --git a/VERSION-PLAN.md b/VERSION-PLAN.md
index 4cff5b920..54a4f7892 100644
--- a/VERSION-PLAN.md
+++ b/VERSION-PLAN.md
@@ -1,6 +1,6 @@
# RustyNES Version Plan
-**Current release: v3.0.1 "Mortar"** — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core. Built on **v2.9.9 "Ballast"** — the release candidate for v3.0.0: the audits re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it. Built on **v2.9.8 "Vanguard"** — the preparation release for v3.0.0: v3.0.0's breaking changes landed early (a save identity that ignores the header, old states and movies refused, movies and netplay that record the machine, the API removals), every staged game was booted and the defects found were fixed, and the game database's corrections reach every platform. Built on **v2.9.7 "Tandem"** — the desktop's features on the web and on phones, the release binaries built with every native feature, and a PPU A12 fix found by real games: Acclaim's MC-ACC games, the J.Y. ASIC and mapper 91 now count at their documented rates. Built on **v2.9.6 "Roster"** — seventeen mapper families written from their NESdev pages (174 → 191), GTROM promoted to Curated with a modelled flash chip whose saves persist, mapper 4's NES 2.0 submappers corrected (MMC6, NEC, MC-ACC, T9552), and the local commercial suites re-baselined after drifting unread since about v2.0.0. Built on **v2.9.5 "Caliper"** — every open accuracy item measured, then fixed or closed: four fixes red first (the `apu_test` frame-counter coincidence, the composite 2C02 scanline-0 sprite glitch, OAM DMA filling the PPU I/O latch, KS7032 at `$6000`), 49 unreferenced test ROMs gated, the MMC3 M2-edge filter lever tried and refuted, and a save-state epoch (`PPU_SNAPSHOT_VERSION` 11). Built on **v2.9.4 "Plumb"** — the records made true, and CI made to run what it only linted: v3.0.0 decided as the API major with a release-candidate core (ADR 0043), CI now running 63 feature-gated tests it never ran, the eight fuzz targets and a 70% line-coverage floor, the mapper tiers, store status and deferred-features catalogue corrected against the code, and the OAM-decay model recorded as derived from Mesen2. Built on **v2.9.3 "Handset"** — the old review threads closed and the mobile run prepared: every dependency moved to its newest release (egui 0.36 with wgpu 30, rcheevos 12.5.0), all 244 review threads left unanswered on PRs #7-#97 answered and the ten findings that still held fixed (Action 53 multicarts rebuilt to the NESdev spec, and a ROM header editor that no longer rewrites bytes you did not edit or saves mappers from 16 up as the wrong mapper), and the Android unit tests and the iOS renderer added to CI. Built on **v2.9.2 "Candidate"** — the full audit acted on, and the release-candidate pair: all 32 findings of a fifth audit have a verdict and 16 are fixed, save states keep the cartridge RAM of twelve board families they used to drop, the MiSTer core no longer loses an NMI raised inside a DMA, and both bitstreams are cut for the SuperStation One session. Built on **v2.9.1 "Hone"** — what the optimisation bars measure, and what clears them: the A/B tool had been timing the old code on both sides of every code comparison and is fixed, a two-screen Vs. cabinet saves about 9x faster, the off-die MiSTer build keeps CHR in its own SDRAM bank, and both bitstreams are pinned at fitter seed 2 and rebuild byte-identically. Built on **v2.9.0 "Survey"** — every audit re-checked, and the SuperStation One surveyed: a Power Cycle no longer erases your save, the off-die MiSTer build boots without the menu core, and 39 new audit findings are fixed or dispositioned. Built on **v2.8.4 "Tether"** — the MiSTer core's SDRAM build, made trustworthy: its controller now reads data on the edge the memory presents it (every off-die read would have been wrong on hardware, and only the new SDRAM timing constraints could see it), the power-up sequence and CAS-latency-3 reads follow the datasheet, the arbiter can no longer return the wrong byte or lose a write, the off-die bitstream builds from a script, both builds are swept and pinned at fitter seed 5, and the co-simulation ladder runs all 165 gates from a clean checkout. Built on **v2.8.3 "Rivet"** — the MiSTer core's reset, area and comments, measured: every reset is released on the clock that uses it and the timing analysis now checks each release, the CPU is about 4% smaller by two exact rewrites the fit report confirmed, four false comments are corrected, and the co-simulation ladder runs from a fresh checkout (164 of its 165 gates; the last needs a hand-built ROM no generator produces). Built on **v2.8.2 "Solder"** — the MiSTer core's on-die RTL, corrected against the oracle and the wiki: an MMC3 IRQ acknowledge is no longer lost to a same-edge counter clock, SNROM's battery RAM obeys its CHR-line enable, the triangle and noise drop a reload landing on a length clock, a `$2002` read leaves the byte it returned on the data bus, and the emulator's MMC1 no longer ignores a reset written on the cycle after another write. **No hardware has run any bitstream.**
+**Current release: v3.1.0 "Bellwether"** — the AccuracyCoin re-sync and an honest 146/146, the CPU overclock and the sprite-limit option in movies and netplay, PAL emphasis, the alternate MMC3, rewind and run-ahead on the Vs. cabinet, and the MiSTer core matched to all of it. Built on **v3.0.1 "Mortar"** — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core. Built on **v2.9.9 "Ballast"** — the release candidate for v3.0.0: the audits re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it. Built on **v2.9.8 "Vanguard"** — the preparation release for v3.0.0: v3.0.0's breaking changes landed early (a save identity that ignores the header, old states and movies refused, movies and netplay that record the machine, the API removals), every staged game was booted and the defects found were fixed, and the game database's corrections reach every platform. Built on **v2.9.7 "Tandem"** — the desktop's features on the web and on phones, the release binaries built with every native feature, and a PPU A12 fix found by real games: Acclaim's MC-ACC games, the J.Y. ASIC and mapper 91 now count at their documented rates. Built on **v2.9.6 "Roster"** — seventeen mapper families written from their NESdev pages (174 → 191), GTROM promoted to Curated with a modelled flash chip whose saves persist, mapper 4's NES 2.0 submappers corrected (MMC6, NEC, MC-ACC, T9552), and the local commercial suites re-baselined after drifting unread since about v2.0.0. Built on **v2.9.5 "Caliper"** — every open accuracy item measured, then fixed or closed: four fixes red first (the `apu_test` frame-counter coincidence, the composite 2C02 scanline-0 sprite glitch, OAM DMA filling the PPU I/O latch, KS7032 at `$6000`), 49 unreferenced test ROMs gated, the MMC3 M2-edge filter lever tried and refuted, and a save-state epoch (`PPU_SNAPSHOT_VERSION` 11). Built on **v2.9.4 "Plumb"** — the records made true, and CI made to run what it only linted: v3.0.0 decided as the API major with a release-candidate core (ADR 0043), CI now running 63 feature-gated tests it never ran, the eight fuzz targets and a 70% line-coverage floor, the mapper tiers, store status and deferred-features catalogue corrected against the code, and the OAM-decay model recorded as derived from Mesen2. Built on **v2.9.3 "Handset"** — the old review threads closed and the mobile run prepared: every dependency moved to its newest release (egui 0.36 with wgpu 30, rcheevos 12.5.0), all 244 review threads left unanswered on PRs #7-#97 answered and the ten findings that still held fixed (Action 53 multicarts rebuilt to the NESdev spec, and a ROM header editor that no longer rewrites bytes you did not edit or saves mappers from 16 up as the wrong mapper), and the Android unit tests and the iOS renderer added to CI. Built on **v2.9.2 "Candidate"** — the full audit acted on, and the release-candidate pair: all 32 findings of a fifth audit have a verdict and 16 are fixed, save states keep the cartridge RAM of twelve board families they used to drop, the MiSTer core no longer loses an NMI raised inside a DMA, and both bitstreams are cut for the SuperStation One session. Built on **v2.9.1 "Hone"** — what the optimisation bars measure, and what clears them: the A/B tool had been timing the old code on both sides of every code comparison and is fixed, a two-screen Vs. cabinet saves about 9x faster, the off-die MiSTer build keeps CHR in its own SDRAM bank, and both bitstreams are pinned at fitter seed 2 and rebuild byte-identically. Built on **v2.9.0 "Survey"** — every audit re-checked, and the SuperStation One surveyed: a Power Cycle no longer erases your save, the off-die MiSTer build boots without the menu core, and 39 new audit findings are fixed or dispositioned. Built on **v2.8.4 "Tether"** — the MiSTer core's SDRAM build, made trustworthy: its controller now reads data on the edge the memory presents it (every off-die read would have been wrong on hardware, and only the new SDRAM timing constraints could see it), the power-up sequence and CAS-latency-3 reads follow the datasheet, the arbiter can no longer return the wrong byte or lose a write, the off-die bitstream builds from a script, both builds are swept and pinned at fitter seed 5, and the co-simulation ladder runs all 165 gates from a clean checkout. Built on **v2.8.3 "Rivet"** — the MiSTer core's reset, area and comments, measured: every reset is released on the clock that uses it and the timing analysis now checks each release, the CPU is about 4% smaller by two exact rewrites the fit report confirmed, four false comments are corrected, and the co-simulation ladder runs from a fresh checkout (164 of its 165 gates; the last needs a hand-built ROM no generator produces). Built on **v2.8.2 "Solder"** — the MiSTer core's on-die RTL, corrected against the oracle and the wiki: an MMC3 IRQ acknowledge is no longer lost to a same-edge counter clock, SNROM's battery RAM obeys its CHR-line enable, the triangle and noise drop a reload landing on a length clock, a `$2002` read leaves the byte it returned on the data bus, and the emulator's MMC1 no longer ignores a reset written on the cycle after another write. **No hardware has run any bitstream.**
RustyNES follows [Semantic Versioning 2.0.0](https://semver.org/).
@@ -73,7 +73,6 @@ day.)
| Version | Scope | Plan |
|---------|-------|------|
-| v3.1.0 | The AccuracyCoin re-sync; the CPU overclock and sprite-limit options in movies and netplay; PAL emphasis; opt-in composite artifacts; the NEC MMC3 option; rewind and run-ahead in Vs. dual mode; an epoch fingerprint gate. MiSTer: small RTL items, the self-hosted runner, submission documents | [`v3.1.0-plan.md`](to-dos/plans/v3.1.0-plan.md) |
| v3.2.0 | Mapper breadth by real titles, the dump corpus, KNOWN_BLANK triage, tier promotions. MiSTer F1: options and about ten cheap families, paddle, Four Score, cheats | [`v3.2.0-plan.md`](to-dos/plans/v3.2.0-plan.md) |
| v3.3.0 | Phi2 write placement and the sprite-0 stale shifter; wgpu 31 / egui 0.37. MiSTer F2: the SDRAM arbiter, DDR3, save states, rewind; the off-die build becomes the headline | [`v3.3.0-plan.md`](to-dos/plans/v3.3.0-plan.md) |
| v3.4.0 | Hosted netplay and the browser RA proxy on Cloudflare, RA hardcore compliance, native 3-4 player netplay. MiSTer F3a: MMC2/4, FME-7/5B, VRC2/4, the Zapper | [`v3.4.0-plan.md`](to-dos/plans/v3.4.0-plan.md) |
@@ -166,7 +165,8 @@ The 1.x line was **additive / off-by-default** — every release stayed byte-ide
| **v2.7.5 "Tally"** | Every audit claim closed with a measurement or a reason -- the sixth and last release of the v2.7.x audit line; the core and frontend ledgers have no open row. Of the core audit's twelve hot-path proposals, ten were measured here before any was built, IMP-12 had been measured in v2.3.1, and the phase-`match` fold was closed by reasoning: one combined ceiling probe (seven proposals at their maximum, knowingly breaking correctness) bounded those seven at zero on two clean runs, and IMP-04 run alone twice also measured zero; IMP-05 (CPU inlining + cold-path outlining, whose premise was true) and the branchless palette got their own runs, also zero; IMP-12 was already v2.3.1's G10. **One adopted:** `BlipBuf::drain_all` keeps its capacity between frames, -0.89% frame time on two runs of a frame-then-drain workload (the stock benches never drain audio, which review on #553 exposed after the probe had first been credited with it); it helps the mobile bridge and the wasm build. 18 dead `rustynes_cpu::Bus` methods (found by the compiler: 5 of the audit's 22 were live) and `ApuBus` are `#[deprecated]`, removal decided at v2.9.0. An HD pack refunds the budget of an image that fails after its header. MOB-06 is blocked on UniFFI releasing mutable borrowed bytes. No emulation behaviour change; AccuracyCoin 144/144, nestest 0-diff. |
| **v2.7.6 "Recount"** | The v2.7.5 deletions measured one at a time -- a measurement release between the v2.7.x and v2.8.x audit lines. v2.7.5 bounded six of the core audit's proposals (§3.1 A/B/C, the IMP-06 stores, §3.5b, §3.6) only in one combined ceiling; each was re-run alone, twice, with the A/B/A control. Five are zero; §3.5b had never been reached (the bench ROMs are silent, 0 sweep-mute checks per frame), and on `spritecans.nes` bounds at about 0.2%, with the cheap byte-identical candidate 0.67% slower, so it is rejected. Three of the IMP-06 stores re-wrote fields the fast path's guard already requires, and are now a `debug_assert!` pinned by a unit test (no ROM reaches the violating state). §3.1 B and C are dead work that goes with the v2.9.0 deprecation decision. Also ships #554 (contributed): the macOS buildbot jobs build again, and 32-bit Windows, 32-bit Linux and webOS targets were added. Plan: `to-dos/plans/v2.7.6-recount-plan.md` |
| **v2.8.0 "Bulkhead"** | The libretro core stops a fault at its own boundary -- the first release of the v2.8.x audit line, from the libretro audit's FFI-safety findings (L-1.1 to L-1.5, L-2.1, L-2.2, L-2.4), each shown failing in a new C-ABI harness that drives the core as a frontend does. The core was built `panic = "abort"`, so its handlers were dead code; it now unwinds on every supported build path (+0.1% instructions per frame; a direct `cargo build --release` still aborts, and now warns and logs it), and every emulating callback contains a panic, stops the game and keeps the console so RetroArch's memory pointers stay valid. `serialize_size` reserves room for the largest expansion device on both ports (a Zapper no longer breaks every later save) and the reader treats an all-zero tail as padding; unload withdraws the memory maps; the vendored `rust-libretro-sys` writes out `retro_game_info`, so the core loads without `GET_GAME_INFO_EXT`. The save state gains the 2A03 internal data bus (older states still load), and the schema audit now covers the bus. No emulation behaviour change; AccuracyCoin 144/144, nestest 0-diff. Plan: `to-dos/plans/v2.8.0-bulkhead-plan.md` |
-| **v3.0.1 "Mortar"** (current) | A maintenance release on v3.0.0, plan [`v3.0.1-mortar-plan.md`](to-dos/plans/v3.0.1-mortar-plan.md). Mapper 45's CHR-RAM is unbanked, so *Famicom Yarou Vol.1* draws its menu (T-GA23C-CHRRAM; `EMULATION_EPOCH` 2 refuses v3.0.0 movies and netplay peers). The MiSTer core's dot-0 A12 rule is gated on whether cycle 0 was rendering, found by a new odd-frame stimulus (`mapper4mmc3oddskip080`, plus an /IRQ-rise comparison); new bitstreams at seed 2 / 261007, a release candidate, not hardware-verified. Rust 1.99 and every dependency at its newest, the libretro buildbot included once test pipeline 119614 passed 15/15. Every bot review left unanswered since PR #1 answered (473 verdicts, 80 fixes). The shared Bisqwit NTSC pass recorded as derived; the TriCNES source moved out of the repository. The plan to v4.0.0 written from 29 maintainer decisions |
+| **v3.0.1 "Mortar"** | A maintenance release on v3.0.0, plan [`v3.0.1-mortar-plan.md`](to-dos/plans/v3.0.1-mortar-plan.md). Mapper 45's CHR-RAM is unbanked, so *Famicom Yarou Vol.1* draws its menu (T-GA23C-CHRRAM; `EMULATION_EPOCH` 2 refuses v3.0.0 movies and netplay peers). The MiSTer core's dot-0 A12 rule is gated on whether cycle 0 was rendering, found by a new odd-frame stimulus (`mapper4mmc3oddskip080`, plus an /IRQ-rise comparison); new bitstreams at seed 2 / 261007, a release candidate, not hardware-verified. Rust 1.99 and every dependency at its newest, the libretro buildbot included once test pipeline 119614 passed 15/15. Every bot review left unanswered since PR #1 answered (473 verdicts, 80 fixes). The shared Bisqwit NTSC pass recorded as derived; the TriCNES source moved out of the repository. The plan to v4.0.0 written from 29 maintainer decisions |
+| **v3.1.0 "Bellwether"** (current) | The first release of the v3.1 to v4.0 line, plan [`v3.1.0-plan.md`](to-dos/plans/v3.1.0-plan.md). AccuracyCoin re-synced to upstream `f5f41dc2`, 146 of 146; the new ROM showed that every earlier 100% included a `Misaligned OAM behavior` failure the old ROM hid, and both defects it found are fixed (`EMULATION_EPOCH` 3, BUS section 3, `PPU_SNAPSHOT_VERSION` 13, `.rnm` 6, netplay 7: v3.0.1 states, movies and peers are refused). The CPU overclock and the sprite-limit option work and ride in `HardwareOptions`; PAL and Dendy emphasis swap red and green; the alternate MMC3 is selectable and asserts on a `$C001` reload to 0; differential phase in the NTSC decode; rewind and run-ahead on the Vs. cabinet; an epoch fingerprint gate. The MiSTer core matches the re-synced battery on all 146 entries (the misaligned X mask, the DMC reload edge), matches the emulator on a `$2006` copy landing on every dot (RTL-1, provisional until a board settles it), gates 30 of 34 AccuracyCoin sub-test ROMs, and moves its pin to v3.1.0. **No hardware has run any bitstream.** |
| **v3.0.0 "Cornerstone"** | The API major -- the first release of the v3 line (ADR 0043). It gathers every break since v2.x in one place (the header-free game identity, refused older states and movies, the protocol and API changes of v2.8.x-v2.9.9) and adds the last: a core timing epoch that movies (`.rnm` format 5) and netplay (protocol 6, `"RNE6"`) carry, so a version that emulates differently is refused with a reason (ADR 0045); `Cartridge`, `BoardDescription`, `HardwareOptions` and `Movie` `#[non_exhaustive]`; `MapperError::Truncated` renamed `WrongLength`. T-MMC3-BG-A12 closes blargg's `4-scanline_timing` (13/13) in the emulator and on the MiSTer core (`PPU_SNAPSHOT_VERSION` 12). Also: the spectator buffer bounded, mapper 45's power-on CHR-AND (two *Famicom Yarou* menus), the mobile apps on the workspace version, and `CI success` that cannot pass untested. The MiSTer bitstreams ship as a release candidate, not hardware-verified; verification moves to v3.x |
| **v2.9.9 "Ballast"** | The release candidate for v3.0.0 -- the tenth release of the v2.9.x line and the last of the line to v3.0.0. All four audit scopes re-run (0 rows regressed; NC-09..17, NF-11..23, NL-11..15, NR-13..17 fixed or decided, NC-17 and NR-14 by the maintainer). T-ORACLE-001 fixed to `4-scanline_timing` sub-test 9: the oracle's `$C001` reload discriminator was the late IRQ; the NESdev rule plus a one-cycle IRQ output deferral replace it (MMC3 section v4). T-MMC5-8X8-SET fixed and wider than the ticket: the MMC5 decodes `$2000`/`$2001` itself and follows the page's CHR table and `$2007` rule, which also fixed *Uchuu Keibitai SDF* (T-COMMERCIAL-GARBLE closed; MMC5 section v6). Audio exact across a save state (APU v5, NL-12). Movie format 4 (NC-10). Mobile save keys migrated to the v2.9.8 identity (NF-21). The sibling's oracle pin moved v2.9.2 -> v2.9.9 with every changed golden attributed, the APU `$4017` / scanline-0 parity in RTL, a frame-sequencer stall found and fixed, and `apu_test` 1-10, `mapper4mmc3irq065` and four checkpoint streams gated. RC bitstreams at seed 6 (261005), byte-identical double compiles. T-GA23C-POWERON searched and left open. |
| **v2.9.8 "Vanguard"** | The preparation release for v3.0.0 -- the ninth release of the v2.9.x line. v3.0.0's planned breaks landed early at the maintainer's direction (ADR 0042/0043 amendments of 2026-10-01; the number stays v2.9.x): `Nes::rom_sha256` hashes the bytes after the header, so earlier saves, cheats and movies are not found; `.rns` epoch 3 and BUS section 2 refuse older states with every legacy reader removed; `.rnm` format 3 and the netplay handshake carry every emulation option (ADR 0044) and Four Score P3/P4; `SystemBus` (was `LockstepBus`), the 2.7.5 deprecations, `serialize_header` and the dead /NMI detector removed, `Header` and `FrameInput` `#[non_exhaustive]`; the Vs. database keyed by the new identity. Every one of 748 staged dumps booted and looked at: fixed from their documentation were the game database rewriting correct NES 2.0 headers (10 games), the iNES 1.0 dirty-tail mapper nibble, mappers 19, 64, 78, 105 (`$4017` inhibit), 153, 191 (non-power-of-two images), 218 and 226, Vs. work RAM and the Goonies palette, and PAL regions now reach iNES 1.0 games. The game database's corrections now reach Android, iOS and libretro; power-on options apply before a game's first frame; Power Cycle keeps the PPU/APU settings; an opt-in Famicom console model; the documented NTSC emphasis model. Performance: the /NMI removal -4.9% to -6.1% on three workloads, three campaign candidates adopted (-1.8% to -4.3% together); end to end only shipped `nestest_fast` is established (-5% to -9% vs v2.9.7), the rest a v2.9.9 lead. MiSTer: aspect/integer-scale/crop/2C03-palette menu options (not seen on hardware), a palette-gate, seed 2 kept after re-sweeps. `cargo test --release --workspace --features test-roms,commercial-roms` 3,367 passed / 0 failed; AccuracyCoin 144/144, nestest 0-diff; co-simulation 175/0/1 on-die, 176/0/1 off-die. **No hardware has run any bitstream.** Plan: `to-dos/plans/v2.9.8-vanguard-plan.md` |
@@ -201,7 +201,7 @@ The 1.x line was **additive / off-by-default** — every release stayed byte-ide
## Accuracy milestones (met)
-- `nestest` 0-diff, blargg / kevtris suites green, **AccuracyCoin 100.00%** -- 141/141 from **v2.0.3** through v2.6.16, and **144/144** from v2.6.18 once the re-synced catalog's last entry closed (139/139 at the v1.0.0 cut; the v2.0.1 oracle re-sync grew the catalog to 141 assigned tests and briefly opened two PPU gaps, so v2.0.1–v2.0.2 shipped an honest 139/141 until the v2.0.3 2-cycle-ALE promotion closed them), and a byte-identical 60-ROM commercial regression oracle. As of v2.3.0 the AccuracyCoin gate is pinned to an **exact 141/141** (zero failing tests), so a single-test regression — e.g. in the hybrid-address model — fails CI. `docs/STATUS.md` is the authoritative pass-count source.
+- `nestest` 0-diff, blargg / kevtris suites green, **AccuracyCoin 100.00%** -- 141/141 from **v2.0.3** through v2.6.16, **144/144** from v2.6.18 once the re-synced catalog's last entry closed (overstated: the older ROM hid a `Misaligned OAM behavior` failure), and **146/146** from v3.1.0 on upstream `f5f41dc2` (139/139 at the v1.0.0 cut; the v2.0.1 oracle re-sync grew the catalog to 141 assigned tests and briefly opened two PPU gaps, so v2.0.1–v2.0.2 shipped an honest 139/141 until the v2.0.3 2-cycle-ALE promotion closed them), and a byte-identical 60-ROM commercial regression oracle. As of v2.3.0 the AccuracyCoin gate is pinned to an **exact 141/141** (zero failing tests), so a single-test regression — e.g. in the hybrid-address model — fails CI. `docs/STATUS.md` is the authoritative pass-count source.
## Git tagging
diff --git a/crates/rustynes-libretro/rustynes_libretro.info b/crates/rustynes-libretro/rustynes_libretro.info
index 39129e6a2..c5c2500c0 100644
--- a/crates/rustynes-libretro/rustynes_libretro.info
+++ b/crates/rustynes-libretro/rustynes_libretro.info
@@ -5,7 +5,7 @@ supported_extensions = "nes|fds|unf|unif"
corename = "RustyNES"
license = "GPLv3+"
permissions = ""
-display_version = "v3.0.1"
+display_version = "v3.1.0"
categories = "Emulator"
# Hardware Information
diff --git a/crates/rustynes-test-harness/golden/epoch_fingerprint.tsv b/crates/rustynes-test-harness/golden/epoch_fingerprint.tsv
new file mode 100644
index 000000000..b31460365
--- /dev/null
+++ b/crates/rustynes-test-harness/golden/epoch_fingerprint.tsv
@@ -0,0 +1,15 @@
+# T-EPOCH-FINGERPRINT: what the panel in tests/epoch_fingerprint.rs produces
+# under the epoch below. Generated; re-bless with
+# RUSTYNES_BLESS_EPOCH_FINGERPRINT=1. `last_release_epoch` is the epoch
+# the last release shipped, set by hand at each release cut: output may
+# move only while EMULATION_EPOCH is above it.
+# epoch 3
+# last_release_epoch 3
+# rom frames framebuffer_fnv audio_fnv ram_fnv cpu_cycles
+accuracycoin/AccuracyCoin.nes 3200 e5edb57f59c38538 47e9857638ba5d28 b93b7feacb471f53 95268158
+blargg/apu_mixer/triangle.nes 600 1b6dac56c42f267a 87f291ef58273cac 983e19fe15e197d6 17838587
+blargg/sprite_overflow_tests/3.Timing.nes 300 1ff4fd9a87d46e5a f7f5b37c7a51cc3d d7e9312891460c14 8904391
+nes-test-roms/dmc_tests/latency.nes 240 b112947dcb56f813 2b4cacae717f270b 286ebb8ad39d4642 7117579
+blargg/mmc3_test_2/4-scanline_timing.nes 120 c1423fdfef9a85fa cceb1ba4374551ca 73baf7b42fa5860e 3543898
+nes-test-roms/sprdma_and_dmc_dma/sprdma_and_dmc_dma.nes 120 bb655ab4bc5277ac 9a8fbf6de707b966 a0779243e8ce2902 3543900
+assorted/flowing_palette.nes 120 72990514f3e3bfb3 8a91bbbb1ead50e8 27337e564377faba 3543884
diff --git a/crates/rustynes-test-harness/tests/snapshots/external_coverage__mapper_146_Sachen_NINA_Millionaire_Sachen.snap b/crates/rustynes-test-harness/tests/snapshots/external_coverage__mapper_146_Sachen_NINA_Millionaire_Sachen.snap
index 9d6950645..dd1fdbbb9 100644
--- a/crates/rustynes-test-harness/tests/snapshots/external_coverage__mapper_146_Sachen_NINA_Millionaire_Sachen.snap
+++ b/crates/rustynes-test-harness/tests/snapshots/external_coverage__mapper_146_Sachen_NINA_Millionaire_Sachen.snap
@@ -9,5 +9,5 @@ fb_bytes=245760
cycles=37902150
audio_samples=1005322
audio_fnv1a64=f087542b1f027e9a
-checkpoint f900 fb_fnv1a64=a71d0c1c59be672e
+checkpoint f900 fb_fnv1a64=7b6fa7c63d09e90e
checkpoint f1100 fb_fnv1a64=02035ffbeb95f997
diff --git a/docs/STATUS.md b/docs/STATUS.md
index 5744c484d..7678a5a55 100644
--- a/docs/STATUS.md
+++ b/docs/STATUS.md
@@ -1,6 +1,6 @@
# RustyNES — Project Status Matrix
-> **Current release: v3.0.1** (2026-10-07) — **"Mortar"**, a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** (2026-10-06) — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core. Built on **v2.9.9 "Ballast"** (2026-10-04) — the release candidate for v3.0.0: the audits re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it. Built on **v2.9.8 "Vanguard"** (2026-10-02) — the preparation release for v3.0.0: v3.0.0's breaking changes landed early (a save identity that ignores the header, old states and movies refused, movies and netplay that record the machine, the API removals), every staged game was booted and the defects found were fixed, and the game database's corrections reach every platform. Built on **v2.9.7 "Tandem"** (2026-09-30) — the desktop's features on the web and on phones, the release binaries built with every native feature, and a PPU A12 fix found by real games: Acclaim's MC-ACC games, the J.Y. ASIC and mapper 91 now count at their documented rates. Built on **v2.9.6 "Roster"** (2026-09-30) — seventeen mapper families written from their NESdev pages (174 → 191), GTROM promoted to Curated with a modelled flash chip whose saves persist, mapper 4's NES 2.0 submappers corrected (MMC6, NEC, MC-ACC, T9552), and the local commercial suites re-baselined after drifting unread since about v2.0.0. Built on **v2.9.5 "Caliper"** (2026-09-29) — every open accuracy item measured, then fixed or closed: four fixes red first (the `apu_test` frame-counter coincidence, the composite 2C02 scanline-0 sprite glitch, OAM DMA filling the PPU I/O latch, KS7032 at `$6000`), 49 unreferenced test ROMs gated, the MMC3 M2-edge filter lever tried and refuted, and a save-state epoch (`PPU_SNAPSHOT_VERSION` 11). Built on **v2.9.4 "Plumb"** (2026-09-29) — the records made true, and CI made to run what it only linted: v3.0.0 decided as the API major with a release-candidate core (ADR 0043), CI now running 63 feature-gated tests it never ran, the eight fuzz targets and a 70% line-coverage floor, the mapper tiers, store status and deferred-features catalogue corrected against the code, and the OAM-decay model recorded as derived from Mesen2. Built on **v2.9.3 "Handset"** (2026-09-29) — the old review threads closed and the mobile run prepared: every dependency moved to its newest release (egui 0.36 with wgpu 30, rcheevos 12.5.0), all 244 review threads left unanswered on PRs #7-#97 answered and the ten findings that still held fixed (Action 53 multicarts rebuilt to the NESdev spec, and a ROM header editor that no longer rewrites bytes you did not edit or saves mappers from 16 up as the wrong mapper), and the Android unit tests and the iOS renderer added to CI. The mobile device runs and the SuperStation One board session move after v3.0.0 (maintainer, 2026-09-29). Built on **v2.9.2 "Candidate"** (2026-09-28) — the full audit acted on, and the release-candidate pair: all 32 findings of a fifth audit have a verdict and 16 are fixed, save states keep the cartridge RAM of twelve board families they used to drop, the MiSTer core no longer loses an NMI raised inside a DMA, and both bitstreams are cut for the SuperStation One session. Built on **v2.9.1 "Hone"** (2026-09-27) — what the optimisation bars measure, and what clears them: the A/B tool had been timing the old code on both sides of every code comparison and is fixed, a two-screen Vs. cabinet saves about 9x faster, the off-die MiSTer build keeps CHR in its own SDRAM bank, and both bitstreams are pinned at fitter seed 2 and rebuild byte-identically. Built on **v2.9.0 "Survey"** (2026-09-26) — every audit re-checked, and the SuperStation One surveyed: a Power Cycle no longer erases your save, the off-die MiSTer build boots without the menu core, and 39 new audit findings are fixed or dispositioned. Built on **v2.8.4 "Tether"** (2026-09-26) — the MiSTer core's SDRAM build, made trustworthy: its controller now reads data on the edge the memory presents it (every off-die read would have been wrong on hardware, and only the new SDRAM timing constraints could see it), the power-up sequence and CAS-latency-3 reads follow the datasheet, the arbiter can no longer return the wrong byte or lose a write, the off-die bitstream builds from a script, both builds are swept and pinned at fitter seed 5, and the co-simulation ladder runs all 165 gates from a clean checkout. Built on **v2.8.3 "Rivet"** (2026-09-25) — the MiSTer core's reset, area and comments, measured: every reset is released on the clock that uses it and the timing analysis now checks each release, the CPU is about 4% smaller by two exact rewrites the fit report confirmed, four false comments are corrected, and the co-simulation ladder runs from a fresh checkout (164 of its 165 gates; the last needs a hand-built ROM no generator produces). Built on **v2.8.2 "Solder"** (2026-09-25) — the MiSTer core's on-die RTL, corrected against the oracle and the wiki: an MMC3 IRQ acknowledge is no longer lost to a same-edge counter clock, SNROM's battery RAM obeys its CHR-line enable, the triangle and noise drop a reload landing on a length clock, a `$2002` read leaves the byte it returned on the data bus, and the emulator's MMC1 no longer ignores a reset written on the cycle after another write. Built on **v2.8.1 "Gasket"** (2026-09-25) — the libretro core fits the frontends around it: four-player games work through a Four Score option, a controller works again after its port leaves the Zapper, RetroArch no longer reads past the core's input-descriptor list, expansion audio no longer clips, the core declares UNIF images, and the Makefile honours PREFIX, platform=win, DEBUG and CARGO_TARGET_DIR. Built on **v2.8.0 "Bulkhead"** (2026-09-25) — the libretro core stops a fault at its own boundary: an internal error no longer closes RetroArch, save states survive plugging in a Zapper, closing a game withdraws its memory maps, the core loads from any libretro frontend, and the save state now carries the 2A03 internal data bus. Built on **v2.7.6 "Recount"** (2026-09-24) — the v2.7.5 deletions measured one at a time: the six performance proposals v2.7.5 bounded only together were each measured alone, where the benchmarks reach them: five are zero, and the sixth, the pulse sweep-mute check, bounds at about 0.2%, and the cheap byte-identical way to take it measured slower; the fast render path now asserts a rendering-history invariant it used to re-write; and the libretro buildbot builds macOS again and gains 32-bit Windows, 32-bit Linux and webOS targets. Built on **v2.7.5 "Tally"** (2026-09-24) — every audit claim closed with a measurement or a reason: the core audit's twelve performance proposals were closed, eleven of them by measurement, and one adopted (the audio buffer keeps its capacity between frames); 18 dead bus methods and the unused ApuBus trait are deprecated; and the core and frontend ledgers have no open row. Built on **v2.7.4 "Pocket"** (2026-09-24) — the mobile apps survive what a phone does to them: an internal error no longer closes the Android or iOS app, battery saves persist on both, the apps pause and give up audio when they should, and saves are written so a dying phone keeps the last good one. Built on **v2.7.3 "Hearth"** (2026-09-23) — the desktop and web frontends keep what they are given: battery saves persist on the desktop, a Lua script can no longer hang or exhaust the emulator, script HTTP cannot reach local services by default, and audio survives a device change. Built on **v2.7.2 "Bankroll"** (2026-09-23) — cartridge memory, every bank a cartridge has and nothing it has not: MMC1 reaches SUROM / SXROM, MMC5 banks its PRG-RAM, Namco 163 selects nametables, and `$6000-$7FFF` reads open bus where a board has nothing there. Built on **v2.7.1 "Keepsake"** (2026-09-23) — six cartridge boards now hand RetroArch their battery save instead of an empty one, every user file the frontend writes is written atomically, and three mapper files are now recorded as derived from Mesen2 and puNES. Built on **v2.7.0 "Palisade"** (2026-09-23) — a corrupt or hand-edited save state now fails at restore with a typed error instead of crashing the emulator one tick later, pulse 1 no longer mutes on the `$4001 = $08` sweep idiom, and the save-state fuzz target can finally reach what it exists to find. Built on **v2.6.23 "Pulse"** (2026-09-20) — the access does not increment, it pulses the load already there. A `$2007` access during rendering pulses the rendering pipeline's existing load rather than computing a private increment, and resolving against that same value is what closed the CHR-during-rendering divergence in the MiSTer sibling: `chrram-live` went from **32,861 of 61,440 differing pixels**, red for eight releases, to "All 61440 pixels match", and `chrram-fetch` from 21,941 to 18. Eleven prior variants had been measured correctly and the conclusion drawn from them was wrong — they swept what the `$2007` arm computes and never what it computes it from. The emulation core is unchanged, so **AccuracyCoin 144/144 and nestest 0-diff hold by construction** and were re-run anyway. The bitstream is re-cut at fitter seed 4 (+0.421 ns setup / +0.112 ns hold) and **no hardware has run it**. The board session is now **v2.9.2** and the hardware-verified core **v3.0.0**, after three audit lines ([ADR 0041](adr/0041-hardware-release-is-v3.0.0.md)). Built on **v2.6.22 "Rigging"** — the instruments for the board, built before the board. **No hardware has run any bitstream**; the session with the board (then planned as "Shakedown"; since ADR 0041, v2.9.2) is the hardware half and this is the non-hardware half of it, cut separately so that name keeps meaning what it says. AccuracyCoin now reads back from hardware as BYTES rather than as a photograph — a mirror ROM copies its result vector into the battery-backed save window, with a byte budget that proves the patch moved nothing and a simulation control that proves it changed no answer. The catalog turned out to have **149 rows and 144 results**: five `Power On State` rows share upstream's omit-sentinel, and counting them made the headline a function of *when the run was sampled*. **144/144 and nestest 0-diff are unchanged** — the count stopped depending on the sampling instant. And the `46199ae4` re-sync turned out to have been half-done: three goldens described a ROM no longer in the tree, which no gate could see because `rom_sha256` was written into every manifest and compared to nothing. Built on **v2.6.21 "Steward"** — the board arrives, and the core is not ready for it. Battery saves exist for the first time; the CHR-during-rendering gate the retrospective audit asked for is added and is **RED at 32,861 of 61,440 pixels**, with the obvious fix measured insufficient at 715 recovered; two gates existed that nothing ran; and the deploy loop the runbook prescribed was built, which found two defects in its own spec. The emulation core is unchanged, so **AccuracyCoin 144/144 and nestest 0-diff hold by construction**. Built on **v2.6.20 "Telltale"** — the counter had no reader, and two knobs turned out to be one decision. The FPGA core's OAM2 address counter was write-only for a release, so `$2004`'s post-fetch rest value was a hardcoded index 0; making it observable took the DUT to **AccuracyCoin 149 of 149** and the co-simulation ladder to **150 of 150, 0 failed**. Restoring the increment window v2.6.19 had narrowed then broke sprite rendering by six pixels, because the window and the deleted dot-257 latch are ONE decision with two self-consistent answers and only one of them is the hardware's. The shipped configuration restores the latch: window 256, the dot-257 freeze latch back and consumed by sprite fetch, and `$2004` reading the live counter. A CHR-RAM write gate was added. **v2.6.21 corrects this sentence: it did NOT close the coverage the audit named.** The audit named writes *while rendering is enabled*, and PROGRAM53 writes with `PPUMASK = 0` and renders afterwards. The real case is gated at v2.6.21 and is RED -- 32,861 of 61,440 pixels. The emulation core is unchanged, so **144/144 and nestest 0-diff hold by construction**. Built on **v2.6.19 "Accession"** — the DUT absorbs two releases of oracle behaviour, and the seed rule catches something for the first time. The FPGA core implements the OAM2 address counter it never had and stops acting on a `$2001` change that lands during dot 256 -- AccuracyCoin on the DUT **148 of 149**, the ladder **147 of 147**. The fitter pin is re-derived over ten seeds and stays at 3. (The contemporaneous claim that seed 1 stopped closing is withdrawn -- that sweep recompiled in place and was order-dependent; seed 1 closes, and seed 8 is the one that does not.) The emulation core is unchanged, so **AccuracyCoin 144/144 and nestest 0-diff hold by construction**. Built on **v2.6.18** (2026-09-12) — **"Errata"**, the recorded cause was wrong in three ways, and the last AccuracyCoin entry closes at 144/144 -- a `$2001` change during dot N must not act on dot N, at the dot-256 vertical increment and the dot-339 OAM2 reset. Built on **v2.6.17** (2026-09-11) — **"Terminus"**, the accuracy battery grows to 144 assigned tests and one of the new ones names the dots a write lands on -- this core is two dots early, measured twice on two independent `$2001` writes, and moving it is refuted by the gate this version wrote in advance, so the documented compensation stays and 143 of 144 ships. Built on **v2.6.16 "Interlock"** (2026-09-04) — the arbiter's numbers describe a stimulus, not the console -- and the console meets the CPU's deadline at zero margin, on every one of 240,303 requests, where the gate said it missed by four. Built on **v2.6.15 "Warrant"** (2026-09-04) — the claims v2.7.0 will make become checkable, and the instrument pays the oracle back. Built on **v2.6.14 "Docket"** (2026-09-03) — the submission checklist becomes auditable, and auditing it finds five boxes already true, two ticked on evidence that expired, and one that could never have been ticked honestly. v2.7.0 IS the submission, so the list in `to-dos/mister/contribution-checklist.md` decides whether the core is ready -- and it had 30 boxes, 16 unticked, and FOURTEEN OF THOSE SIXTEEN saying nothing at all about why. That ambiguity is the defect: an unticked box with no reason cannot be told apart from work outstanding, work blocked outside this repository, and WORK ALREADY DONE AND NEVER TICKED, and the third case occurred five times -- the provenance CI job, the SPDX sweep, the firewall statement, the AccuracyCoin vector and the preservation-value case were all true and all unticked, so the list reported the project as further from submission than it is by a fifth of its own length. ONE BOX COULD NEVER HAVE BEEN TICKED HONESTLY: it asked that `docs/provenance.md` state "that no NES core was ever opened", and that same document's section Do not self-certify forbids exactly that class of finished claim, so satisfying the box required writing the one sentence the provenance rules exist to prevent -- a wrong requirement rather than a missing tick, and only reading every item found it. RE-MEASURING THE TICKED HALF found two more, which is v2.6.9's lesson in a different document: `RustyNES.sdc` still said "there is exactly one core clock", true of v2.6.3 and false since v2.6.13, which added a 4x SDRAM clock and a phase-shifted pin clock that the shipped timing report names alongside it; and the `.qsf` entry said all 109 pin assignments come from `sys/sys.tcl`, where 109 is what that script supplies before stopping short of the I/O board, so a core must also source one of two 36-assignment variants -- 145 in total, from two scripts. THE ALARMING READING OF THE FIRST WAS CHECKED BEFORE IT WAS WRITTEN DOWN: it looks as though the framework's `set_clock_groups -exclusive` might be cutting the console-to-SDRAM crossing and leaving ADR 0039's safety argument unfalsifiable, and it is not -- exclusive cuts paths BETWEEN groups, all three outputs match one glob, and v2.6.13's own -24.769 ns measurement of that crossing is only observable because those paths are analysed. THE NAMING DIVERGENCE IS MEASURED RATHER THAN PARAPHRASED, from `Main_MiSTer/file_io.cpp` instead of the wiki: `get_display_name` searches for the literal underscore-two-zero and TRUNCATES THE DISPLAY NAME THERE, taking the rest as a datecode, and `DirentComp` groups by that truncated name -- so a version-named bitstream makes every release a SEPARATE CORE ENTRY, named for the version rather than the core, ordered alphabetically, which puts v2.6.9 after v2.6.13. The fix is one line and is NOT taken here, because version-naming is a maintainer decision with a stated rationale and reversing it is not an audit's call. THE TASK BOARD HAD THE SAME DEFECT AND ONE ROW WORSE: four delivered items never ticked, a hardware row still naming a version seven releases past, and an SDRAM row whose precondition -- "after a board exists" -- v2.6.13 simply did not follow, having accepted the controller against a behavioural model written from the datasheet instead; that is rung 7's own recorded lesson repeating one row below where it is written, since the blocker applied to hardware ACCEPTANCE rather than to building the thing. A CLAIM v2.6.13 SHIPPED IS RETRACTED: answering a review finding, I wrote that MMC1 was the sixth approved family and not yet implemented, and the cartridge decodes mapper 1 at three sites with a registered gate green since rung 7 opened -- asserted from memory inside a reply correcting somebody else's reading of the same line. The gate that keeps all this true asserts a SHAPE rather than a judgement and is demonstrated by five mutations, one of which proves the continuation-line folding load-bearing by producing nine false violations without it. The bitstream is BYTE-IDENTICAL to v2.6.13's, which is the point: the only sibling change is a comment, and an identical artifact demonstrates it. The emulation core is unchanged, so AccuracyCoin 141/141 and nestest 0-diff hold by construction. Built on **v2.6.13 "Slack"** (2026-09-03) — the cartridge outgrows the die, and three consumers want the same bus. An SDR SDRAM controller, a behavioural part model, a four-way arbiter and a console bridge, all written from the AS4C32M16SB-7 datasheet revision 1.4 -- no third-party controller read, ADR 0037 applying. The budget the previous step worked to was a single figure read off the fetch structure and never measured; `nes_top`'s `CHR_LAT` sweep asks the console directly, and there are THREE answers: a background or sprite fetch tolerates 28 cycles and uses 17, the PPUDATA data port tolerates 8, and the CPU sampling at mc7 has 24 -- four cycles of every budget going to the console-domain crossing, which is not optional, because publishing a runtime modulo combinationally into an 11.64 ns domain costs -24.769 ns of setup. The PPUDATA port could never fit, half its budget being the crossing, so it leaves the shared bus entirely: `ppu2c02` gains `BUFFER_HANDSHAKE` and fills its read buffer through a port of its own on the arbiter, affordable because the CPU does not read that buffer until its next PPUDATA access. THAT FIX SHIPPED A DEFECT ONLY A BANKED CARTRIDGE COULD SEE -- the request carried the RAW fourteen bits the PPU presents, which is right by coincidence on NROM because the mapper's translation is the identity there, and reads BANK ZERO on everything else; `ppu-misc-2007-stress` passed off-die throughout while the two CNROM gates read a zero byte where the oracle reads one, one and two. The fix REMOVES the address rather than correcting it: `cart.sv` already publishes the translation for the fetch path, so the request carries none and there is one source of truth for where CHR lives instead of two that agree only on NROM. A DELETION WAS THEN REFUTED BY ONE CYCLE: with the address fixed, a control issuing at a flat +7 passed both gates, which read as the anti-contention deferral buying nothing, so it was removed -- and the deployed code issued at +6, and both gates failed again. The control and the code differed by a single cycle, which is the measurement of how thin the CPU's off-die deadline is and the concrete argument for scheduling the bus rather than arbitrating it. Two INFERRED LATCHES that Verilator cannot see: `ppu2c02` assigned two signals only under `BUFFER_HANDSHAKE`, so in the shipped on-die build the only assignment either reached was the reset branch, and a variable holding its previous value on every live path is a latch -- Quartus said so twice while the lint gate stayed green, and the comment beside them asserted the defect as a virtue ("outside BUFFER_HANDSHAKE neither signal ever moves", which is true and is exactly the condition). And a defect in the HARNESS: `USE_SDRAM` reaches Verilator as `-G` rather than as a file, so make ran ONE binary under both configurations' names and a log labelled on-die was the off-die build, reproducing the off-die failures exactly -- closed with a stamp-file prerequisite, demonstrated by mutation. THE OPEN ROW: accesses no longer auto-precharge, a hit costs 6 cycles against 10 for a miss, tRAS's 120 us MAXIMUM is respected by an early close in idle, and two defects came out of the rewrite -- re-entering idle with the request still asserted issued every access TWICE, and subtracting one from CAS the way the other waits are computed broke every read to all zeroes, CAS being "data appears at cycle N" rather than a command-to-command gap. Two MiSTer tickets close: `sdram_sz` is consumed VALIDITY BIT FIRST, so `absent` is deliberately not `!present` and a power-on all zeroes cannot read as "no board" (gated exhaustively over all 65,536 values), and `status_menumask` is computed rather than tied off, greying Reset whenever the console is already held in reset. OFF THE DIE THE CONSOLE PASSES 142 OF 142, every gate the on-die build passes, with better timing margin and 384 fewer M10K blocks -- and it still SHIPS ON the die, because an off-die core cannot run at all without the SDRAM add-on while rung 7's five mapper families fit on the die at 468 of 553 blocks. The emulation core is unchanged, so AccuracyCoin 141/141 and nestest 0-diff hold by construction. Built on **v2.6.12 "Groundwork"** (2026-09-02) — the bitstream was an NROM-only console. Rung 7 landed five mapper families and 142 co-simulation gates verify them, and the layer that turns that RTL into a bitstream was never told: `rtl/emu.sv` left `cart_mapper`, `cart_prg_16k_banks` and `cart_chr_8k_banks` unconnected, so Quartus tied all three to GND -- mapper 0 for EVERY cartridge, `prg_8k_count = 0` collapsing PRG to an 8 KiB window, and CHR forced to RAM. The declared 256 KiB PRG and 128 KiB CHR were implemented as 8 KiB each; connecting three wires takes block memory from 666,061 to 3,680,717 bits and timing still closes at all four corners. NOTHING COULD HAVE CAUGHT IT: simulation cannot, because `emu.sv` is not in the testbench file list and the harness drives those ports itself, so all 142 gates exercised a correctly-configured cartridge; and Quartus DID say so three times, in messages that cite an INSTANCE path rather than a file and are absent from the "0 errors, N warnings" tally, so the existing checker read 0 of 125. Two gates close that -- one fails on an unconnected pin of any module this repository declares, the other pins the warning SET rather than its count -- and both are demonstrated to fail by mutation. The `hps_io` tie-off audit that followed raised nine `T-MISTER-*` tickets, and annotating each with a blocker turned "none of these landed" into a measurement: not one is blocked on EFFORT, so the list is the rung-6 agenda rather than a backlog. `T-MISTER-SAVE` was attempted and refuted -- every save route terminates in `hps_io`, which no gate here instantiates. The emulation core is unchanged, so AccuracyCoin 141/141 and nestest 0-diff hold by construction. Built on **v2.6.11 "Exposure"** (2026-09-02) — a picture is a gate the ladder did not have. All 141 co-simulation gates THEN IN THE SUITE were green (it ends this release at 142, the one it added) and TWO OF SIX commercial games rendered wrong -- a CHR-RAM write was taking the shared-pin composite address built for FETCHES instead of `v`, so the layout was right and the tiles were scrambled. The split is exactly CHR-ROM against CHR-RAM, which named the mechanism before any tracing, and a CONTROL says it is not v2.6.10's regression: the pre-M10K-fix RTL differs by the IDENTICAL 16,565 pixels, so the defect dates from the cartridge landing in v2.6.9. It was not UNREACHED -- the DUT asserts `chr_wr` 9,600 times in the Battletoads run -- it was UNCOMPARED: only THREE of the 141 gates compare a framebuffer, all three ship CHR-ROM, every other gate is CPU-side, and AccuracyCoin, the widest gate in the suite, is CHR-ROM too. The rung-7 gates' own comment says what they are for -- "these gates are about BANKING and nothing else" -- and it was accurate, and it was the whole coverage. Six commercial titles now render byte-identically to the oracle over all 61,440 pixels, published as a montage built by a script that REFUSES to publish a tile that differs from the oracle. The v2.6.10 bitstream carries the defect and a published version is immutable, so the corrected `.rbf` ships here. The same release finds EIGHT release leads describing v2.6.10 with v2.6.9's summary, and v2.6.9 gone from the lineage entirely, with `docs/STATUS.md` naming v2.6.10 under the codename "Abeyance" -- every existing check passing CORRECTLY, because they pin the version TOKEN and the token was right. Prose cannot be audited; an ORDERING can, so two gates are added and both are demonstrated to fail by mutation. The emulation core is unchanged, so AccuracyCoin 141/141 and nestest 0-diff hold by construction. Rung 6 does NOT close -- no DE10-Nano and no SuperStation One are attached to this machine, confirmed by checking rather than assumed. Built on **v2.6.10 "Inference"** (2026-09-01) — the cartridge meets the synthesiser. Five cartridge boards verified across 141 co-simulation gates had **never been through Quartus**, and Analysis & Synthesis refused the design: `chr` was written from TWO `always_ff` blocks, which cannot infer as one M10K, so 128 KB of CHR stayed in flip-flops -- **1,048,576 registers against roughly 166,000**. Simulation cannot ask this question: Verilator accepts both forms without complaint. It is v2.6.6's finding one layer out -- that release established that an M10K read is REGISTERED, this one that a correctly registered memory still will not infer with two writers. The fitter was also throttling itself under Auto Fit while the `.qsf` carried no optimisation assignments at all: at full effort **all six seeds close** where two had failed, so the effort settings move the whole distribution across zero and the seed only picks where in it you land -- and the project had been pinned to seed 4, the WORST of the six. Pinned at seed 3, +0.531 ns setup and +0.099 ns hold, byte-identical across two independent compiles. The bitstream v2.6.9 could not produce ships here. Built on **v2.6.9 "Abeyance"** (2026-08-31) — an exclusion hides improvement as well as regression, and both denied co-simulation streams close. The larger one was never the console: `apuconflict039` had been carried for seven releases as a declared diagnostic whose bus surface "carries nine divergences BY DESIGN", and the nine were a defect in the HARNESS -- on a cycle the CPU is held, the testbench built its record's bus data from a stale local rather than from the RTL's own latch. Taking it from the latch makes the stream IDENTICAL on all 357,361 overlapping cycles and all 88 checkpoints, and the local is now dead and deleted. The phrase "by design" is what stopped anyone re-checking it, because it reads as a property of the thing under test when it was a property of the instrument reading it. The other stream differs on EXACTLY ONE cycle, a documented and attributed OAM-corruption asymmetry -- and carrying that needed an instrument the suite did not have, because the PLANNED mechanism was refuted by its own mutation pass: an allowance by checkpoint index cannot work on a rolling hash, since one divergent cycle poisons every checkpoint after it, so allowing the first differing window simply moved the failure to the next one and allowing the rest is the all-or-nothing deny it was meant to replace. A per-cycle nine-field comparator with a scoped allowance costs ONE cycle of coverage instead of seventy-one checkpoints -- 357,360 of 357,361, against nothing at all before -- and it fails BOTH ways, so a DUT that improves cannot leave a stale allowance quietly hiding coverage; six mutations confirm it, including a cycle outside the compared window being REFUSED rather than allowed to match nothing. The emulation core is unchanged, so AccuracyCoin 141/141 and nestest 0-diff hold by construction and were re-run anyway. Rung 6 does NOT close -- no DE10-Nano and no SuperStation One are attached to this machine, confirmed by checking rather than assumed. Built on **v2.6.8 "Arrears"** (2026-08-31) — the gates the previous release fixed and never widened. A deny list is an assertion about the THING UNDER TEST, so v2.6.7 changing both the DUT and the harness re-opened every exclusion -- and nothing re-measured one. FOUR of the six denied checkpoint streams were passing (`irqlat048`, `ppuvbl023`, `ppuvbl024`, `ppuvbl025`), and THREE of those were not run by the suite at all, so removing them from the deny list was INERT until they were also iterated. The nestest gate compared 265,000 cycles against a 5,062,688-cycle golden -- correct while caveat C6 was open, left behind the moment it closed -- and all 5,062,680 overlapping cycles now match, with the window DERIVED from the manifest. CAVEAT C4 CLOSES by demonstration: nestest raises `nmi_line` on 3,592 cycles and had no nine-field comparison at all, so wiring it makes an `o.nmi_line` mutation CAUGHT at checkpoint 28 of 1,237 while the bus gate passes the identical run. Suite 128 passed, 0 failed, 0 skipped; nine-field comparisons 51 -> 57. The emulation core is unchanged, so AccuracyCoin 141/141 and nestest 0-diff hold by construction. Rung 6 does NOT close -- no board is attached, confirmed by checking. Built on **v2.6.7 "Detent"** (2026-08-30) — the bitstream becomes a published release artifact and a one-cycle disagreement is pinned to the cycle it happens on. Every release from here ships a `.rbf` -- committed to the sibling's `releases/` and attached to the GitHub release on BOTH repositories -- reversing v2.6.6, which produced one and withheld it because no hardware had run it: the MiSTer distribution mechanism reads that path out of the REPOSITORY, so an empty `releases/` describes an undistributable core rather than a cautious one, and the caution moves from an absence into a disclosure naming what the ladder cannot reach by construction (the PPU gate compares the pre-palette index and the APU gate per-channel integer levels, so the palette, the video timing constants, the audio absolute level and its band-limiting all sit downstream of every gate). The build is REPRODUCIBLE and that is now measured rather than argued -- a from-scratch compile and an incremental one produce a byte-identical bitstream -- which is also how v2.6.6's published slack figures came to be WITHDRAWN: no corner of a clean rebuild reproduces them, the innocent explanation (a different timing corner) was checked first and refuted, and the correct pair is +0.108 ns setup and +0.042 ns hold at the binding corner. THE RELEASE GATE WAS READING THE WRONG CORNER -- Slow 100C is not the binding one on this design, so a bitstream failing at Slow -40C would have passed while the gate reported three times the real margin -- and the checker that reads it was wrong twice before mutation found both: it first extracted ZERO rows from both summary tables and reported that as "no negative slack", then, once fixed, reported FOURTEEN clocks from a report emptied of its data, having run past the closing rule into the next tables. Caveat C2 splits in two. The first residual was a TRACE OBSERVATION POINT -- the harness built its record after eight of a CPU cycle's twelve master clocks while the oracle reads at end-of-cycle, and the frame-counter interrupt asserts on the final edge -- and closing it took checkpoint comparisons from 3 to 11 and failures at one checkpoint from 30 to 3. The second is REAL, and the FIRST fix for it was REFUTED in a way that found the right one: moving all four effects of the write one cycle later to match the oracle drops blargg from 11/11 to 4 of 11, one of the seven being the ROM written to probe exactly that timing. Read as a measurement that PROVES the sequencer's maturation is correctly placed, which leaves only the other effects the same write schedules -- so separating ONLY the interrupt clear lands it at write+3, the documented cycle, while the frame counter's zeroing stays put. Checkpoint streams go from 11 identical to 52 of 58 and the suite from 87 to 122, with blargg still 11/11 and the bus still matching on all 2,680,239 overlapping cycles. The checkpoint gate is registered over 51 comparisons and STATES ITS BLIND SPOT: not one gated golden ever raises an NMI, so it cannot catch an nmi_line defect, and the attempt to close that hole found a FOURTH divergence cluster that three goldens had been hiding inside a "26 skipped" tally line. The emulation core is unchanged, so AccuracyCoin 141/141 and nestest 0-diff hold by construction. Rung 6 does NOT close -- no DE10-Nano and no SuperStation One are attached to this machine, confirmed by checking rather than assumed. Built on **v2.6.6 "Chassis"** (2026-08-29) — the console becomes a MiSTer core -- `sys/` vendored byte-identical against `Template_MiSTer@3ea1134c` (57 files, 0 content differences), a top level, a clock, a palette, video sync and an audio mixer, compiled by Quartus 17.0.2 into a Cyclone V bitstream with 0 errors, timing CLOSED, and a warning count taken from 111 to THREE -- all three inside the vendored framework or Quartus's own megafunction, none of them citing this project's RTL (worst setup +0.086 ns and worst hold +0.096 ns at the binding corners, seed 3 -- v2.6.7 also withdraws the +0.363/+0.245 v2.6.6 published, which a clean rebuild of that configuration reproduces at no corner, TNS 0.000 on every clock; the console's own clock +13.514 ns at an Fmax of 30.26 MHz against the 21.477272 MHz it needs). The emulation core is unchanged, so the co-simulation suite is an ACCEPTANCE CRITERION rather than a formality -- 87 passed, 0 failed -- and it earned that immediately, because the cartridge memories had to be rewritten: an M10K read is REGISTERED, so 40 KiB of asynchronously-read cartridge was 393,216 registers against roughly 166,000 available, under a comment claiming it inferred block RAM from the source style alone, and the README had stated the correct rule since v2.4.3. Two defects were found only by asking whether the outputs would work on real hardware: the audio would have been a full-scale DC rail, because the mixer output is unipolar with silence at zero and the framework maps unsigned silence to -32768, and two OSD scanline options did nothing because VGA_SL was tied to zero. And a convention enforced by a glob has no error message: sys_top.sdc groups the core clock by matching the hierarchical name pattern *|pll|pll_inst|altera_pll_i|*, so a differently-named PLL matched no group at all and every crossing to the framework audio, HDMI and HPS domains was analysed as synchronous -- -13.901 ns of slack and -422,601 ns of TNS on a design whose Fmax was already above requirement, with the compile succeeding and the Assembler reporting 0 errors and 0 warnings throughout. Built on **v2.6.5 "Muster"** (2026-08-29) — rung 5 closes — the AccuracyCoin status vector is identical entry for entry across all 146 entries, with 146 of 146 executed on both sides and none NotRun, where the same gate read 5 of 146 at the version's start. A muster is a roll call where every name is called AND answered, which is the two-clause acceptance exactly. Five PPU defects close the last six differing entries and four were invisible to every gate that existed when the version opened: the background shift registers' RELOAD and their shift clock need SEPARATE gates (with one shared gate the serial-in test was not merely failing but ARITHMETICALLY UNREACHABLE, since reload dots are absolute and the reload discards the low seven bits, so a serial-in one can never reach bit 7 on any alignment — and modelling both structures reproduces BOTH measured shifter values); the sprite X counters are NOT gated on rendering, which AccuracyCoin states outright and the ROM that states it passes either way, because it expects no hit at X=254 and a sprite shoved 18 dots right is also off the line; the PPUADDR second-write v-copy is DELAYED, as the wiki says inside the write sequence itself, swept 1 to 4 dots against a control at 8 and 12 that fails; and the pre-render line CLEARS secondary OAM, without which scanline 0 draws what scanline 239 left — no sprite can ever render on scanline 0, because OAM Y is one less than the display row, and a sprite-0 probe over the full 134 M-cycle battery found 24 hits with four of them there; and the octal latch holding across the read dot, which is verified by exactly ONE gate and was unverifiable until the v-copy delay landed, the two composing the hybrid address together and neither producing it alone. A DIAGNOSIS IS RETRACTED: the residual was read as a two-dot CPU/PPU alignment error from comparing dot spans across two instruments, and at the committed alignment the two consoles execute identical pc, bus_addr and bus_access for 1,695,131 cycles while a two-dot shift moves the first fork back to 593,228 and takes the differing share from 5.13% to 66.80%. The oracle changes on the default path, so AccuracyCoin 141/141 (RAM decoder) and nestest 0-diff are VERIFIED, not asserted. Built on **v2.6.4 "Rubric"** (2026-08-26) — OAM DMA lands and all nine AccuracyCoin disagreements close, every rule that closed the last three stated by the test ROM and by neither nesdev page — and then the gate that certified them is measured to cover 88 of 146 entries. The emulation core is unchanged. Built on **v2.6.3 "Mainspring"** (2026-08-25) — the DUT runs on one master clock, and four enables that were never enabling — plus AccuracyCoin end to end and a status vector that names its disagreements by test. The emulation core is unchanged. Built on **v2.6.2 "Witness"** (2026-08-24) — rung 4 closes: blargg APU battery 11/11 on the co-simulation DUT, six defects no self-written gate could see, and a suite that had been asserting nothing for five minor releases. The emulation core is unchanged. Built on **v2.6.1 "Interleave"** (2026-08-24) — the DMC and its DMA cycle steal in the MiSTer co-simulation DUT, cycle-exact on the bus. The emulation core is unchanged. Built on **v2.6.0 "Assay"** (2026-08-24) — the triangle, the noise channel and the sweep unit **in the MiSTer co-simulation DUT** — and an audit of how much of the APU was fitted to the oracle rather than derived from documentation. The emulation core is unchanged. Built on **v2.5.9 "Overture"** (2026-08-24) — rung 4 opens: the two pulse channels, the frame counter, and four ROM defects the stimulus measurement found first. Built on **v2.5.8 "Blanking"** (2026-08-24) — VBlank, NMI and the PPUSTATUS race close rung 3 — and both fixes were deletions. Built on **v2.5.7 "Collimation"** (2026-08-24) — sprite rendering closes exact — the phase was wrong by two dots, and every window was compensating. Built on **v2.5.6 "Vestige"** (2026-08-23) — Sprite evaluation closes: all 59,993 overlapping cycles match, nine of nine behavioural mutants caught and two proved inert (announced as seven of eight at the cut), and the fix is a byte index that outlives the walk that set it. Built on **v2.5.5 "Raster"** (2026-08-23) — the first full frame, and three blind spots in the stimulus that fed it. Built on **v2.5.4 "Escapement"** (2026-08-23) — the background fetch pipeline, and an access two dots early that five gates could not see. Built on **v2.5.3 "Hysteresis"** (2026-08-23) — toggling rendering takes effect three dots after the write, and four instruments to prove it. Built on **v2.5.2 "Dormant"** (2026-08-23) — the 2C02 register file, and a gate that passed while testing nothing. Built on **v2.5.1 "Retrace"** (2026-08-23) — the interrupt sweep closes rung 2, and a gate reported a pass it could not have earned. Built on **v2.5.0 "Rungwork"** (2026-08-23) — the 6502 rung, and the two gates it cannot reach. Built on **v2.4.9 "Plumbline II"** (2026-08-23) — the bus half of rung 2, and what it found the day it existed. Built on **v2.4.8 "Palimpsest"** (2026-08-23) — read-modify-write, and a gate that cannot see its own subject. Built on **v2.4.7 "Keystone"** (2026-08-23) — the stack closes, and a dead line proves itself dead. Built on **v2.4.6 "Abacus"** (2026-08-22) — the core learns arithmetic. Built on **v2.4.5 "Compass"** (2026-08-22) — the core reaches memory, and chooses. Built on **v2.4.4 "Ignition"** (2026-08-22) — the first real RTL -- the 6502's eight-cycle reset and the implied opcode group, matching the oracle on all seven CPU fields (29
+> **Current release: v3.1.0** (2026-10-08) — **"Bellwether"**, the AccuracyCoin re-sync and an honest 146/146, the CPU overclock and the sprite-limit option in movies and netplay, PAL emphasis, the alternate MMC3, rewind and run-ahead on the Vs. cabinet, and the MiSTer core matched to all of it. Built on **v3.0.1 "Mortar"** (2026-10-07) — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** (2026-10-06) — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core. Built on **v2.9.9 "Ballast"** (2026-10-04) — the release candidate for v3.0.0: the audits re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it. Built on **v2.9.8 "Vanguard"** (2026-10-02) — the preparation release for v3.0.0: v3.0.0's breaking changes landed early (a save identity that ignores the header, old states and movies refused, movies and netplay that record the machine, the API removals), every staged game was booted and the defects found were fixed, and the game database's corrections reach every platform. Built on **v2.9.7 "Tandem"** (2026-09-30) — the desktop's features on the web and on phones, the release binaries built with every native feature, and a PPU A12 fix found by real games: Acclaim's MC-ACC games, the J.Y. ASIC and mapper 91 now count at their documented rates. Built on **v2.9.6 "Roster"** (2026-09-30) — seventeen mapper families written from their NESdev pages (174 → 191), GTROM promoted to Curated with a modelled flash chip whose saves persist, mapper 4's NES 2.0 submappers corrected (MMC6, NEC, MC-ACC, T9552), and the local commercial suites re-baselined after drifting unread since about v2.0.0. Built on **v2.9.5 "Caliper"** (2026-09-29) — every open accuracy item measured, then fixed or closed: four fixes red first (the `apu_test` frame-counter coincidence, the composite 2C02 scanline-0 sprite glitch, OAM DMA filling the PPU I/O latch, KS7032 at `$6000`), 49 unreferenced test ROMs gated, the MMC3 M2-edge filter lever tried and refuted, and a save-state epoch (`PPU_SNAPSHOT_VERSION` 11). Built on **v2.9.4 "Plumb"** (2026-09-29) — the records made true, and CI made to run what it only linted: v3.0.0 decided as the API major with a release-candidate core (ADR 0043), CI now running 63 feature-gated tests it never ran, the eight fuzz targets and a 70% line-coverage floor, the mapper tiers, store status and deferred-features catalogue corrected against the code, and the OAM-decay model recorded as derived from Mesen2. Built on **v2.9.3 "Handset"** (2026-09-29) — the old review threads closed and the mobile run prepared: every dependency moved to its newest release (egui 0.36 with wgpu 30, rcheevos 12.5.0), all 244 review threads left unanswered on PRs #7-#97 answered and the ten findings that still held fixed (Action 53 multicarts rebuilt to the NESdev spec, and a ROM header editor that no longer rewrites bytes you did not edit or saves mappers from 16 up as the wrong mapper), and the Android unit tests and the iOS renderer added to CI. The mobile device runs and the SuperStation One board session move after v3.0.0 (maintainer, 2026-09-29). Built on **v2.9.2 "Candidate"** (2026-09-28) — the full audit acted on, and the release-candidate pair: all 32 findings of a fifth audit have a verdict and 16 are fixed, save states keep the cartridge RAM of twelve board families they used to drop, the MiSTer core no longer loses an NMI raised inside a DMA, and both bitstreams are cut for the SuperStation One session. Built on **v2.9.1 "Hone"** (2026-09-27) — what the optimisation bars measure, and what clears them: the A/B tool had been timing the old code on both sides of every code comparison and is fixed, a two-screen Vs. cabinet saves about 9x faster, the off-die MiSTer build keeps CHR in its own SDRAM bank, and both bitstreams are pinned at fitter seed 2 and rebuild byte-identically. Built on **v2.9.0 "Survey"** (2026-09-26) — every audit re-checked, and the SuperStation One surveyed: a Power Cycle no longer erases your save, the off-die MiSTer build boots without the menu core, and 39 new audit findings are fixed or dispositioned. Built on **v2.8.4 "Tether"** (2026-09-26) — the MiSTer core's SDRAM build, made trustworthy: its controller now reads data on the edge the memory presents it (every off-die read would have been wrong on hardware, and only the new SDRAM timing constraints could see it), the power-up sequence and CAS-latency-3 reads follow the datasheet, the arbiter can no longer return the wrong byte or lose a write, the off-die bitstream builds from a script, both builds are swept and pinned at fitter seed 5, and the co-simulation ladder runs all 165 gates from a clean checkout. Built on **v2.8.3 "Rivet"** (2026-09-25) — the MiSTer core's reset, area and comments, measured: every reset is released on the clock that uses it and the timing analysis now checks each release, the CPU is about 4% smaller by two exact rewrites the fit report confirmed, four false comments are corrected, and the co-simulation ladder runs from a fresh checkout (164 of its 165 gates; the last needs a hand-built ROM no generator produces). Built on **v2.8.2 "Solder"** (2026-09-25) — the MiSTer core's on-die RTL, corrected against the oracle and the wiki: an MMC3 IRQ acknowledge is no longer lost to a same-edge counter clock, SNROM's battery RAM obeys its CHR-line enable, the triangle and noise drop a reload landing on a length clock, a `$2002` read leaves the byte it returned on the data bus, and the emulator's MMC1 no longer ignores a reset written on the cycle after another write. Built on **v2.8.1 "Gasket"** (2026-09-25) — the libretro core fits the frontends around it: four-player games work through a Four Score option, a controller works again after its port leaves the Zapper, RetroArch no longer reads past the core's input-descriptor list, expansion audio no longer clips, the core declares UNIF images, and the Makefile honours PREFIX, platform=win, DEBUG and CARGO_TARGET_DIR. Built on **v2.8.0 "Bulkhead"** (2026-09-25) — the libretro core stops a fault at its own boundary: an internal error no longer closes RetroArch, save states survive plugging in a Zapper, closing a game withdraws its memory maps, the core loads from any libretro frontend, and the save state now carries the 2A03 internal data bus. Built on **v2.7.6 "Recount"** (2026-09-24) — the v2.7.5 deletions measured one at a time: the six performance proposals v2.7.5 bounded only together were each measured alone, where the benchmarks reach them: five are zero, and the sixth, the pulse sweep-mute check, bounds at about 0.2%, and the cheap byte-identical way to take it measured slower; the fast render path now asserts a rendering-history invariant it used to re-write; and the libretro buildbot builds macOS again and gains 32-bit Windows, 32-bit Linux and webOS targets. Built on **v2.7.5 "Tally"** (2026-09-24) — every audit claim closed with a measurement or a reason: the core audit's twelve performance proposals were closed, eleven of them by measurement, and one adopted (the audio buffer keeps its capacity between frames); 18 dead bus methods and the unused ApuBus trait are deprecated; and the core and frontend ledgers have no open row. Built on **v2.7.4 "Pocket"** (2026-09-24) — the mobile apps survive what a phone does to them: an internal error no longer closes the Android or iOS app, battery saves persist on both, the apps pause and give up audio when they should, and saves are written so a dying phone keeps the last good one. Built on **v2.7.3 "Hearth"** (2026-09-23) — the desktop and web frontends keep what they are given: battery saves persist on the desktop, a Lua script can no longer hang or exhaust the emulator, script HTTP cannot reach local services by default, and audio survives a device change. Built on **v2.7.2 "Bankroll"** (2026-09-23) — cartridge memory, every bank a cartridge has and nothing it has not: MMC1 reaches SUROM / SXROM, MMC5 banks its PRG-RAM, Namco 163 selects nametables, and `$6000-$7FFF` reads open bus where a board has nothing there. Built on **v2.7.1 "Keepsake"** (2026-09-23) — six cartridge boards now hand RetroArch their battery save instead of an empty one, every user file the frontend writes is written atomically, and three mapper files are now recorded as derived from Mesen2 and puNES. Built on **v2.7.0 "Palisade"** (2026-09-23) — a corrupt or hand-edited save state now fails at restore with a typed error instead of crashing the emulator one tick later, pulse 1 no longer mutes on the `$4001 = $08` sweep idiom, and the save-state fuzz target can finally reach what it exists to find. Built on **v2.6.23 "Pulse"** (2026-09-20) — the access does not increment, it pulses the load already there. A `$2007` access during rendering pulses the rendering pipeline's existing load rather than computing a private increment, and resolving against that same value is what closed the CHR-during-rendering divergence in the MiSTer sibling: `chrram-live` went from **32,861 of 61,440 differing pixels**, red for eight releases, to "All 61440 pixels match", and `chrram-fetch` from 21,941 to 18. Eleven prior variants had been measured correctly and the conclusion drawn from them was wrong — they swept what the `$2007` arm computes and never what it computes it from. The emulation core is unchanged, so **AccuracyCoin 144/144 and nestest 0-diff hold by construction** and were re-run anyway. The bitstream is re-cut at fitter seed 4 (+0.421 ns setup / +0.112 ns hold) and **no hardware has run it**. The board session is now **v2.9.2** and the hardware-verified core **v3.0.0**, after three audit lines ([ADR 0041](adr/0041-hardware-release-is-v3.0.0.md)). Built on **v2.6.22 "Rigging"** — the instruments for the board, built before the board. **No hardware has run any bitstream**; the session with the board (then planned as "Shakedown"; since ADR 0041, v2.9.2) is the hardware half and this is the non-hardware half of it, cut separately so that name keeps meaning what it says. AccuracyCoin now reads back from hardware as BYTES rather than as a photograph — a mirror ROM copies its result vector into the battery-backed save window, with a byte budget that proves the patch moved nothing and a simulation control that proves it changed no answer. The catalog turned out to have **149 rows and 144 results**: five `Power On State` rows share upstream's omit-sentinel, and counting them made the headline a function of *when the run was sampled*. **144/144 and nestest 0-diff are unchanged** — the count stopped depending on the sampling instant. And the `46199ae4` re-sync turned out to have been half-done: three goldens described a ROM no longer in the tree, which no gate could see because `rom_sha256` was written into every manifest and compared to nothing. Built on **v2.6.21 "Steward"** — the board arrives, and the core is not ready for it. Battery saves exist for the first time; the CHR-during-rendering gate the retrospective audit asked for is added and is **RED at 32,861 of 61,440 pixels**, with the obvious fix measured insufficient at 715 recovered; two gates existed that nothing ran; and the deploy loop the runbook prescribed was built, which found two defects in its own spec. The emulation core is unchanged, so **AccuracyCoin 144/144 and nestest 0-diff hold by construction**. Built on **v2.6.20 "Telltale"** — the counter had no reader, and two knobs turned out to be one decision. The FPGA core's OAM2 address counter was write-only for a release, so `$2004`'s post-fetch rest value was a hardcoded index 0; making it observable took the DUT to **AccuracyCoin 149 of 149** and the co-simulation ladder to **150 of 150, 0 failed**. Restoring the increment window v2.6.19 had narrowed then broke sprite rendering by six pixels, because the window and the deleted dot-257 latch are ONE decision with two self-consistent answers and only one of them is the hardware's. The shipped configuration restores the latch: window 256, the dot-257 freeze latch back and consumed by sprite fetch, and `$2004` reading the live counter. A CHR-RAM write gate was added. **v2.6.21 corrects this sentence: it did NOT close the coverage the audit named.** The audit named writes *while rendering is enabled*, and PROGRAM53 writes with `PPUMASK = 0` and renders afterwards. The real case is gated at v2.6.21 and is RED -- 32,861 of 61,440 pixels. The emulation core is unchanged, so **144/144 and nestest 0-diff hold by construction**. Built on **v2.6.19 "Accession"** — the DUT absorbs two releases of oracle behaviour, and the seed rule catches something for the first time. The FPGA core implements the OAM2 address counter it never had and stops acting on a `$2001` change that lands during dot 256 -- AccuracyCoin on the DUT **148 of 149**, the ladder **147 of 147**. The fitter pin is re-derived over ten seeds and stays at 3. (The contemporaneous claim that seed 1 stopped closing is withdrawn -- that sweep recompiled in place and was order-dependent; seed 1 closes, and seed 8 is the one that does not.) The emulation core is unchanged, so **AccuracyCoin 144/144 and nestest 0-diff hold by construction**. Built on **v2.6.18** (2026-09-12) — **"Errata"**, the recorded cause was wrong in three ways, and the last AccuracyCoin entry closes at 144/144 -- a `$2001` change during dot N must not act on dot N, at the dot-256 vertical increment and the dot-339 OAM2 reset. Built on **v2.6.17** (2026-09-11) — **"Terminus"**, the accuracy battery grows to 144 assigned tests and one of the new ones names the dots a write lands on -- this core is two dots early, measured twice on two independent `$2001` writes, and moving it is refuted by the gate this version wrote in advance, so the documented compensation stays and 143 of 144 ships. Built on **v2.6.16 "Interlock"** (2026-09-04) — the arbiter's numbers describe a stimulus, not the console -- and the console meets the CPU's deadline at zero margin, on every one of 240,303 requests, where the gate said it missed by four. Built on **v2.6.15 "Warrant"** (2026-09-04) — the claims v2.7.0 will make become checkable, and the instrument pays the oracle back. Built on **v2.6.14 "Docket"** (2026-09-03) — the submission checklist becomes auditable, and auditing it finds five boxes already true, two ticked on evidence that expired, and one that could never have been ticked honestly. v2.7.0 IS the submission, so the list in `to-dos/mister/contribution-checklist.md` decides whether the core is ready -- and it had 30 boxes, 16 unticked, and FOURTEEN OF THOSE SIXTEEN saying nothing at all about why. That ambiguity is the defect: an unticked box with no reason cannot be told apart from work outstanding, work blocked outside this repository, and WORK ALREADY DONE AND NEVER TICKED, and the third case occurred five times -- the provenance CI job, the SPDX sweep, the firewall statement, the AccuracyCoin vector and the preservation-value case were all true and all unticked, so the list reported the project as further from submission than it is by a fifth of its own length. ONE BOX COULD NEVER HAVE BEEN TICKED HONESTLY: it asked that `docs/provenance.md` state "that no NES core was ever opened", and that same document's section Do not self-certify forbids exactly that class of finished claim, so satisfying the box required writing the one sentence the provenance rules exist to prevent -- a wrong requirement rather than a missing tick, and only reading every item found it. RE-MEASURING THE TICKED HALF found two more, which is v2.6.9's lesson in a different document: `RustyNES.sdc` still said "there is exactly one core clock", true of v2.6.3 and false since v2.6.13, which added a 4x SDRAM clock and a phase-shifted pin clock that the shipped timing report names alongside it; and the `.qsf` entry said all 109 pin assignments come from `sys/sys.tcl`, where 109 is what that script supplies before stopping short of the I/O board, so a core must also source one of two 36-assignment variants -- 145 in total, from two scripts. THE ALARMING READING OF THE FIRST WAS CHECKED BEFORE IT WAS WRITTEN DOWN: it looks as though the framework's `set_clock_groups -exclusive` might be cutting the console-to-SDRAM crossing and leaving ADR 0039's safety argument unfalsifiable, and it is not -- exclusive cuts paths BETWEEN groups, all three outputs match one glob, and v2.6.13's own -24.769 ns measurement of that crossing is only observable because those paths are analysed. THE NAMING DIVERGENCE IS MEASURED RATHER THAN PARAPHRASED, from `Main_MiSTer/file_io.cpp` instead of the wiki: `get_display_name` searches for the literal underscore-two-zero and TRUNCATES THE DISPLAY NAME THERE, taking the rest as a datecode, and `DirentComp` groups by that truncated name -- so a version-named bitstream makes every release a SEPARATE CORE ENTRY, named for the version rather than the core, ordered alphabetically, which puts v2.6.9 after v2.6.13. The fix is one line and is NOT taken here, because version-naming is a maintainer decision with a stated rationale and reversing it is not an audit's call. THE TASK BOARD HAD THE SAME DEFECT AND ONE ROW WORSE: four delivered items never ticked, a hardware row still naming a version seven releases past, and an SDRAM row whose precondition -- "after a board exists" -- v2.6.13 simply did not follow, having accepted the controller against a behavioural model written from the datasheet instead; that is rung 7's own recorded lesson repeating one row below where it is written, since the blocker applied to hardware ACCEPTANCE rather than to building the thing. A CLAIM v2.6.13 SHIPPED IS RETRACTED: answering a review finding, I wrote that MMC1 was the sixth approved family and not yet implemented, and the cartridge decodes mapper 1 at three sites with a registered gate green since rung 7 opened -- asserted from memory inside a reply correcting somebody else's reading of the same line. The gate that keeps all this true asserts a SHAPE rather than a judgement and is demonstrated by five mutations, one of which proves the continuation-line folding load-bearing by producing nine false violations without it. The bitstream is BYTE-IDENTICAL to v2.6.13's, which is the point: the only sibling change is a comment, and an identical artifact demonstrates it. The emulation core is unchanged, so AccuracyCoin 141/141 and nestest 0-diff hold by construction. Built on **v2.6.13 "Slack"** (2026-09-03) — the cartridge outgrows the die, and three consumers want the same bus. An SDR SDRAM controller, a behavioural part model, a four-way arbiter and a console bridge, all written from the AS4C32M16SB-7 datasheet revision 1.4 -- no third-party controller read, ADR 0037 applying. The budget the previous step worked to was a single figure read off the fetch structure and never measured; `nes_top`'s `CHR_LAT` sweep asks the console directly, and there are THREE answers: a background or sprite fetch tolerates 28 cycles and uses 17, the PPUDATA data port tolerates 8, and the CPU sampling at mc7 has 24 -- four cycles of every budget going to the console-domain crossing, which is not optional, because publishing a runtime modulo combinationally into an 11.64 ns domain costs -24.769 ns of setup. The PPUDATA port could never fit, half its budget being the crossing, so it leaves the shared bus entirely: `ppu2c02` gains `BUFFER_HANDSHAKE` and fills its read buffer through a port of its own on the arbiter, affordable because the CPU does not read that buffer until its next PPUDATA access. THAT FIX SHIPPED A DEFECT ONLY A BANKED CARTRIDGE COULD SEE -- the request carried the RAW fourteen bits the PPU presents, which is right by coincidence on NROM because the mapper's translation is the identity there, and reads BANK ZERO on everything else; `ppu-misc-2007-stress` passed off-die throughout while the two CNROM gates read a zero byte where the oracle reads one, one and two. The fix REMOVES the address rather than correcting it: `cart.sv` already publishes the translation for the fetch path, so the request carries none and there is one source of truth for where CHR lives instead of two that agree only on NROM. A DELETION WAS THEN REFUTED BY ONE CYCLE: with the address fixed, a control issuing at a flat +7 passed both gates, which read as the anti-contention deferral buying nothing, so it was removed -- and the deployed code issued at +6, and both gates failed again. The control and the code differed by a single cycle, which is the measurement of how thin the CPU's off-die deadline is and the concrete argument for scheduling the bus rather than arbitrating it. Two INFERRED LATCHES that Verilator cannot see: `ppu2c02` assigned two signals only under `BUFFER_HANDSHAKE`, so in the shipped on-die build the only assignment either reached was the reset branch, and a variable holding its previous value on every live path is a latch -- Quartus said so twice while the lint gate stayed green, and the comment beside them asserted the defect as a virtue ("outside BUFFER_HANDSHAKE neither signal ever moves", which is true and is exactly the condition). And a defect in the HARNESS: `USE_SDRAM` reaches Verilator as `-G` rather than as a file, so make ran ONE binary under both configurations' names and a log labelled on-die was the off-die build, reproducing the off-die failures exactly -- closed with a stamp-file prerequisite, demonstrated by mutation. THE OPEN ROW: accesses no longer auto-precharge, a hit costs 6 cycles against 10 for a miss, tRAS's 120 us MAXIMUM is respected by an early close in idle, and two defects came out of the rewrite -- re-entering idle with the request still asserted issued every access TWICE, and subtracting one from CAS the way the other waits are computed broke every read to all zeroes, CAS being "data appears at cycle N" rather than a command-to-command gap. Two MiSTer tickets close: `sdram_sz` is consumed VALIDITY BIT FIRST, so `absent` is deliberately not `!present` and a power-on all zeroes cannot read as "no board" (gated exhaustively over all 65,536 values), and `status_menumask` is computed rather than tied off, greying Reset whenever the console is already held in reset. OFF THE DIE THE CONSOLE PASSES 142 OF 142, every gate the on-die build passes, with better timing margin and 384 fewer M10K blocks -- and it still SHIPS ON the die, because an off-die core cannot run at all without the SDRAM add-on while rung 7's five mapper families fit on the die at 468 of 553 blocks. The emulation core is unchanged, so AccuracyCoin 141/141 and nestest 0-diff hold by construction. Built on **v2.6.12 "Groundwork"** (2026-09-02) — the bitstream was an NROM-only console. Rung 7 landed five mapper families and 142 co-simulation gates verify them, and the layer that turns that RTL into a bitstream was never told: `rtl/emu.sv` left `cart_mapper`, `cart_prg_16k_banks` and `cart_chr_8k_banks` unconnected, so Quartus tied all three to GND -- mapper 0 for EVERY cartridge, `prg_8k_count = 0` collapsing PRG to an 8 KiB window, and CHR forced to RAM. The declared 256 KiB PRG and 128 KiB CHR were implemented as 8 KiB each; connecting three wires takes block memory from 666,061 to 3,680,717 bits and timing still closes at all four corners. NOTHING COULD HAVE CAUGHT IT: simulation cannot, because `emu.sv` is not in the testbench file list and the harness drives those ports itself, so all 142 gates exercised a correctly-configured cartridge; and Quartus DID say so three times, in messages that cite an INSTANCE path rather than a file and are absent from the "0 errors, N warnings" tally, so the existing checker read 0 of 125. Two gates close that -- one fails on an unconnected pin of any module this repository declares, the other pins the warning SET rather than its count -- and both are demonstrated to fail by mutation. The `hps_io` tie-off audit that followed raised nine `T-MISTER-*` tickets, and annotating each with a blocker turned "none of these landed" into a measurement: not one is blocked on EFFORT, so the list is the rung-6 agenda rather than a backlog. `T-MISTER-SAVE` was attempted and refuted -- every save route terminates in `hps_io`, which no gate here instantiates. The emulation core is unchanged, so AccuracyCoin 141/141 and nestest 0-diff hold by construction. Built on **v2.6.11 "Exposure"** (2026-09-02) — a picture is a gate the ladder did not have. All 141 co-simulation gates THEN IN THE SUITE were green (it ends this release at 142, the one it added) and TWO OF SIX commercial games rendered wrong -- a CHR-RAM write was taking the shared-pin composite address built for FETCHES instead of `v`, so the layout was right and the tiles were scrambled. The split is exactly CHR-ROM against CHR-RAM, which named the mechanism before any tracing, and a CONTROL says it is not v2.6.10's regression: the pre-M10K-fix RTL differs by the IDENTICAL 16,565 pixels, so the defect dates from the cartridge landing in v2.6.9. It was not UNREACHED -- the DUT asserts `chr_wr` 9,600 times in the Battletoads run -- it was UNCOMPARED: only THREE of the 141 gates compare a framebuffer, all three ship CHR-ROM, every other gate is CPU-side, and AccuracyCoin, the widest gate in the suite, is CHR-ROM too. The rung-7 gates' own comment says what they are for -- "these gates are about BANKING and nothing else" -- and it was accurate, and it was the whole coverage. Six commercial titles now render byte-identically to the oracle over all 61,440 pixels, published as a montage built by a script that REFUSES to publish a tile that differs from the oracle. The v2.6.10 bitstream carries the defect and a published version is immutable, so the corrected `.rbf` ships here. The same release finds EIGHT release leads describing v2.6.10 with v2.6.9's summary, and v2.6.9 gone from the lineage entirely, with `docs/STATUS.md` naming v2.6.10 under the codename "Abeyance" -- every existing check passing CORRECTLY, because they pin the version TOKEN and the token was right. Prose cannot be audited; an ORDERING can, so two gates are added and both are demonstrated to fail by mutation. The emulation core is unchanged, so AccuracyCoin 141/141 and nestest 0-diff hold by construction. Rung 6 does NOT close -- no DE10-Nano and no SuperStation One are attached to this machine, confirmed by checking rather than assumed. Built on **v2.6.10 "Inference"** (2026-09-01) — the cartridge meets the synthesiser. Five cartridge boards verified across 141 co-simulation gates had **never been through Quartus**, and Analysis & Synthesis refused the design: `chr` was written from TWO `always_ff` blocks, which cannot infer as one M10K, so 128 KB of CHR stayed in flip-flops -- **1,048,576 registers against roughly 166,000**. Simulation cannot ask this question: Verilator accepts both forms without complaint. It is v2.6.6's finding one layer out -- that release established that an M10K read is REGISTERED, this one that a correctly registered memory still will not infer with two writers. The fitter was also throttling itself under Auto Fit while the `.qsf` carried no optimisation assignments at all: at full effort **all six seeds close** where two had failed, so the effort settings move the whole distribution across zero and the seed only picks where in it you land -- and the project had been pinned to seed 4, the WORST of the six. Pinned at seed 3, +0.531 ns setup and +0.099 ns hold, byte-identical across two independent compiles. The bitstream v2.6.9 could not produce ships here. Built on **v2.6.9 "Abeyance"** (2026-08-31) — an exclusion hides improvement as well as regression, and both denied co-simulation streams close. The larger one was never the console: `apuconflict039` had been carried for seven releases as a declared diagnostic whose bus surface "carries nine divergences BY DESIGN", and the nine were a defect in the HARNESS -- on a cycle the CPU is held, the testbench built its record's bus data from a stale local rather than from the RTL's own latch. Taking it from the latch makes the stream IDENTICAL on all 357,361 overlapping cycles and all 88 checkpoints, and the local is now dead and deleted. The phrase "by design" is what stopped anyone re-checking it, because it reads as a property of the thing under test when it was a property of the instrument reading it. The other stream differs on EXACTLY ONE cycle, a documented and attributed OAM-corruption asymmetry -- and carrying that needed an instrument the suite did not have, because the PLANNED mechanism was refuted by its own mutation pass: an allowance by checkpoint index cannot work on a rolling hash, since one divergent cycle poisons every checkpoint after it, so allowing the first differing window simply moved the failure to the next one and allowing the rest is the all-or-nothing deny it was meant to replace. A per-cycle nine-field comparator with a scoped allowance costs ONE cycle of coverage instead of seventy-one checkpoints -- 357,360 of 357,361, against nothing at all before -- and it fails BOTH ways, so a DUT that improves cannot leave a stale allowance quietly hiding coverage; six mutations confirm it, including a cycle outside the compared window being REFUSED rather than allowed to match nothing. The emulation core is unchanged, so AccuracyCoin 141/141 and nestest 0-diff hold by construction and were re-run anyway. Rung 6 does NOT close -- no DE10-Nano and no SuperStation One are attached to this machine, confirmed by checking rather than assumed. Built on **v2.6.8 "Arrears"** (2026-08-31) — the gates the previous release fixed and never widened. A deny list is an assertion about the THING UNDER TEST, so v2.6.7 changing both the DUT and the harness re-opened every exclusion -- and nothing re-measured one. FOUR of the six denied checkpoint streams were passing (`irqlat048`, `ppuvbl023`, `ppuvbl024`, `ppuvbl025`), and THREE of those were not run by the suite at all, so removing them from the deny list was INERT until they were also iterated. The nestest gate compared 265,000 cycles against a 5,062,688-cycle golden -- correct while caveat C6 was open, left behind the moment it closed -- and all 5,062,680 overlapping cycles now match, with the window DERIVED from the manifest. CAVEAT C4 CLOSES by demonstration: nestest raises `nmi_line` on 3,592 cycles and had no nine-field comparison at all, so wiring it makes an `o.nmi_line` mutation CAUGHT at checkpoint 28 of 1,237 while the bus gate passes the identical run. Suite 128 passed, 0 failed, 0 skipped; nine-field comparisons 51 -> 57. The emulation core is unchanged, so AccuracyCoin 141/141 and nestest 0-diff hold by construction. Rung 6 does NOT close -- no board is attached, confirmed by checking. Built on **v2.6.7 "Detent"** (2026-08-30) — the bitstream becomes a published release artifact and a one-cycle disagreement is pinned to the cycle it happens on. Every release from here ships a `.rbf` -- committed to the sibling's `releases/` and attached to the GitHub release on BOTH repositories -- reversing v2.6.6, which produced one and withheld it because no hardware had run it: the MiSTer distribution mechanism reads that path out of the REPOSITORY, so an empty `releases/` describes an undistributable core rather than a cautious one, and the caution moves from an absence into a disclosure naming what the ladder cannot reach by construction (the PPU gate compares the pre-palette index and the APU gate per-channel integer levels, so the palette, the video timing constants, the audio absolute level and its band-limiting all sit downstream of every gate). The build is REPRODUCIBLE and that is now measured rather than argued -- a from-scratch compile and an incremental one produce a byte-identical bitstream -- which is also how v2.6.6's published slack figures came to be WITHDRAWN: no corner of a clean rebuild reproduces them, the innocent explanation (a different timing corner) was checked first and refuted, and the correct pair is +0.108 ns setup and +0.042 ns hold at the binding corner. THE RELEASE GATE WAS READING THE WRONG CORNER -- Slow 100C is not the binding one on this design, so a bitstream failing at Slow -40C would have passed while the gate reported three times the real margin -- and the checker that reads it was wrong twice before mutation found both: it first extracted ZERO rows from both summary tables and reported that as "no negative slack", then, once fixed, reported FOURTEEN clocks from a report emptied of its data, having run past the closing rule into the next tables. Caveat C2 splits in two. The first residual was a TRACE OBSERVATION POINT -- the harness built its record after eight of a CPU cycle's twelve master clocks while the oracle reads at end-of-cycle, and the frame-counter interrupt asserts on the final edge -- and closing it took checkpoint comparisons from 3 to 11 and failures at one checkpoint from 30 to 3. The second is REAL, and the FIRST fix for it was REFUTED in a way that found the right one: moving all four effects of the write one cycle later to match the oracle drops blargg from 11/11 to 4 of 11, one of the seven being the ROM written to probe exactly that timing. Read as a measurement that PROVES the sequencer's maturation is correctly placed, which leaves only the other effects the same write schedules -- so separating ONLY the interrupt clear lands it at write+3, the documented cycle, while the frame counter's zeroing stays put. Checkpoint streams go from 11 identical to 52 of 58 and the suite from 87 to 122, with blargg still 11/11 and the bus still matching on all 2,680,239 overlapping cycles. The checkpoint gate is registered over 51 comparisons and STATES ITS BLIND SPOT: not one gated golden ever raises an NMI, so it cannot catch an nmi_line defect, and the attempt to close that hole found a FOURTH divergence cluster that three goldens had been hiding inside a "26 skipped" tally line. The emulation core is unchanged, so AccuracyCoin 141/141 and nestest 0-diff hold by construction. Rung 6 does NOT close -- no DE10-Nano and no SuperStation One are attached to this machine, confirmed by checking rather than assumed. Built on **v2.6.6 "Chassis"** (2026-08-29) — the console becomes a MiSTer core -- `sys/` vendored byte-identical against `Template_MiSTer@3ea1134c` (57 files, 0 content differences), a top level, a clock, a palette, video sync and an audio mixer, compiled by Quartus 17.0.2 into a Cyclone V bitstream with 0 errors, timing CLOSED, and a warning count taken from 111 to THREE -- all three inside the vendored framework or Quartus's own megafunction, none of them citing this project's RTL (worst setup +0.086 ns and worst hold +0.096 ns at the binding corners, seed 3 -- v2.6.7 also withdraws the +0.363/+0.245 v2.6.6 published, which a clean rebuild of that configuration reproduces at no corner, TNS 0.000 on every clock; the console's own clock +13.514 ns at an Fmax of 30.26 MHz against the 21.477272 MHz it needs). The emulation core is unchanged, so the co-simulation suite is an ACCEPTANCE CRITERION rather than a formality -- 87 passed, 0 failed -- and it earned that immediately, because the cartridge memories had to be rewritten: an M10K read is REGISTERED, so 40 KiB of asynchronously-read cartridge was 393,216 registers against roughly 166,000 available, under a comment claiming it inferred block RAM from the source style alone, and the README had stated the correct rule since v2.4.3. Two defects were found only by asking whether the outputs would work on real hardware: the audio would have been a full-scale DC rail, because the mixer output is unipolar with silence at zero and the framework maps unsigned silence to -32768, and two OSD scanline options did nothing because VGA_SL was tied to zero. And a convention enforced by a glob has no error message: sys_top.sdc groups the core clock by matching the hierarchical name pattern *|pll|pll_inst|altera_pll_i|*, so a differently-named PLL matched no group at all and every crossing to the framework audio, HDMI and HPS domains was analysed as synchronous -- -13.901 ns of slack and -422,601 ns of TNS on a design whose Fmax was already above requirement, with the compile succeeding and the Assembler reporting 0 errors and 0 warnings throughout. Built on **v2.6.5 "Muster"** (2026-08-29) — rung 5 closes — the AccuracyCoin status vector is identical entry for entry across all 146 entries, with 146 of 146 executed on both sides and none NotRun, where the same gate read 5 of 146 at the version's start. A muster is a roll call where every name is called AND answered, which is the two-clause acceptance exactly. Five PPU defects close the last six differing entries and four were invisible to every gate that existed when the version opened: the background shift registers' RELOAD and their shift clock need SEPARATE gates (with one shared gate the serial-in test was not merely failing but ARITHMETICALLY UNREACHABLE, since reload dots are absolute and the reload discards the low seven bits, so a serial-in one can never reach bit 7 on any alignment — and modelling both structures reproduces BOTH measured shifter values); the sprite X counters are NOT gated on rendering, which AccuracyCoin states outright and the ROM that states it passes either way, because it expects no hit at X=254 and a sprite shoved 18 dots right is also off the line; the PPUADDR second-write v-copy is DELAYED, as the wiki says inside the write sequence itself, swept 1 to 4 dots against a control at 8 and 12 that fails; and the pre-render line CLEARS secondary OAM, without which scanline 0 draws what scanline 239 left — no sprite can ever render on scanline 0, because OAM Y is one less than the display row, and a sprite-0 probe over the full 134 M-cycle battery found 24 hits with four of them there; and the octal latch holding across the read dot, which is verified by exactly ONE gate and was unverifiable until the v-copy delay landed, the two composing the hybrid address together and neither producing it alone. A DIAGNOSIS IS RETRACTED: the residual was read as a two-dot CPU/PPU alignment error from comparing dot spans across two instruments, and at the committed alignment the two consoles execute identical pc, bus_addr and bus_access for 1,695,131 cycles while a two-dot shift moves the first fork back to 593,228 and takes the differing share from 5.13% to 66.80%. The oracle changes on the default path, so AccuracyCoin 141/141 (RAM decoder) and nestest 0-diff are VERIFIED, not asserted. Built on **v2.6.4 "Rubric"** (2026-08-26) — OAM DMA lands and all nine AccuracyCoin disagreements close, every rule that closed the last three stated by the test ROM and by neither nesdev page — and then the gate that certified them is measured to cover 88 of 146 entries. The emulation core is unchanged. Built on **v2.6.3 "Mainspring"** (2026-08-25) — the DUT runs on one master clock, and four enables that were never enabling — plus AccuracyCoin end to end and a status vector that names its disagreements by test. The emulation core is unchanged. Built on **v2.6.2 "Witness"** (2026-08-24) — rung 4 closes: blargg APU battery 11/11 on the co-simulation DUT, six defects no self-written gate could see, and a suite that had been asserting nothing for five minor releases. The emulation core is unchanged. Built on **v2.6.1 "Interleave"** (2026-08-24) — the DMC and its DMA cycle steal in the MiSTer co-simulation DUT, cycle-exact on the bus. The emulation core is unchanged. Built on **v2.6.0 "Assay"** (2026-08-24) — the triangle, the noise channel and the sweep unit **in the MiSTer co-simulation DUT** — and an audit of how much of the APU was fitted to the oracle rather than derived from documentation. The emulation core is unchanged. Built on **v2.5.9 "Overture"** (2026-08-24) — rung 4 opens: the two pulse channels, the frame counter, and four ROM defects the stimulus measurement found first. Built on **v2.5.8 "Blanking"** (2026-08-24) — VBlank, NMI and the PPUSTATUS race close rung 3 — and both fixes were deletions. Built on **v2.5.7 "Collimation"** (2026-08-24) — sprite rendering closes exact — the phase was wrong by two dots, and every window was compensating. Built on **v2.5.6 "Vestige"** (2026-08-23) — Sprite evaluation closes: all 59,993 overlapping cycles match, nine of nine behavioural mutants caught and two proved inert (announced as seven of eight at the cut), and the fix is a byte index that outlives the walk that set it. Built on **v2.5.5 "Raster"** (2026-08-23) — the first full frame, and three blind spots in the stimulus that fed it. Built on **v2.5.4 "Escapement"** (2026-08-23) — the background fetch pipeline, and an access two dots early that five gates could not see. Built on **v2.5.3 "Hysteresis"** (2026-08-23) — toggling rendering takes effect three dots after the write, and four instruments to prove it. Built on **v2.5.2 "Dormant"** (2026-08-23) — the 2C02 register file, and a gate that passed while testing nothing. Built on **v2.5.1 "Retrace"** (2026-08-23) — the interrupt sweep closes rung 2, and a gate reported a pass it could not have earned. Built on **v2.5.0 "Rungwork"** (2026-08-23) — the 6502 rung, and the two gates it cannot reach. Built on **v2.4.9 "Plumbline II"** (2026-08-23) — the bus half of rung 2, and what it found the day it existed. Built on **v2.4.8 "Palimpsest"** (2026-08-23) — read-modify-write, and a gate that cannot see its own subject. Built on **v2.4.7 "Keystone"** (2026-08-23) — the stack closes, and a dead line proves itself dead. Built on **v2.4.6 "Abacus"** (2026-08-22) — the core learns arithmetic. Built on **v2.4.5 "Compass"** (2026-08-22) — the core reaches memory, and chooses. Built on **v2.4.4 "Ignition"** (2026-08-22) — the first real RTL -- the 6502's eight-cycle reset and the implied opcode group, matching the oracle on all seven CPU fields (29
> records, `RustyNES_MiSTer@7f092bd`). The oracle settled a question our own
> prose could not: reset is EIGHT cycles, and `docs/cpu-6502.md` said both
> seven and eight. The emulation core is untouched.
@@ -1097,7 +1097,7 @@
> `installDebug`→`installFossDebug` alias. Ad / RevenueCat glue stayed dormant and was
> later removed entirely (ADR 0035 — RustyNES is permanently open-source and
> income-free); on-device dual-flavor verification and F-Droid submission remain a
-> forward step. See `docs/android.md` + `to-dos/v1.8.x-on-device-verification.md`. android.yml CI (the
+> forward step. See `docs/android.md` + `docs/mobile-v2.9.3-run-sheet.md` (the v1.8.x checklist folded into it at v3.1.0). android.yml CI (the
> NDK cross-build + both-flavor Gradle package) is the compile gate for this change.
>
> **The preceding release: v2.0.0 "Timebase"** (2026-07-03) — the **one-clock,
@@ -1881,9 +1881,9 @@ without panicking (no `$6000` status protocol).
| `mmc5` (smoke) | 3 | — | — | 3 | `mapper_mmc5test_v1.nes`, `mapper_mmc5test_v2.nes`, `mapper_mmc5exram.nes` from `christopherpow/nes-test-roms/mmc5test/`. Visual-only; smoke-tested. Deep features (split-screen ExGrafix, audio extension) tested via in-tree mapper unit tests. |
| `holy_mapperel` | 19 | — | — | 19 | Damian Yerrick / tepples cartridge-PCB-assembly test (zlib license). 17 release ROMs across mappers 0/1/2/3/4/7/9/10/34/66/69, plus two 512 KiB MMC1 images (SUROM `M1_P512K_CR8K_S8K`, SXROM `M1_P512K_CR8K_S32K`) built from the v0.02 source tag in v2.7.2, whose build reproduces all 17 release ROMs byte-for-byte. Each screen is pinned by one combined framebuffer snapshot, with settled and non-blank guards, and each was read as `PASS 0000` by eye when it was pinned; there is no status-byte assertion, so none of the 19 is a strict pass by this table's definition (the column said 17 until v2.7.2, against this row's own "smoke-tested only"). Track B1. |
| `vrc24test` | — | — | — | — | **Skipped (Track B1)**: link rot. AWJ's original forum attachment (id=10017 on forums.nesdev.org/viewtopic.php?p=203716) is auth-walled; the deletion is documented at archive.nes.science. No GitHub mirror found. |
-| `AccuracyCoin` | 1 | 1 | — | — | 100thCoin / Chris Siebert single-NROM accuracy battery (MIT license, 149 tests across 22 suites + 5 visual-only `Power On State` tests sharing `$03FF`; the v2.0.1 upstream re-sync grew the catalog from 144 to 146 rows / 139 to 141 assigned tests, adding the PPU "ALE + Read" and "Hybrid Addresses" tests, and the **2026-09 re-sync** to upstream `69c8860` grew it again to 149 rows / 144 assigned across 22 suites — two new pages, `Advanced Background Evaluation` and `Advanced Sprite Evaluation`, which re-home eleven existing PPU tests that had outgrown `PPU Misc.`, plus three genuinely new tests). Interactive (D-Pad menu); the harness presses `START` to "run all tests on the ROM" then takes two parallel measurements. **(1) Framebuffer decoder** reads the 10×16 on-screen result grid by exact-pixel colour (5-colour palette: `#64A0FF` = pass, `#4F1000` = fail, `#DC834C` = partial-pass, `#4C4C4C` = no-test / not-run, `#FFFFFF` = border); this is the legacy path and has a known grid-stride bug that under-samples by ~31 cells. **(2) RAM-direct decoder** reads each test's result byte from its fixed CPU-RAM address (catalogued from upstream `AccuracyCoin.asm` in `crates/rustynes-test-harness/src/accuracy_coin_catalog.rs` and `tests/roms/AccuracyCoin/SOURCE_CATALOG.tsv` — 149 `(suite, name, addr)` triples, regenerated by `scripts/accuracycoin-build/extract_catalog.py`) and decodes per-test pass/fail/error-code names + per-suite breakdowns. This is the authoritative path. **Current measured pass rate (RAM-direct): 100.00% (144/144)** on the default build — the two upstream PPU tests "ALE + Read" and "Hybrid Addresses" (briefly the only gaps at 139/141 after the v2.0.1 catalog re-sync) were **closed in v2.0.3** by promoting the 2-cycle-ALE PPU fetch model to the unconditional default. The `90.65%`, `84.17%` and the trajectory figures below are historical engine-lineage milestones (the pre-promotion v1.0.0-rc2 / Session-26 era), retained as history. Historical trajectory: `64.03%` (post-D2 baseline) → `67.63%` (post-D3, 7 6502 bus-pattern fixes) → `69.06%` (post-Phase-3 OAM DMA parity fix, +1 strict test flipped) → `69.78%` (post-FSM-fix recovery, +1 sprite-related sub-test flipped as a side-benefit of the `crates/rustynes-ppu/src/ppu.rs` dot-64 reset removal) → `76.98%` (post-Cascade-B DMC DMA scheduler, commit `9b0c81c` — closes all 8 tests in the `APU Registers and DMA tests` suite + 3 net elsewhere as side-benefits; +11 tests flipped) → `78.42%` (post-Cascade-A OAMADDR-during-rendering reset, commit `f29f7ca` — hardware-accurate per nesdev: OAMADDR is reset to 0 during dots 257-320 of every rendered scanline; +2 tests flipped — Sprite overflow behavior PASSES, Sprite 0 Hit advances from error 1 → error 13) → `79.14%` (post-session-7 OAMADDR-walks-during-eval + $4-aligned `$2004` write, commit `c230489` — closes `Address $2004 behavior` with code 16; +1 net flip) → `79.86%` (post-session-7 RMW ABS,X/Y unfixed-address dummy read, commit `32d5b18` — 18 RMW opcodes get the canonical cycle-4 unfixed-address dummy; flips `APU Tests :: Controller Clocking` and advances `Implied Dummy Reads` 2→3 + `Frame Counter IRQ` 6→7 via the SLO $4015,X bracket; +1 net flip) → `82.73%` (post-session-8 BG-pipeline cycle-9 reload + post-emit shift, commit `086ce4d` — fixes the long-standing 1-column BG pixel off-by-one identified in `docs/audit/cascade-a-investigation-2026-05-19.md`; flips `Sprite 0 Hit behavior` + `Sprite overflow behavior` + `Suddenly Resize Sprite` + `$2007 read w/ rendering`; +4 net flips, +2.87pp) → `83.45%` (post-session-24 Controller Strobing M2-low-defer write, Session-24 Phase 3 — deferred `$4016` commit buffer on `LockstepBus` mirrors Mesen2's `NesControlManager::ProcessWrites`; flips `APU Tests :: Controller Strobing` from `[error 4]` to PASS; +1 net flip) → **`84.17%` (post-session-26 Sprint 2 iter 5 Frame-Counter-IRQ split, 2026-05-23 — separates `FrameCounter::irq_flag` ($4015 bit 6 visibility) from `FrameCounter::irq_line_active` (CPU IRQ source driver) so Tests I/J/K/L/M/N/O all PASS without spuriously asserting the CPU IRQ line on inhibited frame-counter cycles; flips `APU Tests :: Frame Counter IRQ` from `[error 19]` to PASS; +1 net flip)**. Session-26 Sprint 2 iter 4 (APU Register Activation OAM-DMA chip-select gate) advanced the same suite's APU Register Activation entry internally from `[error 4]` to `[error 6]` but did not flip the catalog-headline metric. The previous `75.93%` headline reflected the framebuffer decoder's stride bug, not real accuracy. Strict floor in CI is **60%** — see `crates/rustynes-test-harness/tests/accuracycoin.rs::MIN_PASS_RATE`. the v0.9.x 80% target and the v1.0.0 90% gate were both cleared, and the default build now measures **100.00% (144/144)** — the master-clock core is the default, the former C1 + sub-cycle residuals are closed, and the two v2.0.1 PPU tests were closed in v2.0.3 (see "Accuracy residuals" below). **No open AccuracyCoin gap, as of v2.6.18.** The 2026-09 re-sync opened two, both in the new `Advanced Sprite Evaluation` page. `Misaligned OAM2 Address` ($0495) closed in v2.6.17 by modelling `OAM2Address` as a live counter through sprite fetch. `Frozen OAM2 Increment` ($0493) closed in **v2.6.18**, and the long diagnosis this row used to carry is **RETRACTED in all three of its claims** — it said a `$2001` enable on dot 256 took effect a dot early (all four writes the ROM names are in force from the START of the dot it names), it blamed test 2 or 3 (the failure was test **4**, the false-positive guard, and the ROM omits an `INC 144 assigned tests across
two new pages, and `46199ae4` changes the catalog not at all; the last of them,
diff --git a/docs/accuracy-ledger.md b/docs/accuracy-ledger.md
index e3732ce1b..958dced8d 100644
--- a/docs/accuracy-ledger.md
+++ b/docs/accuracy-ledger.md
@@ -9,7 +9,7 @@ this ledger is the approximation map. The remediation line is **v2.1.0
"Fathom"** (accuracy work, shipping ahead of the v2.2.0 mobile store launch).
**Headline:** every suite on the oracle path is green, with **no named
-exception** — AccuracyCoin **144/144 (100.00%, RAM decoder)** as of v2.6.18,
+exception** — AccuracyCoin **146/146 (100.00%, RAM decoder)** at upstream `f5f41dc2` since v3.1.0, whose re-sync showed that the 144/144 reported from v2.6.18 to v3.0.1 was overstated, because the older ROM's `Misaligned OAM behavior` fail path fell through to a pass and hid a real failure. Before that: **144/144** as of v2.6.18,
which closed `Advanced Sprite Evaluation :: Frozen OAM2 Increment`, the last
entry the 2026-09 upstream re-sync left open. `KNOWN_FAILING` is now empty; it
is retained, because it fails BOTH ways and is what caught this closure.
@@ -39,7 +39,7 @@ disposition under the v2.1.0 "Fathom" accuracy-remediation line
| Optional OAM decay (F2.3) | Dynamic-RAM sprite decay when rendering stays off: per-8-byte-row 3000-CPU-cycle refresh model (Mesen2 `ReadSpriteRam`/`WriteSpriteRam`), rows refreshed by sprite-eval + `$2004`/DMA, decayed rows read `((a&3)==2)?a&0xE3:a` | No decay test ROM (Mesen2 code-level parity); NTSC/Dendy-only | **Landed on `main`** (Unreleased, next tag; F2.3) — **default-off** so the golden vectors / AccuracyCoin / commercial oracle (all decay-off) stay byte-identical; a real core feature (affects the framebuffer when ON), deterministic off `dot_counter`, save-state v7 tail (relative-age) |
| NTSC generated palette (F1.4) | Optional composite-model base palette synthesized in-core (`rustynes_ppu::generate_base_palette`) in place of the hand table | `full_palette` visual corpus + Mesen2/ares baselines (visual only) | **Shipped** (v2.1.2) — **default-off**, golden-locked cross-target; select via Settings → Palette → "Generated NTSC". Default build byte-identical |
| NTSC composite shader ladder (F2.2) | Display-only GPU post-passes: simplified blur (`Ntsc`) → LMP88959 composite → Bisqwit per-dot (`CompositeRt`) | No pass/fail ROM (visual only); `visual_regression` stays byte-identical with any filter active | **Shipped** (v2.1.2) — three-rung ladder verified end-to-end, live emulator-synced dot-crawl now on both composite passes (`Lmp88959` phase wired), palette↔pass split documented; no separable-kernel rung (LMP covers that tier). See `docs/frontend.md` |
-| Vs. `DualSystem` second screen | Core modeled + `sub_framebuffer()` exposed; desktop frontend now presents both screens (side-by-side / stacked), routes P1-P4 + coin, plays the main console's audio | Synth harness + boot of the 4 DualSystem titles (real-cabinet boot stays fixture-limited — see below) | **Shipped** (v2.1.2 F2.1, desktop) — advanced features (run-ahead / rewind / netplay / TAS / dual save-state) scoped out in dual mode per ADR 0032; wasm/mobile deferred |
+| Vs. `DualSystem` second screen | Core modeled + `sub_framebuffer()` exposed; desktop frontend now presents both screens (side-by-side / stacked), routes P1-P4 + coin, plays the main console's audio | Synth harness + boot of the 4 DualSystem titles (real-cabinet boot stays fixture-limited — see below) | **Shipped** (v2.1.2 F2.1, desktop) — netplay / TAS scoped out in dual mode per ADR 0032; dual save-state since v2.9.7, rewind and run-ahead since v3.1.0 (the ADR's amendments). Since v2.9.7 the browser's wasm-winit build runs the cabinet and presents both screens (the `wasm-canvas` embed runs the main console only; the browser run is a manual check), and the mobile bridge carries the cabinet since v2.9.7, with a screen switch rather than two screens; its on-device rows (run sheet T8-T10) are NOT RUN and the Swift half is uncompiled |
| NSF non-60 Hz playback + NSFe | **Done** (F4.1/F4.2): the play-speed divider (`$6E-$6F`/`$78-$79`) is parsed and a non-standard rate (PAL 50 Hz / custom µs) drives `play` via a mapper cycle-timer IRQ (frame-IRQ-disabled); standard 60 Hz keeps the byte-identical vblank-NMI path. `NSFE` chunked container parsed (INFO/DATA/BANK/auth). | `nsf` unit + core integration tests | **Done** (F4.1/F4.2) |
| FDS medium model (F4.3) | Byte-stream wire medium: gap / `$80` mark / block / **CRC-16-KERMIT** per block, with **per-block CRC re-emitted on write** (`resynth_block_crc`) and an opt-in **continuous belt-velocity head-seek** model (distance-proportional re-seek, default-off) replacing the fixed-cycle window | **CI-verifiable (synthetic):** `medium_write_verify` BIOS-free oracle — write via the register path, re-walk the wire, assert every block's CRC-16 + gap/mark framing round-trips (`fds::tests::synthetic_write_verify_*`). **Local-only:** the real-BIOS write-CRC path (BIOS recomputes CRC in its own RAM → `$4024`) needs a copyright `disksys.rom`, kept in gitignored `tests/roms/external/` and out of CI | **Shipped (v2.2.0 "Capstone")** — additive: default (model-off, non-writing) `.fds` run is **byte-identical**; new state round-trips the **v4** save-state tail. AccuracyCoin has no FDS ROM, so 141/141 is unaffected |
| Famicom microphone ($4016.2) | Not modeled | Built-in controller-2 mic bit surfaced on `$4016` D2 (`Nes::set_microphone`); `famicom_microphone_drives_4016_bit2` bus unit test | **Shipped (v2.2.0)** — additive / default-off (mic released ⇒ `$4016` byte-identical); a `$4016`-only signal (never touches `$4017`). No pass/fail mic ROM exists (real-cart local test only) |
diff --git a/docs/adr/0032-vs-dualsystem-desktop-presentation.md b/docs/adr/0032-vs-dualsystem-desktop-presentation.md
index dbea0d5f0..74bf5afd6 100644
--- a/docs/adr/0032-vs-dualsystem-desktop-presentation.md
+++ b/docs/adr/0032-vs-dualsystem-desktop-presentation.md
@@ -78,3 +78,16 @@ The gate:
Netplay and TAS (`T-PS-dual-netplay`), the debugger and HD packs stay out of
dual mode.
+
+**Outcome (v3.1.0).** Implemented as decided. The cabinet owns a rewind ring
+of whole RVSD containers, framebuffers included, so a step back restores both
+screens without the re-render a single console's slim ring needs.
+`VsDualSystem::restore_quiet` is the restore that keeps the ring, for
+run-ahead's rollback and a rewind step; `restore` (a loaded state) and
+`power_cycle` empty it. Both gate clauses are tests that mutation shows can
+fail: `vs_dualsystem_rewind.rs` (a slim sub block in the capture, and a loud
+restore in the step back, each caught) and `runahead.rs`
+`cabinet_runahead_matches_a_plain_run_on_both_screens` at depths 1 and 2 (no
+rollback, and capture left on across hidden frames, each caught). The stimulus
+is a cart built for it: the protocol cart renders nothing, so a framebuffer
+comparison against it is blind.
diff --git a/docs/adr/0044-movies-and-netplay-carry-the-emulation-options.md b/docs/adr/0044-movies-and-netplay-carry-the-emulation-options.md
index 3e5909303..b4885c87e 100644
--- a/docs/adr/0044-movies-and-netplay-carry-the-emulation-options.md
+++ b/docs/adr/0044-movies-and-netplay-carry-the-emulation-options.md
@@ -111,3 +111,21 @@ What is recorded and what is not, knob by knob, is the survey table in
- **v3.0.0 raises it to format 5 for the core timing epoch.** That adds no
option; it records which emulator behaviour a movie was recorded under. See
[ADR 0045](0045-a-core-timing-epoch-guards-movies-and-netplay.md).
+
+## Amendment (2026-10-07, v3.1.0): two more options, format 6, protocol 7
+
+- **`HardwareOptions` gains `cpu_overclock`** (`T-CPU-OVERCLOCK`, encoded after
+ the extra-scanline count) and, in the same release, the sprite-limit option
+ (`T-SPRITE-LIMIT`), decided together as D22 with one format and one
+ protocol bump for both.
+- **`.rnm` format 6, minimum 6.** A format-5 options record is shorter and
+ would decode as garbage, so it is refused as too old, by this ADR's own rule.
+- **Netplay protocol 7, magic `"RNE7"`.** The `Sync` layout is protocol 6's;
+ the configuration hash's input changed, so the magic changes, and a
+ protocol-6 peer is refused as another emulator version (with its epoch)
+ rather than as a settings mismatch.
+- **The CPU overclock is carried, not held at stock.** The extra-scanline
+ overclock (v2.9.7) is forced to stock while a movie records and under
+ netplay, because it predates this ADR's carriage. The CPU overclock is
+ recorded as set and replayed with it, and peers must match: the carriage
+ this ADR exists for, applied as intended.
diff --git a/docs/adr/0045-a-core-timing-epoch-guards-movies-and-netplay.md b/docs/adr/0045-a-core-timing-epoch-guards-movies-and-netplay.md
index 97e4b4d53..ca187c6a3 100644
--- a/docs/adr/0045-a-core-timing-epoch-guards-movies-and-netplay.md
+++ b/docs/adr/0045-a-core-timing-epoch-guards-movies-and-netplay.md
@@ -90,6 +90,22 @@ entirely, because the bytes do not change.
would enforce the rule. If such a check proves practical, it is added as
part of this decision's implementation; its coverage limit (the panel is
not every game) is stated where it lives.
+
+ **Amendment, 2026-10-07 (v3.1.0, `T-EPOCH-FINGERPRINT`): the check exists.**
+ `crates/rustynes-test-harness/tests/epoch_fingerprint.rs` fingerprints
+ seven committed test ROMs (every frame's framebuffer, all audio, end RAM
+ and the CPU cycle count) against `golden/epoch_fingerprint.tsv`, which
+ records the epoch and `last_release_epoch`, the epoch the last release
+ shipped. Output that moves while `EMULATION_EPOCH` equals
+ `last_release_epoch` fails, and a re-bless is refused in that state; once
+ the epoch is raised, a re-bless records the new output, and a second
+ change in the same release needs only another re-bless. It runs in CI's
+ `test-roms` job. Shown on two seeded mutants (this release's own two
+ accuracy fixes, reverted): each fails, its bless is refused, and raising
+ the epoch then re-blessing passes. **Coverage limit:** a change that moves
+ nothing on the panel passes; the commercial snapshot suites stay the wider
+ net. **The release cut must set `last_release_epoch`** to the shipped epoch
+ (`docs/agents/ci-and-release.md`), or the gate stops guarding.
- **Libretro is unaffected.** RetroArch netplay compares serialized states,
which already differ between core versions.
diff --git a/docs/agents/ci-and-release.md b/docs/agents/ci-and-release.md
index e82c5a75e..d28d8adb0 100644
--- a/docs/agents/ci-and-release.md
+++ b/docs/agents/ci-and-release.md
@@ -35,7 +35,7 @@
- **The standard is zero warnings, verified by reading the logs, not the check marks.** A green run hid: rustup's auto-install deprecation, a cargo-ndk NDK-path mismatch, UniFFI's ktlint warning, two Kotlin warnings in generated bindings, 13 MkDocs dead links, a Node DEP0040 inside `deploy-pages`, a Homebrew tap-trust warning on iOS, and criterion's "Unable to complete N samples" on every bench. The sweep that found them: annotations from `repos/{o}/{r}/check-runs/{job}/annotations` for every job of the last 150 runs, plus `gh run view --log` of the latest run of each workflow grepped for `warning|error|deprecat`, minus lines that merely echo a script's own `::error::` text. Suppress only what is in third-party code, by the narrowest switch (`--disable-warning=DEP0040` on one step), and say where it goes away.
- **`gh pr ready` seconds after a push loses the ready run (2026-09-23, #548).** The push starts a `synchronize` run whose payload still says `draft: true`, so its heavy jobs skip; `gh pr ready` starts a `ready_for_review` run beside it, and the concurrency group cancelled the READY one. What was left was a green `CI success` that had run no `test`, no `test-roms`, no Android and no Pages build. The gate script caught it only because it listed every job's conclusion instead of reading the rollup. **Push, wait until that push's runs have started, then `gh pr ready`.** If the ready-mode runs show `cancelled`, `gh run rerun ` re-runs them with their original `ready_for_review` payload. That is how #548's ready run was recovered: every job passed on attempt 2.
- **A pull request tests Linux only unless its branch is `release/*` (2026-10-03, v2.9.8).** `select matrix` in `ci.yml` gives an ordinary PR `ubuntu-26.04` and `ubuntu-26.04-arm`; macOS and Windows test legs run only on `main`, `release/*` PRs, dispatch and the weekly cron. v2.9.8's release PR was `feat/v2.9.8-cadence`: 36 checks green, merged, then `main` failed on `test (windows-latest)` alone, so Auto Release skipped and the release had no tag. The cause was the checkout, not the code: CRLF on the Windows runner stopped the source-shape tests' split on `"\n#[cfg(test)]\nmod tests {"`, so `every_load_path_installs_a_dual_system_cabinet` counted its own strings (3 against 1). Fixed by `*.rs text eol=lf` in `.gitattributes` (#582, itself on a `release/*` branch so its PR ran Windows). **Name every release PR's branch `release/*`.**
-- **A release whose goldens or commercial snapshots moved must raise `rustynes_core::EMULATION_EPOCH` (v3.0.0, ADR 0045).** The epoch is what tells a movie or a netplay peer which emulator *behaviour* it expects. `.rnm` format 5 records it, protocol 6 sends it, and a mismatch is refused, naming both epochs. Forget the bump and a movie recorded on the old behaviour replays its inputs on the new timing, and a mixed-version session desyncs, both silently: the exact failure the epoch was added to prevent. **The trigger is mechanical:** any re-blessed golden, moved `.snap`, or changed AccuracyCoin/blargg/commercial result between the last release and this one. It does not move for byte-identical refactors or performance work, frontend features, or new mapper families (no earlier output exists to differ from). Check it at the release cut, beside the CHANGELOG.
+- **A release whose goldens or commercial snapshots moved must raise `rustynes_core::EMULATION_EPOCH` (v3.0.0, ADR 0045).** The epoch is what tells a movie or a netplay peer which emulator *behaviour* it expects. `.rnm` format 5 records it, protocol 6 sends it, and a mismatch is refused, naming both epochs. Forget the bump and a movie recorded on the old behaviour replays its inputs on the new timing, and a mixed-version session desyncs, both silently: the exact failure the epoch was added to prevent. **The trigger is mechanical:** any re-blessed golden, moved `.snap`, or changed AccuracyCoin/blargg/commercial result between the last release and this one. It does not move for byte-identical refactors or performance work, frontend features, or new mapper families (no earlier output exists to differ from). Check it at the release cut, beside the CHANGELOG. **Since v3.1.0 a test enforces it** (`tests/epoch_fingerprint.rs`, ADR 0045 amendment): a moved panel fingerprint fails unless the epoch is above the table's `last_release_epoch`. **At every release cut, set `# last_release_epoch` in `crates/rustynes-test-harness/golden/epoch_fingerprint.tsv` to the epoch being shipped**, in the release commit; skip it and the next release can move output under an unchanged epoch without the gate noticing.
- **A runner outage made `CI success` green with nothing tested (2026-10-05, the #585 merge, run 37373901726).** No GitHub-hosted runner ever acquired `detect code changes` ("The job was not acquired by Runner of type hosted even after multiple attempts": no runner name, zero steps, cancelled after 15 minutes), so every gate downstream was skipped. The API reported the job `cancelled`, yet the aggregate's `contains(needs.*.result, 'cancelled')` step was itself skipped, so `needs` did not carry that value. `CI success`, `main`'s only required check, then reported success. **Fixed in v3.0.0** with a step that requires `changes` to have succeeded, and `setup` to have succeeded whenever `changes` found code. A docs-only change still passes, because `setup` skips exactly when `code` is false. The CodeQL "Push on main" run lost `Analyze (rust)` to the same outage. **A default-setup CodeQL run cannot be re-run**: the run and job retry endpoints answer 403, and re-applying the default-setup configuration returns the last run's id rather than starting one. The next push to `main` (or the weekly schedule) is what re-analyses.
- **HOLD THE AUTO RELEASE BY CANCELLING MAIN'S CI RUN (v3.0.0).** `release-auto.yml` fires on `workflow_run: CI completed` on `main`, requires `conclusion == 'success'`, and tags that run's `head_sha`. When the maintainer asked for a full docs sweep after #586 had merged, `gh run cancel` on the main CI run of the merge commit left it `cancelled`, so no tag or release was made. The sweep then goes in as its own PR, and that PR's green main run is what tags v3.0.0, on the swept commit. Confirm with `gh release view vX` and `git ls-remote --tags origin vX`. The cancelled run's own `CI success` shows red, which is the outage gate working, not a failure.
- **THE FIRST MAJOR BUMP FOUND TWO `bump_release.py` BLIND SPOTS (v3.0.0).** (1) Internal path dependencies carried `version = "2.0.0"`, a caret range no 3.x satisfies, so after the bump `cargo update -w` failed to resolve the workspace. No minor or patch bump can see it. `bump_internal_requirements` now moves them on every MAJOR, and run `cargo update -w` in BOTH workspaces (`crates/rustynes-cosim` has its own lock) after any bump. (2) A CHAIN anchor with its own description (`vX "C" released — . Built on ...`, root `ROADMAP.md`) kept the OLD release's description under the new name; CodeRabbit caught "v3.0.0 ... the tenth release of the v2.9.x line". The selftest had that exact shape and asserted only where the insertion landed. A described head now takes `--lead`, and the old text moves into the chain beside its own release.
diff --git a/docs/agents/review-bots.md b/docs/agents/review-bots.md
index 2e8ea5ec8..013e4061c 100644
--- a/docs/agents/review-bots.md
+++ b/docs/agents/review-bots.md
@@ -32,5 +32,5 @@
- **AGY RE-FILES REFUTED FINDINGS EVERY ROUND, SO THE CEREMONY NEEDS A STOPPING RULE (2026-09-23, #545 / sibling #30 / #31).** Four rounds on three docs PRs. By round 4 the Antigravity reviewer was re-raising, sometimes as **BLOCKING**, claims already refuted with line numbers in rounds 1–3: "`$G` may be undefined" (`tb/regress.sh:28` defines it), "`$VERIFY` is unbound under `set -u`" (`tb/fetch-goldens.sh:123` defaults it, three times), and "TSV `#` lines crash the loop" (`:240` skips them). It re-reviews on every push and does not carry forward the rebuttals, so every fix push buys another round with the same claims in it. **Rule:** fix what is real, reply to everything with evidence, and when a round brings only repeats and nits, reply to them and merge on green rather than pushing again. Every push restarts CI (the `test-roms` job alone is tens of minutes) *and* the reviewer. The rounds still paid: they found an ADR consequence describing a rename that did not happen, a `BLARGG_DIR` default ignoring `GOLDEN_DIR` (and the same bug in `mutate_apu.sh`), and a sentence ("measured on the console") that read as a hardware claim. Declining is only safe after checking each claim, because the first "blocking" finding of a round is not always the repeat it looks like.
- **COPILOT REVIEWS ONCE PER PR HERE, AND DOCS7 FLAGS THE PRIVATE SIBLING AS DEAD (2026-09-23).** On all three PRs Copilot posted a single review, on the first push, and never re-reviewed later pushes. So the merge gate can only check that it *has* posted, not that it reviewed the head; agy's comment timestamp is the per-push signal. Context7's **Docs7** bot (a `context7[bot]` issue comment plus a NEUTRAL check) link-checks changed files as an **anonymous** client. Every `https://github.com/doublegate/RustyNES_MiSTer/...` link is therefore reported dead, because that repository is **private** (`gh repo view --json visibility` returns `PRIVATE`). Those are correct links, not findings. Its other catch was real: two pre-existing links into `to-dos/` directories that had moved to `to-dos/archive/`.
-- **CODERABBIT SKIPS A PR OVER 100 FILES; REVIEW IT AS SLICES, AND EXPECT CROSS-SLICE FALSE FINDINGS (v2.9.8, v3.0.0).** "Review skipped: 163 files exceed the limit of 100." Both MAJOR release PRs were reviewed as review-only slice PRs (`review/vX-{a,b}` from `main`, one path partition each, built with `git diff --binary -- | git apply --index`, and proved equal to the head on their paths with `git diff --quiet -- `). A slice does not build alone, so it is committed with `--no-verify` and says so. Keep the slices in sync on every release-branch push, and close them unmerged. The trap: a slice lacks the other half's changes. On #587 CodeRabbit rated Major that `rustynes-core` still built the now-`#[non_exhaustive]` `Cartridge` by literal; that change was in the other slice. Shown the release head's lines, it withdrew. It is also rate-limited hourly on this plan, so schedule the re-request rather than re-posting.
+- **CODERABBIT SKIPS A PR OVER 100 FILES; REVIEW IT AS SLICES, AND EXPECT CROSS-SLICE FALSE FINDINGS (v2.9.8, v3.0.0).** "Review skipped: 163 files exceed the limit of 100." Both MAJOR release PRs were reviewed as review-only slice PRs (`review/vX-{a,b}` from `main`, one path partition each, built with `git diff --binary -- | git apply --index`, and proved equal to the head on their paths with `git diff --quiet -- `). A slice does not build alone, so it is committed with `--no-verify` and says so. Keep the slices in sync on every release-branch push, and close them unmerged. The trap: a slice lacks the other half's changes. On #587 CodeRabbit rated Major that `rustynes-core` still built the now-`#[non_exhaustive]` `Cartridge` by literal; that change was in the other slice. Shown the release head's lines, it withdrew. It is also rate-limited hourly on this plan, so schedule the re-request rather than re-posting. **A slice is also RED in CI by construction (v3.0.1, #591)**: feature-gated tests that live in one slice and depend on the other (`holy_mapperel.rs`, the provenance audit reading files the other slice changes) fail there and nowhere else. Say so on the slice PR at once, with the named tests, and judge CI on the release PR only.
- **AGY'S BLOCKING FINDINGS CAN CITE CODE THAT DOES NOT EXIST (v3.0.0, #586).** One round filed a BLOCKING "state desync in `SpectatorSession::pop_frame`... `push_frame` relies on `relative_idx`"; none of the three exists in the spectator (`git grep` found only the movie attestation builder's `push_frame`). The same round cited `crates/rustynes-core/src/netplay.rs` and `scripts/bump_release.py`, neither of which exists. Another round called `let _ = socket.send_to(..)` a silent failure, against the module's documented contract and four sibling sends. agy edits ONE comment in place, so these sat in the folded "Earlier review rounds" block. `git grep` every cited symbol and `ls` every cited path before acting; refute with the command and the line numbers.
diff --git a/docs/agents/tooling-traps.md b/docs/agents/tooling-traps.md
index cd6a2c781..e67cd3a4a 100644
--- a/docs/agents/tooling-traps.md
+++ b/docs/agents/tooling-traps.md
@@ -31,4 +31,5 @@
- **`set -- $var` DOES NOT WORD-SPLIT IN zsh EITHER (v2.9.8).** Same root cause as the `for x in $var` entry above: a loop `for rb in "repo branch" ...; do set -- $rb; gh api -X DELETE .../$1/.../$2` passed the whole string as `$1` and got three HTTP 404s. Call the command once per item with literal arguments.
- **A 1PASSWORD OUTAGE BLOCKS CHERRY-PICKS, NOT ONLY COMMITS (v2.9.8).** Integrating an agent's branch re-signs each picked commit, so the whole integration stops until 1Password is back. Nothing is lost: the agent's commits stay on its branch, and staged work stays staged. Ask the maintainer to unlock it; never pass `--no-gpg-sign`.
- **PARALLEL WORKTREE AGENTS NEED THE TIP'S MOVES TOLD TO THEM (v2.9.8).** Five agents worked from one base while their siblings' commits landed. Each time the branch moved (a rename, a removed API), the still-running agents were sent the new tip and the breaking facts and told to rebase before their final commit. Integration then needed only CHANGELOG and ADR conflict resolution. To relieve a loaded host, an agent can be paused at a safe point: let its current cargo job finish, write a status file, end its turn, and resume it later by message.
+- **`pgrep -f ` IN A WAIT LOOP MATCHES THE LOOP ITSELF (v3.0.1).** `until ! pgrep -f quartus_fit; do sleep 30; done` never ended: the pattern appears in the loop shell's own command line, so `pgrep` always finds one process. Three background loops stayed alive this way after the jobs they waited for had finished, and had to be stopped with TaskStop. Wait on a PID (`while kill -0 $pid`), on a file the job writes last, or use the bracket trick (`pgrep -f '[q]uartus_fit'`). The same mechanism is why `pkill -f` killed the tool shell (memory, and the trap below).
- **A WORKTREE-ISOLATED AGENT CANNOT RUN GIT HERE, AND ITS WORKTREE STARTS FROM `main` (v3.0.1).** Two forks were given `isolation: worktree` on 2026-10-07. Both worktrees were created from `main` (`9c23715b`), not from the release branch the session was on. The first agent could still run git and reset its own clean worktree to the right commit. The second could not run git at all: the RTK hook rewrites every `git` into `rtk git`, and the worktree-isolation guard refuses a command that "runs rtk with a git command among its operands" (plain `git`, `git -C`, `command git` and `rtk git` were all refused). It made no change, and the harness removed the unchanged worktree itself. For documents-only work, run the fork in the main tree instead, stage by explicit path, and make no commits from the session until it reports. Otherwise, check the agent's first `git log` before trusting anything it writes.
diff --git a/docs/apu-2a03.md b/docs/apu-2a03.md
index 29aa4561f..d46035a58 100644
--- a/docs/apu-2a03.md
+++ b/docs/apu-2a03.md
@@ -326,6 +326,25 @@ why the predicate is `put_cycle` rather than `!put_cycle`). The behavior is
end-to-end via `dmc_tests/latency.nes` (a deterministic DMC fetch-latency audio
signature) and the strictly-passing `sprdma_and_dmc_dma` alignment ROM.
+#### A load DMA refused by a write takes four cycles (v3.1.0)
+
+RDY cannot halt a write. When a pending LOAD DMA reaches the get half on which
+it would enter and that cycle is a CPU write, the load is refused and enters
+on the very next read **whichever half that is**: refused by one write it
+lands on a put half and takes four cycles (`[Put (halt)] [Get] [Put] [Get]`);
+refused by two consecutive writes it lands on a get half and takes three. The
+get-half deferral above therefore does not apply a second time to a
+write-refused load. The bus records the refusal in a one-shot latch
+(`dmc_load_write_delayed`, `crates/rustynes-core/src/bus.rs`), set in
+`Bus::write`, consumed by the DMC entry in `unified_dma_cycle_impl` and cleared
+by the next CPU read; it is in the BUS save-state section (version 3), because
+the refusing write is the last cycle of a store and a snapshot can fall between
+it and the next opcode fetch. Written from AccuracyCoin `DMA Landing on Write`
+test 9 (upstream `f5f41dc2`) and its cycle comments, and confirmed by a
+black-box per-cycle comparison with TriCNES's output at the test's
+`STA $5000`: before the fix the CPU ran the opcode fetch the hardware spends
+halted, one cycle ahead from then on.
+
### Mixer
Per `ref-docs/research-report.md` §APU Mixer, two implementations:
diff --git a/docs/compatibility.md b/docs/compatibility.md
index 6209de55b..67c9794f6 100644
--- a/docs/compatibility.md
+++ b/docs/compatibility.md
@@ -144,10 +144,13 @@ screens via `main_framebuffer()` and `sub_framebuffer()`. As of v2.1.2 "Fathom"
(F2.1) the **desktop frontend presents both screens** — side-by-side (512×240,
default) or stacked (256×480) via `[graphics] dual_screen_layout` — with P1/P2 →
main, P3/P4 → sub, coin (F10) → main acceptor, and the main console's audio (ADR
-0032). The advanced single-`Nes` features (run-ahead / rewind / netplay / TAS /
-dual save-state), the debugger, and HD-pack are **scoped out in dual mode**;
-libretro + wasm + mobile presentation remain deferred; real-cabinet boot stays
-fixture-limited (maincpu-half dumps). The non-DualSystem games (Excitebike, Clu
+0032). Netplay, TAS, the debugger, and HD-pack are **scoped out in dual mode**;
+save states work there since v2.9.7, and rewind and run-ahead since v3.1.0, on
+the whole cabinet. Libretro presents both screens since v2.1.10 (512×240,
+`docs/libretro/advanced_features.md`), the browser since v2.9.7, and the mobile
+bridge carries the cabinet since v2.9.7 (its device rows are T1-T12 of
+`docs/mobile-v2.9.3-run-sheet.md`; the Swift half has not been compiled).
+Real-cabinet boot stays fixture-limited (maincpu-half dumps). The non-DualSystem games (Excitebike, Clu
Clu Land, Castlevania, Pinball, Gradius, Goonies, Ice Climber, Golf, Super Mario
Bros.) boot and render with their correct 2C04 palette.
**PlayChoice-10's second-screen instruction menu and its Z80 coprocessor are out
@@ -211,7 +214,7 @@ unfinished); the fix is a faithful port of TriCNES's eval-pointer model — the
corrupted index is the live secondary-OAM evaluation pointer (`OAM2Address`),
captured at the disable edge during dots 1-64 and committed on re-enable. Default
builds stay byte-identical for games without such a split; AccuracyCoin
-OAM-Corruption (0x047B) + 139/141 (the two newest upstream PPU tests are known gaps), nestest 0-diff, and blargg/kevtris remain
+OAM-Corruption (0x047B) + 139/141 at the time (the two newest upstream PPU tests were then known gaps; 146/146 at upstream `f5f41dc2` since v3.1.0, `docs/STATUS.md`), nestest 0-diff, and blargg/kevtris remain
green; `repro_smb3 --movie` idle drop count went 63-80/240 → 0. See `ppu-2c02.md`
edge-case 2 and `crates/rustynes-test-harness/src/bin/{repro_smb3,smb3_dma_trace}.rs`.
@@ -220,10 +223,10 @@ edge-case 2 and `crates/rustynes-test-harness/src/bin/{repro_smb3,smb3_dma_trace
| Mapper audio | Status | Notes |
|--------------|--------|-------|
| MMC5 (2 pulse + raw PCM) | **Landed** (`mapper-audio`, Track C2 / Phase 2.3) | Castlevania III JP, Just Breed, Laser Invasion |
-| VRC6 (3 channels) | Phase 4 | Akumajou Densetsu, Madara, Esper Dream 2 |
+| VRC6 (3 channels) | **Landed** (`m024_vrc6.rs`, `Mapper::mix_audio`) | Akumajou Densetsu, Madara, Esper Dream 2 |
| VRC7 (FM, 6 channels) | **Landed** — clean-room `emu2413` port (`crates/rustynes-apu/src/opll.rs`, MIT); ADR 0006 supersedes ADR 0004 | Lagrange Point (JP) plays with in-game audio. |
-| Sunsoft 5B (3 channels) | Phase 4 | Gimmick! |
-| Namco 163 (1-8 channels) | Phase 4 | Several Japanese RPGs |
+| Sunsoft 5B (3 channels) | **Landed** (`m069_sunsoft_fme7.rs`, `Mapper::mix_audio`) | Gimmick! |
+| Namco 163 (1-8 channels) | **Landed** (`m019_namco163.rs`, `Mapper::mix_audio`) | Several Japanese RPGs |
| FDS (wavetable + envelope) | **Landed** | 2C33 — 64-entry wavetable + 32-step modulation + envelopes + master volume; behind `mapper-audio` |
## Game compatibility goals (v1.0)
@@ -320,8 +323,9 @@ This section supersedes the early "Out-of-scope" list above where they disagree
punching bag (`Nes::set_bandai_hyper_shot`, the 8-sensor `$4016`-bit-1-
multiplexed read, unit-verified against the `NESdev` "Exciting Boxing Punching
Bag" page). All are additive, default-off `InputDevice` overlays, so
- `ExpansionDevice::None` keeps every read byte-identical.) The microphone
- remains deferred. (DMC-DMA controller-bit corruption is **modelled** as of
+ `ExpansionDevice::None` keeps every read byte-identical.) The Famicom
+ microphone shipped in v2.2.0 (`Nes::set_microphone`, `$4016` D2;
+ `docs/accuracy-ledger.md`). (DMC-DMA controller-bit corruption is **modelled** as of
v1.4.0 — see `Bus::dmc_dma_read`, gated by `dmc_dma_during_read4/dma_4016_read`,
`sprdma_and_dmc_dma`, and `read_joy3/count_errors`.)
- **Vs. System / PlayChoice-10 (2C03/04/05 RGB PPUs) — game-verified.**
@@ -339,11 +343,22 @@ This section supersedes the early "Out-of-scope" list above where they disagree
accepted only when: (a) there is concrete user demand or a notable title that
needs it, **and** (b) a redistributable test fixture or a well-specified
nesdev page exists, **and** (c) it carries NES 2.0 metadata for unambiguous
- detection — weighed against maintenance cost. MMC5's >8 KiB multi-chip PRG-RAM
- configs fall under this policy (no corpus fixture; out of scope until one
- appears).
+ detection — weighed against maintenance cost. (MMC5's >8 KiB multi-chip
+ PRG-RAM configs were named here as falling under this policy; v2.7.2
+ implemented them from `MMC5.xhtml` as the wiki's 64 KiB superset,
+ `docs/audits/core-disposition.md` §5.3.)
## Open questions
+Checked at v3.1.0 against the code, and recorded together as backlog item
+FE-11, which the v3.1 → v4.0 line plan does not schedule:
+
+- the **CRC32 question is half-answered**: the per-game correction database
+ (`crates/rustynes-gamedb`, vendored from TetaNES) is CRC32-keyed, while the
+ Vs. System database (`crates/rustynes-core/src/vs_db.rs`) and save-state
+ naming key on SHA-256;
+- the **region override is still open**: no desktop, mobile or libretro
+ frontend overrides the region the header declares.
+
- **CRC32 vs. SHA-256 for ROM identification.** We use SHA-256 for save state directory naming (lower collision risk). For ROM compatibility databases, CRC32 is the community standard; we may add it as a secondary key.
- **Region override.** Some users want to play PAL versions of NTSC games at NTSC speed. Plan: expose a region override in the settings UI; warn that this may break timing-sensitive ROMs.
diff --git a/docs/dev/TESTING.md b/docs/dev/TESTING.md
index 7793b4ede..1be159404 100644
--- a/docs/dev/TESTING.md
+++ b/docs/dev/TESTING.md
@@ -19,7 +19,7 @@ RustyNES employs a comprehensive testing strategy combining unit tests, integrat
### Testing Goals
-- **AccuracyCoin 144/144 (100.00%)** since v2.6.18, on the catalog re-synced at v2.6.17 (144 assigned tests). It read 139/139 before the v2.0.1 re-sync grew it to 141, and 141/141 from v2.0.3
+- **AccuracyCoin 146/146 (100.00%)** since v3.1.0, on the catalog re-synced to upstream `f5f41dc2` (146 scored tests); the 144/144 reported from v2.6.18 to v3.0.1 was overstated, because the older ROM's `Misaligned OAM behavior` fail path fell through to a pass and hid a real failure. It read 144/144 from v2.6.18, on the catalog re-synced at v2.6.17 (144 assigned tests). It read 139/139 before the v2.0.1 re-sync grew it to 141, and 141/141 from v2.0.3
- **Unit test coverage** for all components
- **Integration tests** for component interactions
- **Regression tests** for mapper edge cases (191 mapper families)
diff --git a/docs/frontend.md b/docs/frontend.md
index 46119a8aa..ecda55cfa 100644
--- a/docs/frontend.md
+++ b/docs/frontend.md
@@ -740,6 +740,20 @@ waterfall/dither transparency tricks. Off by default (a deliberate visual
choice, re-blessed like the generated palette); the default framebuffer +
`visual_regression` corpus stay byte-identical.
+**Differential phase distortion (v3.1.0, `T-COMPOSITE-ARTIFACTS`, ACC-02).** The
+signal-decode pass's sixth knob, `diff_phase` (degrees per palette row, default
+**0 = off**), rotates the demodulated hue by that angle times the centre pixel's
+palette row (0-3), in the direction of a delay. NESdev "NTSC video": the PPU's
+level-dependent output impedance delays the chroma phase more at brighter
+levels, "about 2.5° (2C02E) or 5° (2C02G) of additional rotation for each row of
+the palette". Greys carry no chroma and are unaffected. The other composite
+artifact the page describes, colour error between neighbouring pixels (8
+clocks per pixel against a 12-clock colour cycle), is what this pass already
+reproduces by decoding the true signal, so v3.1.0 adds only the distortion.
+Presentation only: no emulated byte changes, so no epoch rise. The mobile and
+web hosts write the knob as 0 and do not expose it; the CPU Bisqwit filter
+does not model it.
+
**Vs. `DualSystem` two-screen presentation (v2.1.2 F2.1).** A loaded Vs.
`DualSystem` cabinet (Balloon Fight / Wrecking Crew / Tennis / Baseball) runs both
cross-wired consoles and presents them together. The core dual engine
@@ -760,10 +774,20 @@ both consoles. Every load path makes the same decision through
command-line path installed a cabinet image as a single console, which runs the
main CPU alone and never completes the boot handshake. The single-console path is byte-identical (the dual path is a
parallel branch at each chokepoint). **Scoped out in dual mode (ADR 0032):**
-run-ahead, rewind, netplay, TAS, the debugger, and HD-pack — they snapshot a
-single `Nes`. **Save states work in dual mode since v2.9.7**, through the
-cabinet's own "RVSD" snapshot: `EmuCore::save_state_blob` /
-`restore_state_blob` (ADR 0032's amendment). Real-cabinet boot stays fixture-limited (the circulating
+netplay, TAS, the debugger, and HD-pack. **Save states work in dual mode since
+v2.9.7**, through the cabinet's own "RVSD" snapshot: `EmuCore::save_state_blob`
+/ `restore_state_blob` (ADR 0032's first amendment). **Rewind and run-ahead
+work in dual mode since v3.1.0** (`T-PS-dual-runahead`, the second amendment),
+on the whole cabinet and never on one console, because the two share a WRAM
+and drive each other's `/IRQ`. The cabinet has its own rewind ring
+(`VsDualSystem::enable_rewind_with`, sized from `[rewind]` by `rewind_budget`
+like a single console's), whose entries are whole RVSD containers with both
+framebuffers, so a step back (`VsDualSystem::rewind_step_back`) restores both
+screens exactly without re-rendering. Run-ahead is
+`RunAhead::run_cabinet_ahead` / `finish_cabinet`: the single-console cycle on
+the cabinet, rolled back with `VsDualSystem::restore_quiet`, which keeps the
+ring. `produce_dual_frame` routes `rewind_held` and the run-ahead depth to
+them; the cabinet still keeps stock timing (no overclock). Real-cabinet boot stays fixture-limited (the circulating
dumps are the MAME maincpu half only).
**Present-path parity (v2.1.10 "Web Parity").** The **libretro** core
@@ -1025,8 +1049,9 @@ Per-tab content the panel sections render (`debugger/settings_panel.rs`):
netplay drive sites call `force_stock_timing` before a tick, because every
peer must run the same timeline. A Vs. DualSystem cabinet keeps stock timing
(ADR 0032 scopes enhancements out of dual mode). The test harness builds its
- own `Nes` and never sets it. **Disable sprite limit** is still inert: the core
- has no hook for it. The **Accuracy** group above it carries OAM decay and,
+ own `Nes` and never sets it. **Disable sprite limit** reaches the core since
+ v3.1.0 (`Nes::set_sprite_limit_disabled`, applied beside the v3.1.0 CPU
+ overclock outside a movie session; until then the setting was inert). The **Accuracy** group above it carries OAM decay and,
from v2.9.8, **Famicom console (PPU leaves reset early)** —
`[emulation] famicom_console` (default `false`, the NES model, byte-identical).
`console_model_for` maps it to `rustynes_core::ConsoleModel`;
@@ -1210,8 +1235,9 @@ builds stay byte-identical and AccuracyCoin holds 139/141 (the two newest upstre
Frontend-only, additive, English-by-default — with the default locale every
label is byte-identical to v1.6.0. (At v1.7.0 AccuracyCoin held 139/141; the two
-PPU gaps closed at v2.0.3, and the re-synced catalog has held 144/144 since
-v2.6.18.) See
+PPU gaps closed at v2.0.3, the re-synced catalog read 144/144 from v2.6.18,
+and 146/146 from v3.1.0, whose re-sync showed the 144/144 had hidden a
+masked failure.) See
ADR 0023 for the rationale (why a hand-rolled catalog over Fluent/ICU/`rust-i18n`
and the wasm size budget).
@@ -2461,7 +2487,12 @@ says which emulator *behaviour* it was recorded on as well as which options
and board. A format-4 movie is refused as too old, and a format-5 movie from
another epoch fails with `MovieError::EpochMismatch`, naming both epochs. The
epoch is raised whenever a change alters emulated output; the bump rule is in
-ADR 0045.
+ADR 0045. **v3.1.0 moved it to format 6** (`MOVIE_FORMAT_VERSION` 6, minimum
+6): the options record gains the CPU-multiplier overclock and the sprite-limit
+option, so a format-5 movie is refused as too old (every one of them was
+recorded under epoch 1 or 2, which v3.1.0 refuses anyway). Unlike the
+extra-scanline overclock, which a recording holds at stock, the CPU
+overclock is recorded as set and replayed with it.
- **Playback applies the options before frame 0** (`Movie::seek_to_start`), so
the replay does not depend on the player's settings, and the desktop and mobile
@@ -2510,8 +2541,13 @@ ADR 0045.
The `Sync` handshake carries a `rustynes_netplay::SessionIdentity`: the
emulation epoch, the ROM hash, and `rustynes_core::config_digest`, SHA-256 over
-the region, the board and the options (`PROTOCOL_VERSION` 6, magic `"RNE6"`,
-ADR 0045).
+the region, the board and the options (`PROTOCOL_VERSION` 7, magic `"RNE7"`,
+since v3.1.0; protocol 6 / `"RNE6"` from v3.0.0, ADR 0045). Protocol 7 keeps
+protocol 6's 72-byte `Sync` and changes the magic because the options encoding
+under the configuration hash gained two fields: a v3.0.x peer with the same
+settings would otherwise hash differently and be refused as a settings
+mismatch, the wrong reason. Its `"RNE6"` is now one of RustyNES's older magics,
+refused as another emulator version naming its epoch.
- **Another epoch is refused first**, as another emulator version:
`DisconnectReason::EmulatorMismatch` / `NetplayError::EmulatorMismatch` /
diff --git a/docs/ios-v1.9.9-readiness.md b/docs/ios-v1.9.9-readiness.md
index bf71e1eb5..c3d107835 100644
--- a/docs/ios-v1.9.9-readiness.md
+++ b/docs/ios-v1.9.9-readiness.md
@@ -104,6 +104,12 @@ advertised `.fds` / `.nsf`, which would fail on selection. v1.9.9 trims the pick
document types to NES (+ `.zip`) for honesty; FDS + NSF on mobile are a post-v2.0.0
carryover.
+> **Later (pointer added v3.1.0, records item DOC-09; this record is historical
+> and is otherwise left as written).** The carryover shipped in **v2.9.7**: the
+> mobile bridge loads FDS (with a host-supplied BIOS), NSF and the Vs.
+> DualSystem cabinet. The Swift half has not been compiled, and the device rows
+> are T1-T12 of [`mobile-v2.9.3-run-sheet.md`](mobile-v2.9.3-run-sheet.md).
+
## 4. Completeness-critic findings
No merge-blocking defects. Polish items found and dispositioned:
diff --git a/docs/libretro/UPSTREAM_SYNC.md b/docs/libretro/UPSTREAM_SYNC.md
index cf1b3efe8..27c492326 100644
--- a/docs/libretro/UPSTREAM_SYNC.md
+++ b/docs/libretro/UPSTREAM_SYNC.md
@@ -155,11 +155,18 @@ At this point, you can safely navigate to your repository settings on GitHub and
---
-## Sync for v3.0.0 (prepared 2026-10-04, not submitted)
+## Sync for v3.0.0 (prepared 2026-10-04, submitted 2026-10-06)
Both forks were synced to upstream `master` (`libretro-super` `a7054054af`,
-`docs` `36e9222824`) and carry a branch `rustynes-v3.0.0-sync`. No pull request
-is open; v3.0.0 submits them (ADR 0043).
+`docs` `36e9222824`) and carry a branch `rustynes-v3.0.0-sync`.
+
+**Status (checked 2026-10-07, v3.1.0 records item DOC-06).** Both were
+submitted after the v3.0.0 tag. The `.info` PR,
+[libretro/libretro-super#2131](https://github.com/libretro/libretro-super/pull/2131),
+**merged** on 2026-10-06 (`9c08e5e6f3`). The docs page PR,
+[libretro/docs#1215](https://github.com/libretro/docs/pull/1215), is **open**,
+with its one review finding fixed and answered. Until v3.1.0 this section said
+no pull request was open.
| repo | branch commit | change |
| --- | --- | --- |
diff --git a/docs/mappers.md b/docs/mappers.md
index 55eafc8d8..15eea5301 100644
--- a/docs/mappers.md
+++ b/docs/mappers.md
@@ -30,6 +30,9 @@ pub trait Mapper: Send {
fn save_state(&self) -> Vec;
fn load_state(&mut self, data: &[u8]) -> Result<(), MapperError>;
+
+ // v3.1.0 (`T-SPRITE-LIMIT`): `false` when a CHR read changes the board.
+ fn chr_reads_are_pure(&self) -> bool { true }
}
// `#[non_exhaustive]` since v3.0.0: outside `rustynes-mappers`, build one with
@@ -56,6 +59,19 @@ pub struct Cartridge {
pub enum Mirroring { Horizontal, Vertical, SingleScreenA, SingleScreenB, FourScreen, MapperControlled }
```
+**`chr_reads_are_pure` (v3.1.0).** The PPU's "disable sprite limit" option
+makes extra, display-only pattern reads, and only on boards that report
+`true`. Five report `false` because a CHR read changes them: MMC2 (9) and
+MMC4 (10) switch a CHR latch on tiles `$FD` / `$FE`, the J.Y. ASIC (35, 90,
+209, 211) clocks an IRQ counter on PPU reads and mapper 209 latches CHR,
+Bandai 96 follows the last PPU address for its inner CHR bank, and Nanjing 163
+latches PPU A13. **A new board whose `ppu_read` writes `self` must override
+it.** `every_board_that_claims_pure_chr_reads_has_them` (in `mapper.rs`) reads
+all of CHR on every constructible mapper id that claims purity and fails if
+`save_state` moved; it also pins the impure set, so the list here and the code
+cannot drift apart. The set came from a scan of every `ppu_read` body for
+writes to `self`.
+
`rustynes_mappers::parse(&[u8]) -> Result<(Cartridge, Box), RomError>`
parses an iNES or NES 2.0 file (see `cartridge-format.md`), constructs the
appropriate concrete mapper, and returns the metadata header together with
@@ -279,7 +295,7 @@ Sorted by number of commercial titles using each mapper.
| 1 | 1-5 | MMC1 (SUROM, SXROM, etc.) | 2 | — | — | landed (Phase 2) | Serial 5-write protocol; consecutive-write bug. On boards with at most 8 KiB of CHR (v2.7.2, from `nesdev_wiki/MMC1.xhtml`), the CHR bank register's bit 4 selects the 256 KiB PRG half for the whole window, fixed bank included (SUROM / SXROM), and bits 3-2 select the 8 KiB PRG-RAM bank (SOROM: bit 3; SXROM: bit 3 = A14, bit 2 = A13). In 4 KiB CHR mode the driving register is the one the last CHR fetch selected. SNROM's bit-4 RAM enable applies only to <= 256 KiB PRG with <= 8 KiB RAM. holy_mapperel `M1_P512K_CR8K_S8K` / `_S32K` pass `0000`. SZROM is not modelled. |
| 2 | 0-2 | UxROM | 2 | — | — | landed (Phase 2) | UNROM, UOROM, etc. CHR-RAM only. |
| 3 | 0-2 | CNROM | 2 | — | — | landed (Phase 2) | Bus conflict required. |
-| 4 | 0-3 | MMC3 (and MMC6, sub 1) | 4 | — | A12 | landed (Phase 4 / S1) | Sharp vs NEC IRQ revision; default Sharp. The IRQ line is raised at the first per-cycle hook after the A12 rise (v2.9.9, pitfall 2). `mmc3_test/5-MMC3` and `mmc3_test_2/5-MMC3` pass; both `4-scanline_timing` ROMs pass all 13 sub-tests since T-MMC3-BG-A12, a PPU change (`docs/ppu-2c02.md` pitfall 4); they failed at sub-test 9 from v2.9.9. |
+| 4 | 0-3 | MMC3 (and MMC6, sub 1) | 4 | — | A12 | landed (Phase 4 / S1) | Sharp vs NEC IRQ revision; default Sharp. v3.1.0: `Nes::set_mmc3_revision_override` selects the alternate (NEC) revision for any mapper-4 ROM, and its `$C001` reload to 0 now asserts as documented, so `mmc3_test_2/6-MMC3_alt` passes under it. The IRQ line is raised at the first per-cycle hook after the A12 rise (v2.9.9, pitfall 2). `mmc3_test/5-MMC3` and `mmc3_test_2/5-MMC3` pass; both `4-scanline_timing` ROMs pass all 13 sub-tests since T-MMC3-BG-A12, a PPU change (`docs/ppu-2c02.md` pitfall 4); they failed at sub-test 9 from v2.9.9. |
| 5 | — | MMC5 | 4 | yes (landed) | scanline | v0+v1 landed (Phase 4 / S4) | Banking + scanline IRQ + ExRAM modes 10/11 + multiplier (v0). Fill mode (`$5106`/`$5107`), dual sprite/BG CHR registers used for sprite tile fetches, ExGrafix per-tile attribute + CHR override (mode 01) (v1). Vertical split-screen (`$5200-$5202`) via `bg_split_state` (Castlevania III J status bar) and the MMC5 audio extension (two pulse + 7-bit PCM, `$5000-$5015`, behind the default-on `mapper-audio` feature) landed. PRG-RAM banking (v2.7.2, `nesdev_wiki/MMC5.xhtml` §"PRG-RAM configurations"): `$5113` and RAM-mode `$5114-$5116` page the RAM by the bank value's low three bits over the wiki's 64 KiB "compatible superset for all games", because PRG-RAM sizes in headers are unreliable (*L'Empereur*'s NES 2.0 dump under-declares its ETROM board); a 16 KiB window takes A13 from the CPU. `sram()` is the battery-backed part of the header's declared RAM: all of it, except ETROM's 16 KiB, where only the first chip is saved. |
| 7 | 0-2 | AxROM | 2 | — | — | landed (Phase 2) | Single-screen mirroring control. |
| 9 | — | MMC2 | 4 | — | — | landed (Phase 4 / S2) | Punch-Out; latched CHR per fetch ($FD/$FE). |
@@ -1008,7 +1024,21 @@ chunked `NSFE` containers; the FDS-style `$5FF6/$5FF7` RAM banking remains defer
## Open questions
-- **MMC3 default revision** when iNES (no submapper) is detected. Plan: default Sharp (MMC3A) since Star Trek requires it; expose a config override.
-- **Mapper #5 (MMC5) audio scope.** Implementing the 2 extra pulse + raw PCM channels is non-trivial; defer behind a `mmc5-audio` cargo feature.
+Re-checked against the code at v3.1.0 (records item DOC-03): the first two
+and the fourth are answered, and are kept with their answers rather than
+deleted.
+
+- **MMC3 default revision** when iNES (no submapper) is detected. *Answered:*
+ the default is Sharp (the "normal" IRQ behaviour); NES 2.0 submapper 4 selects
+ the alternate one (MMC3A and the non-Sharp MMC3B), and since v3.1.0 any
+ mapper-4 game can be forced either way (`Mapper::set_mmc3_revision_override`,
+ desktop `[emulation] mmc3_irq_revision`, `T-MMC3-NEC-OVERRIDE`). This line
+ used to call the Sharp default "MMC3A", which is the other revision.
+- **Mapper #5 (MMC5) audio scope.** *Answered:* the two extra pulses and the
+ raw PCM channel landed behind `mapper-audio` (Track C2 / Phase 2.3; the audio
+ table in `docs/compatibility.md`).
- **VRC7 FM audio.** YM2413-derived; only Lagrange Point uses it commercially. Banking + IRQ landed in Track C2 / Phase 2.4 (mapper 85; same `mapper-audio` feature flag as VRC6 / Sunsoft 5B / Namco 163 / MMC5). **The FM synthesizer landed** via a clean-room pure-Rust port of `emu2413 v1.5.9` (MIT) at `crates/rustynes-apu/src/opll.rs`; ADR 0006 (`docs/adr/0006-vrc7-audio-landed.md`) supersedes the ADR 0004 deferral. *Lagrange Point* plays with in-game audio (mixed via the `mapper-audio` slot).
-- **Pirate / multicart mappers.** 60+ exist; none in initial scope. Architecture supports adding them but no commitment.
+- **Pirate / multicart mappers.** *Answered by policy:* none were in the initial
+ scope; many are in now (191 families, `docs/STATUS.md`), each admitted under
+ the long-tail policy in `docs/compatibility.md` (demand, a fixture or a
+ specific NESdev page, and NES 2.0 detection).
diff --git a/docs/mobile-v2.9.3-run-sheet.md b/docs/mobile-v2.9.3-run-sheet.md
index 893a4bb8e..65184b8a9 100644
--- a/docs/mobile-v2.9.3-run-sheet.md
+++ b/docs/mobile-v2.9.3-run-sheet.md
@@ -162,6 +162,42 @@ identity, returns a pre-v2.9.9 entry for the same file instead of adding one) an
platform: cloud save-state records (Play Games snapshots, CloudKit), which the
next upload writes under the new key.
+## Rows folded in from the v1.8.x Android checklist (v3.1.0, decision D25)
+
+`to-dos/v1.8.x-on-device-verification.md` was the standing Android checklist
+from v1.8.x to the v2.1.0 launch plan. v3.1.0 folds it in here and deletes it,
+so one document carries the device runs. Only its rows that no row above
+already covers are kept, rewritten for the current tree; its history is in git.
+Dropped as covered: battery SRAM across a restart (A1), audio focus, PiP and
+background (A3-A6), rotate and PiP return (A7), save-state slots (A10) and
+multi-touch (A13). One row was dropped because it is now **wrong**: "the
+picker offers iNES / NES 2.0 only". FDS and NSF shipped in v2.9.7 (T1-T7). Its
+accuracy paragraph ("AccuracyCoin 139/141") is replaced by the rule it stated:
+the device runs the host's byte-identical core, so AccuracyCoin (146/146 at
+v3.1.0) is measured on the host and never on a phone, and a device that
+diverges from the host's frames has a shell bug, not a core one.
+
+| # | Platform | Step | Expect | Result |
+| --- | --- | --- | --- | --- |
+| G1 | Android | Import a `.nes` through the Storage Access Framework picker | It appears in the library and boots | NOT RUN |
+| G2 | Android | Import a NES 2.0 ROM; open its ROM info | Mapper and region read correctly | NOT RUN |
+| G3 | Android | Online, open the library | Box art auto-matches a grid entry (skip on `foss` offline) | NOT RUN |
+| G4 | Android | Re-open a previously imported ROM from the library | It resumes cleanly | NOT RUN |
+| G5 | Android | Hold rewind, then release | The picture runs backward smoothly, then forward | NOT RUN |
+| G6 | Android | Load a `.rns` written by the desktop build of the SAME release, and write one back | Each loads on the other side | NOT RUN |
+| G7 | Android | Load a `.rns` or `.rnm` from v3.0.1 or earlier | Refused with a clean message naming the reason (state layout, or emulation epoch for a movie), never a crash | NOT RUN |
+| G8 | Android | Pair a hardware controller (Xbox, DualSense or MFi, Bluetooth or USB-OTG) | Binds to P1: South=A, West=B, Start, Select, D-pad | NOT RUN |
+| G9 | Android | Pair two to four controllers | Each binds to its own port (the Four Score path) | NOT RUN |
+| G10 | Android | Disconnect and reconnect a controller mid-game | No crash and no stuck input | NOT RUN |
+| G11 | Android | Plug and unplug headphones while playing | Audio keeps playing; the audio thread does not wedge | NOT RUN |
+| G12 | Android | *Super Mario Bros.*: World 1-1 through the first screen | Status bar, sprites and scroll correct, no flicker | NOT RUN |
+| G13 | Android | *The Legend of Zelda*: title, overworld, then an in-game save and reload | Renders correctly; the save round-trips | NOT RUN |
+| G14 | Android | From one save state, play the same inputs twice | Identical results (the determinism contract on the device) | NOT RUN |
+| G15 | Android | On a tablet or unfolded foldable, then a phone | Two-pane and compact layouts both correct; the image letterboxes at any aspect | NOT RUN |
+| G16 | Android | Fold and unfold mid-game; on Android TV, navigate with the D-pad | No restart on fold; every TV control reachable, and the app boots to its TV banner | NOT RUN |
+| G17 | Android | `foss` flavor: inspect the merged manifest and Settings | No `AD_ID` permission, no Play Services metadata, no Billing, Play Games or Cast surface (ADR 0025) | NOT RUN |
+| G18 | Android | `play` flavor (`./gradlew :app:installPlayDebug`): boot a ROM, then open Settings | Installs and plays like `foss`; its Play-Services surface (Play Games, cloud save, Cast, in-app updates) appears, and a device without Play Services still boots and plays | NOT RUN |
+
## Android
EMULATOR is what the `Pixel_8_API_34` emulator showed on this build, before
diff --git a/docs/nesdev-hardware-emulation-checklist.md b/docs/nesdev-hardware-emulation-checklist.md
index f2d5f757e..7fe8afc9e 100644
--- a/docs/nesdev-hardware-emulation-checklist.md
+++ b/docs/nesdev-hardware-emulation-checklist.md
@@ -65,7 +65,7 @@ Primary source clusters:
| Reset sequence | Suppress reset stack writes but decrement S by 3 and fetch $FFFC/$FFFD | Implemented; cold-boot SP regression tests exist |
| Power-up register state | A/X/Y normally 0 on tested hardware; RAM is unreliable; reset preserves most state | Deterministic test mode (default) + seeded randomized developer mode (`Nes::from_rom_with_power_on_seed`) both landed v1.5.0 (T-72-002) |
| Unofficial opcodes | Implement all NMOS 6502 unofficial opcodes including unstable store family | Implemented; **SH\* pass 6/6 in AccuracyCoin under R1** — the deeper internal/external data-bus split is coarse but no test demands more |
-| Interrupt polling | NMI edge-sensitive, IRQ level-sensitive, BRK/IRQ/NMI vector hijacking | **Closed under the R1 master clock:** `cpu_interrupts_v2` 5/5 strict; the only residual is `mmc3_test_2/4` #3 (ADR-0002 axis, deferred) |
+| Interrupt polling | NMI edge-sensitive, IRQ level-sensitive, BRK/IRQ/NMI vector hijacking | **Closed under the R1 master clock:** `cpu_interrupts_v2` 5/5 strict. The last residual, `mmc3_test_2/4` #3, closed in v3.0.0 (`T-MMC3-BG-A12`; `mmc3_test_2_4_scanline_timing_strict`) |
| Dummy reads/writes | Preserve all documented dummy bus accesses and RMW double writes | Implemented for major suites; internal-vs-external bus modeling remains a v1.x TODO |
| DMA halt eligibility | DMA can halt only on CPU read cycles | Implemented; continue to guard with DMC/OAM DMA tests |
@@ -79,7 +79,7 @@ Primary source clusters:
| Loopy v/t/x/w | Keep scroll latch behavior and render-time horizontal/vertical reload timing | Implemented; guarded by PPU tests and visual baselines |
| Background fetch pipeline | Preserve all visible, prefetch, and extra nametable fetches | Implemented; Mesen2 trace tooling documents exact pipeline |
| Sprite evaluation | Per-dot secondary-OAM clear, copy, overflow bug, and OAMADDR walk | Implemented; the sub-cycle flag-timing residuals **closed under R1** (AccuracyCoin sprite-eval 9/9) |
-| Sprite 0 hit | Set during rendering with left-edge, dot, and pre-render restrictions | Implemented; residual stale-shifter cases tracked |
+| Sprite 0 hit | Set during rendering with left-edge, dot, and pre-render restrictions | Implemented; the stale-shifter case is still open, as ACC-04 (v3.3.0 in the v3.1 → v4.0 line plan) |
| NTSC odd-frame skip | Skip dot on odd frames only when rendering is enabled | Implemented and tested by `ppu_vbl_nmi` |
| Region variants | PAL and Dendy timing must not be inferred from NTSC constants | **Region-exact under the R1 master clock:** 3:1 NTSC/Dendy, hardware-true **3.2:1 PAL**; `region_timing` 4/4 |
| PPU variants | 2C03/2C04/2C05/Vs. palettes and behavior are separate from stock 2C02 | Out of scope — a separate platform initiative (Vs. System / PlayChoice-10); load-time diagnostics only |
@@ -89,11 +89,11 @@ Primary source clusters:
| Behavior | Required emulator treatment | Project status |
|---|---|---|
| Frame counter write delay | $4017 effects occur after 3 or 4 CPU clocks depending on APU-cycle phase | Implemented for test ROMs; keep in APU docs |
-| 4-step IRQ | Frame IRQ is set only in 4-step mode when IRQ inhibit is clear; $4015 read clears old flag | Implemented with residual AccuracyCoin frame-IRQ cases |
+| 4-step IRQ | Frame IRQ is set only in 4-step mode when IRQ inhibit is clear; $4015 read clears old flag | Implemented; no AccuracyCoin residual (146/146 at upstream `f5f41dc2`, v3.1.0) |
| 5-step mode | No frame IRQ; mode write can clock quarter/half frame units | Implemented |
| Nonlinear mixer | Use nonlinear pulse and TND paths before console filtering | Implemented |
-| DMC load vs reload DMA | Model different scheduling phase, dummy cycle, and alignment cycle | Implemented; residual $4015/$4016 bracket cases remain |
-| DMA register-read bugs | Repeated halted reads must affect $2007/$4015/$4016/$4017 side-effect registers | Implemented enough for blargg; AccuracyCoin residuals remain |
+| DMC load vs reload DMA | Model different scheduling phase, dummy cycle, and alignment cycle | Implemented; no AccuracyCoin residual (146/146, v3.1.0) |
+| DMA register-read bugs | Repeated halted reads must affect $2007/$4015/$4016/$4017 side-effect registers | Implemented; blargg's DMA suites pass and AccuracyCoin has no residual (146/146, v3.1.0) |
| Controller strobe/read | $4016 low 3 bits latch OUT lines; $4016/$4017 reads clock device and return D0-D4 plus open bus | Standard pads + **Four Score** + **Arkanoid Vaus paddle + Zapper** via the opt-in per-port `InputDevice` overlay; microphone / other expansion devices remain deferred |
| DMC controller conflict | Reads can lose or duplicate joypad bits during DMC DMA | **Resolved (v1.4.0).** Modelled in `Bus::dmc_dma_read` (the `$4016`/`$4017` DMC-fetch conflict clocks the controller shift register, ORing open-bus high bits with the controller read; PAL / non-`$4000`-page guarded). Verified by the strict blargg `dmc_dma_during_read4/dma_4016_read`, `sprdma_and_dmc_dma` (+`_512`), and the `read_joy3/count_errors`/`count_errors_fast` conflict-stress smokes (`count_errors` renders "Conflicts: 149/1000", all compensated) — all green |
@@ -106,7 +106,7 @@ Primary source clusters:
| Nametable layout naming | Prefer explicit CIRAM A10 behavior over ambiguous "horizontal/vertical mirroring" prose | Documented in `docs/mappers.md` and `docs/cartridge-format.md` |
| Bus conflicts | For discrete boards, mapper sees CPU value AND PRG ROM byte at the written address | Implemented for supported conflict mappers |
| MMC1 | Serial 5-write protocol, reset write, and consecutive-cycle write ignore behavior | Implemented |
-| MMC3 | Filtered PPU A12 rising edge after sufficient low time; revision-sensitive IRQ behavior | Implemented with one scanline-timing residual |
+| MMC3 | Filtered PPU A12 rising edge after sufficient low time; revision-sensitive IRQ behavior | Implemented; the scanline-timing residual closed in v3.0.0, and both revisions are selectable since v3.1.0 (`T-MMC3-NEC-OVERRIDE`) |
| MMC5 | PRG/CHR modes, ExRAM, fill, split, multiplier, scanline IRQ, audio | Feature-complete incl. audio (v1.x); only the >8 KiB multi-chip PRG-RAM configs remain (long-tail policy, no fixture) |
| Expansion audio | VRC6, Sunsoft 5B, Namco 163, MMC5, VRC7, FDS require mapper/APU integration | **All landed** via `mix_audio`→`tick_with_external`: VRC6 / 5B / N163 / MMC5 / VRC7 OPLL FM / **FDS 2C33 wavetable** |
| FDS | BIOS, disk timing, IRQs, writable media, and FDS audio are a separate platform surface | **Supported:** parser + RAM adaptor (mapper 20) + user-supplied `disksys.rom` BIOS + read/write drive + timer/transfer IRQs + writable `.fds.sav` + 2C33 wavetable audio. Real-BIOS boot unverified in CI (BIOS non-distributable); device + audio unit-tested |
@@ -117,11 +117,11 @@ Primary source clusters:
|---|---|---|
| CPU reset/power | `cpu_reset`, power-on unit tests, randomized RAM developer mode | Add missing external ROM fixture if license permits |
| CPU instruction edges | `instr_test_v5`, `instr_misc`, `cpu_timing_test6`, dummy reads/writes | `instr_misc` (5) + `instr_timing` (2) + `cpu_reset` vendored + wired v1.5.0 (T-71-002/003) |
-| CPU interrupts | `cpu_interrupts_v2`, IRQ trace fixture, mapper IRQ tests | C1 residual carried forward |
+| CPU interrupts | `cpu_interrupts_v2`, IRQ trace fixture, mapper IRQ tests | Passing; the C1 residual (`mmc3_test_2/4` #3) closed in v3.0.0 |
| PPU vblank/open bus | `ppu_vbl_nmi`, `ppu_open_bus`, PPU state traces | Passing; keep traces for future changes |
-| Sprites/OAM | sprite hit/overflow, `oam_read`, `oam_stress`, AccuracyCoin | Residuals tracked under Cascade A |
-| APU/DMA | `apu_test`, `apu_mixer`, `dmc_dma_during_read4`, `sprdma_and_dmc_dma`, `read_joy3`, `pal_apu_tests`, `240pee` (240p suite) | Passing; AccuracyCoin residuals remain. v1.4.0 added the `240pee` UxROM+BNROM render gates and confirmed the DMC-controller-conflict coverage |
-| Mapper timing | `mmc3_test_2`, `mmc3_irq_tests`, Holy Mapperel, mapper-specific ROMs | MMC3 sub-test #3 residual; VRC24 test source unresolved |
+| Sprites/OAM | sprite hit/overflow, `oam_read`, `oam_stress`, AccuracyCoin | Passing except the sprite-0 stale-shifter case (ACC-04, v3.3.0) |
+| APU/DMA | `apu_test`, `apu_mixer`, `dmc_dma_during_read4`, `sprdma_and_dmc_dma`, `read_joy3`, `pal_apu_tests`, `240pee` (240p suite) | Passing; no AccuracyCoin residual (146/146, v3.1.0). v1.4.0 added the `240pee` UxROM+BNROM render gates and confirmed the DMC-controller-conflict coverage |
+| Mapper timing | `mmc3_test_2`, `mmc3_irq_tests`, Holy Mapperel, mapper-specific ROMs | MMC3: all of `mmc3_test_2` strict (v3.0.0). VRC2/4: AWJ's `vrc24test` is still not in the corpus; coverage is `m22`'s CHR-bank walk (`tests/m22.rs`) and the local dumps in `tests/roms/external/` |
| Input | Standard pads, DMC conflict, Four Score/Zapper/expansion devices | Standard pads only; expanded devices are v1.x |
| Commercial canaries | User-supplied external snapshots, never committed ROM bytes | 60-ROM oracle exists |
diff --git a/docs/netplay-webrtc.md b/docs/netplay-webrtc.md
index 0048b747a..8a7218eee 100644
--- a/docs/netplay-webrtc.md
+++ b/docs/netplay-webrtc.md
@@ -173,7 +173,8 @@ cartridge board and every `HardwareOptions` field), and a difference fails with
options (ADR 0044). From v3.0.0 (`PROTOCOL_VERSION` 6, magic `"RNE6"`, ADR
0045) the identity also carries the emulation epoch. Another epoch, or an
older RustyNES's `Sync` (`"RNES"`, `"RNE5"`, decoded at exactly its own length
-and magic), fails with `MeshError::EmulatorMismatch` and its counterparts, so
+and magic, and since v3.1.0's protocol 7 / `"RNE7"` also v3.0.x's `"RNE6"`,
+which names its epoch), fails with `MeshError::EmulatorMismatch` and its counterparts, so
a mixed-version session is refused with a reason instead of timing out.
**Only a v3.0.0-or-later peer can give that reason.** A v2.9.9 or older peer
ignores the `"RNE6"` magic as a foreign datagram and still times out on its
diff --git a/docs/performance.md b/docs/performance.md
index acaa9ce91..35f980140 100644
--- a/docs/performance.md
+++ b/docs/performance.md
@@ -1212,6 +1212,25 @@ is named as the suspect, not established: one more step, `8f449691` →
`33ea0572` alone, would settle it. **Found and localized; not fixed in
v2.9.9.** Nothing here is claimed as a speed-up beyond NL-15.
+**That step, measured (v3.0.1 cycle, recorded v3.1.0).** `8f449691` →
+`33ea0572` alone, `scripts/perf/ab_check.sh`, two independent runs on a quiet
+host, A/B/A order-bias drift at most 0.95%:
+
+| workload | run 1 | run 2 |
+| --- | --- | --- |
+| `nestest` | +1.5% | +1.8% |
+| `palette` | +0.4% | +1.9% |
+| `nestest_fast` | +0.5% | +1.1% |
+| `palette_fast` | +1.4% | +0.5% |
+
+Slower on all four workloads in both runs, so the direction is established
+and `33ea0572` is confirmed as a contributor. It is **not** the 4-5% the
+bisection step showed: its size is 0.4-1.9%, and the runs disagree about which
+workload pays most. The commit adds a module and accessors but nothing to the
+frame loop, which leaves code layout or inlining as the working explanation,
+untested. No change is adopted; the lead is closed as measured. Logs:
+`salvaged/perf-v3.0.1-palette-ab/` (gitignored).
+
### v2.9.8 — pacing coverage: 60 Hz, Fifo, and run-ahead (the configurations v2.9.3 left unmeasured)
**Finding: presents are even in every configuration measured; run-ahead
diff --git a/docs/ppu-2c02.md b/docs/ppu-2c02.md
index 5f63610cb..94520ee20 100644
--- a/docs/ppu-2c02.md
+++ b/docs/ppu-2c02.md
@@ -127,9 +127,41 @@ proved by `crates/rustynes-test-harness/tests/extra_scanlines.rs`
(`extra_scanlines_zero_is_byte_identical_to_stock`, plus an image-invariance
proof on the first frame and a CPU-cycle-growth check).
-This is **distinct from the CPU-multiplier overclock**, which needs the
-fractional-master-clock timebase rewrite and is a v2.0 item (ADR 0002); only the
-dot-resolution scanline *insertion* is in scope here.
+This is **distinct from the CPU-multiplier overclock** (`Nes::set_cpu_overclock`,
+v3.1.0), which shortens the CPU cycle on the master clock rather than adding
+lines, and lives in the bus (`docs/scheduler.md`, "CPU-multiplier overclock").
+Until v3.1.0 this paragraph called it a v2.0 timebase item; the one-clock
+timebase that made it possible shipped in v2.0.0.
+
+### Disable sprite limit (v3.1.0, `T-SPRITE-LIMIT`, optional, default-OFF)
+
+`Nes::set_sprite_limit_disabled(true)` draws the sprites a scanline drops past
+the eighth. It is **render-only**: evaluation, secondary OAM, the overflow
+flag, sprite-0 hit and all eight real sprite fetches, with their A12 edges,
+are exactly stock, so the game cannot tell. Pinned by
+`crates/rustynes-test-harness/tests/sprite_limit.rs`: with the option on, every
+CPU cycle, work-RAM byte and audio sample matches stock frame by frame, and
+blargg's five sprite-overflow ROMs pass.
+
+- **Which sprites.** After the eighth real fetch (slot 7, dot 316) of a visible
+ line, `fetch_extra_sprites` walks primary OAM from entry 0, aligned, skips the
+ first eight in range for the next line (the ones the hardware draws when
+ evaluation starts at OAMADDR 0) and fetches up to 56 more
+ (`MAX_EXTRA_SPRITES`). Only when evaluation found eight; never on the
+ pre-render line. A line whose evaluation starts misaligned can draw a
+ slightly different set: it is a display enhancement, not hardware.
+- **How they are read.** Through `PpuBus::ppu_read_sprite`, with no
+ `observe_a12_addr`, and only when `PpuBus::chr_reads_are_pure` (the mapper's
+ answer, `docs/mappers.md`): on MMC2, MMC4, the J.Y. ASIC, Bandai 96 and
+ Nanjing 163 a CHR read changes the board, so the option draws eight there.
+- **How they draw.** In `emit_pixel`, only where none of the eight hardware
+ sprites is opaque (a higher OAM index is a lower priority), with their own
+ palette and priority bit, never as sprite 0.
+- **State.** The pending extras for the next line are PPU snapshot v13
+ (`spr_extra_*`), because a snapshot can fall between the fetch and the line.
+ The switch is configuration: carried across a power cycle, and in movies and
+ the netplay `config_digest` through `HardwareOptions` (the picture differs,
+ so frame hashes do).
### Power-up and reset state
@@ -279,10 +311,25 @@ Per `ref-docs/research-report.md` §Sprite evaluation:
- **Cycles 1..=64** — clear secondary OAM to `$FF` (forced reads).
- **Cycles 65..=256** — alternate odd (read primary OAM) / even (write secondary OAM).
- Read Y from `OAM[n][m]` — `m` is normally 0, but `OAMADDR` seeds `n` and
- `m` at dot 0 (`n = (OAMADDR >> 2) & $3F`, `m = OAMADDR & 3`), so a
- misaligned `OAMADDR` starts the walk on a tile / attribute / X byte and
- the y-test reads *that* byte. If in range for the next scanline, copy
- bytes 1..=3.
+ `m` **at dot 65** (`n = (OAMADDR >> 2) & $3F`, `m = OAMADDR & 3`; nesdev
+ "PPU registers" -> OAMADDR: "the value of OAMADDR at this tick determines
+ the starting address"), so a misaligned `OAMADDR` starts the walk on a
+ tile / attribute / X byte and the y-test reads *that* byte. A `$2003`
+ write during the dots 1-64 clear therefore still moves the start. (Until
+ v3.1.0 the FSM seeded at dot 0 only; AccuracyCoin `f5f41dc2` writes
+ `$2003` at dots 28-29 of scanline 0 and exposed it.)
+ - If in range for the next scanline, copy **four bytes** from the walk
+ position, `OAMADDR` stepping by one each, whatever the alignment: a start
+ at `m = 3` copies the last byte of slot `n` and the first three of slot
+ `n + 1` (until v3.1.0 the FSM stopped after one byte there).
+ - The fourth byte copied is the X position, and it is range-tested like Y.
+ **X in range: `OAMADDR += 1` only**, so a misaligned walk stays misaligned.
+ **X out of range: `OAMADDR += 1`, then AND with `$FC`**, realigning.
+ Aligned, both give the next multiple of four, so only misaligned OAM sees
+ the difference (AccuracyCoin `Misaligned OAM behavior` tests 4-7; until
+ v3.1.0 the FSM always realigned, and the pre-`f5f41dc2` ROM recorded the
+ resulting failures as a pass because its fail path did not pop its return
+ address).
- Not in range, secondary OAM **not** full: `OAMADDR += 4`, **then AND with
`$FC`** — so `n` advances and `m` is *cleared*, realigning the walk after
the first out-of-range sprite. When `n` overflows to 0, evaluation
@@ -609,6 +656,15 @@ change core rendering tests.
PPUMASK bit 0 (greyscale): output color ANDed with `$30`. Bits 7-5 (BGR emphasis) are applied through the 512-entry `rgba_lut`, per pixel during emission.
+**PAL and Dendy swap bits 5 and 6 (v3.1.0, `T-PAL-EMPHASIS`).** NESdev "Colour emphasis":
+bit 5 emphasises red on the NTSC 2C02 and **green** on the PAL 2C07 and the Dendy, bit 6 the
+reverse, and bit 7 is blue on all three. `emit_pixel` therefore forms the emphasis index as the
+PHYSICAL tint (bit 0 red, bit 1 green, bit 2 blue), exchanging bits 5 and 6 off NTSC, and
+both framebuffers carry it, so the composite filters see the right tint as well. Until
+v3.1.0 every PAL or Dendy game that set emphasis showed the wrong colour. Pinned by
+`pal_and_dendy_swap_the_red_and_green_emphasis_bits`. The MiSTer core has no PAL mode, so
+there is no RTL counterpart.
+
**Emphasis model (v2.9.8, `T-EMPHASIS-MODEL`).** The hardware has one attenuator shared by
the three bits, armed during the phases of colours `$C`, `$4` and `$8` for bits 5, 6 and 7,
so it runs 6, 10 or 12 of the 12 colour phases for one, two or three bits, and it never
diff --git a/docs/scheduler.md b/docs/scheduler.md
index 8945f39ec..e6c180b40 100644
--- a/docs/scheduler.md
+++ b/docs/scheduler.md
@@ -82,7 +82,9 @@ A small inner struct that tracks:
Scheduling rules per `ref-docs/research-report.md` §DMA:
-- DMA can only halt on a CPU read cycle.
+- DMA can only halt on a CPU read cycle. A load DMA a write refuses enters
+ on the next read whichever half it is (four cycles after one refusing
+ write; `docs/apu-2a03.md`, v3.1.0).
- DMC DMA gets precedence over OAM DMA.
- OAM DMA: 1 halt + (0 or 1 alignment) + 256 read/write pairs = 513 or 514 cycles.
- DMC DMA: 1 halt + 1 dummy + (0 or 1 alignment) + 1 read = 3 or 4 cycles.
@@ -133,6 +135,39 @@ fractional or master-clock representation because its PPU:CPU ratio is 3.2.
APU frame-counter tables also differ by region; do not scale NTSC cycle counts
for PAL.
+### CPU-multiplier overclock (v3.1.0, `T-CPU-OVERCLOCK`)
+
+`Nes::set_cpu_overclock(k)`, `k` in `1..=4` (`MAX_CPU_OVERCLOCK`), divides the
+region's master-clock CPU divider exactly: NTSC 12 becomes 6, 4 or 3 per
+cycle; PAL 16 and Dendy 15 alternate cycle lengths so that every `k` cycles
+take exactly one stock cycle (PAL `x3`: 5, 5, 6; Dendy `x4`: 3, 4, 4, 4; see
+below). The PPU's divider is untouched, so the CPU gets exactly `k` times the
+cycles per frame on every region. The shortest cycle, 3, still leaves both the
+read split `(0, 3)` and the write split `(2, 1)` valid.
+
+Everything that measures console time stays at the stock rate: the APU (and
+with it the DMC), the mappers' `notify_cpu_cycle` IRQ counters, and the PPU's
+open-bus decay and post-reset timers. Every `k` CPU cycles make exactly one
+stock cycle: the bus counts `overclock_phase` through `0..k`, CPU cycle `i` of
+the group lasts `((i+1)*div)/k - (i*div)/k` master clocks (the lengths sum to
+the stock divider `div`), and a **stock step** (those devices advance once)
+runs on the last cycle of each group. The cycle length changes only at a
+cycle's end, because the CPU reads the divider once for each half of a cycle.
+Until the v3.1.0 review the length was `div / k` rounded down, exact on NTSC
+and not elsewhere (PAL `x3` ran 3.2x, Dendy `x4` 5x). The APU is handed its own counter (`apu_cycle`)
+instead of the CPU's, so its put/get phase advances once per stock step. DMA
+follows that phase, so a DMA takes about `k` times as many CPU cycles and the
+same real time.
+
+At `k = 1` the branch is never taken: every cycle is a stock step and the APU
+gets the CPU counter, so the output is byte-identical (the epoch fingerprint
+gate's whole panel runs at `k = 1`). The debt and `apu_cycle` are in the BUS
+save-state section (version 3), because run-ahead restores mid-run; the
+multiplier is configuration, carried by `HardwareOptions` in movies and the
+netplay `config_digest`. `run_frame`'s cycle budget scales by `k`. Not hardware
+behaviour: no console runs its CPU faster than its APU.
+Tests: `crates/rustynes-test-harness/tests/cpu_overclock.rs`.
+
### Frame complete
The PPU reports `frame_complete()` when it transitions from scanline 240 to 241 (i.e., the start of vertical blank). The frontend consumes the framebuffer at this point.
diff --git a/docs/user-guide/compatibility.md b/docs/user-guide/compatibility.md
index 4f4631b5f..6a22ef331 100644
--- a/docs/user-guide/compatibility.md
+++ b/docs/user-guide/compatibility.md
@@ -93,8 +93,9 @@ for how the region is determined.
## Accuracy
RustyNES clears the headline accuracy bar: 100thCoin's **AccuracyCoin** suite
-at **100.00% (144/144)** (the "ALE + Read" and "Hybrid Addresses" gaps closed in
-v2.0.3, the last failing entry in v2.6.18), **nestest** with zero
+at **100.00% (146/146)** on upstream `f5f41dc2` since v3.1.0 (the 144/144
+reported before it hid a masked `Misaligned OAM behavior` failure; the "ALE +
+Read" and "Hybrid Addresses" gaps closed in v2.0.3), **nestest** with zero
golden-log diff over 8,991 instructions, and the entire blargg
`instr_test_v5`, `instr_misc`, `instr_timing`, `cpu_timing_test6`,
`cpu_interrupts_v2`, `ppu_open_bus`, `ppu_vbl_nmi`, `apu_test`,
diff --git a/ref-docs/2026-10-07-mister-core-contribution-requirements-update.md b/ref-docs/2026-10-07-mister-core-contribution-requirements-update.md
new file mode 100644
index 000000000..61cf189c9
--- /dev/null
+++ b/ref-docs/2026-10-07-mister-core-contribution-requirements-update.md
@@ -0,0 +1,125 @@
+# MiSTer core contribution page: what changed after 2026-08-23
+
+**Dated supplemental reference, 2026-10-07.** It supersedes the parts of
+[`2026-08-23-mister-core-contribution-requirements.md`](2026-08-23-mister-core-contribution-requirements.md)
+named below. That file is immutable and stays as written; this one records
+what the page says now. (v3.1.0, SUB-1.)
+
+**Primary source:** the MiSTer-devel wiki page *Contributing a Core to MiSTer
+FPGA*
+(),
+read on 2026-10-07 from the wiki's own git repository
+(`https://github.com/MiSTer-devel/Wiki_MiSTer.wiki.git`, head `1227b217`,
+2026-10-06). The page's latest revision is `317b50e` (2026-09-26). Reading the
+repository rather than the rendered page gives the exact text and the date of
+every edit. Passages below are quoted from it.
+
+## The revision history since the 2026-08-23 record
+
+The 2026-08-23 record was taken from revision `a6c9017` (2026-08-20). Six
+revisions followed:
+
+| revision | date | size of the change | what it did |
+| --- | --- | --- | --- |
+| `6aaf88a` | 2026-09-19 | 61 lines removed | "Update guidelines": the ten-step page removed |
+| `01c1da7` | 2026-09-20 | 84 lines added | the page as it now stands: guidelines, reviewer criteria, FAQ |
+| `e634b2e` | 2026-09-20 | 1 line | wording |
+| `242277d` | 2026-09-21 | 2 lines | wording |
+| `1dfe906` | 2026-09-21 | 1 line | wording |
+| `317b50e` | 2026-09-26 | 7 added, 4 removed | the `update_all` FAQ answer |
+
+**Correction to the plan's wording.** `to-dos/mister/IMPLEMENTATION_PLAN.md`
+says the page "changed on 2026-09-26". The material rewrite is
+`6aaf88a` + `01c1da7`, on 2026-09-19 and 2026-09-20. The 2026-09-26 revision
+edits only the FAQ answer about `update_all`: MiSTer-devel goes from "among the
+most restrictive paths" to "just one of the different paths", Coin-Op
+Collection is added as a contact, and the drop-in database moves into its own
+paragraph.
+
+## What the page now asks
+
+The page is scoped to one repository: "This document is to help folks get
+their cores ready for submission to MiSTer-devel specifically. Other repos may
+have other guidelines."
+
+**Core Submission Guidelines** (numbered on the page):
+
+1. "Your code needs to be published under a compatible open source license
+ like **GPLv3** or **MIT**".
+2. The template repository structure: `sys/`, `rtl/`, `releases/`, and the
+ standard root files (`.qpf`, `.qsf`, `.srf`, `.sdc`, `.sv`, `files.qip`,
+ `clean.bat`, `.gitignore`). Unchanged.
+3. "Your code should not rely on custom edits to framework files in the `sys`
+ folder. Repo admins should be able to maintain your core by dropping in a
+ fresh `sys` folder and building an updated binary."
+4. The MiSTer development principles and coding guidelines. Unchanged.
+5. At least one fully functional compiled release in `releases/` as
+ `_YYYYMMDD.rbf`. Unchanged.
+6. Arcade cores only: MRA files. Unchanged, and not applicable.
+
+**Submission:** "Once your repo is ready, and your code is properly tested,
+send a link to your repo to newcores@misterfpga.org. A MiSTer-devel member will
+reach out once they have a chance to review the repo."
+
+**What Reviewers Are Looking For** (new). The page calls these "much more
+nebulous than the guidelines above":
+
+- "Does the code follow the guidelines above?" A missed guideline goes on a
+ list of fixes; "It's the refusal to follow the guidelines that will
+ disqualify you."
+- "Does the developer understand their code?" The page is explicit that
+ prompting an LLM to build a core is fine ("_There is nothing wrong with
+ this_"), and equally explicit that "if you're submitting to MiSTer-devel,
+ you should understand your code as it is core to the next bullet point."
+- "Is the developer willing to maintain their code?" Fixing bugs, responding
+ to issues, adding features. Reviewers "will be less inclined to believe that
+ this is something you're interested in doing if you are too busy developing
+ your next core."
+- "Does the developer collaborate well with others?"
+
+**FAQ** (new): a review "can take upwards of a month in some situations";
+MiSTer-devel is one route into `update_all` among several (Jotego, Coin-Op
+Collection, theypsilon, or a drop-in downloader database); and "Is MiSTer-devel
+Anti-AI? Not at all ... as long as a developer understands their code, how
+they manifested that code is of little consequence."
+
+## What was removed, against the 2026-08-23 record
+
+| 2026-08-23 record | now |
+| --- | --- |
+| §1 "The core must demonstrate **preservation value** through accurate implementation of the original system" | **gone**. No accuracy or preservation criterion appears on the page |
+| §2 "Fully AI generated code should meet a minimum reasonable bar for readability and include some evidence of quality and accuracy testing" | **gone**, replaced by "Does the developer understand their code?" |
+| step 2 "Publish the project as a public GitHub repository" | the word "public" is gone; the reviewer still needs "a link to your repo" |
+| §6 steps 3-5: accept the organisation invitation, **transfer the repository**, add it to the Cores list with a unique Home folder | **gone** from the page. What follows a review is "an email back with the decision and next steps" |
+| "Your request will be reviewed within a few days" | "upwards of a month in some situations" |
+
+## What this means for RustyNES
+
+These are consequences for the submission case, recorded for the maintainer to
+weigh. None of them is a decision.
+
+- **The case was built on a sentence that is no longer on the page.** The
+ 2026-08-23 record called the AI-code sentence "the single most important
+ sentence on the page for this project", because the co-simulation evidence
+ answers it directly. The evidence is unchanged and still worth presenting:
+ the guidelines ask for code that is "properly tested". It no longer answers
+ a stated criterion by name.
+- **The new central question is about the maintainer, not the code.**
+ "Does the developer understand their code?" and "Is the developer willing
+ to maintain their code?" are asked of a person, and no test suite can
+ answer them. The sibling's long RTL comments (D14), its per-rung documents
+ and its oracle-vs-documentation ledger are evidence a reviewer can use to
+ check the answer, but they cannot give it.
+- **The duplicate-core risk is now unstated rather than gone.** The page no
+ longer mentions preservation value or redundancy. `NES_MiSTer` still
+ exists. Whether reviewers weigh that is not written anywhere.
+- **The repository must be reachable by the reviewer.** `RustyNES_MiSTer` is
+ private today, and the page asks for a link to the repository. Making it
+ public is the maintainer's decision and the last step before the email.
+- **The repository transfer is no longer described.** Whatever "next steps"
+ follow a positive review, the page no longer says the repository moves.
+ The 2026-08-23 record's warning about that one-way action now refers to a
+ step the page does not describe.
+- **Unchanged and already met:** the licence (GPL-3.0-or-later), the template
+ layout, an unmodified `sys/` (the sibling vendors it verbatim, checked by
+ `tb/check_sys.py`), and the release naming (`RustyNES_YYYYMMDD.rbf`).
diff --git a/tests/roms/AccuracyCoin/README.md b/tests/roms/AccuracyCoin/README.md
index 8d37b621d..ea382967d 100644
--- a/tests/roms/AccuracyCoin/README.md
+++ b/tests/roms/AccuracyCoin/README.md
@@ -8,7 +8,7 @@ diagnostic decoder needs at compile time.
| File | Purpose |
|------|---------|
-| `SOURCE_CATALOG.tsv` | 149-row TSV mapping `(suite, name) -> result-byte address`, extracted from upstream `AccuracyCoin.asm`'s `Suite_*` blocks by `scripts/accuracycoin-build/extract_catalog.py`. `include_str!`'d by `rustynes_test_harness::accuracy_coin_catalog`. |
+| `SOURCE_CATALOG.tsv` | 151-row TSV mapping `(suite, name) -> result-byte address`, extracted from upstream `AccuracyCoin.asm`'s `Suite_*` blocks by `scripts/accuracycoin-build/extract_catalog.py`. `include_str!`'d by `rustynes_test_harness::accuracy_coin_catalog`. |
| `sub-tests/*.nes` | Custom-built sub-test ROMs that boot directly into one target test (bypass menu + full-battery loop). Built by `scripts/accuracycoin-build/build_sub_test_rom.py`. Used to unblock the Session-22 Mesen2 wall-time oracle blocker. Inherits upstream MIT license. See `docs/audit/session-23-custom-accuracycoin-sub-test-roms-2026-05-22.md`. |
The runtime `.nes` ROM lives at [`../accuracycoin/AccuracyCoin.nes`](../accuracycoin/AccuracyCoin.nes)
@@ -152,8 +152,9 @@ pass / fail breakdowns.
## Source
`https://github.com/100thCoin/AccuracyCoin` (main branch; re-synced to
-upstream commit `46199ae4` on 2026-09-19; previously `69c8860`, 2026-09-11,
-and `71f57fb` in v2.0.1). The `46199ae4` re-sync left this catalog
+upstream commit `f5f41dc2` on 2026-10-07 for v3.1.0; previously `46199ae4`,
+2026-09-19, `69c8860`, 2026-09-11, and `71f57fb` in v2.0.1). The `46199ae4`
+re-sync left this catalog
**byte-identical** — re-running `extract_catalog.py` against the new
`AccuracyCoin.asm` reproduces the committed TSV exactly, because the two
upstream commits insert `INC 151 rows / 144 -> 146
+scored: two new `CPU Behavior 2` tests, `DMA Landing on Write` (`$0496`) and
+`DMC Reload Timing` (`$0497`). It also changed the row grammar. To save ROM
+space upstream replaced the unofficial-opcode suites' `table` rows with
+`tblf1` / `tblf2`, which store a one-byte token for the words "indirect",
+"zeropage", "absolute" and "immediate". `extract_catalog.py` (and
+`derive_indices.py`, which shares its patterns) rebuild those names from
+the ROM's own `PrintTextSpecialStrings`, so the eight `Unofficial
+Immediates` rows now read `immediate` in lower case, as the ROM prints them
+(`Immediate` before; the result addresses are unchanged). Before that fix
+the extractor returned 85 of the 151 rows and exited 0, because it only
+checked that the total was non-zero; it now aborts on any row-shaped line
+it cannot read, and on any suite that yields none.
+
## License
MIT (same as the runtime ROM; full text in
diff --git a/tests/roms/AccuracyCoin/SOURCE_CATALOG.tsv b/tests/roms/AccuracyCoin/SOURCE_CATALOG.tsv
index 1a49da088..660ff72e5 100644
--- a/tests/roms/AccuracyCoin/SOURCE_CATALOG.tsv
+++ b/tests/roms/AccuracyCoin/SOURCE_CATALOG.tsv
@@ -71,14 +71,14 @@ Unofficial Instructions: SH* $9B SHS absolute,Y 0x0448
Unofficial Instructions: SH* $9C SHY absolute,X 0x0449
Unofficial Instructions: SH* $9E SHX absolute,Y 0x044A
Unofficial Instructions: SH* $BB LAE absolute,Y 0x044B
-Unofficial Immediates $0B ANC Immediate 0x0410
-Unofficial Immediates $2B ANC Immediate 0x0411
-Unofficial Immediates $4B ASR Immediate 0x0412
-Unofficial Immediates $6B ARR Immediate 0x0413
-Unofficial Immediates $8B ANE Immediate 0x0414
-Unofficial Immediates $AB LXA Immediate 0x0415
-Unofficial Immediates $CB AXS Immediate 0x0416
-Unofficial Immediates $EB SBC Immediate 0x0417
+Unofficial Immediates $0B ANC immediate 0x0410
+Unofficial Immediates $2B ANC immediate 0x0411
+Unofficial Immediates $4B ASR immediate 0x0412
+Unofficial Immediates $6B ARR immediate 0x0413
+Unofficial Immediates $8B ANE immediate 0x0414
+Unofficial Immediates $AB LXA immediate 0x0415
+Unofficial Immediates $CB AXS immediate 0x0416
+Unofficial Immediates $EB SBC immediate 0x0417
CPU Interrupts Interrupt flag latency 0x0461
CPU Interrupts NMI Overlap BRK 0x0462
CPU Interrupts NMI Overlap IRQ 0x0463
@@ -106,6 +106,8 @@ CPU Behavior 2 Implied Dummy Reads 0x046D
CPU Behavior 2 Branch Dummy Reads 0x048B
CPU Behavior 2 JSR Edge Cases 0x047C
CPU Behavior 2 Internal Data Bus 0x0490
+CPU Behavior 2 DMA Landing on Write 0x0496
+CPU Behavior 2 DMC Reload Timing 0x0497
Power On State PPU Reset Flag 0x03FF
Power On State CPU RAM 0x03FF
Power On State CPU Registers 0x03FF
diff --git a/tests/roms/AccuracyCoin/mirror/AccuracyCoin-mirror.nes b/tests/roms/AccuracyCoin/mirror/AccuracyCoin-mirror.nes
index 038fa10aa1a675060bc7495fa5d0518c239aacd2..b1f0a6ed4ba18f8cf8e9e2f237aa71071d05b44d 100644
GIT binary patch
delta 19521
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
cW}z-q5+?7yvvWY14{k@hx;
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, commit `46199ae4` |
+| Upstream | , commit `f5f41dc2` (rebuilt at v3.1.0; `46199ae4` before) |
| Upstream licence | MIT (Copyright (c) 2025 Chris Siebert) — the text is at `../../accuracycoin/LICENSE`, and the corpus-wide index is `../../LICENSES.md` |
-| Base ROM md5 | `2f9d83104969a5984caf21a77d6746bd` (identical to `tests/roms/accuracycoin/AccuracyCoin.nes`) |
-| This ROM md5 | `8162ca0ae099220401e76719e88762fc` |
+| Base ROM md5 | `a3635c87ffb1754f58923d070548898b` (identical to `tests/roms/accuracycoin/AccuracyCoin.nes`) |
+| This ROM md5 | `bbd842dfa221cdc5a3ea44f8dfcc37b0` |
| Built by | `scripts/accuracycoin-build/build_mirror_rom.py` |
| Assembler | upstream's own `nesasm.exe` under `wine`, so the output is the author's toolchain rather than an equivalent one |
@@ -32,11 +32,11 @@ RustyNES.
```bash
git clone https://github.com/100thCoin/AccuracyCoin /tmp/ac-src
-git -C /tmp/ac-src checkout 46199ae4
+git -C /tmp/ac-src checkout f5f41dc2
python3 scripts/accuracycoin-build/build_mirror_rom.py /tmp/ac-src \
--out tests/roms/AccuracyCoin/mirror/AccuracyCoin-mirror.nes \
- --expect-upstream-md5 2f9d83104969a5984caf21a77d6746bd \
+ --expect-upstream-md5 a3635c87ffb1754f58923d070548898b \
--wine /usr/bin/wine
```
diff --git a/tests/roms/AccuracyCoin/sub-tests/BUILD-PROVENANCE.tsv b/tests/roms/AccuracyCoin/sub-tests/BUILD-PROVENANCE.tsv
index e32e93964..3f099ff02 100644
--- a/tests/roms/AccuracyCoin/sub-tests/BUILD-PROVENANCE.tsv
+++ b/tests/roms/AccuracyCoin/sub-tests/BUILD-PROVENANCE.tsv
@@ -64,6 +64,22 @@
# does not stand alone or the entry needs the battery's preceding state, and
# both are open questions rather than DUT defects.
#
+# v3.1.0 (the f5f41dc2 re-sync): TWO ROMS FROM THE NEW SOURCE.
+# * `sprite-eval-misaligned-oam.nes` is REBUILT from f5f41dc2 (suite 18,
+# test 6; it was 17/5 in the legacy build). The legacy build carries the
+# upstream bug fixed in `adacbc23`: `FAIL_MisalignedOAM_Behavior` jumped
+# out of a JSR'd routine without popping the return address, so a failure
+# in tests 2-7 fell through to a pass. Its `Pass` proved nothing, and it
+# was the ROM `RustyNES_MiSTer`'s ladder gated that entry by. Settles at
+# frame 175 (the legacy build: 211).
+# * `dmc-reload-timing.nes` is NEW (suite 14, test 6, `$0497`), for the test
+# upstream added in f5f41dc2. It reaches its verdict in 15 frames against
+# the battery's 4500, and it is what located the MiSTer core's DMC reload
+# defect to one bus cycle (sibling ledger 3.52).
+# Both built with `build_sub_test_rom.py` from a clone at f5f41dc2, under
+# upstream's own `nesasm.exe` through wine; rows measured by
+# `subtest_identify`.
+#
# RE-BLESSED IN v2.9.5: `ppu-misc-sprites-on-scanline-0` records
# `PassWithCode(1)`, the composite-2C02 outcome, where it recorded
# `PassWithCode(2)`, "RGB PPU", until then. The core now draws the odd-frame
@@ -104,6 +120,14 @@
# offset, 0x10B5, 0x02 -> 0x03 (the `LDX #test` immediate; `LDY #suite` sits at
# 0x10AB).
#
+# v3.1.0: `sprite-eval-misaligned-oam` is REBUILT from upstream `f5f41dc2`,
+# whose fail path exits instead of falling through to a pass. The rebuilt ROM
+# settles on frame 175 with 18/6 (see its row). The legacy build it replaces
+# settled on frame 211 after the misaligned-evaluation fixes (212 before):
+# its tests 3, 6 and 7 then passed on their own path instead of failing into
+# the unpopped fail path, which ran a different number of cycles. That legacy
+# figure describes a ROM no longer in this directory.
+#
# REGENERATE WITH THE GENERATOR, AND THE SCHEMA LINE BELOW COMES FROM IT:
#
# cargo run -p rustynes-test-harness --release --features test-roms \
@@ -124,6 +148,7 @@ controller-strobing 13 7 0x045F Controller Strobing Pass 29 legacy-unrecorded
cpu-open-bus 0 6 0x0407 Dummy write cycles Pass 31 legacy-unrecorded
dma-open-bus 12 0 0x046C DMA + Open Bus Pass 26 legacy-unrecorded
dmc-bus-conflicts 12 6 0x046B DMC DMA Bus Conflicts Pass 29 legacy-unrecorded
+dmc-reload-timing 14 6 0x0497 DMC Reload Timing Pass 15 f5f41dc2
fc-4step 13 3 0x0468 Frame Counter 4-step Pass 33 legacy-unrecorded
frame-counter-irq 13 2 0x0467 Frame Counter IRQ Pass 66 legacy-unrecorded
iflag-latency 11 0 0x0461 Interrupt flag latency Pass 29 legacy-unrecorded
@@ -147,6 +172,6 @@ sh-shx-9E 9 4 0x044A $9E SHX absolute,Y Pass 27 legacy-unrecorded
sh-shy-9C 9 3 0x0449 $9C SHY absolute,X Pass 27 legacy-unrecorded
sprite-eval-2002-flag-timing 17 2 0x048D $2002 flag timing Pass 73 legacy-unrecorded
sprite-eval-arbitrary-sprite-zero 17 4 0x0458 Arbitrary Sprite zero Fail(1) 29 legacy-unrecorded
-sprite-eval-misaligned-oam 17 5 0x045A Misaligned OAM behavior Pass 212 legacy-unrecorded
+sprite-eval-misaligned-oam 18 6 0x045A Misaligned OAM behavior Pass 175 f5f41dc2
sprite-eval-oam-corruption 17 7 0x047B OAM Corruption Pass 111 legacy-unrecorded
sprite-zero-hit-behavior 17 1 0x0457 Sprite 0 Hit behavior Fail(1) 29 legacy-unrecorded
diff --git a/tests/roms/AccuracyCoin/sub-tests/dmc-reload-timing.nes b/tests/roms/AccuracyCoin/sub-tests/dmc-reload-timing.nes
new file mode 100644
index 0000000000000000000000000000000000000000..ef79c9da731a157dc32414eef0e5cd11819fa68c
GIT binary patch
literal 40976
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