Skip to content

Navigation Menu

Sign in
Sign up

Latest commit

History

126 Commits

Folders and files

NameName
Last commit message
Last commit date

Rusty2600

A cycle-accurate Atari 2600 / Video Computer System emulator in Rust.

Build Status License: MIT OR Apache-2.0 Version Rust: 1.96
Accuracy battery SingleStepTests Try in browser
Platform

Overview

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.


Why Rusty2600?

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/RDY beam-stalls modeled as a genuine sub-instruction freeze — see docs/scheduler.md and 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) is no_std + alloc, one-directional, and independently testable; the only unsafe lives behind the frontend/FFI boundary.

Highlights

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).


Features

Emulation core

  • 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 — see docs/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.

Cartridges

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.

Modern features

  • Debugger (debug-hooks, default-on) — live 6507/TIA/RIOT/memory panels, breakpoints/step/continue, a side-effect-free Bus::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-cheevos vendors the rcheevos C 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 existing serde derives; 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 of v2.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 and docs/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/.mkv via an external ffmpeg process; a read-only tap that never touches emulator state.

Quick Start

Download binaries

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

Build from source

# 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

Default Controls

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 (INPT03, 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.


Architecture

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).


Compatibility and Accuracy

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.rs asserts the invariant in CI on every push. See docs/STATUS.md for the authoritative, currently-accurate per-suite numbers and board matrix, and docs/testing-strategy.md for 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.


Performance

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 Support

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

Documentation

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

Contributing

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.

License

Rusty2600 is dual-licensed under MIT OR Apache-2.0. See LICENSE-MIT and LICENSE-APACHE.

Acknowledgments

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.

About

[Rusty2600] Cycle-accurate Atari 2600 VCS emulator built in pure Rust — 26-of-26 bankswitch schemes, WebAssembly-ready, an ARM7TDMI Thumb coprocessor interpreter, TAS movie tooling, Lua scripting, and RetroAchievements integration for game preservation.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /