Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
41367fd
fix(accuracy): re-sync AccuracyCoin to f5f41dc2; fix two defects it f…
doublegate Oct 7, 2026
64aaf62
test(epoch): enforce the emulation-epoch rule with an output fingerprint
doublegate Oct 7, 2026
31f1d05
docs: record the palette A/B result and two v3.0.1 tooling traps
doublegate Oct 7, 2026
687a5a8
feat(options): CPU overclock, a working sprite-limit option, format 6
doublegate Oct 8, 2026
5382218
feat(accuracy): PAL emphasis swap, NEC MMC3 override, differential phase
doublegate Oct 8, 2026
8a680bd
feat(dual): rewind and run-ahead on the Vs. DualSystem cabinet
doublegate Oct 8, 2026
aa7435b
docs(mister): record the rewritten contribution page; re-scope the ch…
doublegate Oct 8, 2026
638d5d3
docs: v3.1.0 records -- DOC-01..09, the v1.8.x checklist fold, a mask…
doublegate Oct 8, 2026
3df59e8
test(accuracycoin): rebuild the misaligned-OAM sub-test; add DMC reload
doublegate Oct 8, 2026
fb8a124
release: v3.1.0 "Bellwether" -- the version, the anchors, the records
doublegate Oct 8, 2026
5e99b07
fix(test): two v3.1.0 tests read ROMs that exist only on the dev host
doublegate Oct 8, 2026
11c96b9
fix(core): make the CPU overclock multiplier exact on PAL and Dendy
doublegate Oct 8, 2026
69180c8
docs: correct four v3.1.0 records the release PR review found stale
doublegate Oct 8, 2026
5f7eac6
refactor(netplay): name each older Sync magic instead of indexing the…
doublegate Oct 8, 2026
b398f1a
fix: apply the new options on the Vs. cabinet; keep the MMC3 setting …
doublegate Oct 8, 2026
8af50dc
fix(test): let the epoch gate bless a new probe; close a catalog-row …
doublegate Oct 8, 2026
c749c43
test(core): pin that switching the overclock off is an ordinary frame
doublegate Oct 8, 2026
c95a8d1
docs: correct sixteen stale v3.1.0 records; gate drafting placeholders
doublegate Oct 8, 2026
b607687
docs(core): drop the last "debt" wording from the overclock snapshot
doublegate Oct 8, 2026
17823b6
docs(release): the v3.1.0 "Bellwether" release notes
doublegate Oct 8, 2026
7274754
docs: v3.1.0 test count at the release head, 3,269 / 0 / 11
doublegate Oct 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .github/release-notes/v3.1.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# RustyNES v3.1.0 — "Bellwether"

The first release of the v3.1 to v4.0 line. AccuracyCoin is re-synced to its newest upstream, and the new ROM showed that every earlier 100% score included a failure the old ROM hid; both defects it found are fixed. Two long-shown options now work and travel with movies and netplay: a CPU overclock and the sprite-limit switch. PAL and Dendy emphasis is right, the alternate MMC3 revision is selectable and correct, the NTSC decode gains differential phase, and the two-screen Vs. cabinet gets rewind and run-ahead. The MiSTer core (RTL-1) now matches the emulator when a `$2006` write meets the PPU's address pipeline. **Save states, movies and netplay from v3.0.1 are refused.**

## Breaking changes

- **Save states from v3.0.1 do not load**: BUS section 3 (the DMC write-refusal latch and the overclock's position) and `PPU_SNAPSHOT_VERSION` 13 (the sprite-limit option's pending sprites).
- **Movies from v3.0.1 are refused**: `.rnm` format 6, whose options record carries the CPU overclock, the sprite-limit switch and the MMC3 revision. `EMULATION_EPOCH` rises from 2 to 3, because the two AccuracyCoin fixes change bus cycles and sprite evaluation.
- **Netplay**: protocol 7 (`"RNE7"`). A v3.0.x peer is refused as another emulator version, naming its epoch.

## AccuracyCoin, and an overstated score

AccuracyCoin is re-synced to upstream `f5f41dc2`: 151 rows, 146 scored, **146 of 146 pass**. The old ROM had a bug: when `Misaligned OAM behavior` failed part-way, its fail path returned into the test instead of exiting, and the test recorded a pass. Upstream fixed it. On the fixed ROM RustyNES failed parts of that test, and so did every earlier release. **Every AccuracyCoin 100% reported before v3.1.0 (144/144, and 141/141 before it) included that masked failure.** v3.1.0 fixes it, and the DMC load-on-write defect the new `DMA Landing on Write` test found, so this 146/146 is the first without it.

## Fixed

- **Misaligned sprite evaluation**: evaluation starts from OAMADDR as of dot 65, copies four bytes from a start at a slot's last byte, and stays misaligned after an in-range X.
- **A DMC load DMA refused by a write takes four cycles, not three.**
- **PAL and Dendy colour emphasis**: PPUMASK bits 5 and 6 swap meaning on the 2C07 and the Dendy, so every PAL game that set emphasis was tinted the wrong way.
- **The alternate MMC3 revision fires on a `$C001` reload to 0**, the single IRQ the NESdev page documents; blargg's `6-MMC3_alt` now passes under it.
- **The pattern viewer and HD packs no longer change the game on MMC2 / MMC4 boards** (Punch-Out!!, Fire Emblem) or three others: their "side-effect-free" CHR read flipped the board's CHR latch.

## Added

- **CPU overclock**, 2 to 4 times, against the same picture and sound: the APU, mapper counters and PPU stay at the stock rate. Movies record it; netplay peers must match.
- **"Disable 8-sprite-per-scanline limit" works.** It draws the dropped sprites behind the eight the console shows and changes nothing the game can see.
- **MMC3 IRQ revision** setting (Auto / Sharp / Alternate), carried in movies and netplay.
- **Differential phase** in the raw NTSC decode, display only, off by default.
- **Rewind and run-ahead on the Vs. DualSystem cabinet**, on the whole cabinet, so the two consoles never fall out of step.
- **The emulation-epoch rule is a test** (`T-EPOCH-FINGERPRINT`): output that moves without an epoch rise fails CI.

## The MiSTer core

- **Release candidate, not hardware-verified.** No hardware has run either bitstream.
- **It matches the re-synced AccuracyCoin, all 146 entries.** The new ROM found the core's version of both defects: a misaligned sprite evaluation now masks the address after an out-of-range X byte, and a `$4010` write on the DMC timer's reload edge sets that reload's period. The old `Misaligned OAM behavior` sub-test ROM, whose fail path fell through to a pass, is rebuilt from the new source; on it the core without the fix fails.
- **A `$2006` write that meets the PPU's address pipeline matches the emulator** (RTL-1). A new test ROM lands the copy on every dot of a rendering line: the core diverged on 28,129 of 331,838 background fetches and now matches all of them. One of the three rules involved, a copy delay that depends on where in the line the write lands, is the emulator's and is not documented (NESdev says a constant "1 to 1.5 dots"); it is provisional until a board can settle it.
- **30 of 34 AccuracyCoin sub-test ROMs now gate the core**, the other four out for stated reasons, and the core's oracle pin moves to v3.1.0: every golden that changed is attributed, and the other 646 artifacts are byte-identical.
- **The co-simulation ladder is 212 passed, 0 failed, 1 expected failure on-die and 213 passed, 0 failed, 1 expected failure off-die**, each from one run of a frozen tree with nothing skipped: v3.0.1's 200 / 201 plus twelve new gates, and no existing gate moved.
- **New bitstreams, because the fixes are RTL.** Both builds are compiled at fitter seed 1, chosen from eight seeds swept on one build date (261008), every one of which closes on both builds. Each was compiled twice to the same bytes:
- on-die `RustyNES_MiSTer-v3.1.0.rbf`, md5 `4afffd23f6c6d2577c2ead24fa2e6f54` (timing margin +0.255 ns setup, +0.100 ns hold, the largest on-die hold of the eight seeds);
- off-die `RustyNES_MiSTer-v3.1.0-offdie.rbf`, md5 `7b48198cc3b6a822e71ce54e53d2adbc` (+0.188 / +0.001 ns; the SDRAM read +0.449 / +1.184 ns, assuming zero board delay). The off-die build shares the on-die build's seed, and at it closes by one picosecond of hold, its thinnest of the eight.
- The bitstreams carry the sweep's build date, which may be earlier than the release date.
- A self-hosted workflow now runs the whole ladder on the maintainer's runner, on manual dispatch.

## Records

The MiSTer contribution page was rewritten in September; the new page and what it means for the submission are recorded, and the checklist re-scoped. Nine stale documents were corrected against the code, and the v1.8.x Android checklist is folded into the mobile run sheet.

## Verification

- `--features test-roms`: 3,269 passed, 0 failed, 11 ignored.
- The local commercial suites: `external_real_games` 60/0, `external_extended` 137/0, `external_coverage` 6/0 over 744 staged ROMs. The one moved baseline is *Millionaire* (Sachen), a PAL game whose emphasis colours the PAL fix corrected.
- AccuracyCoin 146/146 at upstream `f5f41dc2`, nestest 0-diff.
- The epoch fingerprint gate passes at epoch 3, now recorded as the released epoch.

## Install

