Skip to content

Latest commit

 

History

History
132 lines (84 loc) · 20.6 KB

OVERVIEW.md

File metadata and controls

132 lines (84 loc) · 20.6 KB

RustyNES Overview

Document Version: 2.1.0 Last Updated: 2026-10-06 Applies to: RustyNES v3.5.0


Table of Contents


Project Vision

RustyNES is the definitive NES emulator for the modern era — combining cycle-perfect accuracy with a complete contemporary feature set and the safety guarantees of Rust. It is more than an emulator: it is a platform for NES preservation, competitive online play, tool-assisted speedrunning, and homebrew development.

As of v1.0.0, that vision was realized: RustyNES clears the Mesen2 / higan / ares accuracy bar, ships a polished desktop application and a browser build, and supports the full platform surface — netplay, achievements, TAS movies, a debugger, FDS, and arcade (Vs. / PlayChoice-10) hardware. Since then the additive v1.x line added three more platforms (native Android, iOS / iPadOS, and a Libretro / RetroArch core), v2.0.0 "Timebase" replaced the scheduler substrate with the one-clock / every-cycle-bus-access model (ADR 0029 — the first designated breaking release), and the v2.1.x → v2.3.x lines deepened accuracy, presentation, and analysis tooling. The current release is v3.5.0 "Chorus" — creator tools: movies record every input the core takes (.rnm format 7), Lua gains WebSockets and real shared memory, an MCP server, speedrun auto-timing and dump checks arrive, the MMC5 scanline IRQ is fixed (epoch 6), and the MiSTer core adds the MMC5, VRC6, Namco 163, VRC7 (silent) and Bandai FCG boards, off the die only. Built on v3.4.2 "Countersign" — the RetroArch core loads on Apple Silicon Macs again. Built on v3.4.1 "Beacon" — the hosted netplay lobby live with the browser and mobile builds pointing at it (the TURN relay deployed but held off: Cloudflare kept honouring expired and revoked credentials), the browser RetroAchievements build fixed (web RA held until RetroAchievements answers the integration request), version-named MiSTer release assets, and the MiSTer save engine's cartridge-change defects and v3.4.0's two build findings fixed. Built on v3.4.0 "Waypoint" — netplay authenticated and three and four players on the desktop, RetroAchievements hardcore held to RA's full block list, the hosted services shipped as code (deployed in v3.4.1), the window thread's CPU halved, and the MiSTer core's headline build made the off-die one, with save states, rewind and Phase F3a's boards. Built on v3.3.0 "Plumbline" — v3.2.0's leftovers settled and its claims re-measured: CHR-RAM writes go through the bank registers on seven boards, mapper 71 decodes its bank select as documented, v3.1.x Bandai states load again, a v3.2.0 performance figure is withdrawn, the IRQ trace goldens became a test, and, in simulation, the MiSTer core's off-die build reads CHR-RAM back and loads CHR correctly. Built on v3.2.0 "Confluence" — every v3.2.0 branch in both repositories brought into one release: 17 mapper families (208 in all), the VRC4 IRQ fix, an AI remediation branch audited change by change, netplay protocol 8, slim libretro states, and sixteen more boards on the MiSTer core. Built on v3.1.0 "Bellwether" — the AccuracyCoin re-sync and an honest 146/146, the CPU overclock and the sprite-limit option in movies and netplay, PAL emphasis, the alternate MMC3, rewind and run-ahead on the Vs. cabinet, and the MiSTer core matched to all of it. Built on v3.0.1 "Mortar" — a maintenance release: one game's graphics fixed, the MiSTer core's last MMC3 rule exception tested, Rust 1.99 everywhere, every unanswered bot review answered, and the plan to v4.0.0. Built on v3.0.0 "Cornerstone" — the API major: every break since v2.x in one place, a core timing epoch for movies and netplay, the last MMC3 timing gap closed in both cores, and a release-candidate MiSTer core. Built on v2.9.9 "Ballast" — the release candidate for v3.0.0: the audits re-run, MMC3 and MMC5 by their documentation, audio exact across save states, and the MiSTer core moved onto it. Built on v2.9.8 "Vanguard" — the preparation release for v3.0.0: v3.0.0's breaking changes landed early (a save identity that ignores the header, old states and movies refused, movies and netplay that record the machine, the API removals), every staged game was booted and the defects found were fixed, and the game database's corrections reach every platform. Built on v2.9.7 "Tandem" — the desktop's features on the web and on phones, the release binaries built with every native feature, and a PPU A12 fix found by real games: Acclaim's MC-ACC games, the J.Y. ASIC and mapper 91 now count at their documented rates. Built on v2.9.6 "Roster" — seventeen mapper families written from their NESdev pages (174 → 191), GTROM promoted to Curated with a modelled flash chip whose saves persist, mapper 4's NES 2.0 submappers corrected (MMC6, NEC, MC-ACC, T9552), and the local commercial suites re-baselined after drifting unread since about v2.0.0. Built on v2.9.5 "Caliper" — every open accuracy item measured, then fixed or closed: four fixes red first (the apu_test frame-counter coincidence, the composite 2C02 scanline-0 sprite glitch, OAM DMA filling the PPU I/O latch, KS7032 at $6000), 49 unreferenced test ROMs gated, the MMC3 M2-edge filter lever tried and refuted, and a save-state epoch (PPU_SNAPSHOT_VERSION 11). Built on v2.9.4 "Plumb" — the records made true, and CI made to run what it only linted: v3.0.0 decided as the API major with a release-candidate core (ADR 0043), CI now running 63 feature-gated tests it never ran, the eight fuzz targets and a 70% line-coverage floor, the mapper tiers, store status and deferred-features catalogue corrected against the code, and the OAM-decay model recorded as derived from Mesen2. Built on v2.9.3 "Handset" — the old review threads closed and the mobile run prepared: every dependency moved to its newest release (egui 0.36 with wgpu 30, rcheevos 12.5.0), all 244 review threads left unanswered on PRs #7-#97 answered and the ten findings that still held fixed (Action 53 multicarts rebuilt to the NESdev spec, and a ROM header editor that no longer rewrites bytes you did not edit or saves mappers from 16 up as the wrong mapper), and the Android unit tests and the iOS renderer added to CI. Built on v2.9.2 "Candidate" — the full audit acted on, and the release-candidate pair: all 32 findings of a fifth audit have a verdict and 16 are fixed, save states keep the cartridge RAM of twelve board families they used to drop, the MiSTer core no longer loses an NMI raised inside a DMA, and both bitstreams are cut for the SuperStation One session. Built on v2.9.1 "Hone" — what the optimisation bars measure, and what clears them: the A/B tool had been timing the old code on both sides of every code comparison and is fixed, a two-screen Vs. cabinet saves about 9x faster, the off-die MiSTer build keeps CHR in its own SDRAM bank, and both bitstreams are pinned at fitter seed 2 and rebuild byte-identically. Built on v2.9.0 "Survey" — every audit re-checked, and the SuperStation One surveyed: a Power Cycle no longer erases your save, the off-die MiSTer build boots without the menu core, and 39 new audit findings are fixed or dispositioned. Built on v2.8.4 "Tether" — the MiSTer core's SDRAM build, made trustworthy: its controller now reads data on the edge the memory presents it (every off-die read would have been wrong on hardware, and only the new SDRAM timing constraints could see it), the power-up sequence and CAS-latency-3 reads follow the datasheet, the arbiter can no longer return the wrong byte or lose a write, the off-die bitstream builds from a script, both builds are swept and pinned at fitter seed 5, and the co-simulation ladder runs all 165 gates from a clean checkout. Built on v2.8.3 "Rivet" — the MiSTer core's reset, area and comments, measured: every reset is released on the clock that uses it and the timing analysis now checks each release, the CPU is about 4% smaller by two exact rewrites the fit report confirmed, four false comments are corrected, and the co-simulation ladder runs from a fresh checkout (164 of its 165 gates; the last needs a hand-built ROM no generator produces). Built on v2.8.2 "Solder" — the MiSTer core's on-die RTL, corrected against the oracle and the wiki: an MMC3 IRQ acknowledge is no longer lost to a same-edge counter clock, SNROM's battery RAM obeys its CHR-line enable, the triangle and noise drop a reload landing on a length clock, a $2002 read leaves the byte it returned on the data bus, and the emulator's MMC1 no longer ignores a reset written on the cycle after another write. Built on v2.8.1 "Gasket" — the libretro core fits the frontends around it: four-player games work through a Four Score option, a controller works again after its port leaves the Zapper, RetroArch no longer reads past the core's input-descriptor list, expansion audio no longer clips, the core declares UNIF images, and the Makefile honours PREFIX, platform=win, DEBUG and CARGO_TARGET_DIR. Built on v2.8.0 "Bulkhead" — the libretro core stops a fault at its own boundary: an internal error no longer closes RetroArch, save states survive plugging in a Zapper, closing a game withdraws its memory maps, the core loads from any libretro frontend, and the save state now carries the 2A03 internal data bus. Built on v2.7.6 "Recount" — the v2.7.5 deletions measured one at a time: the six performance proposals v2.7.5 bounded only together were each measured alone, where the benchmarks reach them: five are zero, and the sixth, the pulse sweep-mute check, bounds at about 0.2%, and the cheap byte-identical way to take it measured slower; the fast render path now asserts a rendering-history invariant it used to re-write; and the libretro buildbot builds macOS again and gains 32-bit Windows, 32-bit Linux and webOS targets. Built on v2.7.5 "Tally" — every audit claim closed with a measurement or a reason: the core audit's twelve performance proposals were closed, eleven of them by measurement, and one adopted (the audio buffer keeps its capacity between frames); 18 dead bus methods and the unused ApuBus trait are deprecated; and the core and frontend ledgers have no open row. Built on v2.7.4 "Pocket" — the mobile apps survive what a phone does to them: an internal error no longer closes the Android or iOS app, battery saves persist on both, the apps pause and give up audio when they should, and saves are written so a dying phone keeps the last good one. Built on v2.7.3 "Hearth" — the desktop and web frontends keep what they are given: battery saves persist on the desktop, a Lua script can no longer hang or exhaust the emulator, script HTTP cannot reach local services by default, and audio survives a device change. Built on v2.7.2 "Bankroll" — cartridge memory, every bank a cartridge has and nothing it has not: MMC1 reaches SUROM / SXROM, MMC5 banks its PRG-RAM, Namco 163 selects nametables, and $6000-$7FFF reads open bus where a board has nothing there. Built on v2.7.1 "Keepsake" — six cartridge boards now hand RetroArch their battery save instead of an empty one, every user file the frontend writes is written atomically, and three mapper files are now recorded as derived from Mesen2 and puNES. Built on v2.7.0 "Palisade" — a corrupt or hand-edited save state now fails at restore with a typed error instead of crashing the emulator one tick later, pulse 1 no longer mutes on the $4001 = $08 sweep idiom, and the save-state fuzz target can finally reach what it exists to find. Built on v2.6.23 "Pulse" — the access does not increment, it pulses the load already there: the CHR-during-rendering gate closes at 61,440 of 61,440 pixels, and no hardware has run any bitstream — built on v2.6.22 "Rigging", the instruments for the board, built before the board: AccuracyCoin reads back from hardware as bytes rather than as a photograph, the catalog turns out to carry 149 rows and 144 results so the headline no longer depends on when the run was sampled (at the same 144/144), and a rom_sha256 recorded in every golden manifest and compared to nothing is now rung 0 — built on v2.6.21 "Steward", which brought battery saves, the CHR-during-rendering gate RED at 32,861 of 61,440 pixels, and the deploy loop the runbook prescribed and v2.6.20 "Telltale", itself on v2.6.19 "Accession" and v2.6.18 "Errata", built on v2.6.17 "Terminus". The never-tagged v2.4.0 "Concordance" shipped inside v2.4.1 "Fabric" — this sentence had attached that fact to whichever release was current, carried forward by three mechanical version bumps, and said it of v2.4.2, v2.4.3 and v2.4.4 in turn.

