An open API service indexing awesome lists of open source software.

https://github.com/doublegate/rusty2600

[Rusty2600] Cycle-accurate Atari 2600 VCS emulator built in pure Rust — 23-of-25 bankswitch schemes, WebAssembly-ready, an ARM7TDMI Thumb coprocessor interpreter, TAS movie tooling, Lua scripting, and RetroAchievements integration for game preservation.
https://github.com/doublegate/rusty2600

6502 8-bit atari atari-2600 bankswitching cross-platform cycle-accurate emulation emulator game-preservation lua no-std retro-gaming retroachievements rust tas wasm webassembly

Last synced: 2 days ago
JSON representation

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

Awesome Lists containing this project

README

          

# 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](https://doublegate.github.io/Rusty2600/)** — 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 |
| **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)* | 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), and hqNx/xBRZ edge-directed pixel-art smoothing, 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)* | 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 |
| **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`](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`](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 `$1F00-$1FFF`; 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`](docs/cart.md) and
[`docs/thumb.md`](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.

---

## Quick Start

### Download binaries

Grab the latest release for your platform from the
[Releases page](https://github.com/doublegate/Rusty2600/releases).

```bash
# Linux / macOS
tar xzf rusty2600--.tar.gz
./rusty2600

# Windows (PowerShell)
Expand-Archive rusty2600--x86_64-pc-windows-msvc.zip
.\rusty2600.exe
```

### Build from source

```bash
# 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

# 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):

```bash
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 (`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`](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`](docs/architecture.md) for the full picture and
[`docs/adr/`](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 256×256×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`](docs/STATUS.md) for
the authoritative, currently-accurate per-suite numbers and board matrix,
and [`docs/testing-strategy.md`](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`](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](https://doublegate.github.io/Rusty2600/) |
| `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`](docs/STATUS.md) | The single source of truth: current pass counts, board matrix, version policy |
| [`docs/architecture.md`](docs/architecture.md) | The Bus/scheduler design, the crate graph, the determinism contract |
| [`docs/scheduler.md`](docs/scheduler.md) | The master-clock lockstep scheduler in detail |
| [`docs/cpu.md`](docs/cpu.md) / [`docs/tia.md`](docs/tia.md) / [`docs/riot.md`](docs/riot.md) | Per-chip specs |
| [`docs/cart.md`](docs/cart.md) | The full bankswitch catalogue and Core/Curated/BestEffort tiering |
| [`docs/thumb.md`](docs/thumb.md) | The ARM7TDMI Thumb-1 interpreter (`rusty2600-thumb`): architecture, scope, deviations from the Go reference |
| [`docs/movie.md`](docs/movie.md) | The `.r26m` TAS movie format and TAStudio-lite panel: architecture and scope |
| [`docs/scripting.md`](docs/scripting.md) | The Lua scripting engine (`rusty2600-script`): the `emu` API, `ScriptBus`, determinism gating, scope |
| [`docs/netplay.md`](docs/netplay.md) | Rollback netplay (`rusty2600-netplay`): the `ggrs` integration, scope, the rollback-desync test |
| [`docs/mobile.md`](docs/mobile.md) | The mobile UniFFI bridge (`rusty2600-mobile`), the Android app, and the real-emulator verification |
| [`docs/frontend.md`](docs/frontend.md) | The frontend crate's shape and behavior |
| [`docs/testing-strategy.md`](docs/testing-strategy.md) | The layered accuracy-oracle methodology |
| [`docs/performance.md`](docs/performance.md) | Measured Criterion baselines and profiling guidance |
| [`docs/compatibility.md`](docs/compatibility.md) | Per-game/board compatibility notes and open questions |
| [`docs/adr/`](docs/adr/) | Numbered architectural decision records |
| [`to-dos/ROADMAP.md`](to-dos/ROADMAP.md) | The full phase/sprint plan through `v2.0.0` |
| [`CHANGELOG.md`](CHANGELOG.md) | What actually shipped in each release |

---

## Contributing

Contributions are welcome — see [`CONTRIBUTING.md`](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`](LICENSE-MIT) and [`LICENSE-APACHE`](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](https://stella-emu.github.io/) (the canonical C++
Atari 2600 emulator) and [Gopher2600](https://github.com/JetSetIlly/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.