- Download the pre-built binaries for Linux, macOS, and Windows below.
- The MiSTer core bitstreams are attached below, as a release candidate, not hardware-verified. The on-die build is `RustyNES_MiSTer-v3.1.0.rbf`, also attached under its datecoded name `RustyNES_20261008.rbf`, and the off-die build is `RustyNES_MiSTer-v3.1.0-offdie.rbf`.
- 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.
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Enforcement lives alongside the prose: `/ref-proj/` is gitignored/`.dockerignore

RustyNES is a cycle-accurate Nintendo Entertainment System emulator written in pure Rust. The accuracy bar is Mesen2 / higan / ares: tight lockstep scheduling at PPU-dot resolution on a master-clock-precise timebase, sub-instruction PPU events visible to subsequent CPU code, and a lookup-table non-linear audio mixer with band-limited synthesis. The frontend is pure Rust (`winit` + `wgpu` + `cpal` + `egui`).

**Current release: v3.0.1 "Mortar"** (2026-10-07) — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** (2026-10-06) — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core ([ADR 0043](docs/adr/0043-v3-is-the-api-major-and-a-release-candidate-core.md)). **No hardware has run any MiSTer bitstream**: hardware verification moves to a later v3.x release (ADR 0043 supersedes [ADR 0041](docs/adr/0041-hardware-release-is-v3.0.0.md)'s hardware-verified v3.0.0), and the SDRAM pin constraints stay provisional until the SuperStation One's memory is read. Suite counts, mapper matrix and per-release detail live in `docs/STATUS.md`, `CHANGELOG.md` and the GitHub releases; they are deliberately not duplicated here. The earlier release-by-release narrative and other condensed paragraphs are archived verbatim in `docs/history/AGENTS-archive.md`.
**Current release: v3.1.0 "Bellwether"** (2026-10-08) — the AccuracyCoin re-sync and an honest 146/146, the CPU overclock and the sprite-limit option in movies and netplay, PAL emphasis, the alternate MMC3, rewind and run-ahead on the Vs. cabinet, and the MiSTer core matched to all of it. Built on **v3.0.1 "Mortar"** (2026-10-07) — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on **v3.0.0 "Cornerstone"** (2026-10-06) — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core ([ADR 0043](docs/adr/0043-v3-is-the-api-major-and-a-release-candidate-core.md)). **No hardware has run any MiSTer bitstream**: hardware verification moves to a later v3.x release (ADR 0043 supersedes [ADR 0041](docs/adr/0041-hardware-release-is-v3.0.0.md)'s hardware-verified v3.0.0), and the SDRAM pin constraints stay provisional until the SuperStation One's memory is read. Suite counts, mapper matrix and per-release detail live in `docs/STATUS.md`, `CHANGELOG.md` and the GitHub releases; they are deliberately not duplicated here. The earlier release-by-release narrative and other condensed paragraphs are archived verbatim in `docs/history/AGENTS-archive.md`.

- **Native Android app** — the **v1.8.0 → v1.8.9 "Android"** train (`crates/rustynes-mobile` UniFFI bridge + `crates/rustynes-android` JNI/NDK host + a Jetpack Compose app, ADR 0024): full on-device emulation, multi-touch + P1–P4 hardware controllers, wgpu `SurfaceView` rendering + the shared WGSL shader stack, save-states / battery SRAM, Lua, RetroAchievements, direct-IP + CGNAT/TURN room-code netplay, a box-art ROM library, and platform polish (adaptive / foldable / TV, Material You, capture / PiP / home-screen widget). Distributed as **GitHub-Releases sideload**; a free store listing is an unversioned later step with no monetization (ADR 0035, which superseded ADR 0025's v2.3.0 date; the `foss`/`play` flavour split remains).
- **Native iOS / iPadOS app** — the **v1.9.0 → v1.9.9 "iOS" TestFlight train** (`crates/rustynes-ios` Metal + CoreAudio shim reusing `rustynes-mobile` verbatim → UniFFI-generated Swift, ADR 0026): a native SwiftUI shell over wgpu→Metal, multi-touch + GameController, the shader stack, TAS / HD-pack / palettes / per-game DB, Lua + RetroAchievements, LAN + room-code netplay, CloudKit save-state sync, accessibility + EN/ES i18n + ReplayKit + Game Center, and the v1.9.9 creator tools (Cheats, a FOSS-gated read-only debugger, a touch TAStudio piano-roll, foreign movie import, a host audio-depth DSP). Ships to **TestFlight**; a free App Store listing is an unversioned later step with no monetization (ADR 0035, which superseded ADR 0027's v2.3.0 date). Mobile ROM loading was iNES / NES 2.0 only until v2.9.7, which adds FDS (host-supplied BIOS), NSF and the Vs. DualSystem cabinet to the bridge (the Swift half uncompiled; run-sheet rows T1-T12). Readiness record: `docs/ios-v1.9.9-readiness.md`.
Expand Down Expand Up @@ -236,7 +236,7 @@ that is a reason to add a tenth — not a reason to grow this section back.
- `ref-docs/` is immutable. Research updates go in dated supplemental files.
- ADRs go in `docs/adr/` (Michael Nygard format).
- `rustynes-core` re-exports the public types from the chip crates; downstream consumers (`rustynes-frontend`, `rustynes-test-harness`) should depend on `rustynes-core` rather than the chip crates directly.
- When relabeling old engine "v2.x" narrative for users, present it as upstream lineage/history — **never as a current RustyNES release version.** The current release is **v3.0.1 "Mortar"** (2026-10-07). **Never claim any version *later* than v3.0.1 is released.** Two distinct "v2.0"s exist and must not be conflated: the **engine-lineage v2.0** master-clock work shipped as the **v1.0.0** production core (2026-06-13) and was the only scheduler through v1.10.0; RustyNES's own **v2.0.0 "Timebase"** (2026-07-03) is a different milestone that REPLACES that dot-lockstep scheduler with the one-clock, every-cycle-bus-access model (ADR 0002 / 0028 / 0029) and broke byte-identity and save-state compatibility, by design (v2.9.8 later broke save, movie and API compatibility again, ahead of v3.0.0). The per-release narrative that used to be inlined here is in `CHANGELOG.md`, the per-release notes under `.github/release-notes/`, and the published GitHub releases — three places that are maintained, against one copy here that was not.
- When relabeling old engine "v2.x" narrative for users, present it as upstream lineage/history — **never as a current RustyNES release version.** The current release is **v3.1.0 "Bellwether"** (2026-10-08). **Never claim any version *later* than v3.1.0 is released.** Two distinct "v2.0"s exist and must not be conflated: the **engine-lineage v2.0** master-clock work shipped as the **v1.0.0** production core (2026-06-13) and was the only scheduler through v1.10.0; RustyNES's own **v2.0.0 "Timebase"** (2026-07-03) is a different milestone that REPLACES that dot-lockstep scheduler with the one-clock, every-cycle-bus-access model (ADR 0002 / 0028 / 0029) and broke byte-identity and save-state compatibility, by design (v2.9.8 later broke save, movie and API compatibility again, ahead of v3.0.0). The per-release narrative that used to be inlined here is in `CHANGELOG.md`, the per-release notes under `.github/release-notes/`, and the published GitHub releases — three places that are maintained, against one copy here that was not.
- **Forward plans + roadmap live in `to-dos/`.** `to-dos/ROADMAP.md` (updated in #129) is the planning entry point and frames the release line + "the path to v2.0.0 and beyond"; `to-dos/plans/` holds the per-release plan docs (through `v1.7.0-forge-plan.md` on `main`, plus the staged-forward `v1.8.0-android-plan.md` / `v1.9.0-ios-plan.md` / `v2.0.0-master-clock-plan.md`) + the `to-dos/plans/engine-lineage/` history archive + a `to-dos/plans/research/` reference-mining archive.
- The v1.0.0 release + GitHub Pages/CI + post-release record is in `docs/v1.0.0-synthesis-handoff-2026-06-13.md` — read it before touching CI, Pages, or release tooling. Full per-release history is in `CHANGELOG.md`.
- **Markdownlint is a CI gate** (pre-commit, pinned `markdownlint-cli v0.49.1`). The pin was v0.39.0 until the v2.6.3 dependency refresh, held because the newer local binary reported rules the pin lacked — chiefly **MD060** (`table-column-style`), which was therefore NOT gated. That is now measured and resolved: MD060's inferred default reads this corpus as style `compact` and reports **1,936 findings across 122 files** and nothing else, so `.markdownlint.json` pins `MD060` to the style actually in use (`leading_and_trailing`), which measures **zero** and rewrites no document. It IS a gate now. Still verify with `pre-commit run markdownlint --all-files` rather than the bare binary — the pin and the local build can drift apart again. `.markdownlint.json` also keeps `MD013`/`MD033`/`MD041` disabled by design (long technical tables, the README HTML banner/`<img>`, 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.
Expand Down
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**Document Version:** 2.1.0
**Last Updated:** 2026-10-07
**Applies to:** RustyNES v3.0.1 (the scheduling model is v2.0.0 "Timebase" onward)
**Applies to:** RustyNES v3.1.0 (the scheduling model is v2.0.0 "Timebase" onward)

This document fixes the high-level architecture of RustyNES. The per-subsystem specs under `docs/` (`cpu-6502.md`, `ppu-2c02.md`, `apu-2a03.md`, `mappers.md`, `scheduler.md`) take these decisions as given and elaborate one chip each. After reading this you should know the workspace shape, the scheduling model, the public boundary, and the load-bearing invariants. The canonical, always-current architecture spec is [`docs/architecture.md`](docs/architecture.md); this file is the top-level companion.

Expand Down
Loading
Loading