RustyNES's emulation core descends from an extensively-documented accuracy program. Where this and related docs reference deep "v1.x"/"v2.x" engine narrative, read it as upstream engine lineage (engineering history), not as RustyNES release versions. Two distinct "v2.0"s exist and must not be conflated: the engine-lineage v2.0 master-clock work shipped as RustyNES v1.0.0, while RustyNES's own v2.0.0 "Timebase" (2026-07-03) is the later release that replaced that same scheduler. The current release is v3.5.0.


Design Philosophy

1. Accuracy first, speed second

Every CPU cycle is a real bus access, driven from a single canonical cycle counter with a split-around-the-access PPU catch-up, on a master-clock-precise timebase -- the v2.0.0 "Timebase" model, which replaced the earlier five-counter PPU-dot lockstep outright. The accuracy it buys is the same dot-resolution accuracy; the mechanism is not. This makes sub-instruction edge cases (sprite-zero hit at a precise dot, mid-scanline scroll writes, mapper IRQ timing) correct by construction rather than patched per-quirk. Performance work is byte-identical by construction and gated by a commercial-ROM regression oracle.

2. Determinism as a contract

Same seed + ROM + input ⇒ bit-identical framebuffer and audio. No system time, thread scheduling, or OS RNG touches the core. This single contract is what makes save-states, rewind, frame-perfect TAS replay, and rollback netplay all correct.

