diff --git a/.github/release-notes/v2.9.3.md b/.github/release-notes/v2.9.3.md new file mode 100644 index 000000000..7187bd95f --- /dev/null +++ b/.github/release-notes/v2.9.3.md @@ -0,0 +1,49 @@ +# RustyNES v2.9.3 — "Handset" + +The fourth release of the v2.9.x line that [ADR 0041](https://github.com/doublegate/RustyNES/blob/main/docs/adr/0041-hardware-release-is-v3.0.0.md) put before the SuperStation One core. It moves every dependency to its newest release, answers every review thread left open on the project's older pull requests, fixes the ten findings among them that still held, and makes the mobile device run ready to go. + +**The device run is not in this release.** The maintainer moved the mobile device runs, the SuperStation One board session, and the fixes each produces to after v3.0.0 (2026-09-29). Every Swift change since v2.7.4 is therefore still uncompiled, no Android row has run on a device, and **no hardware has run any bitstream**. ADR 0041 defined v3.0.0 as the hardware-verified core, so its [2026-09-29 amendment](https://github.com/doublegate/RustyNES/blob/main/docs/adr/0041-hardware-release-is-v3.0.0.md) records the conflict and leaves what v3.0.0 ships open. + +## Every dependency at its newest + +- **egui 0.36 with wgpu 30 and naga 30.** The web build needs a fix to `egui-winit` that upstream has merged but not released, so a vendored copy of the published 0.36.2 carries a three-line guard until the next release. +- **Frames are presented evenly under wgpu 30.** On a 120 Hz Wayland desktop the median present interval went from 5.5 to 8.4 ms and the p95 from 15.6 to about 11 ms, in two alternating A/B pairs against v2.9.2. Before, presents came in bunches; now they land one refresh apart. +- **rcheevos 12.5.0** for RetroAchievements. Its `time_t` fields now use the platform's own size, which fixes the struct layout on 32-bit Android and 32-bit Linux. +- UniFFI 0.32.2, Gradle 9.8.0, and every compatible update. `bincode` is removed rather than bumped: nothing used it. + +## Old review threads, checked against today's code + +244 review comments on PRs #7-#97 had never been answered. Each was checked against the current code: 234 were answered and resolved, and these ten still held and are fixed: + +- **Action 53 multicarts (mapper 28) are rebuilt to the NESdev spec.** PRG banking was wrong for every outer bank size above 32 KiB and in both UNROM modes, the CHR bank register and the one-screen select did nothing, and the board powered on at the wrong bank. A unit test checks every mode, outer bank and inner bank against the wiki's table. Old save states still load. +- **The ROM header editor writes only the fields you change.** It used to re-encode all 16 bytes, which zeroed Vs. UniSystem protection types, the extended console type, bytes 14-15, the NVRAM sizes and anything in iNES 1.0 bytes 8-15. **It also saved every mapper from 16 up as the wrong mapper** (mapper 66 as 2), since v1.7.0. Loading ROMs was never affected. +- **A palette file that cannot be read no longer erases your palette setting**, and loading one reads only the 192 bytes it uses. +- **NSF bank registers read as open bus**, as the NSF spec lists them write-only. +- **The Bisqwit NTSC filter no longer darkens the picture edges.** +- The debugger's event heatmap keeps its proportions; a NaN audio gain falls back to unity; the `fds_trace` diagnostic no longer calls a cut-short block a match; a stale documentation pointer is fixed. + +## For developers + +- `rustynes_mappers::serialize_header` is **deprecated**: it zeroes every header bit `Header` has no field for. Use `serialize_header_preserving(header, original_bytes)`. The old function is removed at v3.0.0 with the other deprecations ([ADR 0042](https://github.com/doublegate/RustyNES/blob/main/docs/adr/0042-v3-removes-the-v2-7-5-deprecations-and-the-dead-nmi-edge-detector.md)). +- CI now runs the Android app's Kotlin unit tests (on `main` pushes, and on pull requests that touch the app, its UniFFI bridge or the Android workflow) and compiles the iOS renderer on every pull request that changes code. + +## The mobile run, ready for later + +[`docs/mobile-v2.9.3-run-sheet.md`](https://github.com/doublegate/RustyNES/blob/main/docs/mobile-v2.9.3-run-sheet.md) consolidates every row of the v2.7.4 checklist and v2.9.2's additions, plus rows for this release's wgpu and rcheevos changes. Some of the Android rows were run on an emulator (Android 14): the battery save survives force-stops, audio stops when the screen turns off, paused games stay paused, picture-in-picture keeps playing, and the GPU renderer survives rotation and sleep (A1, A3, A4, A6, A7, D1, and A10 for function). A2 could run on an emulator and did not, and A8 has only a 30-second reading against its one-minute step. Those are emulator results, not device results; the device columns are empty. + +## Verification + +| Check | Result | +| --- | --- | +| `cargo test --workspace --features test-roms` | 2,887 passed, 0 failed, 20 ignored | +| AccuracyCoin / nestest | 144/144 / 0-diff | +| fmt, clippy (every feature set, wasm), rustdoc, no_std build | clean | +| Android: NDK cross-build, Kotlin unit tests | pass (CI) | + +## Install + +- Download the pre-built binaries for Linux, macOS, and Windows below. +- The MiSTer core is unchanged since v2.9.2; its bitstreams are attached to [v2.9.2](https://github.com/doublegate/RustyNES/releases/tag/v2.9.2). Neither has run on hardware. +- The WebAssembly build is live at [doublegate.github.io/RustyNES](https://doublegate.github.io/RustyNES/). +- The RetroArch core is in RetroArch's Online Updater on the platforms the libretro buildbot publishes to. +- Licensed under GPL-3.0-or-later. diff --git a/AGENTS.md b/AGENTS.md index 30040b4dc..01d15005d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -43,7 +43,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: 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). v2.9.2 triaged a fifth, AI-written audit of both repositories (`docs/audits/v2.9.2-full-audit-report.md`, 32 findings) in its ledger, `docs/audits/v2.9.2-full-audit-disposition.md`: 16 fixed red-first, 2 changed but verifiable only on a device (the Swift halves of AUD-10/14), and the rest refuted, declined or deferred with evidence. The sweep written for AUD-02 found that save states dropped the cartridge RAM of twelve board families; `every_board_snapshot_carries_cartridge_ram` now round-trips the RAM of all 174 mapper ids. It changes emulation behaviour in one deliberate place: an unmapped `$4020-$FFFF` read now updates the CPU's internal data bus (AUD-03). Opposing-direction cancel (AUD-08/09/10) is on by default, by the maintainer's decision (2026-09-28). The MiSTer CPU's /NMI edge detector ran on the DMA-stalled enable and lost an NMI raised inside a DMA (AUD-24, gate `dmanmi074`). v2.9.1 fixed `scripts/perf/ab_check.sh` (both sides had built into one target directory) and re-measured the old rejections; the dead NMI detector's per-dot call (−4.1% to −4.7% on palette frames) is removed at v3.0.0 with ADR 0042. The libretro core is built with `panic = "unwind"`, exercised by a C-ABI harness (`crates/rustynes-libretro/src/abi_tests.rs`), and patches a vendored `rust-libretro-sys` (`vendor/`). **AccuracyCoin 144/144 and nestest 0-diff** hold on the v2.9.2 tree, and the full `--features test-roms` suite passes 2,867 tests. v2.7.4's mobile changes, and v2.9.2's Swift, have a device checklist (`docs/mobile-v2.7.4-device-checklist.md`), scheduled as v2.9.3. The co-simulation is **173 passed, 0 failed, 1 expected failure** on-die and **174 / 0 / 1** off-die (`USE_SDRAM=1`), each ONE frozen-worktree ladder run with nothing skipped; both bitstreams compile at fitter seed 2, chosen from eight per build at one build date (on-die +0.510 ns setup / +0.108 ns hold, off-die +0.390 / +0.081; the SDRAM read is +0.447 setup / +1.184 hold), and two clean compiles of each are byte-identical. The SDRAM pin constraints stay provisional until the SuperStation One's memory is read. **No hardware has run any bitstream**. The board session runs v2.9.2's bitstreams, with `tools/stage_board_kit.sh` in the sibling staging what it needs, and the hardware-verified core is **v3.0.0** ([ADR 0041](docs/adr/0041-hardware-release-is-v3.0.0.md)). Per-release detail lives in `CHANGELOG.md` and the GitHub releases; it is deliberately not duplicated here. +**Current release: 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). v2.9.2 triaged a fifth, AI-written audit of both repositories (`docs/audits/v2.9.2-full-audit-report.md`, 32 findings) in its ledger, `docs/audits/v2.9.2-full-audit-disposition.md`: 16 fixed red-first, 2 changed but verifiable only on a device (the Swift halves of AUD-10/14), and the rest refuted, declined or deferred with evidence. The sweep written for AUD-02 found that save states dropped the cartridge RAM of twelve board families; `every_board_snapshot_carries_cartridge_ram` now round-trips the RAM of all 174 mapper ids. It changes emulation behaviour in one deliberate place: an unmapped `$4020-$FFFF` read now updates the CPU's internal data bus (AUD-03). Opposing-direction cancel (AUD-08/09/10) is on by default, by the maintainer's decision (2026-09-28). The MiSTer CPU's /NMI edge detector ran on the DMA-stalled enable and lost an NMI raised inside a DMA (AUD-24, gate `dmanmi074`). v2.9.1 fixed `scripts/perf/ab_check.sh` (both sides had built into one target directory) and re-measured the old rejections; the dead NMI detector's per-dot call (−4.1% to −4.7% on palette frames) is removed at v3.0.0 with ADR 0042. The libretro core is built with `panic = "unwind"`, exercised by a C-ABI harness (`crates/rustynes-libretro/src/abi_tests.rs`), and patches a vendored `rust-libretro-sys` (`vendor/`). **AccuracyCoin 144/144 and nestest 0-diff** hold on the v2.9.2 tree, and the full `--features test-roms` suite passes 2,867 tests. v2.7.4's mobile changes, and v2.9.2's Swift, have a device checklist (`docs/mobile-v2.7.4-device-checklist.md`), consolidated at v2.9.3 into `docs/mobile-v2.9.3-run-sheet.md` with an emulator pre-run; the device run itself moved after v3.0.0 (maintainer, 2026-09-29). The co-simulation is **173 passed, 0 failed, 1 expected failure** on-die and **174 / 0 / 1** off-die (`USE_SDRAM=1`), each ONE frozen-worktree ladder run with nothing skipped; both bitstreams compile at fitter seed 2, chosen from eight per build at one build date (on-die +0.510 ns setup / +0.108 ns hold, off-die +0.390 / +0.081; the SDRAM read is +0.447 setup / +1.184 hold), and two clean compiles of each are byte-identical. The SDRAM pin constraints stay provisional until the SuperStation One's memory is read. **No hardware has run any bitstream**. The board session was planned on v2.9.2's bitstreams (`tools/stage_board_kit.sh` in the sibling stages what it needs); the maintainer moved it after v3.0.0 (2026-09-29), which leaves open what v3.0.0 itself ships, since [ADR 0041](docs/adr/0041-hardware-release-is-v3.0.0.md) defined it as the hardware-verified core (see its 2026-09-29 amendment). Per-release detail lives in `CHANGELOG.md` and the GitHub releases; it is deliberately not duplicated here. - **Timebase (v2.0.0)** — the scheduler substrate is rewritten from a five-counter dot-lockstep model to a single canonical cycle counter, every CPU cycle clocked in two halves (`start_cycle` / `end_cycle`) with any bus access split between them, and the PPU caught up to each half (ADR 0002 / ADR 0029), now the *only* scheduler path. This is a MAJOR-boundary breaking change (ADR 0003): `.rns` save-state and `.rnm` movie format epochs bump (ADR 0028) — a pre-v2.0.0 `.rns` slot now fails to load with a clear error instead of silently misinterpreting stale bytes. Landed across five betas + rc.1 (PRs #217–223). Also new: core-level **Vs. `DualSystem`** dual-console support (`Emu::Dual`, `crates/rustynes-core`) for the four Vs. arcade cabinet boards — core-and-test-harness-only, frontend wiring deferred. The R1/R2 MMC3 IRQ-timing residual is by-design-deferred beyond this release with a mechanism-level finding recorded in ADR 0002 (not closed, not silently dropped). **AccuracyCoin now measures 141/141 (100.00%)**: the v2.0.1 upstream AccuracyCoin re-sync grew the catalog to 146 rows / 141 assigned tests and briefly opened two new PPU gaps ("ALE + Read" $0491, "Hybrid Addresses" $0492), which **v2.0.3** closed by promoting the 2-cycle-ALE PPU fetch model to the unconditional default (both experimental flags retired; additive `PPU_SNAPSHOT_VERSION` v5 tail). AccuracyCoin held 100% (139/139) throughout the v2.0.0 betas and final cut, dipped to 139/141 under the v2.0.1 re-sync, and is back to a full 141/141 from v2.0.3 onward. @@ -225,7 +225,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 **v2.9.2 "Candidate"** (2026-09-28). **Never claim any version *later* than v2.9.2 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 is the one release that broke byte-identity and save-state compatibility, by design. 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 **v2.9.3 "Handset"** (2026-09-29). **Never claim any version *later* than v2.9.3 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 is the one release that broke byte-identity and save-state compatibility, by design. 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 c187ad70a..b7b6f5d79 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2,7 +2,7 @@ **Document Version:** 2.1.0 **Last Updated:** 2026-08-30 -**Applies to:** RustyNES v2.9.2 (the scheduling model is v2.0.0 "Timebase" onward) +**Applies to:** RustyNES v2.9.3 (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 99e68157b..865b2b892 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,25 @@ cycle-accurate core later replaced. ## [Unreleased] +## [2.9.3] - 2026-09-29 - "Handset" (the old review threads closed, and the mobile run prepared) + +The fourth release of the v2.9.x line ([ADR 0041](docs/adr/0041-hardware-release-is-v3.0.0.md)): +**every dependency at its newest release, every old review thread answered, +and the mobile device run made ready.** All 244 review threads left unanswered +on PRs #7-#97 were checked against current code; 234 were answered and the ten +findings that still held are fixed below. The mobile run sheet +([`docs/mobile-v2.9.3-run-sheet.md`](docs/mobile-v2.9.3-run-sheet.md)) carries +every device row, checksummed builds and an Android-emulator pre-run. **The +device runs, the SuperStation One board session, and the fixes they produce +move after v3.0.0** (maintainer, 2026-09-29), so every Swift change since +v2.7.4 is still uncompiled and no Android row has run on a device. That move +conflicts with ADR 0041's definition of v3.0.0 as the hardware-verified core; +its [2026-09-29 amendment](docs/adr/0041-hardware-release-is-v3.0.0.md) records +the conflict and leaves what v3.0.0 ships open. Emulation output is unchanged +outside mapper 28, NSF `$5FF8-$5FFF` reads and the Bisqwit filter's edge +columns; AccuracyCoin 144/144 and nestest 0-diff hold, and the full +`--features test-roms` suite passes 2,887 tests. + ### Fixed - **Mapper 28 (Action 53 multicarts) is rebuilt to the NESdev spec.** PRG banking @@ -86,7 +105,7 @@ still hold. runs `:app:testFossDebugUnitTest` (the Kotlin tests, including the opposing-direction cancel added in v2.9.2) on every `main` push and on any pull request that changes the app. Until now they had only ever run by hand. -- **The iOS renderer is compiled on every pull request.** +- **The iOS renderer is compiled on every pull request that changes code.** `scripts/ios-host-typecheck.sh`, already in CI's lint job, now type-checks the real `gfx_metal.rs` against the workspace's wgpu. Before this, a wgpu change there was first compiled when a release was cut. Rendering itself diff --git a/Cargo.lock b/Cargo.lock index a81ce137d..19f5658fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4278,7 +4278,7 @@ dependencies = [ [[package]] name = "rustynes-android" -version = "2.9.2" +version = "2.9.3" dependencies = [ "android-activity", "android_logger", @@ -4296,7 +4296,7 @@ dependencies = [ [[package]] name = "rustynes-apu" -version = "2.9.2" +version = "2.9.3" dependencies = [ "bitflags 2.13.2", "criterion", @@ -4309,7 +4309,7 @@ dependencies = [ [[package]] name = "rustynes-cheevos" -version = "2.9.2" +version = "2.9.3" dependencies = [ "cc", "libc", @@ -4318,7 +4318,7 @@ dependencies = [ [[package]] name = "rustynes-core" -version = "2.9.2" +version = "2.9.3" dependencies = [ "bitflags 2.13.2", "criterion", @@ -4335,7 +4335,7 @@ dependencies = [ [[package]] name = "rustynes-cpu" -version = "2.9.2" +version = "2.9.3" dependencies = [ "bitflags 2.13.2", "criterion", @@ -4346,7 +4346,7 @@ dependencies = [ [[package]] name = "rustynes-frontend" -version = "2.9.2" +version = "2.9.3" dependencies = [ "anstyle", "arboard", @@ -4405,18 +4405,18 @@ dependencies = [ [[package]] name = "rustynes-gamedb" -version = "2.9.2" +version = "2.9.3" dependencies = [ "rustynes-core", ] [[package]] name = "rustynes-gfx-shaders" -version = "2.9.2" +version = "2.9.3" [[package]] name = "rustynes-hdpack" -version = "2.9.2" +version = "2.9.3" dependencies = [ "lewton", "png", @@ -4427,7 +4427,7 @@ dependencies = [ [[package]] name = "rustynes-ios" -version = "2.9.2" +version = "2.9.3" dependencies = [ "bytemuck", "cpal", @@ -4441,7 +4441,7 @@ dependencies = [ [[package]] name = "rustynes-libretro" -version = "2.9.2" +version = "2.9.3" dependencies = [ "libc", "rust-libretro", @@ -4450,7 +4450,7 @@ dependencies = [ [[package]] name = "rustynes-mappers" -version = "2.9.2" +version = "2.9.3" dependencies = [ "bitflags 2.13.2", "criterion", @@ -4462,7 +4462,7 @@ dependencies = [ [[package]] name = "rustynes-mobile" -version = "2.9.2" +version = "2.9.3" dependencies = [ "rustynes-core", "rustynes-hdpack", @@ -4477,7 +4477,7 @@ dependencies = [ [[package]] name = "rustynes-netplay" -version = "2.9.2" +version = "2.9.3" dependencies = [ "futures-util", "js-sys", @@ -4493,7 +4493,7 @@ dependencies = [ [[package]] name = "rustynes-ppu" -version = "2.9.2" +version = "2.9.3" dependencies = [ "bitflags 2.13.2", "criterion", @@ -4505,21 +4505,21 @@ dependencies = [ [[package]] name = "rustynes-probe" -version = "2.9.2" +version = "2.9.3" dependencies = [ "rustynes-core", ] [[package]] name = "rustynes-ra" -version = "2.9.2" +version = "2.9.3" dependencies = [ "rustynes-cheevos", ] [[package]] name = "rustynes-script" -version = "2.9.2" +version = "2.9.3" dependencies = [ "mlua", "piccolo", @@ -4530,7 +4530,7 @@ dependencies = [ [[package]] name = "rustynes-test-harness" -version = "2.9.2" +version = "2.9.3" dependencies = [ "insta", "png", diff --git a/Cargo.toml b/Cargo.toml index d0aa39954..d0a06db1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ default-members = ["crates/rustynes-libretro"] # `release-auto.yml` reads the `## [X.Y.Z]` line for BOTH the release body # fallback and the title codename — so the date and quoted codename are load- # bearing, not decoration. -version = "2.9.2" +version = "2.9.3" edition = "2024" rust-version = "1.96" license = "GPL-3.0-or-later" diff --git a/OVERVIEW.md b/OVERVIEW.md index 6338e4cbc..2da69535e 100644 --- a/OVERVIEW.md +++ b/OVERVIEW.md @@ -2,7 +2,7 @@ **Document Version:** 2.1.0 **Last Updated:** 2026-09-25 -**Applies to:** RustyNES v2.9.2 +**Applies to:** RustyNES v2.9.3 --- @@ -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 one deliberate breaking release), and the v2.1.x → v2.3.x lines deepened accuracy, presentation, and analysis tooling. The current release is **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 one deliberate breaking release), and the v2.1.x → v2.3.x lines deepened accuracy, presentation, and analysis tooling. The current release is **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 **v2.9.2**. +> 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 **v2.9.3**. --- diff --git a/README.md b/README.md index 676b1b213..8c0ea985d 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

- Build Status License: GPL-3.0-or-later Version Rust: 1.96
+ Build Status License: GPL-3.0-or-later Version Rust: 1.96
AccuracyCoin nestest Try in browser
Platform

@@ -674,7 +674,21 @@ and the Material-for-MkDocs documentation handbook at ## Current Release -RustyNES's current release is **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). +RustyNES's current release is **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. 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). + +v2.9.3 prepares the mobile device run and closes old review threads. Every +dependency moved to its newest release (egui 0.36 with wgpu 30, which now +presents frames one refresh apart on the measured desktop). All 244 review +threads left unanswered on PRs #7-#97 were checked against current code; ten +still held and are fixed. Action 53 multicarts (mapper 28) are rebuilt to the +NESdev spec. The ROM header editor now writes only the fields you change, and +no longer saves mappers from 16 up as the wrong mapper. CI now runs the Android app's unit +tests (on `main` pushes and on pull requests that touch the app or its UniFFI +bridge) and compiles the iOS renderer on every pull request that changes code. The run +sheet for the device run is `docs/mobile-v2.9.3-run-sheet.md`, with a +partial emulator pre-run of the Android rows. **The device runs and the +SuperStation One board session move after v3.0.0**, by the maintainer's +decision (2026-09-29). v2.9.2 acts on a fifth, AI-written audit of both repositories: all 32 findings have a verdict and their evidence in @@ -736,7 +750,7 @@ the CHR-during-rendering divergence in the MiSTer sibling. | MiSTer bitstream | published for Cyclone V, timing closed at every corner | | MiSTer commercial rendering | six titles, one per supported board (NROM from rung 5, plus rung 7's MMC1, UxROM, CNROM, MMC3 and AxROM), each **byte-identical** to this emulator over all 61,440 pixels — [the montage](screenshots/mister-montage.png) | -**The MiSTer core has not run on hardware.** No DE10-Nano or SuperStation One has been attached — the bring-up runs v2.9.2's release-candidate bitstreams, ahead of the hardware-verified core at v3.0.0 ([ADR 0041](docs/adr/0041-hardware-release-is-v3.0.0.md)); a booting core, a synced display, audible sound and a working pad are not claimed. The palette, the video timing constants and the audio's absolute level are unverified by construction, because every gate in the co-simulation ladder compares something upstream of them. +**The MiSTer core has not run on hardware.** No DE10-Nano or SuperStation One has been attached — the bring-up, planned on v2.9.2's release-candidate bitstreams, moved after v3.0.0 by the maintainer's decision (2026-09-29; see [ADR 0041](docs/adr/0041-hardware-release-is-v3.0.0.md)'s amendment); a booting core, a synced display, audible sound and a working pad are not claimed. The palette, the video timing constants and the audio's absolute level are unverified by construction, because every gate in the co-simulation ladder compares something upstream of them. - **Download:** [GitHub Releases](https://github.com/doublegate/RustyNES/releases) — desktop binaries for Linux, macOS (aarch64) and Windows, plus the MiSTer `.rbf`. - **Try it in a browser:** @@ -773,10 +787,8 @@ closed on, at v2.6.5). The catalog is **149 rows** and the ladder **152 gates green, 0 failed, 1 expected failure** as of v2.6.21 — the expected one being a documented CHR-during-rendering divergence, registered so that it fails the suite if it ever starts passing. The console compiles to a Cyclone V bitstream with timing -closed at every corner, and that bitstream is published. **Rung 6 is open and closes -at v2.9.2**: a SuperStation One is in hand, and the bring-up waits until the audit -lines have landed their last RTL change, so that it measures the bitstream v3.0.0 -will ship — but **no hardware has run any bitstream yet**, +closed at every corner, and that bitstream is published. **Rung 6 is open**: a SuperStation One is in hand, and the maintainer moved the +bring-up after v3.0.0 (2026-09-29) — but **no hardware has run any bitstream yet**, so nothing about a booting core, a synced display, audible audio or a working pad is claimed. Every rung is labelled in [`docs/mister.md`](docs/mister.md) by whether it has an **independent** oracle — diff --git a/ROADMAP.md b/ROADMAP.md index 9c1e57600..162a299f7 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,14 +1,14 @@ # RustyNES Development Roadmap **Document Version:** 2.0.4 -**Last Updated:** 2026-09-28 -**Project Status:** v2.9.2 "Candidate" released — the full audit acted on, and the release-candidate pair, the third release of the v2.9.x line. **No hardware has run any bitstream**; the SuperStation One core is planned for v3.0.0. +**Last Updated:** 2026-09-29 +**Project Status:** v2.9.3 "Handset" released — the old review threads closed and the mobile run prepared, the fourth release of the v2.9.x line. **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 **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 **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 309308d81..f06e23e79 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,7 +2,7 @@ ## Supported Versions -The current release is **v2.9.2 "Candidate"**. Built on **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 **v2.9.3 "Handset"**. Built on **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 1dd2f0ace..74df18299 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -94,7 +94,7 @@ 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 **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 **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?** diff --git a/VERSION-PLAN.md b/VERSION-PLAN.md index 82fc220cb..2e33f04b7 100644 --- a/VERSION-PLAN.md +++ b/VERSION-PLAN.md @@ -1,6 +1,6 @@ # RustyNES Version Plan -**Current release: 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: 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/). @@ -64,9 +64,8 @@ released version; these are plans, and `to-dos/plans/` holds the detail. | Version | Scope | Plan | |---------|-------|------| -| v2.8.x | The **libretro and RTL audits**: FFI safety and build fixes in the libretro core; RTL correctness and robustness; the off-die SDRAM build; one seed sweep per build at the line's close | [`v2.8.x-libretro-rtl-audit-plan.md`](to-dos/plans/v2.8.x-libretro-rtl-audit-plan.md) | -| v2.9.x | **Re-audit, optimise, and the board**: the audits re-run against the fixed tree, final seed sweeps, the release-candidate `.rbf` pair, the bring-up on the SuperStation One, and (v2.9.3) the v2.7.4 mobile device checklist | [`v2.9.x-final-audit-and-hardware-plan.md`](to-dos/plans/v2.9.x-final-audit-and-hardware-plan.md) | -| v3.0.0 | **The SuperStation One core**: the first hardware-verified FPGA bitstream. On-die `.rbf` as the headline, off-die as a labelled secondary; a MAJOR bump under [ADR 0041](docs/adr/0041-hardware-release-is-v3.0.0.md) | [`v3.0.0-superstation-core-plan.md`](to-dos/plans/v3.0.0-superstation-core-plan.md) | +| v3.0.0 | **Scope under revision** (2026-09-29). Planned as the SuperStation One core, the first hardware-verified FPGA bitstream, a MAJOR bump under [ADR 0041](docs/adr/0041-hardware-release-is-v3.0.0.md); the maintainer then moved the board session and the mobile device runs after v3.0.0, so what it ships is open (ADR 0041's 2026-09-29 amendment). ADR 0042's API removals are scheduled for it either way | [`v3.0.0-superstation-core-plan.md`](to-dos/plans/v3.0.0-superstation-core-plan.md) (under revision) | +| after v3.0.0 | **The hardware work**: the SuperStation One board session on the prepared kit, the mobile device runs on `docs/mobile-v2.9.3-run-sheet.md`, and the fixes each produces | [`v2.9.x-final-audit-and-hardware-plan.md`](to-dos/plans/v2.9.x-final-audit-and-hardware-plan.md) (Strands A-F) | ### Post-1.0 release line (v1.1.0 → current) @@ -149,7 +148,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` | -| **v2.9.2 "Candidate"** (current) | The full audit acted on, and the release-candidate pair -- the third release of the v2.9.x line. A fifth, AI-written audit of both repositories (32 findings, `docs/audits/v2.9.2-full-audit-report.md`) triaged in its ledger: 17 FIXED (16 of its own plus AUD-02b), 2 CHANGED UNVERIFIED (the Swift halves of AUD-10/14), 4 REFUTED, 4 NOT A DEFECT, 1 split, 3 DECLINED, 2 DEFERRED (AUD-04 to v3.0.0, AUD-28 to the board). Rust: a netplay peer could size six per-frame tables (AUD-06, now bounded at +1024 frames); VRC2/4/6/7 save states dropped cartridge RAM (AUD-02), and the sweep written for it found the same on nine more boards -- now a standing test over all 174 mapper ids; FDS side-count wrap on 32-bit (AUD-01); unmapped reads now latch the internal data bus (AUD-03, the one emulation change); gamepad unplug, signaling ghost slots, HD-frame panic containment, libretro deinit memory maps and serialize sizing. Opposing-direction cancel (AUD-08/09/10) is default-on pending the maintainer. MiSTer: the /NMI edge detector ran on the DMA-stalled enable and lost an NMI raised inside a DMA (AUD-24; new gate `dmanmi074`, 156,031 of 357,820 cycles red, 0 green); refresh-loss and overrun gates; `build-fast` no longer rewrites the `.qsf`; `tools/stage_board_kit.sh`; oracle pin -> v2.9.1, 117 stems byte-identical. Seed sweep of eight seeds per build at one build date (260928; the first on-die attempt crossed midnight and was re-run whole), all sixteen closing; the pin stays at 2 (on-die +0.510 / +0.108 ns, off-die +0.390 / +0.081). Gates: `--features test-roms` 2,867 passed / 0 failed, AccuracyCoin 144/144, nestest 0-diff; co-simulation on-die 173 / 0 / 1 and off-die 174 / 0 / 1, each ONE frozen-worktree run, nothing skipped. **No hardware has run any bitstream.** Plan: `to-dos/plans/v2.9.2-candidate-plan.md` | +| **v2.9.3 "Handset"** (current) | The old review threads closed and the mobile run prepared -- the fourth release of the v2.9.x line. #570 moved every dependency to its newest release (egui 0.36 with wgpu 30 and a vendored `egui-winit` for the web build, rcheevos 12.5.0 with platform-sized `time_t`, UniFFI 0.32.2, Gradle 9.8.0); under wgpu 30 presents land one refresh apart (median 5.5 -> 8.4 ms, p95 15.6 -> 10.4-11.4 ms, two A/B pairs). All 244 review threads left unanswered on PRs #7-#97 were checked against current code: 234 answered, ten fixed -- mapper 28 rebuilt to the NESdev table, the header editor writing only changed fields via the new `serialize_header_preserving` (`serialize_header` deprecated, ADR 0042 amendment) and no longer saving mappers >= 16 wrong, NSF `$5FF8-$5FFF` open bus, the Bisqwit edge window, a NaN gain guard, a palette load that no longer erases the setting or reads without bound. CI now runs the Android JVM unit tests and type-checks the iOS renderer. The mobile run sheet (`docs/mobile-v2.9.3-run-sheet.md`) carries an emulator pre-run; **the device runs and the SuperStation One board session move after v3.0.0 by the maintainer's decision (2026-09-29)**, which reopens what v3.0.0 ships (ADR 0041 amendment). `cargo test --workspace --features test-roms` 2,887 passed, 0 failed; AccuracyCoin 144/144, nestest 0-diff. Plan: `to-dos/plans/v2.9.3-handset-plan.md` | +| **v2.9.2 "Candidate"** | The full audit acted on, and the release-candidate pair -- the third release of the v2.9.x line. A fifth, AI-written audit of both repositories (32 findings, `docs/audits/v2.9.2-full-audit-report.md`) triaged in its ledger: 17 FIXED (16 of its own plus AUD-02b), 2 CHANGED UNVERIFIED (the Swift halves of AUD-10/14), 4 REFUTED, 4 NOT A DEFECT, 1 split, 3 DECLINED, 2 DEFERRED (AUD-04 to v3.0.0, AUD-28 to the board). Rust: a netplay peer could size six per-frame tables (AUD-06, now bounded at +1024 frames); VRC2/4/6/7 save states dropped cartridge RAM (AUD-02), and the sweep written for it found the same on nine more boards -- now a standing test over all 174 mapper ids; FDS side-count wrap on 32-bit (AUD-01); unmapped reads now latch the internal data bus (AUD-03, the one emulation change); gamepad unplug, signaling ghost slots, HD-frame panic containment, libretro deinit memory maps and serialize sizing. Opposing-direction cancel (AUD-08/09/10) is default-on pending the maintainer. MiSTer: the /NMI edge detector ran on the DMA-stalled enable and lost an NMI raised inside a DMA (AUD-24; new gate `dmanmi074`, 156,031 of 357,820 cycles red, 0 green); refresh-loss and overrun gates; `build-fast` no longer rewrites the `.qsf`; `tools/stage_board_kit.sh`; oracle pin -> v2.9.1, 117 stems byte-identical. Seed sweep of eight seeds per build at one build date (260928; the first on-die attempt crossed midnight and was re-run whole), all sixteen closing; the pin stays at 2 (on-die +0.510 / +0.108 ns, off-die +0.390 / +0.081). Gates: `--features test-roms` 2,867 passed / 0 failed, AccuracyCoin 144/144, nestest 0-diff; co-simulation on-die 173 / 0 / 1 and off-die 174 / 0 / 1, each ONE frozen-worktree run, nothing skipped. **No hardware has run any bitstream.** Plan: `to-dos/plans/v2.9.2-candidate-plan.md` | | **v2.9.1 "Hone"** | What the optimisation bars measure, and what clears them -- the second release of the v2.9.x line. `scripts/perf/ab_check.sh` built both sides into one cargo target directory, so the candidate ran the reference binary: proven by a candidate that did its work twice, which the old script called "no change" and the fixed one +146%. Both sides now build into fresh directories. NL-09 adopted: `VsDualSystem::snapshot_into` (libretro serialize of a two-screen Vs. cabinet) −88.9% / −88.5% on two runs; the pooled restore buffer measured no change and was reverted. Re-measured with the fixed tool, two runs each: 12 rebuilt rejections, 3 verdicts overturned (the dead NMI detector's per-dot call, −4.1% to −4.7% on palette workloads, removed at v3.0.0 per ADR 0042 by maintainer decision; the palette-mirror and unmapped-read ceilings are not zero), and 3 correct candidates built under those ceilings (a `prg_window_can_float` capability, a palette fold table, a branchless fold), all rejected because `nestest_fast` is slower in both runs. MiSTer core: NR-08 puts CHR in SDRAM bank 1 (byte `$1000000`; `$800000` was a word address and measured nothing): single-bank precharges 132,235 -> 24,880 on `ppurender`, worst CHR 20 -> 18 of 22, worst PRG unchanged at 22 of 24 (refresh bounds it); S2, a scheduled arbiter, decided for after v3.0.0. The ladder runs the instruction battery itself; `check_timing.py` reads the SDRAM period from the constraints and checks the 4:1 clock ratio; the NR-07 PLL-reset warnings are pinned; `tb/ORACLE_COMMIT` moves to v2.9.0. Seed sweep of eight seeds per build, all sixteen closing; pin 5 -> 2 (on-die +0.542 / +0.115 ns, both the best of eight; off-die +0.193 / +0.080, SDRAM read +0.447 / +1.184). Two clean compiles per build byte-identical. Gates: `--features test-roms` 2,812 passed / 0 failed with AccuracyCoin 144/144 and nestest; co-simulation on-die 171 / 0 / 1 and off-die 172 / 0 / 1, each ONE clean-checkout run with the battery inside it. **No hardware has run any bitstream.** | | **v2.9.0 "Survey"** | Every audit re-checked, and the SuperStation One surveyed -- the first release of the v2.9.x line. All four scopes re-run by Claude subagents (maintainer decision): 139 ledger rows re-verified, 0 regressed, 39 new findings (core 8, frontend 9, libretro 10, RTL 12), each fixed red-first with a caught mutant or dispositioned in its ledger; reports verbatim in `docs/audits/v2.9.0-*-reaudit.md`. Found by the release itself: a Power Cycle erased the battery save (`Bus::power_cycle` cleared battery RAM, and the v2.7.3 writer persisted the zeros), and TAStudio exported a mid-game anchor as a power-on movie. Maintainer decisions implemented: power-on movies start from cleared save RAM; TAStudio anchors on power-on; Lua pattern matching is a metered Rust matcher derived from Lua 5.4 `lstrlib.c` (MIT, attributed); ADR 0042 (v3.0.0 removes the v2.7.5 deprecations and the dead NMI detector, and renames `LockstepBus`); the off-die MiSTer build probes its SDRAM when the HPS has not reported it (`bootcore=`); the mobile device checklist moves to v2.9.3. MiSTer core: the off-die loader lost 164 of 512 bytes silently and now applies backpressure; the SDRAM DQ hold constraint sat a period early (real margin +1.181 ns, reported +12.822); NES 2.0 headers read correctly and oversize images refused; the SDRAM gates now run in the ladder and CI. Gates: full `--features test-roms` 2,811 passed / 0 failed with AccuracyCoin 144/144 and nestest; co-simulation on-die 171 / 0 / 1 and off-die 172 / 0 / 1 (each a clean-checkout ladder plus the 16-gate instruction battery run separately in the same checkout, because the ladder's `ORACLE` pointed at a pinned clone without those ROMs); Quartus seed 5 on-die +0.122 / +0.080 ns, off-die +0.141 / +0.119 ns (both recompiled after review changed the SDRAM probe). **No hardware has run any bitstream.** | | **v2.8.4 "Tether"** | The MiSTer core's SDRAM build, made trustworthy -- the fifth and last release of the v2.8.x line; the emulator does not change. The off-die build is v3.0.0's secondary bitstream. R-3.4a / R-3.4b: the SDRAM model now registers CKE and masks read data as the part does, each shown red against the old controller (a command on the edge CKE rose; every CL3 read `0x0000`); the controller raises CKE with a NOP first and holds DQM low for reads. R-3.4c / R-3.4d: the arbiter latches its byte lane at grant and `wr_busy` covers the strobe. R-4.6: provisional SDRAM pin constraints from the AS4C32M16SB datasheet, and they exposed the release's real finding: the model presented read data one edge later than the datasheet (Figure 20) and the controller sampled to match, so every off-die read would have been wrong on hardware; the first off-die compile failed the DQ capture by 11.185 ns. Model and CAS wait corrected (reads a clock faster), the capture edge constrained; DQ capture now +0.439 ns setup at zero board delay, which leaves the SDRAM clock phase to v2.9.2. R-5.2 NOT A DEFECT (one PLL, the crossing is analysed: +2.022 / +0.158 ns). Line close: both builds swept, every seed closes, pin 4 -> 5 (on-die +0.414 / +0.078, off-die +0.270 / +0.081). The ladder runs from a clean checkout at last (v2.8.3's claim was not true; `ppumin` is a generator program, `ppuspr0` is committed): 165 / 0 / 1 on-die, 166 / 0 / 1 off-die, nothing skipped. **No hardware has run any bitstream.** Plan: `to-dos/plans/v2.8.4-tether-plan.md` | diff --git a/crates/rustynes-cosim/Cargo.lock b/crates/rustynes-cosim/Cargo.lock index 54fe4ad95..e85747bbb 100644 --- a/crates/rustynes-cosim/Cargo.lock +++ b/crates/rustynes-cosim/Cargo.lock @@ -98,7 +98,7 @@ dependencies = [ [[package]] name = "rustynes-apu" -version = "2.9.2" +version = "2.9.3" dependencies = [ "bitflags", "libm", @@ -107,7 +107,7 @@ dependencies = [ [[package]] name = "rustynes-core" -version = "2.9.2" +version = "2.9.3" dependencies = [ "bitflags", "lz4_flex", @@ -121,7 +121,7 @@ dependencies = [ [[package]] name = "rustynes-cosim" -version = "2.9.2" +version = "2.9.3" dependencies = [ "rustynes-core", "sha2", @@ -129,7 +129,7 @@ dependencies = [ [[package]] name = "rustynes-cpu" -version = "2.9.2" +version = "2.9.3" dependencies = [ "bitflags", "thiserror", @@ -137,7 +137,7 @@ dependencies = [ [[package]] name = "rustynes-mappers" -version = "2.9.2" +version = "2.9.3" dependencies = [ "bitflags", "rustynes-apu", @@ -146,7 +146,7 @@ dependencies = [ [[package]] name = "rustynes-ppu" -version = "2.9.2" +version = "2.9.3" dependencies = [ "bitflags", "libm", diff --git a/crates/rustynes-cosim/Cargo.toml b/crates/rustynes-cosim/Cargo.toml index 494fcbde7..60f121fd0 100644 --- a/crates/rustynes-cosim/Cargo.toml +++ b/crates/rustynes-cosim/Cargo.toml @@ -8,7 +8,7 @@ description = "RustyNES as a co-simulation oracle for an external HDL device-und # The duplication is PINNED, not merely noticed: `cosim_manifest_audit.rs` in # `rustynes-test-harness` asserts these values still match the workspace's, so # drift fails a test instead of accumulating quietly. -version = "2.9.2" +version = "2.9.3" edition = "2024" rust-version = "1.96" license = "GPL-3.0-or-later" diff --git a/crates/rustynes-libretro/rustynes_libretro.info b/crates/rustynes-libretro/rustynes_libretro.info index 35e6b70ed..36fd31651 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 = "v2.9.2" +display_version = "v2.9.3" categories = "Emulator" # Hardware Information diff --git a/docs/STATUS.md b/docs/STATUS.md index 173f6c92d..b81c4c1fc 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -1,6 +1,6 @@ # RustyNES — Project Status Matrix -> **Current release: v2.9.2** (2026-09-28) — **"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"** (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 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: v2.9.3** (2026-09-29) — **"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. 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 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. diff --git a/docs/adr/0041-hardware-release-is-v3.0.0.md b/docs/adr/0041-hardware-release-is-v3.0.0.md index 5b1c5eb81..ab33029f7 100644 --- a/docs/adr/0041-hardware-release-is-v3.0.0.md +++ b/docs/adr/0041-hardware-release-is-v3.0.0.md @@ -99,3 +99,23 @@ hardware core. this build targets the DE10-Nano's 5CSEBA6U23. Stock MiSTer cores are reported running on the SS1 unmodified, so the build is expected to load, but that is confirmed only at v2.9.2's Strand A, on the stock core, before ours is loaded. + +## Amendment (2026-09-29, v2.9.3): the hardware work moves after v3.0.0 + +The maintainer moved three things after v3.0.0 (2026-09-29): the mobile device +runs (`docs/mobile-v2.9.3-run-sheet.md`), the SuperStation One board session +(Strands A-F of the v2.9.x plan, on v2.9.2's bitstreams), and the fixes each of +them produces. v2.9.3 "Handset" shipped without its device run on that basis. + +This conflicts with the Decision above, which defines v3.0.0 as the release in +which a bitstream is **verified on real hardware**, and with the v3.0.0 plan's +gate (every bring-up row PASS). The two cannot both stand, and this amendment +does not choose between them. **What v3.0.0 now ships, and under which MAJOR +trigger, is open for the maintainer.** ADR 0042's API break alone would qualify +it as MAJOR, so the version number does not force either answer. Until it is +decided: + +- the "no hardware has run any bitstream" anchors stay true and unflipped; +- the v3.0.0 plan's gate is marked as under revision, not deleted; +- the board and device work keep their prepared material (the board kit, the + run sheet, the emulator pre-run) for whenever they run. diff --git a/docs/mobile-v2.9.3-run-sheet.md b/docs/mobile-v2.9.3-run-sheet.md index 6e6bfa6e5..2a705a0e3 100644 --- a/docs/mobile-v2.9.3-run-sheet.md +++ b/docs/mobile-v2.9.3-run-sheet.md @@ -1,5 +1,11 @@ # v2.9.3 "Handset" — the mobile run sheet +> **The device run moved after v3.0.0** (maintainer, 2026-09-29). v2.9.3 +> shipped with this sheet prepared and its emulator column filled; the device +> columns are still empty. When the run happens, rebuild the APKs from that +> day's `main` and record the new checksums here first, as "The build" below +> requires. + One sheet for the maintainer's device run. It carries every row of the [v2.7.4 checklist](mobile-v2.7.4-device-checklist.md) (A1-A12, I1-I15) and its v2.9.2 additions (A13-A15, I16-I19) unchanged, plus rows for what the diff --git a/to-dos/ROADMAP.md b/to-dos/ROADMAP.md index 4e3bbfec1..c544df79a 100644 --- a/to-dos/ROADMAP.md +++ b/to-dos/ROADMAP.md @@ -57,13 +57,14 @@ v2.8.0 → v0.9.7; the synthesis itself = **v1.0.0**. ## Status -- **Current release:** **RustyNES 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.** +- **Current release:** **RustyNES 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.** +- **Re-plan (maintainer, 2026-09-29):** the mobile device runs (`docs/mobile-v2.9.3-run-sheet.md`) and the SuperStation One board session, with the fixes each produces, move after v3.0.0. [ADR 0041](../docs/adr/0041-hardware-release-is-v3.0.0.md) defined v3.0.0 as the hardware-verified core, so its 2026-09-29 amendment records the move and leaves what v3.0.0 now ships to be decided. - **Programme after v2.6.23 — the line to v3.0.0, the SuperStation One core** ([ADR 0041](../docs/adr/0041-hardware-release-is-v3.0.0.md), 2026-09-22). The first hardware-verified FPGA core is a new deliverable class, so it ships as a MAJOR version, and the board session moves from v2.7.0 to **v2.9.2** so that it measures the bitstream the audit lines produce rather than one they are about to replace. Three minor lines come first, each acting on AI-written audits in [`docs/audits/`](../docs/audits/README.md), every finding triaged against the code, pinned by a test that fails first, and gated: **v2.7.x** the core and frontend audits ([plan](plans/v2.7.x-core-frontend-audit-plan.md)); **v2.8.x** the libretro and RTL audits, and the off-die SDRAM build ([plan](plans/v2.8.x-libretro-rtl-audit-plan.md)); **v2.9.x** the re-audit, final seed sweeps, the release-candidate `.rbf` pair and the bring-up on the board ([plan](plans/v2.9.x-final-audit-and-hardware-plan.md)); **v3.0.0** the on-die `.rbf` as the headline, with the off-die `.rbf` as a labelled secondary ([plan](plans/v3.0.0-superstation-core-plan.md)). Features the older plans deferred "to v2.8+" are now deferred **past v3.0.0**, so each version number means one thing. - **Shipped, inside v2.4.1 — v2.4.0 "Concordance".** It merged to `main` and was never tagged, because the workspace version never sat at 2.4.0 on any commit; v2.4.1 carries it. There is deliberately no `v2.4.0` tag. Its scope was: A concordance is an index of where every term actually occurs, and the release is scoped as one: reconcile what the project says about itself with what is true outside it. Four items, each traceable to a recorded deferral rather than newly invented — **(A)** the **owed upstream libretro sync** (`libretro-super` + `libretro/docs`), the one carried obligation with an outside deadline; **(B)** a core-side **timeline generation counter** replacing the last-seen-`cycle()` heuristic for stale telemetry (it covers a restore to a *later* state, which the heuristic cannot), deliberately **not** serialized, so it must land with its consumers and be AccuracyCoin-**verified**; **(C)** a **shared atomic-write helper**, lifting v2.3.9's seven properties out of `config.rs` and giving the Windows tail a real implementation rather than a portable spine; and **(D)** `skip_serializing_if` on `hd_packs` / `shader_presets`, which carry the same false byte-identity claim v2.3.9 corrected in prose only. Explicitly out of scope, and recorded as decisions rather than oversights: the remaining RAM Atlas exports (a cheat is a **write**, so it needs a locked-session predicate the watch export correctly does without), RAM Atlas per-game persistence (a restored verdict without its evidence is a claim that cannot be checked — this panel's whole argument in reverse), APU workstreams **D2 and D4** (unmeasured on purpose; their prior is a null, not an unknown), a CHANGELOG gate (**measured and rejected** — 62% false positives against the project's own history), and any store launch. See [`plans/v2.4.0-concordance-plan.md`](plans/v2.4.0-concordance-plan.md). - **Programme after v2.4.0 — the v2.4.1 → v2.5.0 "Fabric" line, and the v2.6–v2.9 programme behind it** *(since 2026-09-22 the programme ends at v3.0.0; ADR 0041)*. An **independently-written NES core in SystemVerilog for MiSTer FPGA and the Retro Remake SuperStation One, verified against RustyNES as an oracle.** Not a port, and it cannot be one: a MiSTer core is SystemVerilog compiled by Quartus 17.0.2 into a Cyclone V bitstream. The reference firewall therefore extends to HDL — `NES_MiSTer` and `fpganes` `rtl/` are **strict black boxes**, instantiable as opaque modules to compare *outputs*, never readable as source. **v2.5.0 is scoped to "the 6502 rung closes"** — the co-simulation harness plus a cycle-exact 6502, gated, **as planned**, on nestest 0-diff and per-cycle bus equality — of which **per-cycle bus equality was achieved and nestest 0-diff was not**: it stops at a `$2002` read where *both sides address it* and only the data differs, because the DUT has no PPU. That and the 5 M-cycle window are **reclassified as rung-3 acceptance criteria** rather than carried as v2.5.0 debt — because the arithmetic does not support more: a from-scratch cycle-accurate NES core is **7–13 months FTE** against a two-to-four-week window at demonstrated cadence. PPU, APU and MiSTer integration are **v2.6–v2.9**; stating that now is better than discovering it at v2.4.6. The design is **replay, not lockstep** (the determinism contract makes a pre-recorded trace exactly the trace a lockstep run would produce, and `Nes` has no per-cycle step to lockstep *with*), **no DPI-C** (it would put `` `ifdef SIMULATION `` guards into RTL that must also pass Quartus — the exact construct that lets a simulated netlist drift from the synthesised one), and **hash first, capture on divergence** (a 4200-frame AccuracyCoin run is ~7.5 GB of per-cycle CSV; 4096-cycle hash checkpoints are ~480 KB). **Two risks are accepted in writing:** the core may be **declined as a duplicate** — `NES_MiSTer` already scores 121/125 on AccuracyCoin, and *real Famicom AV hardware also scores ~121/125*, so there is no published accuracy headroom; and **the oracle can be wrong**, since 141/141 is not "matches silicon", so every rung is labelled by whether it has an **independent** oracle. Retro Remake is a planned fallback home, not a contingency. See ADR 0037, `docs/mister.md`, and [`plans/v2.5.0-fabric-plan.md`](plans/v2.5.0-fabric-plan.md). - **Programme after v2.5.0 — the v2.5.1 → v2.7.0 line: the rest of the console, and a contributable package.** The Fabric line is delivered and the 6502 rung is closed; this line builds the PPU, APU, mappers and MiSTer integration, and takes the core to a state worth submitting to MiSTer-devel. **Maintainer decisions, 2026-08-23:** hardware is **both boards eventually** — a DE10-Nano **plus the SDRAM add-on** (mandatory: the NES reads cartridge ROM directly and the onboard DDR3 is too slow) and a SuperStation One (128 MB integrated), with **one `.rbf` booting both** turning "SS1 runs MiSTer cores unmodified" from an inherited claim into a measured one; mappers are **the top six** — NROM, MMC1, UxROM, CNROM, MMC3, AxROM, ~90% of the licensed library by title count, explicitly **not** FDS, expansion audio, or the remaining ~168 families; and v2.7.0 is **scoped to what genuinely fits**, with the arithmetic stated up front (**rung 3 8–16 wk · rung 4 4–8 wk · rung 5 2–4 wk + a 4–12 wk tail · rung 6 2–4 wk · rung 7 4–8 wk = 20–40 weeks FTE** before the AccuracyCoin tail, across twenty release slots — **milestones, not dates**). **Rung 6 comes before rung 7 deliberately**: NROM at 327 Kb fits on-chip, so hardware bring-up needs no memory controller, and getting a board in the loop before writing the SDRAM controller de-risks the second largest technical item. Two v2.5.0 gates — **nestest 0-diff and the 5 M-cycle window** — are not carried as debt but reclassified as **rung-3 acceptance criteria**: both stop at a `$2002` read where *both sides address it* and only the data differs, because the DUT has no PPU. The contribution requirements were **fetched from the MiSTer-devel wiki rather than recalled**, and one line of it is the whole case for this programme: on AI-generated code the project asks for *"a minimum reasonable bar for readability and… evidence of quality and accuracy testing"* — the co-simulation apparatus **is** that evidence, and no incumbent core can show its equivalent. See [`plans/v2.7.0-mister-core-plan.md`](plans/v2.7.0-mister-core-plan.md), [`mister/`](mister/), and the four dated research files in `ref-docs/`. *Superseded 2026-09-22 by ADR 0041: the hardware-verified core and the contribution package are v3.0.0.* - **Historical detail — v2.2.4** (2026-07-24) — a **libretro / RetroArch distribution** cut whose purpose is that the RustyNES core **builds and installs cleanly through the Libretro buildbot** () for in-RetroArch use. **Zero emulation-core changes** — the deterministic `#![no_std]` chip stack, save-state / TAS / netplay formats, and every golden vector are byte-identical to v2.2.3, so **AccuracyCoin holds 141/141 (100.00%)**, nestest 0-diff, by construction. The work is a libretro-completeness audit + metadata correction: the core is confirmed to inherit every v2.2.3 change automatically (the fast-dot-path default, the `PPU_SNAPSHOT_VERSION` 8 / APU v4 save-state schema handled transparently by the dynamic `snapshot_core_into` sizing, the `Mapper::mix_audio` i32 widening, the Zapper model, and the `mNNN_` mapper rename), and both buildbot cross-ABIs the GitHub gate models — `x86_64-pc-windows-gnu` and `aarch64-linux-android` — build clean. `rustynes_libretro.info` (the metadata RetroArch's core downloader reads) is corrected: **`disk_control` `false` → `true`** (the FDS multi-side Disk Control interface has been wired since the buildbot recipe landed, but was advertised as absent — the real fix), `display_version` `v1.0.0` → `v2.2.4`, and the mapper count `168` → `172`. Also: the reviewer-tooling standardization onto the shared Antigravity template rides along (`scripts/agy-review.sh` + workflow). Documented libretro follow-up: **core options** (region / overscan / palette / accuracy toggles) remain unexposed (`core_options = "false"` is accurate, not stale) — a deliberate future enhancement, not a v2.2.4 gap. See `docs/STATUS.md` (single source of truth) + `CHANGELOG.md` `[2.2.4]` + `docs/libretro/`. -- **Release line since v2.1.0:** the v2.1.x **"Fathom"** accuracy line (v2.1.0 → v2.1.10) → **v2.2.0 "Capstone"** (the milestone cut closing the "deepen the existing project" run) → **v2.2.1** (housekeeping) → **v2.2.2 "Conduit"** (build / distribution / CI-integrity) → **v2.2.3 "Datum"** (performance appraisal + the last two Holy Mapperel residuals closed) → **v2.2.4 "Cartridge"** (the libretro/RetroArch distribution cut) → **v2.2.5 "Colophon"** → **v2.2.6 "Almanac"** → **v2.2.7 "Timbre II"** → **v2.2.8 "Aperture II"** → **v2.2.9 "Studio II"** → **v2.3.0 "Datum II"** → **v2.3.1 "Plumb Line"** → **v2.3.2 "Lucid"** → **v2.3.3 "Cadence"** → **v2.3.4 "Ledger"** → **v2.3.5 "Manifest"** → **v2.3.6 "Sounding"** → **v2.3.7 "Overtone"** → **v2.3.8 "Parallax"** → **v2.3.9 "Crucible"** → the **v2.4.x "Fabric"** co-simulation line (**v2.4.1 "Fabric"** → **v2.4.2 "Cairn"** → **v2.4.3 "Touchstone"** → **v2.4.4 "Ignition"** → **v2.4.5 "Compass"** → **v2.4.6 "Abacus"** → **v2.4.7 "Keystone"** → **v2.4.8 "Palimpsest"** → **v2.4.9 "Plumbline II"** → **v2.5.0 "Rungwork"** → **v2.5.1 "Retrace"** → **v2.5.2 "Dormant"** → **v2.5.3 "Hysteresis"** → **v2.5.4 "Escapement"** → **v2.5.5 "Raster"** → **v2.5.6 "Vestige"** → **v2.5.7 "Collimation"** → **v2.5.8 "Blanking"** → **v2.5.9 "Overture"** → **v2.6.0 "Assay"** → **v2.6.1 "Interleave"** → **v2.6.2 "Witness"** → **v2.6.3 "Mainspring"** → **v2.6.4 "Rubric"** → **v2.6.5 "Muster"** → **v2.6.6 "Chassis"** → **v2.6.7 "Detent"** (the bitstream becomes a published, reproducible release artifact and a one-cycle disagreement is attributed rather than fitted away) → **v2.6.8 "Arrears"** (the gates the previous release fixed and never widened: four of six denied checkpoint streams had been passing unnoticed, three of them not run by the suite at all, and nestest widens 19x and gains the nine-field comparison that closes the gate's stated `nmi_line` blind spot) → **v2.6.9 "Abeyance"** (an exclusion hides improvement as well as regression -- both denied co-simulation streams close, and the larger one was a defect in the HARNESS rather than the console, carried for seven releases behind the phrase "by design") → **v2.6.10 "Inference"** (the cartridge meets the synthesiser, and simulation could not have asked the question -- rung 7's five boards had never been through Quartus, and `chr` written from two separate `always_ff` blocks could not infer as an M10K, so 128 KB stayed in flip-flops: 1,048,576 registers against roughly 166,000. The fix is behaviourally invisible -- 141 gates unchanged -- and the release carries the bitstream the previous one could not produce) → **v2.6.11 "Exposure"** (a picture is a gate the ladder did not have -- all 141 co-simulation gates green and two of six commercial games rendering wrong, because only THREE of those gates compare a framebuffer and all three ship CHR-ROM, so a CHR-RAM write taking the shared-pin composite address built for fetches was reached 9,600 times and compared never)) -> **v2.6.12 "Groundwork"** (the bitstream was an NROM-only console -- `emu.sv` left `cart_mapper`, `cart_prg_16k_banks` and `cart_chr_8k_banks` unconnected, so Quartus tied all three to GND and the fitted cartridge was mapper 0 with 8 KiB of PRG and 8 KiB of CHR against a declared 256 and 128; no gate could see it because `emu.sv` is not in the testbench file list, so all 142 gates exercised a correctly-configured cartridge, and Quartus said so three times in messages that cite an instance path rather than a file) -> **v2.6.13 "Slack"** (the cartridge outgrows the die -- an SDR SDRAM controller, a behavioural part model, a four-way arbiter and a console bridge written from the AS4C32M16SB-7 datasheet, and three consumers measured to three different budgets rather than the one figure the previous step assumed; the PPUDATA port leaves the shared bus through a handshake and that fix shipped a defect only a banked cartridge could see, the request carrying the raw PPU address where the mapper's translation was needed, so every banked-CHR board read bank zero; off the die the console passes 142 of 142 and it still ships on the die, because an off-die core cannot run without the add-on) -> **v2.6.14 "Docket"** (the submission checklist becomes auditable -- 30 boxes, 16 unticked and fourteen of those saying nothing about why, so an unticked box could not be told apart from work outstanding, work blocked elsewhere, and work already done and never ticked; the third case occurred five times. One box asked `docs/provenance.md` to state that no NES core was ever opened, which that document's own Do-not-self-certify section forbids, so it could only have been ticked by writing the sentence the provenance rules exist to prevent. Re-measuring the ticked half found two claims that had expired, the task board four delivered items never ticked and an SDRAM precondition the previous release did not follow, and its claim that MMC1 was unimplemented is retracted) → **v2.6.15 "Warrant"** (the claims the submission will make become checkable, and the instrument pays the oracle back — the `.rbf` name this core shipped would have distributed NOTHING, because `Distribution_MiSTer`'s builder skips any file whose stem does not end in `_` plus eight digits, so an accepted core would appear in the Cores table and ship nothing with no error anywhere; two of the four R1/R2 residuals ADR 0002 closed turn out never to have been IRQ-timing residuals, resting on an assertion blargg WITHDREW in the successor ROM; `sys/` verbatim, the `.qsf`'s single seed table and the bitstream name all become checks instead of claims; the nine rung-1 gates run in CI against a PINNED oracle commit, so the accuracy evidence stops being a document describing a check a reader cannot run; `cpu_interrupts_v2` lands on the DUT as the first INDEPENDENT interrupt oracle, five of five; and `T-ORACLE-001`'s opening claim is retracted — RustyNES does clock the MMC3 counter on the pre-render line, which `2-details` sub-test 8 has asserted all along) → **v2.6.16 "Interlock"** (the arbiter's numbers describe a stimulus, not the console: the gate's worst case issues a CHR and a PRG request on the same cycle and the running console never does — zero coincidences and a minimum separation of two over fifteen million SDRAM cycles — so both published figures are wrong about it in opposite directions, the CPU MEETING its deadline at 24 against 24 on all 240,303 requests where the gate said it missed by four, and the fetch reaching 23 against a derived 22 that a `CHR_LAT` sweep shows understates the real tolerance by at least nine cycles; the slot-scheduled arbiter this version was scoped around is therefore NOT built, and is still worth building because zero margin means one added cycle anywhere breaks the CPU) → **v2.6.17 "Terminus"** (a write lands where the cycle ENDS, and this core does not move to meet it. The AccuracyCoin and TriCNES oracles re-sync to upstream, and the battery grows 141 → 144 assigned tests across two new pages that RE-HOME eleven existing PPU tests, so a re-sync is not an append and a suite-keyed baseline has to be regenerated rather than extended. `OAM2Address` becomes a live counter through sprite fetch and the frozen fetch reads index 0 for every byte, closing two of the three new tests and carrying an approved SAVE-STATE EPOCH — `PPU_SNAPSHOT_VERSION` 8 → 9, so pre-v9 `.rns` states no longer load. The third names the dots its two `$2001` writes land on, which is how the core came to be measured TWO DOTS EARLY on both: a 6502 commits a write at phi2, the last of a CPU cycle's three PPU dots, and this core applies PPU register writes at M2-low, the first. The move was then MADE, measured against a first-difference control captured beforehand, and NOT ADOPTED — the write alone reads 141/144 and breaks the independent `ppu_vbl_nmi/10-even_odd_timing`, and the best combination found reads 142/144 against the 143/144 that ships, for a save-state epoch and six re-baselined goldens. `mask_for_skip_check` is PROVEN to be a compensation for the placement — under phi2 it needs one delay stage rather than two — and two of its three dependants are still un-re-derived, so adopting the mechanism now would replace a documented compensation with an undocumented one. `Misaligned OAM2 Address` was also found to be passing via TWO CANCELLING ERRORS, both since fixed independently. Ships one core fix nothing in the corpus could see — the misaligned-OAM `+4 & $FC` realignment, reached 114 times per battery run out of 56,953,944 out-of-range branches and pinned by a targeted test because no ROM's verdict moves — plus `terminus_control.rs` as a standing first-difference gate. The follow-up version the write-placement work had been scoped into is FOLDED here: that split existed because Terminus was to be a scheduler change with its own ADR and a full re-baseline, and the refutation means it is not — so v2.6.17 carries both). v2.6.18 "Errata" closes the last AccuracyCoin entry on top of it, and v2.6.19 "Accession" carried both into the FPGA core, v2.6.20 "Telltale" made the OAM2 counter observable, v2.6.21 "Steward" gave the core battery saves and gated CHR writes during rendering, v2.6.22 "Rigging" built the instruments a board would need before the board existed, v2.6.23 "Pulse" closed the CHR-during-rendering divergence by making the `$2007` access pulse the rendering pipeline's load instead of computing its own, and v2.7.0 "Palisade", the first of the ADR 0041 audit line, made a corrupt save state fail at restore rather than one tick later, stopped pulse 1 muting on the `$4001 = $08` sweep idiom, and gave the save-state fuzz target the reach to find five defects the audit had not, v2.7.1 "Keepsake" stopped six cartridge boards writing an empty battery save and moved every user file the frontend writes onto the atomic writer, v2.7.2 "Bankroll" gave MMC1, MMC5 and Namco 163 the banks their boards have and made `$6000-$7FFF` read open bus on the 205 board variants with nothing there, v2.7.3 "Hearth" made the desktop keep battery saves, closed three ways around the Lua sandbox, gated script HTTP destinations, and brought audio back after a device change, v2.7.4 "Pocket" kept the Android and iOS apps alive through an internal error, gave both battery saves, paused them and released audio when they leave the screen, and made their saves atomic, v2.7.5 "Tally" closed the core audit's twelve performance proposals (eleven by measurement, one adopted: the audio buffer keeps its capacity), deprecated 18 dead bus methods and `ApuBus`, and closed the core and frontend ledgers, v2.7.6 "Recount" measured alone the six deletions v2.7.5 had bounded only together (five zero; the sixth, never reached by the benchmarks before, bounds at 0.2%), turned three redundant fast-path stores into an assertion, and shipped the contributed libretro buildbot fixes and targets, v2.8.0 "Bulkhead" opened the v2.8.x libretro + RTL audit line: the libretro core unwinds and contains a panic at its own boundary, keeps save states the right size with a Zapper, withdraws its memory maps on unload, loads through the standard `retro_game_info` via a vendored binding, and the save state carries the internal data bus, v2.8.1 "Gasket" closed the libretro audit: Four Score, four described ports (the list now NULL-terminated, where RetroArch had read past it), the Zapper unplugged, unclipped expansion audio, UNIF, and a Makefile that honours its callers, v2.8.2 "Solder" corrected the MiSTer core's on-die RTL against the oracle and the wiki (MMC3 acknowledge, SNROM PRG-RAM, the triangle and noise reload drop, the `$2002` latch) and found the audit's MMC1 finding inverted: the emulator was the one ignoring a reset, and v2.8.3 "Rivet" released every reset on the clock that uses it, cut the CPU by about 4% with two exact rewrites the fit report confirmed, and made the ladder run from a fresh checkout (164 of its 165 gates, a claim v2.8.4 found overstated), and v2.8.4 "Tether" closed the v2.8.x line: it found every off-die read would have been sampled a clock late on hardware (the SDRAM model and controller shared the error, and only the new SDRAM timing constraints could see it) and fixed both, pinned both builds at fitter seed 5 after a sweep of each, and made the ladder run all 165 gates from a clean checkout, and v2.9.0 "Survey" opened the v2.9.x line by re-running all four audits (139 rows re-verified, none regressed, 39 new findings fixed or dispositioned), including a Power Cycle that erased the battery save and an off-die MiSTer build that hung under `bootcore=`, v2.9.1 "Hone" found that the A/B tool had been timing the old code on both sides and re-measured what it had rejected, made a Vs. cabinet save-state about 9x faster, moved the off-die build's CHR into its own SDRAM bank, and pinned both builds at fitter seed 2 with two byte-identical clean compiles each, and v2.9.2 "Candidate", the current release, triaged a fifth, 32-finding audit of both repositories (16 fixed, each finding with its evidence in its ledger), made save states keep the cartridge RAM of twelve board families that dropped it, fixed the MiSTer CPU losing an NMI raised inside a DMA, and cut the release-candidate bitstream pair the SuperStation One session runs. AccuracyCoin held **141/141** through v2.6.16, read **143/144 (99.31%)** at v2.6.17, where the upstream re-sync grew the battery to 144 assigned tests and `Frozen OAM2 Increment` was the single named failure, and has read **144/144 (100.00%)** since v2.6.18 closed it — and the number was never always *by construction*: v2.3.4, v2.3.7, v2.3.9, the rung-3 releases v2.5.4-v2.5.6 and v2.6.17 change the core, so for those it is **verified** rather than inherited, and saying which is which is the point. **Full per-release detail is in `CHANGELOG.md` and `docs/STATUS.md` (the single source of truth)** — the entries below (v2.1.0 "Fathom" was the prior anchor here; v2.0.8 → v2.0.1) are the older historical trail, retained rather than duplicated. +- **Release line since v2.1.0:** the v2.1.x **"Fathom"** accuracy line (v2.1.0 → v2.1.10) → **v2.2.0 "Capstone"** (the milestone cut closing the "deepen the existing project" run) → **v2.2.1** (housekeeping) → **v2.2.2 "Conduit"** (build / distribution / CI-integrity) → **v2.2.3 "Datum"** (performance appraisal + the last two Holy Mapperel residuals closed) → **v2.2.4 "Cartridge"** (the libretro/RetroArch distribution cut) → **v2.2.5 "Colophon"** → **v2.2.6 "Almanac"** → **v2.2.7 "Timbre II"** → **v2.2.8 "Aperture II"** → **v2.2.9 "Studio II"** → **v2.3.0 "Datum II"** → **v2.3.1 "Plumb Line"** → **v2.3.2 "Lucid"** → **v2.3.3 "Cadence"** → **v2.3.4 "Ledger"** → **v2.3.5 "Manifest"** → **v2.3.6 "Sounding"** → **v2.3.7 "Overtone"** → **v2.3.8 "Parallax"** → **v2.3.9 "Crucible"** → the **v2.4.x "Fabric"** co-simulation line (**v2.4.1 "Fabric"** → **v2.4.2 "Cairn"** → **v2.4.3 "Touchstone"** → **v2.4.4 "Ignition"** → **v2.4.5 "Compass"** → **v2.4.6 "Abacus"** → **v2.4.7 "Keystone"** → **v2.4.8 "Palimpsest"** → **v2.4.9 "Plumbline II"** → **v2.5.0 "Rungwork"** → **v2.5.1 "Retrace"** → **v2.5.2 "Dormant"** → **v2.5.3 "Hysteresis"** → **v2.5.4 "Escapement"** → **v2.5.5 "Raster"** → **v2.5.6 "Vestige"** → **v2.5.7 "Collimation"** → **v2.5.8 "Blanking"** → **v2.5.9 "Overture"** → **v2.6.0 "Assay"** → **v2.6.1 "Interleave"** → **v2.6.2 "Witness"** → **v2.6.3 "Mainspring"** → **v2.6.4 "Rubric"** → **v2.6.5 "Muster"** → **v2.6.6 "Chassis"** → **v2.6.7 "Detent"** (the bitstream becomes a published, reproducible release artifact and a one-cycle disagreement is attributed rather than fitted away) → **v2.6.8 "Arrears"** (the gates the previous release fixed and never widened: four of six denied checkpoint streams had been passing unnoticed, three of them not run by the suite at all, and nestest widens 19x and gains the nine-field comparison that closes the gate's stated `nmi_line` blind spot) → **v2.6.9 "Abeyance"** (an exclusion hides improvement as well as regression -- both denied co-simulation streams close, and the larger one was a defect in the HARNESS rather than the console, carried for seven releases behind the phrase "by design") → **v2.6.10 "Inference"** (the cartridge meets the synthesiser, and simulation could not have asked the question -- rung 7's five boards had never been through Quartus, and `chr` written from two separate `always_ff` blocks could not infer as an M10K, so 128 KB stayed in flip-flops: 1,048,576 registers against roughly 166,000. The fix is behaviourally invisible -- 141 gates unchanged -- and the release carries the bitstream the previous one could not produce) → **v2.6.11 "Exposure"** (a picture is a gate the ladder did not have -- all 141 co-simulation gates green and two of six commercial games rendering wrong, because only THREE of those gates compare a framebuffer and all three ship CHR-ROM, so a CHR-RAM write taking the shared-pin composite address built for fetches was reached 9,600 times and compared never)) -> **v2.6.12 "Groundwork"** (the bitstream was an NROM-only console -- `emu.sv` left `cart_mapper`, `cart_prg_16k_banks` and `cart_chr_8k_banks` unconnected, so Quartus tied all three to GND and the fitted cartridge was mapper 0 with 8 KiB of PRG and 8 KiB of CHR against a declared 256 and 128; no gate could see it because `emu.sv` is not in the testbench file list, so all 142 gates exercised a correctly-configured cartridge, and Quartus said so three times in messages that cite an instance path rather than a file) -> **v2.6.13 "Slack"** (the cartridge outgrows the die -- an SDR SDRAM controller, a behavioural part model, a four-way arbiter and a console bridge written from the AS4C32M16SB-7 datasheet, and three consumers measured to three different budgets rather than the one figure the previous step assumed; the PPUDATA port leaves the shared bus through a handshake and that fix shipped a defect only a banked cartridge could see, the request carrying the raw PPU address where the mapper's translation was needed, so every banked-CHR board read bank zero; off the die the console passes 142 of 142 and it still ships on the die, because an off-die core cannot run without the add-on) -> **v2.6.14 "Docket"** (the submission checklist becomes auditable -- 30 boxes, 16 unticked and fourteen of those saying nothing about why, so an unticked box could not be told apart from work outstanding, work blocked elsewhere, and work already done and never ticked; the third case occurred five times. One box asked `docs/provenance.md` to state that no NES core was ever opened, which that document's own Do-not-self-certify section forbids, so it could only have been ticked by writing the sentence the provenance rules exist to prevent. Re-measuring the ticked half found two claims that had expired, the task board four delivered items never ticked and an SDRAM precondition the previous release did not follow, and its claim that MMC1 was unimplemented is retracted) → **v2.6.15 "Warrant"** (the claims the submission will make become checkable, and the instrument pays the oracle back — the `.rbf` name this core shipped would have distributed NOTHING, because `Distribution_MiSTer`'s builder skips any file whose stem does not end in `_` plus eight digits, so an accepted core would appear in the Cores table and ship nothing with no error anywhere; two of the four R1/R2 residuals ADR 0002 closed turn out never to have been IRQ-timing residuals, resting on an assertion blargg WITHDREW in the successor ROM; `sys/` verbatim, the `.qsf`'s single seed table and the bitstream name all become checks instead of claims; the nine rung-1 gates run in CI against a PINNED oracle commit, so the accuracy evidence stops being a document describing a check a reader cannot run; `cpu_interrupts_v2` lands on the DUT as the first INDEPENDENT interrupt oracle, five of five; and `T-ORACLE-001`'s opening claim is retracted — RustyNES does clock the MMC3 counter on the pre-render line, which `2-details` sub-test 8 has asserted all along) → **v2.6.16 "Interlock"** (the arbiter's numbers describe a stimulus, not the console: the gate's worst case issues a CHR and a PRG request on the same cycle and the running console never does — zero coincidences and a minimum separation of two over fifteen million SDRAM cycles — so both published figures are wrong about it in opposite directions, the CPU MEETING its deadline at 24 against 24 on all 240,303 requests where the gate said it missed by four, and the fetch reaching 23 against a derived 22 that a `CHR_LAT` sweep shows understates the real tolerance by at least nine cycles; the slot-scheduled arbiter this version was scoped around is therefore NOT built, and is still worth building because zero margin means one added cycle anywhere breaks the CPU) → **v2.6.17 "Terminus"** (a write lands where the cycle ENDS, and this core does not move to meet it. The AccuracyCoin and TriCNES oracles re-sync to upstream, and the battery grows 141 → 144 assigned tests across two new pages that RE-HOME eleven existing PPU tests, so a re-sync is not an append and a suite-keyed baseline has to be regenerated rather than extended. `OAM2Address` becomes a live counter through sprite fetch and the frozen fetch reads index 0 for every byte, closing two of the three new tests and carrying an approved SAVE-STATE EPOCH — `PPU_SNAPSHOT_VERSION` 8 → 9, so pre-v9 `.rns` states no longer load. The third names the dots its two `$2001` writes land on, which is how the core came to be measured TWO DOTS EARLY on both: a 6502 commits a write at phi2, the last of a CPU cycle's three PPU dots, and this core applies PPU register writes at M2-low, the first. The move was then MADE, measured against a first-difference control captured beforehand, and NOT ADOPTED — the write alone reads 141/144 and breaks the independent `ppu_vbl_nmi/10-even_odd_timing`, and the best combination found reads 142/144 against the 143/144 that ships, for a save-state epoch and six re-baselined goldens. `mask_for_skip_check` is PROVEN to be a compensation for the placement — under phi2 it needs one delay stage rather than two — and two of its three dependants are still un-re-derived, so adopting the mechanism now would replace a documented compensation with an undocumented one. `Misaligned OAM2 Address` was also found to be passing via TWO CANCELLING ERRORS, both since fixed independently. Ships one core fix nothing in the corpus could see — the misaligned-OAM `+4 & $FC` realignment, reached 114 times per battery run out of 56,953,944 out-of-range branches and pinned by a targeted test because no ROM's verdict moves — plus `terminus_control.rs` as a standing first-difference gate. The follow-up version the write-placement work had been scoped into is FOLDED here: that split existed because Terminus was to be a scheduler change with its own ADR and a full re-baseline, and the refutation means it is not — so v2.6.17 carries both). v2.6.18 "Errata" closes the last AccuracyCoin entry on top of it, and v2.6.19 "Accession" carried both into the FPGA core, v2.6.20 "Telltale" made the OAM2 counter observable, v2.6.21 "Steward" gave the core battery saves and gated CHR writes during rendering, v2.6.22 "Rigging" built the instruments a board would need before the board existed, v2.6.23 "Pulse" closed the CHR-during-rendering divergence by making the `$2007` access pulse the rendering pipeline's load instead of computing its own, and v2.7.0 "Palisade", the first of the ADR 0041 audit line, made a corrupt save state fail at restore rather than one tick later, stopped pulse 1 muting on the `$4001 = $08` sweep idiom, and gave the save-state fuzz target the reach to find five defects the audit had not, v2.7.1 "Keepsake" stopped six cartridge boards writing an empty battery save and moved every user file the frontend writes onto the atomic writer, v2.7.2 "Bankroll" gave MMC1, MMC5 and Namco 163 the banks their boards have and made `$6000-$7FFF` read open bus on the 205 board variants with nothing there, v2.7.3 "Hearth" made the desktop keep battery saves, closed three ways around the Lua sandbox, gated script HTTP destinations, and brought audio back after a device change, v2.7.4 "Pocket" kept the Android and iOS apps alive through an internal error, gave both battery saves, paused them and released audio when they leave the screen, and made their saves atomic, v2.7.5 "Tally" closed the core audit's twelve performance proposals (eleven by measurement, one adopted: the audio buffer keeps its capacity), deprecated 18 dead bus methods and `ApuBus`, and closed the core and frontend ledgers, v2.7.6 "Recount" measured alone the six deletions v2.7.5 had bounded only together (five zero; the sixth, never reached by the benchmarks before, bounds at 0.2%), turned three redundant fast-path stores into an assertion, and shipped the contributed libretro buildbot fixes and targets, v2.8.0 "Bulkhead" opened the v2.8.x libretro + RTL audit line: the libretro core unwinds and contains a panic at its own boundary, keeps save states the right size with a Zapper, withdraws its memory maps on unload, loads through the standard `retro_game_info` via a vendored binding, and the save state carries the internal data bus, v2.8.1 "Gasket" closed the libretro audit: Four Score, four described ports (the list now NULL-terminated, where RetroArch had read past it), the Zapper unplugged, unclipped expansion audio, UNIF, and a Makefile that honours its callers, v2.8.2 "Solder" corrected the MiSTer core's on-die RTL against the oracle and the wiki (MMC3 acknowledge, SNROM PRG-RAM, the triangle and noise reload drop, the `$2002` latch) and found the audit's MMC1 finding inverted: the emulator was the one ignoring a reset, and v2.8.3 "Rivet" released every reset on the clock that uses it, cut the CPU by about 4% with two exact rewrites the fit report confirmed, and made the ladder run from a fresh checkout (164 of its 165 gates, a claim v2.8.4 found overstated), and v2.8.4 "Tether" closed the v2.8.x line: it found every off-die read would have been sampled a clock late on hardware (the SDRAM model and controller shared the error, and only the new SDRAM timing constraints could see it) and fixed both, pinned both builds at fitter seed 5 after a sweep of each, and made the ladder run all 165 gates from a clean checkout, and v2.9.0 "Survey" opened the v2.9.x line by re-running all four audits (139 rows re-verified, none regressed, 39 new findings fixed or dispositioned), including a Power Cycle that erased the battery save and an off-die MiSTer build that hung under `bootcore=`, v2.9.1 "Hone" found that the A/B tool had been timing the old code on both sides and re-measured what it had rejected, made a Vs. cabinet save-state about 9x faster, moved the off-die build's CHR into its own SDRAM bank, and pinned both builds at fitter seed 2 with two byte-identical clean compiles each, v2.9.2 "Candidate" triaged a fifth, 32-finding audit of both repositories (16 fixed, each finding with its evidence in its ledger), made save states keep the cartridge RAM of twelve board families that dropped it, fixed the MiSTer CPU losing an NMI raised inside a DMA, and cut the release-candidate bitstream pair for the SuperStation One session, and v2.9.3 "Handset", the current release, moved every dependency to its newest release, answered all 244 review threads left open on PRs #7-#97 and fixed the ten findings that still held (mapper 28 rebuilt, a header editor that writes only what you change), and prepared the mobile device run, which the maintainer then moved after v3.0.0 with the board session. AccuracyCoin held **141/141** through v2.6.16, read **143/144 (99.31%)** at v2.6.17, where the upstream re-sync grew the battery to 144 assigned tests and `Frozen OAM2 Increment` was the single named failure, and has read **144/144 (100.00%)** since v2.6.18 closed it — and the number was never always *by construction*: v2.3.4, v2.3.7, v2.3.9, the rung-3 releases v2.5.4-v2.5.6 and v2.6.17 change the core, so for those it is **verified** rather than inherited, and saying which is which is the point. **Full per-release detail is in `CHANGELOG.md` and `docs/STATUS.md` (the single source of truth)** — the entries below (v2.1.0 "Fathom" was the prior anchor here; v2.0.8 → v2.0.1) are the older historical trail, retained rather than duplicated. - **Preceding release:** **RustyNES v2.0.8 "Harbor"** (2026-07-09) — the eighth release of the **v2.0.x mobile-finalization train** and the **iOS release candidate** ("Harborlight"), the final release of the iOS finalization window (**v2.0.5 → v2.0.8**). A **host / iOS-only** cut: the cycle-accurate core is **unchanged and byte-identical to v2.0.7** (AccuracyCoin still **141/141, 100.00%**; nestest 0-diff; `#![no_std]` chip stack untouched). It stages the App Store scaffolding for v2.1.0: version-controlled **App Store Connect listing metadata** (`fastlane/metadata/ios/{en-US,es-ES}/`, mirroring the Android tree, files-only), a **dormant App Store `release` lane** in `fastlane/Fastfile` that stages the build + listing but **does not submit** (`submit_for_review: false`) and is **not** CI-wired (the interim channel stays **TestFlight**), and an **App-Review §4.7 self-audit** (no bundled/downloadable ROMs, ownership notice, searchable library, 4+ rating) in `docs/ios-v2.0.8-readiness.md`. Version bump (workspace `2.0.7 → 2.0.8`; iOS `MARKETING_VERSION → 2.0.8`). **No store submission** (that is v2.1.0); screenshots, real signing, the listing upload, and the App-Review submission are the **maintainer / v2.0.9 / v2.1.0** closeout. See `docs/STATUS.md` (single source of truth) + `CHANGELOG.md` `[2.0.8]` + `docs/ios-v2.0.8-readiness.md` + `to-dos/plans/v2.0.5-v2.0.8-ios-finalization-plan.md`. - **Earlier in the train:** **RustyNES v2.0.7 "Harbor"** (2026-07-09) — the seventh release of the **v2.0.x mobile-finalization train** and the **third iOS finalization release** ("Trim"), continuing the iOS window (**v2.0.5 → v2.0.8**). A **host / iOS-only** cut: the cycle-accurate core is **unchanged and byte-identical to v2.0.6** (AccuracyCoin still **141/141, 100.00%**; nestest 0-diff; `#![no_std]` chip stack untouched). It wires the **App Store submission floor** (Apple mandates the **iOS 26 SDK / Xcode 26** for every App Store Connect upload from **2026-04-28**, so the tag-gated iOS CI now selects the newest Xcode 26.x on the runner — a build-SDK pin, non-breaking fallback on older images), **reconciles the deployment target `iOS 15.0 → 17.0`** to match the code's real API floor (`NavigationStack` iOS 16 + `.topBarTrailing` iOS 17, unguarded at 12+ sites — the prior 15.0 was never buildable), and **re-audits `PrivacyInfo.xcprivacy`** against the v2.0.6 crash reporter (no new data type / required-reason API — local-only, backup-excluded, off by default). Version bump (workspace `2.0.6 → 2.0.7`; iOS `MARKETING_VERSION → 2.0.7`). **TestFlight-only** (App Store + AltStore PAL deferred to v2.1.0); on-device profiling + the Xcode-26 archive are a **maintainer / v2.0.9** step. See `docs/STATUS.md` (single source of truth) + `CHANGELOG.md` `[2.0.7]` + `docs/ios-v2.0.7-readiness.md` + `to-dos/plans/v2.0.5-v2.0.8-ios-finalization-plan.md`. - **Earlier in the train:** **RustyNES v2.0.6 "Harbor"** (2026-07-09) — the sixth release of the **v2.0.x mobile-finalization train** and the **second iOS finalization release** ("Parity"), continuing the iOS window (**v2.0.5 → v2.0.8**). A **host / iOS-only** cut: the cycle-accurate core is **unchanged and byte-identical to v2.0.5** (AccuracyCoin still **141/141, 100.00%**; nestest 0-diff; `#![no_std]` chip stack untouched), so no accuracy / save-state / determinism number moves. It adds a **new opt-in, privacy-first crash-reporting surface** (off by default — the iOS analogue of the Android v1.8.8 `CrashReporter`, closing the v1.9.9 iOS-applicable deferral): **Settings → Diagnostics** installs an uncaught-`NSException` handler that writes **local** crash logs the user can view + copy in-app — **nothing is uploaded**, so the "Data Not Collected" privacy label is unchanged (EN + ES); the handler re-checks the live opt-in at crash time so opting out stops new logs immediately. It also records the **feature-parity re-verification** of the v1.9.x host features (Game Center, CloudKit save sync, MFi controllers, capture / PiP, accessibility) against the unchanged v2.0.0 bridge surface. Version bump (workspace `2.0.5 → 2.0.6`; iOS `MARKETING_VERSION → 2.0.6`). **TestFlight-only** (App Store + AltStore PAL deferred to v2.1.0); on-device crash-capture verification is a **maintainer / v2.0.9** step. See `docs/STATUS.md` (single source of truth) + `CHANGELOG.md` `[2.0.6]` + `docs/ios-v2.0.6-readiness.md` + `to-dos/plans/v2.0.5-v2.0.8-ios-finalization-plan.md`. diff --git a/to-dos/plans/README.md b/to-dos/plans/README.md index 1dab9a08f..fc0ff60e0 100644 --- a/to-dos/plans/README.md +++ b/to-dos/plans/README.md @@ -27,11 +27,11 @@ lines before it act on the four audits in | [`v2.7.x-core-frontend-audit-plan.md`](v2.7.x-core-frontend-audit-plan.md) | v2.7.x — the core and frontend audits | Shipped | | [`v2.7.0-palisade-plan.md`](v2.7.0-palisade-plan.md) | v2.7.0 "Palisade" — untrusted save-state input stops at the boundary; the pulse-1 sweep clamp | **Shipped** | | [`v2.8.x-libretro-rtl-audit-plan.md`](v2.8.x-libretro-rtl-audit-plan.md) | v2.8.x — the libretro and RTL audits, and the off-die build | Shipped | -| [`v2.9.x-final-audit-and-hardware-plan.md`](v2.9.x-final-audit-and-hardware-plan.md) | v2.9.x — re-audit, optimise, and the bring-up on the board | **In progress** | +| [`v2.9.x-final-audit-and-hardware-plan.md`](v2.9.x-final-audit-and-hardware-plan.md) | v2.9.x — re-audit, optimise, and the bring-up on the board | Shipped through v2.9.3; the board and device work moved after v3.0.0 (2026-09-29) | | [`v2.9.0-survey-plan.md`](v2.9.0-survey-plan.md) | v2.9.0 "Survey" — every audit re-checked, the SuperStation One surveyed, the v3.0.0 API decision (ADR 0042) | **Shipped** | | [`v2.9.2-candidate-plan.md`](v2.9.2-candidate-plan.md) | v2.9.2 "Candidate" — the full audit (AUD-01..AUD-32) acted on, and the release-candidate `.rbf` pair for the board | Released 2026-09-28 | -| [`v2.9.3-handset-plan.md`](v2.9.3-handset-plan.md) | v2.9.3 "Handset" — the mobile device checklist, and what can be proven before it | **In progress** | -| [`v3.0.0-superstation-core-plan.md`](v3.0.0-superstation-core-plan.md) | v3.0.0 — the hardware-verified FPGA core | Planned | +| [`v2.9.3-handset-plan.md`](v2.9.3-handset-plan.md) | v2.9.3 "Handset" — the mobile device checklist, and what can be proven before it | Released 2026-09-29; the device run moved after v3.0.0 | +| [`v3.0.0-superstation-core-plan.md`](v3.0.0-superstation-core-plan.md) | v3.0.0 — the hardware-verified FPGA core | Planned; **scope under revision** (ADR 0041, 2026-09-29 amendment) | | [`v2.7.0-shakedown-plan.md`](v2.7.0-shakedown-plan.md) | v2.7.0 "Shakedown" — the board session as first planned | **Superseded** by ADR 0041; strands A–F carried into the v2.9.x plan | | [`v2.7.0-mister-core-plan.md`](v2.7.0-mister-core-plan.md) | v2.5.1 → v2.7.0 — the MiSTer core ladder | **Superseded** by ADR 0041 | diff --git a/to-dos/plans/v2.9.3-handset-plan.md b/to-dos/plans/v2.9.3-handset-plan.md index 62b1569f6..de5ffa4f3 100644 --- a/to-dos/plans/v2.9.3-handset-plan.md +++ b/to-dos/plans/v2.9.3-handset-plan.md @@ -71,3 +71,26 @@ The SuperStation One board session (it runs v2.9.2's bitstreams on its own schedule); a mobile opt-out switch for opposing-direction cancel (the maintainer kept it on by default at v2.9.2; desktop has the switch); any new feature. + +## Outcome (released 2026-09-29) + +**The device run did not happen in this release.** The maintainer moved the +mobile device runs, the SuperStation One board session and their fixes after +v3.0.0 (2026-09-29), and released v2.9.3 with everything this side could do: + +| Item | Result | +| --- | --- | +| Run sheet | `docs/mobile-v2.9.3-run-sheet.md`, every v2.7.4 and v2.9.2 row plus D1-D5 for #570; APKs built from `1b4c5885` and checksummed | +| Emulator pre-run | A1, A3 (corrected step), A4, A6, A7, D1 PASS; A10 PASS for function; A8 NOT RUN to its one-minute step (a 30 s reading only); A2 NOT RUN though an emulator could run it; the rest need a device | +| CI | `kotlin-unit-tests` runs `:app:testFossDebugUnitTest`; `ios-host-typecheck.sh` now compiles `gfx_metal.rs` | +| Dependencies | #570 merged as `d8b02d2f`; wgpu 30 pacing measured (`docs/performance.md`, v2.9.3) | +| Sibling pin | moved to v2.9.2 (sibling #44), 117 stems byte-identical | + +Added along the way, beyond the plan: all 244 review threads left unanswered on +PRs #7-#97 answered, ten real findings fixed (mapper 28, the header writer and +the canonical encoder's mapper nibble, the palette load, NSF open bus, Bisqwit +edges, the event heatmap, a NaN gain, `fds_trace`, a stale doc pointer), and +`serialize_header` deprecated in favour of `serialize_header_preserving`. + +**Not established:** every Swift change since v2.7.4 is still uncompiled, and no +Android row has run on a device. diff --git a/to-dos/plans/v2.9.x-final-audit-and-hardware-plan.md b/to-dos/plans/v2.9.x-final-audit-and-hardware-plan.md index bb928d70a..b64e9edfe 100644 --- a/to-dos/plans/v2.9.x-final-audit-and-hardware-plan.md +++ b/to-dos/plans/v2.9.x-final-audit-and-hardware-plan.md @@ -7,6 +7,13 @@ four audits are closed in their ledgers and the last planned RTL change has landed. This line checks that claim, spends what optimisation clears the bars, cuts the release-candidate bitstreams, and **runs them on the board**. +> **Re-planned (maintainer, 2026-09-29).** The board session (v2.9.2's Strands +> A-F) and the mobile device runs (v2.9.3) move after v3.0.0, with the fixes +> each produces; v2.9.4+ as written below does not happen in this line. v2.9.3 +> shipped its prepared run sheet and emulator pre-run without the device run. +> See [ADR 0041](../../docs/adr/0041-hardware-release-is-v3.0.0.md)'s 2026-09-29 +> amendment. + The bring-up strands below were written for v2.7.0 "Shakedown" ([`v2.7.0-shakedown-plan.md`](v2.7.0-shakedown-plan.md), now superseded). They were written for the board and they are still right, so they are carried here diff --git a/to-dos/plans/v3.0.0-superstation-core-plan.md b/to-dos/plans/v3.0.0-superstation-core-plan.md index 2f98c27ef..eb9a2c4f4 100644 --- a/to-dos/plans/v3.0.0-superstation-core-plan.md +++ b/to-dos/plans/v3.0.0-superstation-core-plan.md @@ -9,6 +9,12 @@ reached through three audit and hardening lines: [v2.8.x](v2.8.x-libretro-rtl-audit-plan.md) and [v2.9.x](v2.9.x-final-audit-and-hardware-plan.md). +> **Under revision (2026-09-29).** The maintainer moved the SuperStation One +> board session and the mobile device runs after v3.0.0. The gate below, which +> requires every bring-up row to pass, therefore no longer matches the plan; +> what v3.0.0 ships is open ([ADR 0041](../../docs/adr/0041-hardware-release-is-v3.0.0.md), +> 2026-09-29 amendment). The text below is kept as written until that is decided. + ## The deliverable | Artefact | Status at v3.0.0 |