Build Status
License: MIT OR Apache-2.0
Version
Rust: 1.96
Accuracy battery
SingleStepTests
Try in browser
Platform
Rusty2600 is a cycle-accurate Atari 2600 (VCS) emulator written in pure
Rust. It targets the Mesen2 / ares / higan accuracy bar: a master-clock
lockstep scheduler running at TIA-color-clock resolution (3.579545 MHz NTSC),
with the 6507 CPU advancing on every third clock and WSYNC/RDY
beam-stalls freezing the CPU mid-cycle while the rest of the machine keeps
running. It clears its bundled Klaus2m5 functional and decimal-mode oracles
bit-exact and passes the trimmed SingleStepTests 6502 corpus
4,660/4,660 cases across all 233 opcodes, with the full ~10K-case-per-opcode
corpus verified weekly in CI.
Beyond reference accuracy, Rusty2600 is a real emulation platform, not just a
timing-accurate core: all 26 of the console's catalogued bankswitch
schemes (all 8 Curated-tier schemes plus all 16 BestEffort
schemes) wired into
automatic detection, a real debugger (live 6507/TIA/RIOT/memory panels,
breakpoints, a standalone disassembler), a real RetroAchievements
backend (rcheevos-vendored, per-frame achievement tracking and hardcore
mode), save-states and rewind, and a growing accuracy battery
(rusty2600-test-harness) gated in CI so a regression can never silently
ship. 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.
Rusty2600 combines accuracy-first emulation of one of the industry's oldest and most idiosyncratic consoles with modern tooling and the safety guarantees of Rust. The 2600 has no framebuffer of its own — the TIA races the electron beam and the program itself is responsible for timing every visible pixel — which makes cycle-exactness a correctness requirement, not a nice-to-have: get the timing wrong and games don't just look different, they visibly break.
Key differentiators:
- Reference-grade timing — an integer TIA-color-clock lockstep
scheduler (not catch-up), the 6507 on every third clock,
WSYNC/RDYbeam-stalls modeled as a genuine sub-instruction freeze — seedocs/scheduler.mdand ADR 0001. - Determinism as a hard contract — same seed, ROM, and input sequence yield a bit-identical framebuffer and audio (ADR 0004). This is what makes save-state round-trips and regression testing correct by construction, and what save-states, rewind, run-ahead, and the planned rollback-netplay work all build on.
- An honesty gate, not a marketing number — every bankswitch scheme is classified Core / Curated / BestEffort (ADR 0003); a BestEffort scheme can never silently back the accuracy oracle, enforced structurally by CI, not just documented.
- Safe, modular Rust — the chip stack (
rusty2600-cpu,-tia,-riot,-cart,-core) isno_std + alloc, one-directional, and independently testable; the onlyunsafelives behind the frontend/FFI boundary.
| Feature | Description |
|---|---|
| Cycle-Accurate Core | Integer TIA-color-clock lockstep scheduler; the 6507 (documented + undocumented opcodes) cycle-exact against SingleStepTests (233/233 opcodes) and Bruce Clark's exhaustive decimal-mode test |
| All 26 of 26 Bankswitch Schemes | 2K/4K/F8/F6/F4/CV/FA/Superchip/DPC/E7 (all 8 Curated-tier) + F0/E0/3F/3E/EF/DF/BF/UA/0840/FE/SB/X07/4A50/AR-Supercharger/DPC+/CDF-CDFJ-CDFJ+ (all 16 BestEffort) — classified behind a CI-enforced Core/Curated/BestEffort honesty gate (ADR 0003) |
| Real Debugger | Live 6507/TIA/RIOT/memory panels, breakpoints/step/continue, a side-effect-free memory peek, a standalone disassembler — default-on |
| Debugger Depth (v1.3.0) | A watch/conditional-breakpoint expression engine, a live JSR/RTS call stack, a per-scanline TIA write-scatter viewer, and a player/missile/ball position panel |
| Debugger Depth II (v2.12.0) | A capped instruction-trace ring buffer with export; an inline 6507 assembler (opcode table derived at runtime from the CPU panel's own canonical disassembly table, so the two can never drift apart) queuing writes through the same cpu_write path Lua's emu.poke uses; a cart-info panel (scheme/tier/ROM size — no iNES-style header, since 2600 carts have none); and a perf-monitor panel (rolling frame-interval history + sparkline) |
| RetroAchievements | Native rcheevos integration: login, a live achievement list, leaderboards, rich presence, per-frame achievement tracking, hardcore mode, and a recent-unlocks toast list (off by default) |
| Save-States + Rewind (v1.1.0) | A versioned binary snapshot format (ADR 0007) reusing the core's own serde derives; a rewind ring built on the same format |
| Run-Ahead (v1.2.0) | Speculatively simulates a few frames ahead to hide a game's internal input lag, built on the save-state snapshot primitives — off by default, 0..=4 frames, adjustable live from Settings |
| Shader Stack (v1.4.0, expanded v2.10.0/v2.13.0) | An arbitrary-length composable post-process stack (rusty2600-gfx-shaders) — CRT scanline darkening, an honestly-labeled composite-artifact color-bleed approximation, a genuine YIQ-domain NTSC composite decode (chroma/luma bandwidth-differential, NTSC-only), hqNx/xBRZ edge-directed pixel-art smoothing, and (v2.13.0) 3 named CRT presets (CRT-Royale, CRT Guest Advanced, Sony Megatron — independent re-derivations of each technique's characteristic beam/mask/curvature look), toggleable from Settings, plus a constrained RetroArch .slangp/.cgp preset importer; empty stack (the default) is byte-identical to the direct blit |
| 4A50 Bankswitching (v1.5.0) | Three independently relocatable ROM/RAM segments plus a previous-access-gated hotspot state machine, ported faithfully from Stella's Cartridge4A50 — BestEffort tier |
| ARM7TDMI Thumb Interpreter (v1.6.0, wired v2.2.0/v2.3.0) | A real Thumb-1 interpreter (rusty2600-thumb) ported from Gopher2600's Go implementation — registers, N/Z/C/V flags, all 19 instruction-format classes. v2.2.0 wired it into BankDpcPlus; v2.3.0 wired it into BankCdf too, including a real ARMinterrupt fault-servicing dispatch (CDF's driver makes genuine host-serviced calls, unlike DPC+'s no-op stub) needing zero changes to this crate. Both verified running real hand-assembled Thumb-1 programs |
| TAS Movies (v1.7.0) | A .r26m movie format (rusty2600-core::movie) — a seeded power-on or embedded save-state start point plus a per-frame input log — and a TAStudio-lite piano-roll debugger panel with jump-to-frame and branch points |
| Accuracy Battery | A real AccuracyScore-gated battery (rusty2600-test-harness), CI-enforced, growing honestly rather than claiming an inflated pass rate. (v1.8.0) GoldenLogDiffer now bundles a genuine externally-oracled golden CPU trace (20,000 instructions vs. an independent Gopher2600 run) |
| Lua Scripting Engine (v1.9.0, wired v2.1.0, overlay v2.3.0, drawLine v2.13.0) | A real, tested rusty2600-script crate (mlua native backend, off by default): a deliberately-smaller-than-RustyNES emu API over a host-agnostic ScriptBus trait, gated by a WritesLocked determinism lock. v2.1.0 wired a real ScriptBus impl into the frontend's render loop plus a Tools -> Load/Unload Script menu; v2.3.0 wired drawText/drawRect/drawPixel output into the actual screen, piggybacked on the frontend's existing egui pass; v2.13.0 added emu.drawLine as the fourth HUD primitive, at parity with RustyNES |
| A/V Recording (v2.13.0) | av-record feature (off by default, native-only): a read-only frontend tap that never advances the emulator, appending raw video frames + mono f32le audio to two temp files during recording, then muxing an .mp4/.mkv via one ffmpeg spawn at stop. Zero new Rust dependencies; ffmpeg availability probed at start so arming fails gracefully when absent |
| ROM Info Panel (v2.13.0) | An always-visible Tools -> ROM Info window (not gated behind debug-hooks, unlike the debugger's own cart-info panel) showing the loaded cartridge's scheme, Tier (ADR 0003), ROM size, and region |
| Rollback Netplay (v1.10.0, wired v2.1.0, STUN v2.3.0, WebRTC v2.6.0) | A real, tested rusty2600-netplay crate: 2-player rollback netplay wrapping ggrs (GGPO-style), resync() built on the existing save-state substrate, input-delay/max-prediction-window matching GGPO convention, a genuine rollback-desync test. v2.1.0 wired a real Tools -> Netplay... Connect dialog (direct-IP/LAN); v2.3.0 added a real, live-tested STUN client (RFC 5389 via stun_codec) plus a hole-punch attempt; v2.6.0 added a browser WebRtcSocket transport (wasm32-only, ADR 0008) — connection-establishment (offer/answer/ICE) verified end-to-end in real Chromium, but the data channel itself never reached "open" in this sandbox (zero ICE candidates gathered — a sandbox-specific limitation, not a code bug). Real cross-NAT verification remains deferred for both transports |
| HD-Pack Live Rendering (sprite-pack data model v1.4.0, object-ID mask + live splice v2.7.0) | A TIA object-ID mask (rusty2600-tia, hd-pack feature, off by default) tags every rendered pixel with which object won color-priority resolution plus, for player pixels, the exact live GRPx/NUSIZx — captured per-pixel so mid-scanline GRPx rewrites (sprite multiplexing) resolve correctly, strictly additive so video_buffer stays byte-identical with the feature off. EmuCore::step_frame consults it against a loaded SpritePack and substitutes a matching replacement bitmap for a player object's on-screen footprint, alpha-blended against the underlying TIA color — proven end-to-end by a hand-assembled synthetic-ROM integration test. Player-only by design; a real replacement-art library doesn't exist yet (proof-of-mechanism only) |
| Real Paddle Input (v2.1.0) | A faithful port of Stella's AnalogReadout dump-capacitor RC-circuit simulation — INPT0..=INPT3 now respond to real analog paddle timing (NTSC-only) instead of a plain digital byte, wired end-to-end through native, Android, and iOS |
| AR/Supercharger Bankswitching (v2.1.0) | A full "fast-load" port of Stella's CartridgeAR, including a byte-exact 294-byte dummy-BIOS port and the 5-distinct-access delayed-write RAM protocol — BestEffort tier |
| DPC+ ARM Coprocessor Bankswitching (v2.2.0, music audio v2.3.0) | A full port of Gopher2600's dpcplus package — the complete register window, a real ThumbMemory impl, and a synchronous ARM-execution entry point driving rusty2600-thumb's interpreter. v2.3.0 added the music-mode audio phase-accumulator advance (a small, self-contained fix). BestEffort tier |
| CDF/CDFJ/CDFJ+ ARM Coprocessor Bankswitching (v2.3.0) | Closes the cart catalogue to 26/26. One struct (BankCdf) covers all four sub-versions via a CdfVersion const table, ported from Gopher2600's cdf package — reuses DPC+'s ARM entry shape plus a FastJMP data-fetcher redirect and a real ARMinterrupt fault-servicing dispatch (CDF's driver makes genuine host-serviced calls DPC+'s never did). BestEffort tier |
| Android (v1.11.0, save-state UI v2.11.0) | A real, tested rusty2600-mobile UniFFI bridge crate reused from Kotlin — zero hand-written JNI/unsafe (a design win over the original plan). A real Gradle/Kotlin app, verified running on a real emulator (booted, installed, launched crash-free, a real homebrew ROM loaded via the system file picker). v2.11.0 "Field Trip" added an 8-slot Save State / Load State picker (SaveSlots.kt, CRC32-keyed per-ROM directories, matching desktop's .r26s/slot convention) — round-tripped for real on-device (save, verify the on-disk file + timestamp, load, confirm via Toast), not just built |
| iOS (v1.12.0, save-state UI v2.11.0) | Reuses the rusty2600-mobile bridge unchanged for a SwiftUI app (ios/) — genuinely tool-generated Swift bindings, real Metal/AVFoundation source, and a touch-drag rotary paddle control. v2.11.0 "Field Trip" added the same 8-slot Save State / Load State picker (SaveSlots.swift, FNV-1a-keyed, SaveStateSlotPickerView.swift). This sandbox has no Xcode toolchain, so the whole iOS app (including this addition) stays honestly unverified by compilation; a future Mac session completes real Xcode/Simulator/device verification |
| Manual Save-State Slots (v2.4.0) | File -> Save State / Load State — 8 numbered slots per ROM, built on the already-real SaveState format (ADR 0007). Each ROM's slots are keyed by an FNV-1a hash of its raw bytes, so a slot can never silently load against the wrong cartridge; loading a slot clears the rewind ring so Rewind doesn't jump to a pre-load timeline. Native-only for now |
| WebAssembly | The live demo runs via a canvas-2D bootstrap (wasm-canvas; real keyboard input + Web Audio API output). (v2.5.0) A real winit+wgpu+egui build (wasm-winit) — the SAME shell the native binary uses — now compiles cleanly for wasm32-unknown-unknown and a real trunk build produces a working bundle, but its wgpu rendering step is not yet confirmed working in a real browser (a sandboxed-headless-Chromium-specific GPU-adapter limitation, independently reproduced twice — see docs/frontend.md), so wasm-canvas stays the deployed build for now. (v2.8.0) wasm-winit gained an on-screen touch D-pad/fire/console-switch overlay (View -> Touch overlay), a Settings panel confirmed structurally wasm32-safe, and real Config persistence via localStorage (replacing the previous no-op stub). (v2.9.0) A ?settings= share-link round-trips the whole Config (region/video/audio/key bindings) through a hand-rolled, native-unit-tested base64url codec; debug-hooks is now confirmed wasm32-safe for wasm-winit, giving the CPU/TIA/RIOT/Memory panels (and nearly everything else in the native debugger) a working in-browser counterpart, gated only on TAStudio's native-only "Save branch" file dialog; and a PWA manifest + service worker make whichever build is deployed here installable and offline-capable (the actually-deployed wasm-canvas build measures ~324 KiB total; the not-yet-deployed wasm-winit build is much larger, ~7+ MiB unoptimized, since winit+wgpu+egui — not the 2600 core — dominate wasm bundle size). All of this is shared code, verified via cargo check/clippy --target wasm32-unknown-unknown + native unit tests for the pure logic, but NOT live-browser-verified for the same reason rendering itself isn't yet |
.zip ROM Loading (v2.4.0) |
Both the native File -> Open ROM dialog and the wasm demo's file loader can extract a ROM directly from a .zip archive (the common ROM-redistribution format) — bounded-read (decompression-bomb guard), never panics on malformed input |
| Pure Rust | winit + wgpu + cpal + egui frontend; a safe no_std + alloc chip stack behind a one-directional crate graph |
The v1.1.0 -> v2.0.0 RustyNES-parity line is complete; v2.1.0
through v2.3.0 closed every follow-up gap that release's own
reconciliation carried forward except mobile-store submission and a real
Xcode-verified iOS build (both explicitly deferred, not gaps).
v2.4.0 "Save Point" opened a new gap-closure arc against RustyNES itself
(v2.4.0 -> v3.0.0, see to-dos/ROADMAP.md),
landing manual save-state slots (the headline item — Rusty2600 previously
had no player-facing "save my game" feature at all despite the
underlying format being real since v1.10.0), a CI-gated performance-
regression bench, a Stella-oracle differential test strengthening the
real paddle-timing simulation, .zip ROM-archive loading, and a GitHub
repo hygiene pass. v2.5.0 "Web Awakens" followed with a real
winit+wgpu+egui wasm build (compiles and builds cleanly; rendering
unconfirmed in a real browser — see the WebAssembly row above), a
debugger Lua console panel, and a Keyboard Controller/Trak-Ball research
decision. v2.6.0 "Rollback Bridge" added browser WebRTC netplay (see
the Rollback Netplay row above) and a master dependency-upgrade sweep
consolidating 12 Dependabot PRs. v2.7.0 "True Colors" built the TIA
object-ID mask and wired the HD-pack live rendering splice (see the
HD-Pack Live Rendering row above). v2.8.0 "Touchpoint" built the first
wave of wasm-winit web parity — an on-screen touch overlay, a
wasm32-safety review of the Settings panel, and real localStorage
config persistence (see the WebAssembly row above). v2.9.0 "Full Circle"
built the second wave — a ?settings= share-link, a wasm32-safe
debug-hooks debugger overlay, and PWA install/offline support (see the
WebAssembly row above). In-browser Lua scripting was investigated for
real this release (not just re-deferred on old reasoning) and stays
deliberately unsupported on wasm32: mlua's vendored C build is
confirmed to be a hard wall on wasm32-unknown-unknown, and the one
realistic pure-Rust fallback, piccolo, isn't yet a workable substrate
either — its only published release implements almost none of Lua's
string/table stdlib, and its more-complete unpublished branch is
explicitly pre-1.0-unstable by its own maintainer's admission (see
docs/scripting.md's v2.9.0 status section) — see
CHANGELOG.md for exactly what shipped in each release.
Open follow-up work (each its own well-scoped future release): real
Xcode-verified iOS build/run (this sandbox has no Xcode toolchain),
netplay's WebRTC transport and real cross-NAT verification, DPC+/CDF
per-player console-switch/paddle modeling, in-browser Lua scripting
(blocked on piccolo publishing a crates.io release with real
string/table coverage — an upstream milestone, not one this project
controls), and a real HD-pack replacement-art library (only a
proof-of-mechanism placeholder exists
today). The .r26m movie format and TAStudio-lite panel shipped in
v1.7.0; live per-frame auto-recording into the emu-thread's hot path is
honestly deferred (docs/movie.md).
- 6507 CPU — documented and undocumented opcodes, cycle-exact against
the full SingleStepTests corpus (weekly CI) and Bruce Clark's exhaustive
ADC/SBC decimal-mode sweep (
ERROR=0, bit-exact). - TIA — beam-raced video (RESPx/HMOVE comb, playfield, players/missiles/ ball, all 15 pairwise collision latches) and two-channel poly-counter audio synthesis, unit-tested against Stella/Gopher2600 as differential oracles.
- RIOT — the console's only 128 B of RAM, the DDR/I/O ports, and the
interval timer (prescale, underflow/
INSTAT, read-after-write, and the post-underflow decrement-rate reversion — seedocs/riot.md). - Master-clock lockstep scheduler — the Bus owns every chip; the 2600 has no separate work RAM, so save-states/rewind only ever need to capture the CPU + Bus (TIA + RIOT + cart) state.
All 26 catalogued schemes are implemented and wired into automatic
detect() — all 8 Curated-tier schemes (2K, 4K, F8, F6, F4, CV,
FA/CBS-RAM, Superchip, DPC, E7) plus all 16 BestEffort schemes (F0, E0,
3F, 3E, EF/EFSC, DF/DFSC, BF/BFSC, UA, 0840, FE, SB, X07, 4A50,
AR/Supercharger, DPC+, CDF/CDFJ/CDFJ+). Two Board hooks
(snoop_write/snoop_read) let a scheme react to accesses the console
routes to TIA/RIOT space, not just the 1000ドル+ cart window — needed for
the 3F/3E/UA/0840/FE/X07/SB/4A50/AR families (4A50 also uses a smaller
in-window instance of its hotspot state machine at 1ドルF00-1ドルFFF; AR's
BIOS handoff additionally uses a Board::take_oob_pokes() hook to stage
direct RIOT-RAM writes, mirroring Stella's System::pokeOob). Both
Harmony/Melody ARM-coprocessor families consume the rusty2600-thumb
ARM7TDMI Thumb-1 interpreter (v1.6.0): DPC+ (v2.2.0) and CDF/CDFJ/CDFJ+
(v2.3.0), the latter also exercising a real ARMinterrupt fault-
servicing dispatch DPC+ never needed — both verified running real
hand-assembled Thumb-1 programs. See docs/cart.md and
docs/thumb.md for the full catalogue, tiering, and
interpreter architecture.
- Debugger (
debug-hooks, default-on) — live 6507/TIA/RIOT/memory panels, breakpoints/step/continue, a side-effect-freeBus::peek/peek_range, a standalone disassembler, a watch/conditional-breakpoint expression engine, a JSR/RTS call stack, a TIA write-scatter viewer, and a player/missile/ball position panel. - RetroAchievements (
retroachievements, off by default) —rusty2600-cheevosvendors thercheevosC library; login, a live achievement list, leaderboards, rich presence, per-frame achievement tracking, hardcore mode, and a recent-unlocks toast list all work. - Save-states + rewind — a versioned binary snapshot format
(
rusty2600-core::save_state, ADR 0007) built on the chip stack's existingserdederives; the rewind ring reuses the same encoding rather than paying a raw-clone's worst-case cost. - Run-ahead — speculatively simulates a few frames ahead to hide a game's internal input lag, built on the save-state snapshot primitives.
- WebAssembly — a canvas-2D bootstrap with real keyboard input and Web
Audio API output; native-only features compile out automatically. A full
winit+wgpu+egui browser build (
wasm-winit, matching the native binary) compiles cleanly and, as ofv2.9.0, has a wasm32-safe debugger overlay and a?settings=share-link; live-browser rendering confirmation and a deployed switch-over remain open (see the WebAssembly feature-matrix row above anddocs/frontend.md). - PWA install (
v2.9.0) — a web-app manifest + service worker (crates/rusty2600-frontend/web/manifest.json/sw.js) make the deployed wasm demo installable and offline-capable after a first visit. - A/V recording (
av-record, off by default, native-only,v2.13.0) — Tools -> Start/Stop Recording mux an.mp4/.mkvvia an externalffmpegprocess; a read-only tap that never touches emulator state.
Grab the latest release for your platform from the Releases page.
# Linux / macOS tar xzf rusty2600-<version>-<target>.tar.gz ./rusty2600 # Windows (PowerShell) Expand-Archive rusty2600-<version>-x86_64-pc-windows-msvc.zip .\rusty2600.exe
# Clone the repository git clone https://github.com/doublegate/Rusty2600.git cd Rusty2600 # Build the workspace (release) cargo build --release --workspace # Run a ROM you legally own (or launch bare and use File -> Open ROM). # .a26/.bin/.rom, or a .zip containing one, both work. cargo run --release -p rusty2600-frontend -- path/to/rom.a26 # Optional: build with RetroAchievements (needs a C compiler for vendored rcheevos) cargo build --release -p rusty2600-frontend --features retroachievements # Optional: the "cargo --full equivalent" -- one command for the most # fully-featured native binary (retroachievements + scripting + hd-pack + # netplay, on top of the always-on default set). Needs a C compiler. cargo full-build cargo full-run path/to/rom.a26 # The no_std embedded-target gate (confirms the core stays no_std + alloc) cargo build -p rusty2600-core --target thumbv7em-none-eabihf --no-default-features
On Linux the frontend needs the wgpu/winit/cpal system dependencies (CachyOS / Arch shown; substitute your distro's equivalents):
sudo pacman -S --needed libxkbcommon wayland alsa-lib systemd-libs
| Input | Key |
|---|---|
| Joystick (P0) D-pad | Arrow keys |
| Joystick (P0) fire | Z |
| Joystick (P1) D-pad | W A S D |
| Joystick (P1) fire | Q |
| Console: Select / Reset | F1 / F2 |
| Console: Color ↔ B&W | F3 |
| Console: Left / Right difficulty A↔B | F4 / F5 |
| Toggle debugger overlay | ` |
| Open ROM / Quit | F12 / Esc |
Paddles (INPT0–3, analog) bind to the mouse / gamepad axes. USB gamepads
auto-bind to P0. All bindings live in config.toml (see
docs/frontend.md); a key-rebind UI is planned.
Rusty2600 is a Cargo workspace with a strictly one-directional chip-crate
graph — no chip crate depends on another except rusty2600-tia, which reads
cart-mediated bus state via rusty2600-cart:
| Crate | Role |
|---|---|
rusty2600-cpu |
The MOS 6507 (a 6502 in a 28-pin package: A0–A12, no exposed NMI/IRQ pins) |
rusty2600-tia |
The TIA (Television Interface Adaptor) — beam-raced video AND audio |
rusty2600-riot |
The MOS 6532 RIOT — the console's only RAM (128 B) + I/O ports + interval timer |
rusty2600-cart |
The tiered bankswitch catalogue, gated by the ADR-0003 honesty marker |
rusty2600-core |
The Bus (owns every chip) + the master-clock lockstep scheduler + save-states |
rusty2600-frontend |
The winit + wgpu + cpal + egui shell (binary rusty2600), including the debugger |
rusty2600-cheevos |
Native-only RetroAchievements integration — a safe wrapper around vendored rcheevos |
rusty2600-gfx-shaders |
no_std WGSL post-process shader sources for the composable shader stack |
rusty2600-test-harness |
The accuracy oracle + the bankswitch-tier honesty gate |
The Bus owns everything mutable; the CPU borrows &mut Bus for the duration
of a step. The 2600 has no separate work RAM, so there's no WRAM field on
the Bus — the only RAM in the whole console lives in the RIOT's 128 bytes.
See docs/architecture.md for the full picture and
docs/adr/ for the numbered architectural decisions (the
master-clock scheduler, the fractional-timebase question, the accuracy
honesty gate, the determinism contract, TIA revision modeling, power-on RAM
seeding, and save-state versioning).
| Suite | Layer | Pass / Total |
|---|---|---|
Klaus 6502_functional_test |
CPU oracle | 1 / 1 |
Klaus 6502_decimal_test (BCD) |
CPU oracle | 1 / 1 — exhaustive ×ばつ2-carry-in ADC/SBC sweep, bit-exact |
SingleStepTests 6502 (trimmed) |
cycle-exact audit | 4,660 / 4,660 cases, 233 / 233 opcodes |
SingleStepTests 6502 (full corpus) |
cycle-exact audit | weekly CI cron (~700 MB across 233 opcodes, not per-push) |
| Accuracy battery | rusty2600-test-harness |
2 / 2 (100%), CI-enforced, ≥90% v1.0 threshold — growing honestly as real test-ROM fixtures are sourced, not inflated |
| Workspace test suite | cargo test --workspace |
183 / 183 |
Every bankswitch scheme is classified Core (register-decode trivial, always oracle-gated), Curated (a redistributable fixture + full test coverage, oracle-gated), or BestEffort (reference-ported, register-decode
- boot-smoke tested only — never allowed to back the accuracy oracle).
This isn't a documentation promise:
mapper_tier_honesty.rsasserts the invariant in CI on every push. Seedocs/STATUS.mdfor the authoritative, currently-accurate per-suite numbers and board matrix, anddocs/testing-strategy.mdfor the full layered methodology (unit tests → CPU golden-log → the accuracy battery → tolerance-aware snapshot comparison → a commercial-ROM regression oracle gated behind locally-supplied, never-committed ROM dumps).
Known accuracy work fixed along the way: a RIOT interval-timer bug that
permanently stalled Pitfall II's boot-time wait loop (found via a rebuilt
Gopher2600/Stella differential probe, confirmed against Stella's own
M6532::peek/updateEmulation) — see docs/riot.md.
Rusty2600's per-frame workload is a fraction of a modern console's — a 2600
frame is ~262 scanlines ×ばつ 228 color clocks with a handful of chips, not a
PPU/APU pipeline juggling thousands of tiles. Real Criterion baselines for
the CPU/TIA/RIOT/cart crates live in docs/performance.md,
measured with cargo bench on a pinned dev host — treat the documented
numbers as deltas across changes, not absolute cross-machine guarantees.
| Platform | Status |
|---|---|
| Linux (x86_64) | Native binary, CI-built every release |
| macOS (aarch64) | Native binary, CI-built every release |
| Windows (x86_64) | Native binary, CI-built every release |
| WebAssembly | Runs in-browser — try it live |
no_std embedded target |
rusty2600-core builds for thumbv7em-none-eabihf with --no-default-features — a CI gate, not a shipped product |
| Doc | Covers |
|---|---|
docs/STATUS.md |
The single source of truth: current pass counts, board matrix, version policy |
docs/architecture.md |
The Bus/scheduler design, the crate graph, the determinism contract |
docs/scheduler.md |
The master-clock lockstep scheduler in detail |
docs/cpu.md / docs/tia.md / docs/riot.md |
Per-chip specs |
docs/cart.md |
The full bankswitch catalogue and Core/Curated/BestEffort tiering |
docs/thumb.md |
The ARM7TDMI Thumb-1 interpreter (rusty2600-thumb): architecture, scope, deviations from the Go reference |
docs/movie.md |
The .r26m TAS movie format and TAStudio-lite panel: architecture and scope |
docs/scripting.md |
The Lua scripting engine (rusty2600-script): the emu API, ScriptBus, determinism gating, scope |
docs/netplay.md |
Rollback netplay (rusty2600-netplay): the ggrs integration, scope, the rollback-desync test |
docs/mobile.md |
The mobile UniFFI bridge (rusty2600-mobile), the Android app, and the real-emulator verification |
docs/frontend.md |
The frontend crate's shape and behavior |
docs/testing-strategy.md |
The layered accuracy-oracle methodology |
docs/performance.md |
Measured Criterion baselines and profiling guidance |
docs/compatibility.md |
Per-game/board compatibility notes and open questions |
docs/adr/ |
Numbered architectural decision records |
to-dos/ROADMAP.md |
The full phase/sprint plan through v2.0.0 |
CHANGELOG.md |
What actually shipped in each release |
Contributions are welcome — see CONTRIBUTING.md for the
workflow, commit-message conventions, and the pre-tag quality gate
(cargo fmt, cargo test --workspace, cargo clippy -- -D warnings, the
no_std build). Never commit commercial ROMs; the commercial-roms feature
and tests/roms/external/ exist precisely so local dumps stay local.
Rusty2600 is dual-licensed under MIT OR Apache-2.0. See
LICENSE-MIT and LICENSE-APACHE.
Rusty2600's differential-oracle workflow leans on two open-source
references studied (never vendored — both are GPL-licensed, incompatible
with this project's MIT/Apache-2.0 dual license) for behavioral
cross-checking: Stella (the canonical C++
Atari 2600 emulator) and Gopher2600
(a Go implementation used as a headless differential-testing harness). Test
ROMs are drawn from Klaus Dormann's public-domain 6502 functional/decimal
test suites and the trimmed SingleStepTests 65x02 corpus.