3. Safe Rust by default

The chip stack is #![no_std] + alloc and free of unsafe except at FFI boundaries (RetroAchievements via the vendored rcheevos C library) and one native priority hook — each guarded by a // SAFETY: comment. The whole stack cross-compiles to thumbv7em-none-eabihf in CI.

4. Test ROMs are the spec

The blargg / kevtris / mmc3_test_2 / AccuracyCoin suites are the closed-form definition of "cycle-accurate." When the docs and a passing ROM disagree, the ROM wins.

5. Modular and reusable

A one-directional crate graph keeps each chip (rustynes-cpu, rustynes-ppu, rustynes-apu, rustynes-mappers) independently usable, fuzzable, and benchmarkable; adding a mapper touches no chip code.


Accuracy

Test Result
AccuracyCoin 100.00% (146/146) (RAM-direct decoder) at upstream f5f41dc2 since v3.1.0, whose re-sync showed that the 144/144 reported from v2.6.18 to v3.0.1 was overstated, because the older ROM's Misaligned OAM behavior fail path fell through to a pass and hid a real failure. Before it: the 2026-09 upstream re-sync grew the battery to 144 assigned tests and v2.6.18 closed the last one (Advanced Sprite Evaluation :: Frozen OAM2 Increment), so KNOWN_FAILING is empty; the two newest upstream PPU tests ("ALE + Read", "Hybrid Addresses") were closed by the v2.0.3 2-cycle-ALE promotion
nestest 0-diff against the Nintendulator golden log
blargg / kevtris / mmc3_test_2 Green
Commercial-ROM oracle 60-ROM byte-identical regression gate + extended visual survey
Region NTSC / PAL / Dendy with exact CPU:PPU ratios (3:1, 3.2:1)

