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:
172 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 |
| 172 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) |
| 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 172 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.
- 172 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 |
172 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 |
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 — the v2.2.6 → v2.3.0 de-monetization + NESdev-remediation line and beyond |
| Release plans | Per-release design plans (v1.0.0 → the v2.0.0 "Timebase" set and the v2.1.x "Fathom" line) + 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 |
| 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–0036, 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, and 0036 the relicense to
GPL-3.0-or-later as a derivative work). (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.3.0 "Datum II" — the capstone that closes the v2.2.6 → v2.3.0 NESdev-remediation line. Tool panels now open as real OS windows (the v2.2.9 affordance only embedded them, so the Windows-10 "trapped window" report is now genuinely resolved), and every tool window is detachable. Profiling that work uncovered a frame-pacing defect that had been degrading any session with a debugger panel open: the render path held the emulator lock across the blocking swapchain wait and present, stalling frame production — now split so the lock covers only the UI build. The PPU's per-dot helpers also got −5.13% / −3.51% frame cost, byte-identically. Both remaining forum-reported accuracy items (SMB left edge, the Rad Racer hybrid-address render) were investigated and found already correct. AccuracyCoin 141/141 (100%), nestest 0-diff — now enforced as an exact count, not a 60% floor.
The v2.2.6 → v2.3.0 line was a de-monetization + NESdev-remediation run on the v2.0.0
"Timebase" one-clock scheduler: v2.2.6 "Almanac" made RustyNES permanently open-source and
income-free (ADR 0035; the apps stay free FOSS — no ads, tracking, or paid unlock), and
v2.2.7 "Timbre II" / v2.2.8 "Aperture II" / v2.2.9 "Studio II" / v2.3.0 "Datum II" address
forum-reported audio, presentation, TAS/movie/windowing, and PPU-accuracy items — the core
staying byte-identical except where a change is an intentional, oracle-gated accuracy fix.
The same cycle-accurate core powers the desktop,
browser, Android, iOS, and Libretro builds. Full per-version detail — every release back
through v2.0.0 "Timebase" and the v1.x line — is in CHANGELOG.md.
- Download: the GitHub Releases page — desktop binaries for Linux, macOS (aarch64), and Windows.
- Full per-version history:
CHANGELOG.md. - Authoritative current state:
docs/STATUS.md— the per-suite pass-count and mapper matrix (its release-header version can lag a patch-release bump;CHANGELOG.mdand the Releases page are authoritative for the latest tag).
The v2.2.6 → v2.3.0 line — de-monetization (ADR 0035) plus a pass through NESdev-forum feedback, all on the v2.0.0 "Timebase" core — is now complete:
- Shipped: v2.2.6 "Almanac" (de-monetization; permanently open-source and income-free),
v2.2.7 "Timbre II" (VRC6 / Sunsoft 5B expansion-audio fidelity), v2.2.8 "Aperture II"
(gamma-correct scanlines), v2.2.9 "Studio II" (TAStudio wiring,
.bk2playback, tool-window detach), and v2.3.0 "Datum II" — true multi-viewport OS-window detach, the emulator-lock frame-pacing fix, a −5.1% PPU optimization, and the PPU-accuracy capstone (both forum-reported items verified already-correct), holding AccuracyCoin 141/141. - Next: no line is locked. Candidates include a free-app store launch (no
monetization, per ADR 0035), further frontend performance work, and continued
mapper / accuracy breadth. See
to-dos/ROADMAP.md.
A free mobile store listing (Google Play / F-Droid / App Store) is 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; 172 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





