Precise. Pure. Powerful.
RustyNES is a cycle-accurate Nintendo Entertainment System emulator written in
pure Rust. It targets the Mesen2 / higan / ares accuracy bar — tight, lockstep
scheduling at PPU-dot resolution on a master-clock-precise timebase — clearing
AccuracyCoin 100% (141/141) and matching the Nintendulator golden log on
nestest with zero diff. (As of v2.0.3 every assigned test passes, including the
two newest upstream PPU tests, "ALE + Read" and "Hybrid Addresses", via the promoted
2-cycle-ALE fetch model — ADR 0030.)
Development note — AI-assisted: RustyNES is heavily AI-assisted software, built with LLM tooling under a human-directed, test-driven workflow (public test ROMs as the oracle, a
no_stdcore, and continuous CI). Seedocs/originality-and-provenance.mdfor what that means for originality and licensing, and the Acknowledgments for the references and components it builds on. Accuracy claims are meant to be checked by running the public suites, not taken on faith; comparisons to other emulators are comparisons, not a claim of being "better."
Beyond reference accuracy, RustyNES is a complete, modern emulation platform:
174 mapper families covering the vast majority of the commercial library (plus a
UNIF .unf cartridge loader), the full Famicom Disk System (real-BIOS boot with a
timed disk-head model), Vs. System / PlayChoice-10 arcade games in true RGB,
GGPO-style rollback netplay (native UDP and browser WebRTC, 2-4 players),
RetroAchievements, a native Libretro core for RetroArch, a scriptable TAStudio piano-roll TAS editor with .fm2 /
.bk2 / .fcm / .fmv / .vmv movie interop, editing-capable debug tools
(palette / nametable / CHR / OAM writeback, an iNES / NES 2.0 header editor, an inline
6502 assembler), save states with rewind, run-ahead latency reduction, a Mesen2-class
debugger (expression / conditional breakpoints, R/W/X watchpoints, a hex editor, RAM
search, a callstack, .dbg source maps), A/V recording, HD-pack video + audio
(with an HD-Pack Builder), a shader / filter ecosystem, and a localized
(i18n) UI — all on a strict bit-determinism contract. The frontend is pure Rust (winit + wgpu +
cpal + egui) with native binaries for Linux, macOS, and Windows, plus a WebAssembly
build that runs in the browser.
Try it in your browser — no install required.
RustyNES combines accuracy-first emulation with modern features and the safety guarantees of Rust. Whether you are a casual player, a TAS creator, a speedrunner, or a homebrew developer, RustyNES provides a comprehensive and faithful platform for NES emulation.
Key differentiators:
- Reference-grade accuracy — a from-scratch core on a
u64master clock with run-to-timestamp catch-up; region-exact 3:1 NTSC/Dendy and 3.2:1 PAL clock ratios; sub-instruction PPU events visible to subsequent CPU code. - Determinism as a hard contract — same seed, ROM, and input sequence yield a bit-identical framebuffer and audio. This is what makes save-state round-trips, regression testing, and rollback netplay correct by construction.
- Modern features — RetroAchievements, rollback netplay, a scriptable TAStudio, run-ahead, display-sync pacing, an Android app, and a Mesen2-class, editing-capable debugger (read-only by default, determinism-preserving).
- Safe, modular Rust — the chip stack is
no_std + allocwith a one-directional workspace graph, so each component (CPU, PPU, APU) is independently fuzzable and benchmarkable. The onlyunsafelives behind opt-in feature boundaries.
| Feature | Description |
|---|---|
| Cycle-Accurate | Master-clock-precise CPU / PPU / APU — AccuracyCoin 100% (141/141), nestest 0-diff |
| One-Clock Timebase | A single canonical cycle counter, every CPU cycle a real bus access, with a split-around-the-access PPU catch-up |
| 174 Mapper Families | NROM through MMC5, the full VRC line, Sunsoft FME-7, Namco 163, Taito, J.Y. Company ASIC, reusable-ASIC multicarts (FK23C / COOLBOY / MINDKIDS / Sachen / Waixing / Kaiser), and Vs.-System boards — classified Core / Curated / BestEffort behind a CI accuracy-honesty gate — plus a UNIF (.unf) loader |
| Famicom Disk System | .fds games with real-BIOS boot, writable disks, side-swapping, a timed disk-head model, and 2C33 wavetable audio |
| Vs. / PlayChoice-10 | Arcade ROMs in true 2C03 / 2C04 / 2C05 RGB with per-game DIP presets; Vs. DualSystem two-screen presentation on desktop |
| RetroAchievements | Native rcheevos integration: achievements, leaderboards, rich presence, hardcore mode |
| Rollback Netplay | GGPO-style rollback for up to 4 players over UDP or browser WebRTC — room-code / TURN traversal, matchmaking / lobby, and spectators |
| TAStudio + Movie Interop | A piano-roll TAS editor (drag-paint grid, save-state greenzone, lag log, markers, forkable branches) with .fm2 / .bk2 / .fcm / .fmv / .vmv import and the native .rnm format |
| Run-Ahead & Rewind | Input-lag-hiding run-ahead and a tiered (Zwinder) rewind window, on the deterministic snapshot path |
| Mesen2-Class Debugger | Expression / conditional breakpoints, R/W/X watchpoints, a hex editor, RAM search, a callstack, and .dbg source maps — editing-capable (palette / nametable / CHR / OAM writeback, header editor, inline 6502 assembler), read-only by default |
| Lua Scripting | Sandboxed Lua 5.4 — memory / state access, frame & access callbacks, a tastudio.* API, HUD overlay, and host-IPC automation (opt-in) |
| Shaders & HD Packs | An NES-NTSC composite / S-video filter, a composable CRT / scanline shader stack (CRT-Royale / guest-advanced / Megatron look), a generated NTSC palette, custom .pal palettes, and a Mesen-style HD-pack loader + builder (video + OGG audio) |
| Cheats & Peripherals | A ~10,800-code Game Genie database with per-game nomination + encoder, raw RAM cheats, and a broad peripheral set (Four Score, Zapper, Arkanoid, Power Pad, keyboards, mouse) |
| A/V Recording | Synchronized video + audio capture to .mp4 / .mkv via an ffmpeg pipe (opt-in, output-only) |
| NSF / NSFe Player | Chiptune playback through the real APU + expansion synths, honoring non-60 Hz play-speed dividers |
| Android & iOS Apps | Complete native apps on the byte-identical core — touch + hardware controllers, save-states, netplay, RetroAchievements, and the shader stack (sideload / TestFlight; free store listing possible later) |
| Libretro Core | A cycle-accurate rustynes_libretro core for RetroArch (RetroAchievements, dynamic audio sync, deterministic rollback / save-state, region-correct NTSC / PAL / Dendy pacing, FDS multi-disk swapping, Game Genie cheats, and the NES Zapper on ports 1-2) |
| Pure Rust | winit + wgpu + cpal + egui frontend; safe no_std + alloc chip stack |
A cross-section of the commercial library running pixel-accurately on RustyNES — launch classics like Donkey Kong, Excitebike, and Super Mario Bros.; the Famicom Disk System's Kid Icarus; Konami's Castlevania and Contra; the Mega Man boss-select; and Mike Tyson's Punch-Out!! — spanning NROM up through MMC3 / MMC5, FME-7, and the full VRC line, plus Vs.-arcade RGB.
The full per-mapper visual corpus lives in
screenshots/external/ (Core / Curated) and
screenshots/besteffort/ (BestEffort) — boot / title /
gameplay frames spanning the bulk of the 174 mapper families.
- Master-clock-precise scheduler. A
u64master clock drives the CPU, PPU, and APU off the fundamental NES timebase with run-to-timestamp catch-up (the TetaNES / Mesen2 model). This is the central architectural choice and the reason mid-instruction PPU events — a sprite-zero hit at a precise dot, an MMC3 IRQ at a PPU dot, a mid-scanline scroll write — work without per-quirk patches. - Cycle-accurate 6502 CPU — all 256 opcodes including the full unofficial set (incl. the unstable SH* / TAS / LAS / XAA family), per-cycle bus interleaving, cycle-exact interrupt-sample timing, and sub-instruction DMC/OAM DMA via one unified dispatch.
- Cycle-accurate 2C02 PPU — per-dot scheduling, the full cycle-resolution
sprite-evaluation FSM (including the hardware
n+moverflow increment bug), the background-fetch pipeline, thePPUMASK→dot-skip delay, and a rendering-time$2007state machine. - Cycle-accurate 2A03 APU — the non-linear lookup mixer, 256-phase × 32-tap Blackman-windowed sinc synthesis (SFDR 81.6 dB), a 3-stage analog filter chain, and the DMC byte timer on the shared master clock.
- 174 mapper families covering the bulk of the licensed library — NROM, all
MMC1-5, the full VRC1/2/4/6/7 line (incl. VRC6 and VRC7 expansion audio), Sunsoft
FME-7/1/2/3/4 (+ 5B audio), Namco 163 (+ wavetable), the Taito
TC0190/TC0690/X1-005/X1-017, J.Y. Company ASIC boards, and the
Irem/Jaleco/Bandai/Tengen and Vs.-System mappers — classified Core / Curated /
BestEffort behind a CI accuracy-honesty gate. A UNIF (
.unf) cartridge loader resolves board names to the corresponding mapper. Seedocs/mappers.md. - Famicom Disk System —
.fdsgames with a user-supplieddisksys.romBIOS: the disk drive and IRQs, writable disks (.fds.sav,F9side-swap), 2C33 wavetable audio, a timed disk-head position / not-ready model,$4032drive-status auto-insert, and a per-game CRC quirk table. Real-BIOS boot works — Zelda, Metroid, and others boot into the game. - Vs. System / PlayChoice-10 — the 2C03 / 2C04 / 2C05 RGB PPUs with per-game DIP presets and exact palettes; real arcade ROMs render in true RGB.
- RetroAchievements (opt-in, native-only) — login, achievements, leaderboards, rich
presence, and hardcore mode, via the vendored MIT
rcheevoslibrary. - Rollback netplay — GGPO-style rollback over UDP for up to 4 players (predict →
advance → roll back on the deterministic core), plus a browser WebRTC mesh with a
deployable signaling / STUN bundle (
deploy/), room-code / TURN traversal, matchmaking / lobby, and read-only spectators. - TAS + TAStudio — frame-perfect deterministic record / replay in the versioned
.rnmformat, plus a Mesen2 / BizHawk-class piano-roll editor: a drag-paint button grid, a save-state greenzone for instant seeking, a lag log, markers, forkable branches, and.rnmprojprojects. Imports FCEUX.fm2/ BizHawk.bk2/.fcm/.fmv/.vmv. - Save state, rewind, run-ahead — instant save / load, a thumbnail manager, a tiered (Zwinder) rewind window, and input-lag-hiding run-ahead — all on the deterministic snapshot path.
- Speed, pacing, audio — 25 %–300 % speed presets, hold-to-fast-forward, frame advance;
an
auto/display/vrr/wallclockdisplay-sync matrix; and a lock-free audio ring with dynamic rate control, per-channel mutes, and a 5- / 20-band equalizer. - Lua scripting (opt-in, native-only) — a sandboxed Lua 5.4 engine: read / write
memory, inspect state, react to per-frame / per-access events, draw an HUD, and drive
movies (
emu.run/emu.frameadvance) and the piano-roll (tastudio.*), with a host-mediated IPC sandbox. The browser build runs an experimentalpiccolobackend (observational, never in the determinism oracle). Seedocs/scripting.md. - Cheats + peripherals — a Game Genie encoder plus a bundled ~10,800-code database with
per-game nomination (header-robust CRC matching), raw RAM cheats, and a broad peripheral
set (standard pad, Four Score, Arkanoid Vaus, Zapper, Power Pad, SNES mouse, Family BASIC
and Subor keyboards, Family Trainer, Hyper Shot). Turbo / autofire, an all-device
input-display overlay, and USB gamepads (
gilrs) with deadzone + hot-plug. - Debugger + devtools (opt-in
debug-hooks) — a read-only CPU / PPU / APU / memory / OAM / mapper inspector by default; opt-in expression / conditional breakpoints, R/W/X watchpoints, a watch window, conditional + cycle trace, an event viewer, a full hex editor (poke / freeze / heatmap / find), RAM search, and a callstack with step in / over / out — all determinism-preserving when off. - A/V recording (opt-in
av-record, native-only) — capture to.mp4/.mkvvia an externalffmpegpipe; a read-only tap on the produced framebuffer / audio, so it never touches the core.
- Editing-capable debug tools — the inspectors become editors: palette / nametable /
CHR / OAM writeback, an iNES / NES 2.0 header editor, and an inline 6502 assembler; plus
ca65/cc65.dbgsource maps (and.sym/.mlb/.nl) for source-level debugging. - Host IPC / automation — a host-mediated
comm.*/client.*/userdata.*sandbox lets an external process drive and observe the emulator over IPC for CI harnesses, behind a documented security posture. - HD packs — an HD-Pack Builder authors Mesen-format packs from the running game, and
the loader mixes HD-pack
<bgm>/<sfx>OGG audio through$4100. - Audio depth — stereo panning, Schroeder reverb + crossfeed, an output-device picker, and per-context (game / menu) volume.
- Per-game config + i18n — a
<rom>.jsonoverlay (region / mapper / mirroring overrides), a DIP-switch editor, a lag-frame counter, and a compile-time i18n catalog (English default + universal fallback; Spanish shipped).
- Video filters + shaders — a full NES-NTSC composite / S-video filter and a composable
CRT / scanline shader stack (curvature, scanlines, aperture mask; LMP88959 composite,
hqNx / xBRZ upscalers, and a constrained RetroArch
.slangp/.cgpimporter), plus a three-rung composite-shader ladder (blur → LMP88959 → Bisqwit per-dot) with live emulator-synced dot-crawl and custom.palpalettes — all display-only and off by default, so the pre-shader framebuffer stays byte-identical. Seedocs/frontend.md. - Generated NTSC palette (opt-in) — an in-core synthesizer builds the 64-entry palette
from a 2C02 composite model (tunable saturation / hue / contrast / brightness / gamma),
byte-identical across all targets via
libmand locked by a committed golden. - APU filter model — pick the analog filter:
nes(default, authentic front-loader),famicom(fuller low end), orclean(Mesen2-like) — tonal-only, byte-identical on the default. - NSF / NSFe player — chiptune playback through the real APU and expansion synths, with a
track selector and metadata, honoring non-60 Hz play-speed dividers and the chunked
NSFEcontainer. - OAM decay (opt-in) — Mesen2-modeled dynamic-RAM decay of un-refreshed OAM rows; off by default (byte-identical), deterministic when on, and round-trips the save-state.
The browser build runs the same core with web-specific glue (native builds are byte-identical):
- Lua in the browser — the experimental
piccolobackend runs from a.luapicker / paste box (observational, off by default, never in the determinism oracle). - File System Access API — TAS
.rnmexports use a native "Save As" on Chromium, with a download fallback on Firefox / Safari. - Gamepad API —
navigator.getGamepads()is polled each frame at the same late-latch as touch / keyboard, so it records and replays identically. - PWA + share-links — an installable, offline-capable manifest + service worker (within a
5 MiB budget), plus
?settings=URL share-links for a curatedConfigsubset.
RustyNES runs as a complete native Android app on the byte-identical core (so
AccuracyCoin holds 141/141 as on desktop), built on a shared rustynes-mobile
UniFFI bridge, a rustynes-android JNI layer, and a Jetpack Compose shell:
- Rendering + audio — wgpu on a
SurfaceView, reusing the desktop WGSL CRT / scanline / NTSC shaders (shared viarustynes-gfx-shaders), plus low-latencyAudioTrack. - Input — a multi-touch on-screen NES controller (foldable-aware and resizable) and full hardware-gamepad support (players 1–4, hot-plug, per-pad remapping, turbo).
- Library + state — a SHA-256-keyed box-art ROM library with SAF import, save-states and battery-SRAM, and save-on-background / auto-resume.
- Connectivity — Lua scripting, RetroAchievements, and direct-IP / LAN plus
CGNAT / TURN room-code rollback netplay over the same
rustynes-script/rustynes-ra/rustynes-netplaycores as desktop. - Platform polish — adaptive / foldable / TV (Leanback) layouts, Material You and EN/ES i18n, screenshot / MP4 capture, Picture-in-Picture, widgets, and accessibility (high-contrast + Okabe-Ito).
The apps ship now as GitHub-Releases / sideload, full-featured; a possible
free Google Play / F-Droid listing — a free app with the foss / play flavor
split distinguishing pure-AOSP builds from optional free Google Play services
(achievements, Cast, Integrity, in-app update, cloud save) — is a later step with
no fixed version (see Roadmap). RustyNES is permanently open-source and
income-free (ADR 0035): no ads, no tracking, no paid unlock. Details in
docs/android.md.
RustyNES runs as a native iOS / iPadOS app on the byte-identical core (maintaining the same 141/141 AccuracyCoin bar as desktop), built on the shared rustynes-mobile UniFFI bridge and a native SwiftUI shell:
- Rendering + audio — Metal via
wgpuwith the same full WGSL shader pipelines (CRT, NTSC, Bisqwit) and ProMotion pacing, plus a low-latency CoreAudio hot path. - Input — multi-touch on-screen pad (NES-001 style), responsive sizing, GameController framework for P1–P4 (hot-plug), and Core Haptics.
- Connectivity & Tooling — room-code netplay (CGNAT/TURN) and LAN rollback, RetroAchievements, iCloud save-state sync (CloudKit), Lua console, and power-user tooling (TAS
.rnmmovies,.palpalettes,.zipROMs, HD-pack loading). - Platform polish — ReplayKit capture, Game Center, accessibility, EN/ES i18n, and a 4-slot save-state manager. (No monetization — the app is free; see ADR 0035.)
The apps are currently distributed via TestFlight; a future free App Store listing (no ads, no purchase) is possible but has no fixed version. Details in docs/ios.md.
Pre-built binaries for the latest release are available on the
Releases page, built automatically
for aarch64 macOS (Apple silicon), x86_64 Linux, and x86_64 Windows. Other targets
(Intel macOS, Linux ARM64, Android) build from source using the instructions below.
# Linux / macOS
tar xf rustynes-<tag>-<target>.tar.gz && ./rustynes path/to/rom.nes
# Windows (PowerShell)
Expand-Archive rustynes-<tag>-x86_64-pc-windows-msvc.zip; .\rustynes.exe path\to\rom.nesPrerequisites:
- Rust 1.96 — pinned via
rust-toolchain.tomland auto-installed by rustup. - Linux desktop dependencies for
winit/wgpu/cpal/egui(see below). - Git.
# Clone the repository
git clone https://github.com/doublegate/RustyNES.git
cd RustyNES
# Build the workspace (release)
cargo build --release --workspace
# Run a ROM you legally own (or launch bare and use F12 / drag-and-drop)
cargo run --release -p rustynes-frontend -- path/to/rom.nes
# Optional: build with RetroAchievements (needs a C compiler for vendored rcheevos)
cargo run --release -p rustynes-frontend --features retroachievements -- path/to/rom.nes
# Maximal NATIVE build — the "cargo --full equivalent". The `full` feature
# aggregates every native feature (RetroAchievements + Lua scripting + host IPC +
# HD-pack + debugger telemetry + A/V recording). Aliases make it a one-liner:
cargo full-run path/to/rom.nes # run the most fully-featured desktop binary
cargo full-run --fullscreen rom.nes # the alias ends in `--`, so flags forward to the binary
cargo full-build # build it (= --release -p rustynes-frontend --features full)The full build is purely opt-in — the default/shipped build and the emulation
core are unchanged. The WASM-only features (script-wasm, browser-cheevos,
wasm-canvas) are deliberately excluded, since full targets a native binary.
The frontend opens a 256×240 window (scaled, with 8:7 pixel-aspect correction), starts audio via the OS default device, and runs the ROM.
The native binary ships a clap 4 CLI with styled --help, a help subcommand,
shell completions, and an interactive terminal help browser:
rustynes --help # styled usage + examples + keyboard summary
rustynes help # browse all topics (interactive TUI on a terminal)
rustynes help mappers # one topic, printed (also works piped: `… | less`)
rustynes completions fish # print a shell-completion scriptHelp topics: controls, hotkeys, gamepad, features, mappers, config,
scripting, netplay, about. The interactive browser is behind the default-on
help-tui cargo feature; piped / non-terminal output falls back to a static page.
Ubuntu / Debian:
sudo apt-get install -y libxkbcommon-dev libwayland-dev libxkbcommon-x11-dev libasound2-dev libudev-devCachyOS / Arch:
sudo pacman -S --needed libxkbcommon wayland alsa-lib systemd-libsmacOS / Windows: no extra system dependencies are required for the default build.
The optional retroachievements feature additionally needs a C compiler for the
vendored rcheevos sources.
A hosted demo is live at
doublegate.github.io/RustyNES. To build
it yourself you need trunk (cargo install trunk):
cd crates/rustynes-frontend/web
trunk serve # dev server at http://127.0.0.1:8081
trunk build --release # the full winit + wgpu + egui build in ./dist
# Or a lightweight canvas-2D embed:
trunk build --release --no-default-features --features wasm-canvasThe desktop frontend frames the NES image with an always-on menu bar (top) and
status bar (bottom); the egui debugger is a separate overlay toggled with `.
Everything has a keyboard shortcut, but nothing requires one.
- Menu bar — File (Open ROM, Open Recent, save / load state, a ten-slot (0–9) Save Slot picker, a thumbnail Save States… manager, Take Screenshot, Copy Screenshot to Clipboard), Emulation (Pause, Reset, Power Cycle, Speed 25–300 %, Run-Ahead 0–3, the region label, Vs. Insert Coin / FDS Swap Disk Side when relevant), Tools (Cheats, TAS Movies, the TAStudio piano-roll editor, the Audio Mixer, Record A/V, Netplay, RetroAchievements, a read-only ROM Info browser, and the Performance Monitor — opened as floating panels; on native, every tool panel also offers a Detach button that pops it out into a real, separate OS window you can move to another monitor), View (Settings, Theme, 8:7 Pixel Aspect, Hide Overscan, Fullscreen, Window Size 1x–4x, Show FPS, Pause When Unfocused, Show Menu Bar), Debug (the debugger overlay + per-chip panels), and Help (Keyboard Shortcuts, About).
- Status bar — ROM name, region, mapper, run-ahead depth, Running / Paused / Netplay state, the current speed when not 100 %, and the FPS readout.
- Settings window — a tabbed Display / Audio / Input / Advanced dialog (View → Settings…) with a live master-volume slider + mute, per-APU-channel mutes, a gamepad deadzone slider, live theme / pixel-aspect / overscan / FPS toggles, and a Reset-to-Defaults button per section.
- Quality-of-life — 25 %–300 % emulation-speed presets, hold-to-fast-forward (audio muted) and single-frame advance while paused, a thumbnail save-state browser, integer window-size presets (1x–4x), optional overscan cropping, optional pause-when-unfocused, light / dark / system themes, a pause-dim "PAUSED" overlay, a recent-ROMs list (missing files greyed out), controller hot-plug toasts, and a first-run Welcome modal.
Every binding is TOML-rebindable (and remappable in the in-app Settings); see the
controls guide for the full schema. USB gamepads
auto-bind to player 1 (Xbox-style: South = A, West = B, plus Start, Back / Select, and
the D-pad), and you can drag-and-drop a .nes / .fds onto the window to load it any time.
| Action | Player 1 | Player 2 |
|---|---|---|
| D-Pad | Arrow keys | W / A / S / D |
| A / B | Z / X | Q / E |
| Start / Select | Enter / Right-Shift | P / L |
| Action | Key | Action | Key |
|---|---|---|---|
| Pause / Resume | Space | Save / Load state | F1 / F4 |
| Fast-forward (hold) | Tab | Rewind (hold) | F5 |
| Frame-advance (while paused) | \ (backslash) |
Reset / Power-cycle | F2 / F3 |
| Speed up / down / reset | = / - / 0 | Open ROM | F12 |
| TAS record / play / branch | F6 / F7 / F8 | Swap disk side (FDS) | F9 |
| Toggle menu bar | M | Insert coin (Vs.) | F10 |
| Toggle debugger | ` (backtick) |
Fullscreen | F11 |
| Quit / exit fullscreen | Esc | Save-state slot | 0 – 9 |
RustyNES is a Cargo workspace of focused crates. Three load-bearing decisions, detailed
in docs/architecture.md and docs/scheduler.md:
- A shared master-clock timebase. The CPU advances a
u64master clock by the region'scpu_dividerper cycle; the PPU is caught up tomaster_clock − ppu_offsetin both halves of every access (APU and DMA share the same clock). This makes the region-exact 3.2:1 PAL ratio and cycle-exact interrupt / DMA timing expressible, and makes sub-instruction PPU events work naturally. - The Bus owns everything mutable.
rustynes-core::Busholds the PPU, APU, mapper, WRAM, controllers, and open-bus latch; the CPU borrows&mut Busduringtick(). This single choice avoids the borrow-checker fight the alternative creates. - A one-directional workspace graph.
rustynes-cpuhas norustynes-ppuorrustynes-apudependency; each chip is fuzzable and benchmarkable in isolation.
| Crate | Role |
|---|---|
rustynes-cpu |
Cycle-accurate 6502 / 2A03 CPU core |
rustynes-ppu |
Dot-level 2C02 PPU |
rustynes-apu |
Hardware-accurate 2A03 APU with band-limited synthesis |
rustynes-mappers |
174 mapper families + expansion audio + UNIF loader |
rustynes-core |
Integration layer: Bus, scheduler, console, save states |
rustynes-script |
Sandboxed Lua 5.4 scripting engine (native mlua, wasm piccolo) |
rustynes-frontend |
winit + wgpu + cpal + egui app (binary: rustynes) |
rustynes-netplay |
GGPO-style rollback netcode (UDP + WebRTC) |
rustynes-cheevos |
RetroAchievements rcheevos FFI (opt-in, native-only) |
rustynes-ra |
Shared RetroAchievements session state (RaClient, native-only) |
rustynes-libretro |
Native Libretro API core wrapper (RetroArch) |
rustynes-gfx-shaders |
Shared WGSL presentation shaders (desktop + Android renderers) |
rustynes-hdpack |
HD-pack loader + compositor + HD audio (shared desktop + mobile) |
rustynes-mobile |
UniFFI bridge for the mobile platforms (Android, and v1.9.0 iOS) |
rustynes-android |
Android JNI glue over the mobile bridge |
rustynes-test-harness |
Integration tests and the accuracy / commercial-ROM oracles |
crates/ Cargo workspace: the crates above
docs/ Implementation specs, ADRs, the user guide,
STATUS.md (single source of truth), and release notes
deploy/ Docker / compose for the browser-netplay signaling server + STUN/TURN
ref-docs/ Deep-research NES hardware reference
tests/ Integration tests + vendored CC0 / MIT / zlib test ROMs (no commercial ROMs)
screenshots/ Committed commercial-game visual corpus + showcase montages
scripts/ Regression-bisect + ROM-survey tooling
fuzz/ cargo-fuzz harnesses
RustyNES demonstrates reference-grade emulation accuracy. The single validated scheduler is the master-clock core; the RAM-direct AccuracyCoin decoder over 141 assigned tests is the authoritative source.
| Suite | Result |
|---|---|
| AccuracyCoin | 100% (141/141) — every assigned test passes, including the two newest upstream PPU tests ("ALE + Read", "Hybrid Addresses"), via the promoted 2-cycle-ALE fetch model (v2.0.3, ADR 0030) |
| nestest | 0-diff vs the Nintendulator golden log |
blargg cpu_interrupts_v2 |
5/5 strict · SH* 6/6 |
blargg blargg_apu_2005 |
11/11 NTSC — length counters + table, frame-IRQ flag and timing, clock jitter, length timing in both frame-counter modes, reset timing, length halt/reload ordering |
blargg pal_apu_tests |
10/10 PAL — the region-calibrated rebuild of the same corpus, forced to PAL timing |
region_timing |
4/4 (PAL 3.2:1) · $2007 Stress 170/170 |
| Commercial-ROM oracle | 99 titles (60-ROM gate + 39-title survey), SHA-256-pinned, byte-identical |
The commercial-ROM oracle is a regression gate, not a correctness check — a visual
99-title survey is what catches rendering bugs. The wasm32 target shares the exact
emulator core, so the browser build runs the same scheduler. The sole strict
expected-fail is mmc3_test_2/4 sub-test #3 (a 1-PPU-clock MMC3 reload-pending
bracket that affects no AccuracyCoin score and breaks no commercial game). The full
per-suite breakdown, the mapper coverage matrix, and the version policy live in
docs/STATUS.md.
v1.6.0's off-axis accuracy pass (Workstream D) was a pin-test-first audit that
confirmed the cycle-accurate engine already models the dot/CPU-cycle-granular off-axis
cluster — the DMC/OAM-DMA ↔ $4016 / $4017 controller-read double-clock / dropped-bit
conflict, the $2007 (PPUDATA) read-during-active-rendering window with its deferred
state-machine reload and v-increment glitch, and the buggy sprite-overflow n+m
evaluation with the three-group open-bus / MDR decay timer — all verified by committed
oracles with no engine change. Those residuals were subsequently taken up by the
v2.0.0 "Timebase" one-clock scheduler rewrite (ADR 0002 / ADR 0029) and the v2.1.0
accuracy-remediation pass, which closed the MMC3 R1/R2 scanline-IRQ residual by design
(the full disposition of every remaining approximation lives in
docs/accuracy-ledger.md).
Everything added since the v1.0.0 core is additive and off-by-default — each new
workstream is a frontend tap or an opt-in feature flag, so the shipped / native /
no_std / wasm builds stay byte-identical — with two deliberate exceptions to
that byte-identity guarantee: the v2.0.0 one-clock "Timebase" scheduler and the
v2.0.3 promotion of the 2-cycle-ALE PPU fetch model (ADR 0030), which together
bring AccuracyCoin to 100% (141/141) — both newest upstream PPU tests, "ALE +
Read" and "Hybrid Addresses", now pass on the shipped default.
A note on test counts: RustyNES is validated by closed-form test ROMs (AccuracyCoin, nestest, blargg, mmc3_test, Holy Mapperel) and a commercial-ROM oracle, not by a headline unit-test number. When a doc and a passing test ROM disagree, the ROM wins — that is the project's definition of "cycle-accurate."
RustyNES's accuracy claims are meant to be checked, not taken on faith: run the
public suites yourself (AccuracyCoin, nestest, blargg, Holy Mapperel — see
Compatibility & Accuracy). Any comparison to
another emulator is exactly that — a comparison against a reference RustyNES was
measured against (e.g. Mesen2 / higan / ares — see the Acknowledgments) —
and is not a claim that RustyNES is "better." For an honest
account of where the project advances, diverges from, or independently re-derives
NES emulation technique (and its license posture), see
docs/originality-and-provenance.md.
The screenshot below is an early-milestone image — Super Mario Bros. at "first light," among the first commercial titles to render during development. It predates much of the current accuracy work and is kept as a representative gameplay shot, not a claim about any particular sub-system.
The headless core is comfortably real-time. On an Intel i9-10850K (rustc 1.86, release), against the 16.639 ms NTSC frame deadline:
| Workload | Frame time | Headroom |
|---|---|---|
nestest (static menu) |
3.92 ms | 4.25× realtime · 255 fps |
flowing_palette (render-heavy) |
2.49 ms | 6.69× realtime · 402 fps |
The reproducible record (methodology, all benches, and the historical A/B) is in
docs/benchmarks.md.
| Platform | Status |
|---|---|
| Windows x64 | Primary (release binary) |
| Linux x64 | Primary (release binary) |
| macOS ARM64 | Primary (release binary; Apple silicon) |
| macOS x64 | Supported (Intel; build from source) |
| WebAssembly | Primary (hosted demo + build) |
| Android (arm64) | Supported (v1.8.x; GitHub-Releases / sideload — see docs/android.md) |
| Linux ARM64 | Supported (cross-compile) |
| Libretro Core | Supported (RetroArch via rustynes-libretro) |
| iOS / iPadOS | Supported (v1.9.x TestFlight; free App Store listing possible later) |
- Rust 1.96 stable (pinned via
rust-toolchain.toml; auto-installed byrustup). - A GPU with a
wgpu-supported backend (Vulkan / Metal / DX12, or WebGPU / WebGL2 in the browser). - The optional
retroachievementsfeature needs a C compiler for the vendored rcheevos sources; the default build does not.
| Document | Description |
|---|---|
| User guide | Install, controls, save states + rewind, debugger, config, FAQ |
| Project status matrix | Per-suite pass count, mapper coverage, feature flags, version policy |
| Architecture | System design and the load-bearing decisions |
| Scheduler | The master-clock lockstep model |
| CHANGELOG.md | Version history and release notes |
| Documentation handbook | The Material-for-MkDocs site rendering the subsystem specs + user guide (also on GitHub Pages) |
| Roadmap | The forward roadmap — currently the v2.5.1 → v2.7.0 MiSTer core line (the v2.2.6 → v2.3.0 de-monetization + NESdev-remediation line is complete) |
| Release plans | Per-release design plans (v1.0.0 → the v2.0.0 "Timebase" set, the v2.1.x "Fathom" line, and the current v2.7.0 MiSTer core plan) + the reference-emulator research dives that fed them |
| iOS / iPadOS App | Native SwiftUI shell over Metal (wgpu) — v1.9.x TestFlight |
| Libretro Core | Libretro core architecture, snapshot determinism, and RetroArch setup |
| MiSTer co-simulation | The v2.5.1 → v2.7.0 programme: a NEW NES core in SystemVerilog, written from public documentation in a sibling repository, with this emulator as its verification oracle. The rung ladder, the compare surfaces, and what each rung cannot verify |
| Accuracy ledger | Known residuals in this emulator, including the ones the co-simulation ladder found in the oracle rather than the DUT — the first being NROM's PRG-RAM window, which this emulator provides and the board does not |
| Component | Location |
|---|---|
| CPU (6502) | docs/cpu-6502.md |
| PPU (2C02) | docs/ppu-2c02.md |
| APU (2A03) | docs/apu-2a03.md |
| Mappers | docs/mappers.md |
| Testing | docs/testing-strategy.md |
| Netplay | docs/netplay-webrtc.md |
Architecture Decision Records live in docs/adr/ (0001–0038, including
0028–0029 the v2.0.0 "Timebase" one-clock timebase + save-state/movie-format break,
0030 the AccuracyCoin 2-cycle-ALE / octal-latch closure, 0031 the game-database
must-not-override-mapper-controlled-state gate, 0032 the Vs. DualSystem desktop
presentation, 0035 RustyNES is permanently non-commercial, 0036 the relicense to
GPL-3.0-or-later as a derivative work, 0037 the provenance firewall extended to
HDL for the MiSTer co-simulation programme, and 0038 the co-simulation
interrupt-injection API). (The deeper engine-development audit logs are
kept locally, outside the public repo.)
The hosted GitHub Pages deployment serves three sections from one artifact: the playable WebAssembly demo at doublegate.github.io/RustyNES, the workspace API docs (rustdoc) at doublegate.github.io/RustyNES/api/, and the Material-for-MkDocs documentation handbook at doublegate.github.io/RustyNES/docs/.
RustyNES's current release is v2.6.9 "Abeyance" — an exclusion hides improvement as well as regression. Both remaining denied co-simulation streams close. One had been carried for seven releases as a "declared diagnostic, nine divergences by design" and was a defect in the harness, not the console; it is now identical on all 357,361 cycles and all 88 checkpoints. The other differs on exactly one attributed cycle, carried by a new per-cycle nine-field comparator whose scoped allowance fails both ways — so a DUT that improves cannot leave a stale allowance quietly hiding coverage.
The emulation core is unchanged in this release, so its accuracy numbers hold by construction.
| AccuracyCoin | 141/141 (100.00%), RAM decoder |
| nestest | 0-diff against the reference log |
| Mapper families | 174 — 51 Core, 95 Curated, 28 BestEffort |
| Co-simulation suite | 141 gates green, 0 failed, 0 skipped |
| MiSTer bitstream | published for Cyclone V, timing closed at every corner |
The MiSTer core has not run on hardware. No DE10-Nano or SuperStation One has been attached; 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 — desktop binaries for Linux, macOS (aarch64) and Windows, plus the MiSTer
.rbf. - Try it in a browser: https://doublegate.github.io/RustyNES/
- Per-release detail:
CHANGELOG.md— every release back through v2.0.0 "Timebase" and the v1.x line. - Authoritative current state:
docs/STATUS.md— per-suite pass counts and the mapper matrix. - Version policy and the forward table:
VERSION-PLAN.md.
The active line is v2.5.1 → v2.7.0 — the MiSTer core. It builds a new NES
core in SystemVerilog, written from public hardware documentation in the sibling
RustyNES_MiSTer repository, with this emulator as its verification oracle.
RustyNES is not being ported to FPGA and cannot be — a MiSTer core is
SystemVerilog compiled by Quartus into a Cyclone V bitstream, and high-level
synthesis of a cycle-accurate emulator's control flow does not produce usable
hardware. What is buildable is a new implementation verified against this one,
and crates/rustynes-cosim is the boundary: a narrow C ABI a Verilator
testbench links, plus a nes_golden_export CLI emitting golden traces.
Progress is a rung ladder, and a rung may not open until the one below is
green: rung 0 the compare surface, rung 1 the 6502, rung 2 the bus and
interrupts, rung 3 the 2C02, rung 4 the 2A03, rung 5 AccuracyCoin parity, rung 6
hardware bring-up, rung 7 mappers. Rungs 0-5 are closed — the 6502, the
bus and interrupts, the 2C02, the 2A03, and AccuracyCoin parity, with the status
vector identical entry for entry across all 146 entries and 128 gates green,
0 failed, 0 skipped. The console compiles to a Cyclone V bitstream with timing
closed at every corner, and that bitstream is published. Rung 6 is open and
blocked on hardware: no DE10-Nano or SuperStation One is attached, so nothing
about a booting core is claimed. Every rung is labelled in
docs/mister.md by whether it has an independent oracle —
because 141/141 on AccuracyCoin is not the same as "matches silicon", and a rung
verified only against this emulator inherits whatever this emulator has wrong.
Rung 5 has already shown why that caveat is not decorative. A cpu_bus
module written from the wiki disagreed with this emulator at $6000-$7FFF: an
NROM board decodes nothing there and reads open bus, while RustyNES allocates
8 KiB of PRG-RAM unconditionally. The wiki names that emulator default as a
problem and lists games that crash on it. The DUT is the more accurate of the
two, and the finding is recorded in
docs/accuracy-ledger.md rather than fixed inside a
co-simulation step — it changes shipped behaviour on every iNES NROM cartridge.
The emulation core is unchanged by this line. Releases in it touch the co-simulation apparatus and the DUT, not the shipped emulator, so AccuracyCoin 141/141 and nestest 0-diff hold throughout — verified rather than assumed on any release that does touch a chip crate.
Two risks are accepted in writing rather than discovered later: NES_MiSTer
already scores 121/125 on AccuracyCoin and real Famicom AV hardware scores about
the same, so there is no published accuracy headroom and the core may be
declined as a duplicate (Retro Remake / openFPGA are planned routes, not
contingencies); and the oracle can be wrong.
A free mobile store listing (Google Play / F-Droid / App Store) remains a possible later, unversioned step with no monetization attached (ADR 0035). Per-release scope beyond the current step is planning, not a shipped promise.
The longer forward arc lives as research-grounded design plans in
to-dos/plans/; see to-dos/ROADMAP.md
for the full roadmap and docs/STATUS.md for the current state.
Contributions of all kinds are welcome — code, testing, documentation, and design.
Please read CONTRIBUTING.md for the quality-gate contract, the
conventional-commit format, and the chip-behavior-change rule (a chip change touches
both the code and its docs/<subsystem>.md in the same PR).
# 1. Fork and clone, then create a feature branch
git checkout -b feat/my-feature
# 2. Make changes and run the quality gates
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --all --check
# 3. Commit using conventional commits, then push and open a PR
git commit -m "feat(cpu): implement <thing>"
git push origin feat/my-featureThe four quality gates (fmt, clippy, doc, and the test suite) all run in CI and
must be green. See GitHub Discussions
if you need guidance.
RustyNES is licensed GPL-3.0-or-later.
Why GPLv3, and provenance. RustyNES is a derivative work of GPL-licensed NES
emulators: it incorporates code derived from Mesen2 (GPL-3.0-or-later) and, for
several mappers and the FDS drive model, from puNES, FCEUX, and Nestopia UE
(GPL-2.0-or-later). An earlier version of this project incorrectly described that code
as "oracle cross-checks" and licensed it MIT/Apache-2.0; that was wrong. Following a
NESdev community review, the project is relicensed GPL-3.0-or-later and the derivation
is credited per subsystem in docs/originality-and-provenance.md
and NOTICE (see also ADR 0036). Contributions are accepted under
GPL-3.0-or-later.
AI-assistance disclosure. RustyNES is heavily AI-assisted software. That does not change the above: code an LLM reproduces from GPL sources is still GPL-derived, and the maintainer is responsible for what lands in the tree — which is why the provenance is now stated plainly rather than scrubbed.
Reference firewall (so it does not recur). The failure that led to the relicense —
an AI reproducing reference-emulator source despite a black-box instruction, then later
scrubbing the honest "ported from" comments — is documented as a forensic post-mortem
(docs/provenance-failure-postmortem.md) and
distilled into a preventive, console-agnostic ruleset,
docs/ai-emulator-provenance-guardrails.md
(themed PDFs of both in ref-docs/). It is the project's top development
rule, ingested into AGENTS.md: reference emulators are black-box oracles whose
output may be observed but whose source is never read or reproduced; the local
ref-proj/ reference-emulator clone has been removed from the repo and stays
gitignored so that source is out of reach by design; hardware behavior is implemented
from public documentation and test ROMs; and any genuine derivation is attributed and
license-checked rather than laundered. The guardrails are shared as community
best-guidance for other AI-assisted emulator projects.
Incorporated permissive components (all GPL-compatible, notices in NOTICE):
emu2413 (MIT), TriCNES (MIT), the optional crates/rustynes-cheevos crate's vendored
RetroAchievements rcheevos (MIT),
blip_buf (LGPL-2.1-or-later), and the bundled fonts.
Test ROMs under tests/roms/ are individually CC0, MIT, or zlib licensed. No
commercial Nintendo ROMs are included, and they will never be bundled — dumps for the
commercial-ROM oracle are the user's responsibility and must come from cartridges they
legally own.
RustyNES stands on the shoulders of giants:
- The Nesdev wiki community for decades of hardware documentation and forum research.
- Mesen2 (GPL-3.0-or-later) — the primary derivation source. RustyNES is a derivative work and incorporates code derived from it (CPU unstable-store opcodes, the PPU sprite-evaluation / OAM model, ~15 mapper boards, the Bisqwit NTSC tables, and EEPROM / UNIF / debug-symbol / PGO code). higan and ares set the accuracy bar and serve as behavioral / trace oracles.
- puNES, FCEUX, and Nestopia UE (GPL-2.0-or-later) — derivation for specific subsystems: the puNES FDS drive-timing table, the FCEUX / puNES JV001 / mapper-147 code, and the Nestopia FME-7 model.
- TetaNES for the Bus-owns-everything architecture postmortem and Rust patterns.
- blargg, kevtris' nestest, Tepples' Holy Mapperel, and 100thCoin's AccuracyCoin as the closed-form definition of "cycle-accurate" used by this project.
- RetroAchievements and the
rcheevoslibrary that powers the achievement integration. - emu2413 (Mitsutaka Okazaki, MIT) — the YM2413 / OPLL model behind VRC7 audio — and TriCNES (Chris Siebert, MIT), the cycle-accurate C# emulator (a detailed sub-cycle CPU/PPU/APU/DMA state machine) whose PPU / DMA models RustyNES ports (MIT-licensed, its source vendored in-repo with attribution) and also uses as a golden oracle. GeraNES (GPL-3.0-only) served as a behavioral oracle — consulted, not incorporated.
- The community CRT shaders and NTSC filters whose looks RustyNES independently
reimplements — CRT-Royale (TroggleMonkey), crt-guest-advanced (guest.r),
Sony Megatron (MajorPainInTheCactus),
NTSC-CRT (EMMIR), and Bisqwit's
NES composite model — plus the Press Start 2P (OFL) and Font Awesome
fonts. Full attribution and the complete license posture are in
NOTICE.
If you use RustyNES in academic research, please cite:
@software{rustynes2026,
author = {RustyNES Contributors},
title = {RustyNES: A Cycle-Accurate NES Emulator in Rust},
year = {2026},
version = {2.3.0},
url = {https://github.com/doublegate/RustyNES},
note = {Cycle-accurate NES emulator on a master-clock-precise scheduler;
AccuracyCoin 100\% (141/141), nestest 0-diff; 174 mapper families,
Famicom Disk System, Vs./PlayChoice-10 RGB, rollback netplay,
RetroAchievements, a TAStudio piano-roll TAS editor with .fm2/.bk2
movie interop, and a Mesen2-class debugger; pure-Rust
winit/wgpu/cpal/egui frontend with a WebAssembly build}
}
Built with Rust. Powered by passion for retro gaming.
Preserving video game history, one frame at a time.
Get Started · Play in Browser · Contribute · Documentation · Discuss