docs/STATUS.md is the authoritative, always-current pass-count and mapper-coverage matrix.


Emulation Approach

RustyNES uses cycle-accurate emulation rather than scanline-based shortcuts. Since v2.0.0 "Timebase" the scheduler is a single canonical cycle counter in which every CPU cycle is a real bus access, with a split-around-the-access start_cycle/end_cycle PPU catch-up (ADR 0002 / ADR 0029). The APU advances every other CPU cycle.

This paragraph described the retired five-counter dot-lockstep model — "the scheduler advances one PPU dot at a time" — which v2.0.0 replaced outright and which is no longer a path in the code. Corrected in v2.4.5, found by review rather than by a gate: release anchors are pinned by release_anchor_audit, and ordinary architecture prose is not. The Bus owns all mutable device state, and the CPU borrows it during tick() — the architectural choice (per the TetaNES postmortem) that avoids the borrow-checker fight a split bus creates. See ARCHITECTURE.md and docs/scheduler.md.


Target Audience

  1. Emulation enthusiasts — reference-grade accuracy with a modern, themeable desktop UX and an in-app debugger.
  2. The TAS community — frame-perfect deterministic .rnm movie record / playback / branching built directly on the determinism contract.
  3. Netplay users — GGPO-style rollback netplay (2–4 players), native (UDP) and in the browser (WebRTC).
  4. Homebrew developers — broad mapper coverage (208 families), FDS, an instruction/PPU/memory debugger, and an embeddable no_std core.
  5. Rust developers — a clean, modular workspace and a reusable 6502 CPU crate.

Feature Summary

Area What ships today
Accuracy One-clock scheduler (v2.0.0 "Timebase"), master-clock timebase, AccuracyCoin 146/146 (100.00%) on the battery as re-synced to upstream f5f41dc2 in v3.1.0 (the 144/144 before it hid a masked failure), nestest 0-diff
Cartridges 208 mapper families incl. expansion audio (VRC6/VRC7-OPLL/Sunsoft 5B/N163/MMC5)
Platforms iNES / NES 2.0, Famicom Disk System (real-BIOS boot, read/write, multi-side), Vs. System / PlayChoice-10 RGB
Online Rollback netplay, UDP (native) + WebRTC (browser), 2–4 players
Achievements RetroAchievements (opt-in, native-only) — login, hardcore, toasts, badges
Tooling TAS movies, save-states, rewind, run-ahead, Game Genie + raw-RAM cheats, egui debugger
Input Standard pad, Four Score (4-player), Arkanoid Vaus, Zapper; keyboard + USB gamepad
Frontend winit + wgpu + cpal + egui; display-sync pacing, dedicated emu thread, low-latency audio; desktop UX shell (menu bar, recent ROMs, tabbed settings, themes, 8:7 pixel-aspect, status bar); optional NTSC filter
Web WebAssembly / GitHub Pages build (winit+wgpu and a lightweight canvas embed)

Technical Highlights

Rust-specific advantages

  • A #![no_std] + alloc chip stack proven against core + alloc only in CI — embeddable beyond the desktop.
  • Memory and thread safety enforced by the compiler; unsafe confined to FFI and one priority hook, each documented.
  • Strong typing (newtype addresses, bitflag status registers) catches whole classes of bug at compile time.

Performance

  • A dedicated native emulation thread isolates emulation cadence from UI/GPU/file-I-O stalls.
  • A lock-free SPSC audio ring with dynamic rate control, late-latched input, and a display-sync pacing matrix deliver the smoothest, lowest-latency play.
  • A MapperCaps capability cache, a pixel-emit LUT, fat LTO, and auto-vectorization keep the rendering-heavy path well under the NTSC frame deadline — all byte-identical by construction.

Cross-platform

Native Linux / macOS / Windows plus a browser build, all from one winit + wgpu + cpal + egui frontend, with multi-platform CI (incl. wasm32) gating every change.


Conclusion

RustyNES v1.0.0 delivers reference-grade NES accuracy in safe, modular Rust, wrapped in a complete modern application and online/TAS/achievement platform. See ARCHITECTURE.md for the system design, ROADMAP.md for delivered milestones and post-1.0 directions, and docs/STATUS.md for the live status matrix.

Related documentation