{"id":51368677,"url":"https://github.com/doublegate/rusty2600","last_synced_at":"2026-07-03T04:01:19.952Z","repository":{"id":368731157,"uuid":"1284454917","full_name":"doublegate/Rusty2600","owner":"doublegate","description":"[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.","archived":false,"fork":false,"pushed_at":"2026-07-02T00:58:03.000Z","size":3154,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-02T01:18:14.122Z","etag":null,"topics":["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"],"latest_commit_sha":null,"homepage":"https://doublegate.github.io/Rusty2600/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/doublegate.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-06-29T22:04:58.000Z","updated_at":"2026-07-02T00:51:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/doublegate/Rusty2600","commit_stats":null,"previous_names":["doublegate/rusty2600"],"tags_count":23,"template":false,"template_full_name":null,"purl":"pkg:github/doublegate/Rusty2600","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doublegate%2FRusty2600","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doublegate%2FRusty2600/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doublegate%2FRusty2600/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doublegate%2FRusty2600/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/doublegate","download_url":"https://codeload.github.com/doublegate/Rusty2600/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doublegate%2FRusty2600/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35071463,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-03T02:00:05.635Z","response_time":110,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["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"],"created_at":"2026-07-03T04:00:49.321Z","updated_at":"2026-07-03T04:01:19.910Z","avatar_url":"https://github.com/doublegate.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- markdownlint-disable MD033 MD041 --\u003e\n\u003cdiv align=\"center\"\u003e\n\n# Rusty2600\n\n**A cycle-accurate Atari 2600 / Video Computer System emulator in Rust.**\n\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/doublegate/Rusty2600/actions\"\u003e\u003cimg src=\"https://github.com/doublegate/Rusty2600/workflows/CI/badge.svg\" alt=\"Build Status\"\u003e\u003c/a\u003e\n  \u003ca href=\"#license\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg\" alt=\"License: MIT OR Apache-2.0\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/doublegate/Rusty2600/releases\"\u003e\u003cimg src=\"https://img.shields.io/badge/version-v2.11.0-blue.svg\" alt=\"Version\"\u003e\u003c/a\u003e\n  \u003ca href=\"rust-toolchain.toml\"\u003e\u003cimg src=\"https://img.shields.io/badge/rust-1.96-orange.svg\" alt=\"Rust: 1.96\"\u003e\u003c/a\u003e\n  \u003cbr\u003e\n  \u003ca href=\"#compatibility-and-accuracy\"\u003e\u003cimg src=\"https://img.shields.io/badge/accuracy%20battery-100%25%20(2%2F2)-brightgreen.svg\" alt=\"Accuracy battery\"\u003e\u003c/a\u003e\n  \u003ca href=\"#compatibility-and-accuracy\"\u003e\u003cimg src=\"https://img.shields.io/badge/SingleStepTests-233%2F233%20opcodes-brightgreen.svg\" alt=\"SingleStepTests\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://doublegate.github.io/Rusty2600/\"\u003e\u003cimg src=\"https://img.shields.io/badge/play-in%20browser-success.svg\" alt=\"Try in browser\"\u003e\u003c/a\u003e\n  \u003cbr\u003e\n  \u003ca href=\"#platform-support\"\u003e\u003cimg src=\"https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS%20%7C%20Web-lightgrey.svg\" alt=\"Platform\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## Overview\n\n**Rusty2600 is a cycle-accurate Atari 2600 (VCS) emulator written in pure\nRust.** It targets the Mesen2 / ares / higan accuracy bar: a master-clock\nlockstep scheduler running at TIA-color-clock resolution (3.579545 MHz NTSC),\nwith the 6507 CPU advancing on every third clock and `WSYNC`/`RDY`\nbeam-stalls freezing the CPU mid-cycle while the rest of the machine keeps\nrunning. It clears its bundled Klaus2m5 functional and decimal-mode oracles\nbit-exact and passes the trimmed SingleStepTests `6502` corpus\n**4,660/4,660 cases across all 233 opcodes**, with the full ~10K-case-per-opcode\ncorpus verified weekly in CI.\n\nBeyond reference accuracy, Rusty2600 is a real emulation platform, not just a\ntiming-accurate core: **all 26 of the console's catalogued bankswitch\nschemes** (all 8 Curated-tier schemes plus all 16 BestEffort\nschemes) wired into\nautomatic detection, a real **debugger** (live 6507/TIA/RIOT/memory panels,\nbreakpoints, a standalone disassembler), a real **RetroAchievements**\nbackend (`rcheevos`-vendored, per-frame achievement tracking and hardcore\nmode), **save-states and rewind**, and a growing **accuracy battery**\n(`rusty2600-test-harness`) gated in CI so a regression can never silently\nship. The frontend is pure Rust (`winit` + `wgpu` + `cpal` + `egui`) with\nnative binaries for Linux, macOS, and Windows, plus a WebAssembly build that\nruns in the browser.\n\n**[Try it in your browser](https://doublegate.github.io/Rusty2600/)** — no\ninstall required.\n\n---\n\n## Why Rusty2600?\n\nRusty2600 combines **accuracy-first emulation** of one of the industry's\noldest and most idiosyncratic consoles with **modern tooling** and the\n**safety guarantees of Rust**. The 2600 has no framebuffer of its own — the\nTIA races the electron beam and the program itself is responsible for timing\nevery visible pixel — which makes cycle-exactness a *correctness*\nrequirement, not a nice-to-have: get the timing wrong and games don't just\nlook different, they visibly break.\n\n**Key differentiators:**\n\n- **Reference-grade timing** — an integer TIA-color-clock lockstep\n  scheduler (not catch-up), the 6507 on every third clock, `WSYNC`/`RDY`\n  beam-stalls modeled as a genuine sub-instruction freeze — see\n  `docs/scheduler.md` and ADR 0001.\n- **Determinism as a hard contract** — same seed, ROM, and input sequence\n  yield a bit-identical framebuffer and audio (ADR 0004). This is what makes\n  save-state round-trips and regression testing correct by construction, and\n  what save-states, rewind, run-ahead, and the planned rollback-netplay work\n  all build on.\n- **An honesty gate, not a marketing number** — every bankswitch scheme is\n  classified Core / Curated / BestEffort (ADR 0003); a BestEffort scheme\n  can *never* silently back the accuracy oracle, enforced structurally by\n  CI, not just documented.\n- **Safe, modular Rust** — the chip stack (`rusty2600-cpu`, `-tia`, `-riot`,\n  `-cart`, `-core`) is `no_std + alloc`, one-directional, and independently\n  testable; the only `unsafe` lives behind the frontend/FFI boundary.\n\n---\n\n## Highlights\n\n| Feature | Description |\n|---|---|\n| **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 |\n| **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) |\n| **Real Debugger** | Live 6507/TIA/RIOT/memory panels, breakpoints/step/continue, a side-effect-free memory peek, a standalone disassembler — default-on |\n| **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 |\n| **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) |\n| **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 |\n| **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 |\n| **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 |\n| **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 |\n| **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 |\n| **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 |\n| **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) |\n| **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 -\u003e Load/Unload Script` menu; `v2.3.0` wired `drawText`/`drawRect`/`drawPixel` output into the actual screen, piggybacked on the frontend's existing egui pass |\n| **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 -\u003e 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 |\n| **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) |\n| **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 |\n| **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 |\n| **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 |\n| **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 |\n| **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 |\n| **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 |\n| **Manual Save-State Slots** *(v2.4.0)* | `File -\u003e 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 |\n| **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 -\u003e 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 |\n| **`.zip` ROM Loading** *(v2.4.0)* | Both the native `File -\u003e 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 |\n| **Pure Rust** | `winit` + `wgpu` + `cpal` + `egui` frontend; a safe `no_std + alloc` chip stack behind a one-directional crate graph |\n\nThe `v1.1.0 -\u003e v2.0.0` RustyNES-parity line is complete; `v2.1.0`\nthrough `v2.3.0` closed every follow-up gap that release's own\nreconciliation carried forward except mobile-store submission and a real\nXcode-verified iOS build (both explicitly deferred, not gaps).\n`v2.4.0` \"Save Point\" opened a new gap-closure arc against RustyNES itself\n(`v2.4.0 -\u003e v3.0.0`, see [`to-dos/ROADMAP.md`](to-dos/ROADMAP.md)),\nlanding manual save-state slots (the headline item — Rusty2600 previously\nhad no player-facing \"save my game\" feature at all despite the\nunderlying format being real since `v1.10.0`), a CI-gated performance-\nregression bench, a Stella-oracle differential test strengthening the\nreal paddle-timing simulation, `.zip` ROM-archive loading, and a GitHub\nrepo hygiene pass. `v2.5.0` \"Web Awakens\" followed with a real\n`winit`+`wgpu`+`egui` wasm build (compiles and builds cleanly; rendering\nunconfirmed in a real browser — see the WebAssembly row above), a\ndebugger Lua console panel, and a Keyboard Controller/Trak-Ball research\ndecision. `v2.6.0` \"Rollback Bridge\" added browser WebRTC netplay (see\nthe Rollback Netplay row above) and a master dependency-upgrade sweep\nconsolidating 12 Dependabot PRs. `v2.7.0` \"True Colors\" built the TIA\nobject-ID mask and wired the HD-pack live rendering splice (see the\nHD-Pack Live Rendering row above). `v2.8.0` \"Touchpoint\" built the first\nwave of `wasm-winit` web parity — an on-screen touch overlay, a\nwasm32-safety review of the Settings panel, and real `localStorage`\nconfig persistence (see the WebAssembly row above). `v2.9.0` \"Full Circle\"\nbuilt the second wave — a `?settings=` share-link, a wasm32-safe\n`debug-hooks` debugger overlay, and PWA install/offline support (see the\nWebAssembly row above). In-browser Lua scripting was investigated for\nreal this release (not just re-deferred on old reasoning) and stays\ndeliberately unsupported on `wasm32`: `mlua`'s vendored C build is\nconfirmed to be a hard wall on `wasm32-unknown-unknown`, and the one\nrealistic pure-Rust fallback, `piccolo`, isn't yet a workable substrate\neither — its only published release implements almost none of Lua's\n`string`/`table` stdlib, and its more-complete unpublished branch is\nexplicitly pre-1.0-unstable by its own maintainer's admission (see\n`docs/scripting.md`'s `v2.9.0` status section) — see\n[`CHANGELOG.md`](CHANGELOG.md) for exactly what shipped in each release.\nOpen follow-up work (each its own well-scoped future release): real\nXcode-verified iOS build/run (this sandbox has no Xcode toolchain),\nnetplay's WebRTC transport and real cross-NAT verification, DPC+/CDF\nper-player console-switch/paddle modeling, in-browser Lua scripting\n(blocked on `piccolo` publishing a crates.io release with real\n`string`/`table` coverage — an upstream milestone, not one this project\ncontrols), and a real HD-pack replacement-art library (only a\nproof-of-mechanism placeholder exists\ntoday). The `.r26m` movie format and TAStudio-lite panel shipped in\nv1.7.0; live per-frame auto-recording into the emu-thread's hot path is\nhonestly deferred (`docs/movie.md`).\n\n---\n\n## Features\n\n### Emulation core\n\n- **6507 CPU** — documented and undocumented opcodes, cycle-exact against\n  the full SingleStepTests corpus (weekly CI) and Bruce Clark's exhaustive\n  ADC/SBC decimal-mode sweep (`ERROR=0`, bit-exact).\n- **TIA** — beam-raced video (RESPx/HMOVE comb, playfield, players/missiles/\n  ball, all 15 pairwise collision latches) and two-channel poly-counter audio\n  synthesis, unit-tested against Stella/Gopher2600 as differential oracles.\n- **RIOT** — the console's only 128 B of RAM, the DDR/I/O ports, and the\n  interval timer (prescale, underflow/`INSTAT`, read-after-write, and the\n  post-underflow decrement-rate reversion — see `docs/riot.md`).\n- **Master-clock lockstep scheduler** — the Bus owns every chip; the 2600\n  has no separate work RAM, so save-states/rewind only ever need to capture\n  the CPU + Bus (TIA + RIOT + cart) state.\n\n### Cartridges\n\n**All 26 catalogued schemes are implemented and wired into automatic\n`detect()`** — all 8 Curated-tier schemes (2K, 4K, F8, F6, F4, CV,\nFA/CBS-RAM, Superchip, DPC, E7) plus all 16 BestEffort schemes (F0, E0,\n3F, 3E, EF/EFSC, DF/DFSC, BF/BFSC, UA, 0840, FE, SB, X07, 4A50,\nAR/Supercharger, DPC+, CDF/CDFJ/CDFJ+). Two `Board` hooks\n(`snoop_write`/`snoop_read`) let a scheme react to accesses the console\nroutes to TIA/RIOT space, not just the `$1000+` cart window — needed for\nthe 3F/3E/UA/0840/FE/X07/SB/4A50/AR families (4A50 also uses a smaller\nin-window instance of its hotspot state machine at `$1F00-$1FFF`; AR's\nBIOS handoff additionally uses a `Board::take_oob_pokes()` hook to stage\ndirect RIOT-RAM writes, mirroring Stella's `System::pokeOob`). Both\nHarmony/Melody ARM-coprocessor families consume the `rusty2600-thumb`\nARM7TDMI Thumb-1 interpreter (v1.6.0): DPC+ (v2.2.0) and CDF/CDFJ/CDFJ+\n(v2.3.0), the latter also exercising a real `ARMinterrupt` fault-\nservicing dispatch DPC+ never needed — both verified running real\nhand-assembled Thumb-1 programs. See [`docs/cart.md`](docs/cart.md) and\n[`docs/thumb.md`](docs/thumb.md) for the full catalogue, tiering, and\ninterpreter architecture.\n\n### Modern features\n\n- **Debugger** (`debug-hooks`, default-on) — live 6507/TIA/RIOT/memory\n  panels, breakpoints/step/continue, a side-effect-free `Bus::peek`/\n  `peek_range`, a standalone disassembler, a watch/conditional-breakpoint\n  expression engine, a JSR/RTS call stack, a TIA write-scatter viewer, and a\n  player/missile/ball position panel.\n- **RetroAchievements** (`retroachievements`, off by default) —\n  `rusty2600-cheevos` vendors the `rcheevos` C library; login, a live\n  achievement list, leaderboards, rich presence, per-frame achievement\n  tracking, hardcore mode, and a recent-unlocks toast list all work.\n- **Save-states + rewind** — a versioned binary snapshot format\n  (`rusty2600-core::save_state`, ADR 0007) built on the chip stack's\n  existing `serde` derives; the rewind ring reuses the same encoding rather\n  than paying a raw-clone's worst-case cost.\n- **Run-ahead** — speculatively simulates a few frames ahead to hide a\n  game's internal input lag, built on the save-state snapshot primitives.\n- **WebAssembly** — a canvas-2D bootstrap with real keyboard input and Web\n  Audio API output; native-only features compile out automatically. A full\n  winit+wgpu+egui browser build (`wasm-winit`, matching the native binary)\n  compiles cleanly and, as of `v2.9.0`, has a wasm32-safe debugger overlay\n  and a `?settings=` share-link; live-browser rendering confirmation and a\n  deployed switch-over remain open (see the WebAssembly feature-matrix row\n  above and `docs/frontend.md`).\n- **PWA install** (`v2.9.0`) — a web-app manifest + service worker\n  (`crates/rusty2600-frontend/web/manifest.json`/`sw.js`) make the deployed\n  wasm demo installable and offline-capable after a first visit.\n\n---\n\n## Quick Start\n\n### Download binaries\n\nGrab the latest release for your platform from the\n[Releases page](https://github.com/doublegate/Rusty2600/releases).\n\n```bash\n# Linux / macOS\ntar xzf rusty2600-\u003cversion\u003e-\u003ctarget\u003e.tar.gz\n./rusty2600\n\n# Windows (PowerShell)\nExpand-Archive rusty2600-\u003cversion\u003e-x86_64-pc-windows-msvc.zip\n.\\rusty2600.exe\n```\n\n### Build from source\n\n```bash\n# Clone the repository\ngit clone https://github.com/doublegate/Rusty2600.git\ncd Rusty2600\n\n# Build the workspace (release)\ncargo build --release --workspace\n\n# Run a ROM you legally own (or launch bare and use File -\u003e Open ROM).\n# .a26/.bin/.rom, or a .zip containing one, both work.\ncargo run --release -p rusty2600-frontend -- path/to/rom.a26\n\n# Optional: build with RetroAchievements (needs a C compiler for vendored rcheevos)\ncargo build --release -p rusty2600-frontend --features retroachievements\n\n# The no_std embedded-target gate (confirms the core stays no_std + alloc)\ncargo build -p rusty2600-core --target thumbv7em-none-eabihf --no-default-features\n```\n\nOn Linux the frontend needs the wgpu/winit/cpal system dependencies\n(CachyOS / Arch shown; substitute your distro's equivalents):\n\n```bash\nsudo pacman -S --needed libxkbcommon wayland alsa-lib systemd-libs\n```\n\n---\n\n## Default Controls\n\n| Input | Key |\n|---|---|\n| Joystick (P0) D-pad | Arrow keys |\n| Joystick (P0) fire | Z |\n| Joystick (P1) D-pad | W A S D |\n| Joystick (P1) fire | Q |\n| Console: Select / Reset | F1 / F2 |\n| Console: Color ↔ B\u0026W | F3 |\n| Console: Left / Right difficulty A↔B | F4 / F5 |\n| Toggle debugger overlay | `` ` `` |\n| Open ROM / Quit | F12 / Esc |\n\nPaddles (`INPT0`–`3`, analog) bind to the mouse / gamepad axes. USB gamepads\nauto-bind to P0. All bindings live in `config.toml` (see\n[`docs/frontend.md`](docs/frontend.md)); a key-rebind UI is planned.\n\n---\n\n## Architecture\n\nRusty2600 is a Cargo workspace with a strictly one-directional chip-crate\ngraph — no chip crate depends on another except `rusty2600-tia`, which reads\ncart-mediated bus state via `rusty2600-cart`:\n\n| Crate | Role |\n|---|---|\n| `rusty2600-cpu` | The MOS 6507 (a 6502 in a 28-pin package: A0–A12, no exposed NMI/IRQ pins) |\n| `rusty2600-tia` | The TIA (Television Interface Adaptor) — beam-raced **video AND audio** |\n| `rusty2600-riot` | The MOS 6532 RIOT — the console's only RAM (128 B) + I/O ports + interval timer |\n| `rusty2600-cart` | The tiered bankswitch catalogue, gated by the ADR-0003 honesty marker |\n| `rusty2600-core` | The Bus (owns every chip) + the master-clock lockstep scheduler + save-states |\n| `rusty2600-frontend` | The `winit` + `wgpu` + `cpal` + `egui` shell (binary `rusty2600`), including the debugger |\n| `rusty2600-cheevos` | Native-only RetroAchievements integration — a safe wrapper around vendored `rcheevos` |\n| `rusty2600-gfx-shaders` | `no_std` WGSL post-process shader sources for the composable shader stack |\n| `rusty2600-test-harness` | The accuracy oracle + the bankswitch-tier honesty gate |\n\nThe Bus owns everything mutable; the CPU borrows `\u0026mut Bus` for the duration\nof a step. The 2600 has no separate work RAM, so there's no WRAM field on\nthe Bus — the only RAM in the whole console lives in the RIOT's 128 bytes.\nSee [`docs/architecture.md`](docs/architecture.md) for the full picture and\n[`docs/adr/`](docs/adr/) for the numbered architectural decisions (the\nmaster-clock scheduler, the fractional-timebase question, the accuracy\nhonesty gate, the determinism contract, TIA revision modeling, power-on RAM\nseeding, and save-state versioning).\n\n---\n\n## Compatibility and Accuracy\n\n| Suite | Layer | Pass / Total |\n|---|---|---|\n| Klaus `6502_functional_test` | CPU oracle | 1 / 1 |\n| Klaus `6502_decimal_test` (BCD) | CPU oracle | 1 / 1 — exhaustive 256×256×2-carry-in `ADC`/`SBC` sweep, bit-exact |\n| SingleStepTests `6502` (trimmed) | cycle-exact audit | 4,660 / 4,660 cases, 233 / 233 opcodes |\n| SingleStepTests `6502` (full corpus) | cycle-exact audit | weekly CI cron (~700 MB across 233 opcodes, not per-push) |\n| **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 |\n| Workspace test suite | `cargo test --workspace` | 183 / 183 |\n\nEvery bankswitch scheme is classified **Core** (register-decode trivial,\nalways oracle-gated), **Curated** (a redistributable fixture + full test\ncoverage, oracle-gated), or **BestEffort** (reference-ported, register-decode\n+ boot-smoke tested only — **never** allowed to back the accuracy oracle).\nThis isn't a documentation promise: `mapper_tier_honesty.rs` asserts the\ninvariant in CI on every push. See [`docs/STATUS.md`](docs/STATUS.md) for\nthe authoritative, currently-accurate per-suite numbers and board matrix,\nand [`docs/testing-strategy.md`](docs/testing-strategy.md) for the full\nlayered methodology (unit tests → CPU golden-log → the accuracy battery →\ntolerance-aware snapshot comparison → a commercial-ROM regression oracle\ngated behind locally-supplied, never-committed ROM dumps).\n\nKnown accuracy work fixed along the way: a RIOT interval-timer bug that\npermanently stalled Pitfall II's boot-time wait loop (found via a rebuilt\nGopher2600/Stella differential probe, confirmed against Stella's own\n`M6532::peek`/`updateEmulation`) — see `docs/riot.md`.\n\n---\n\n## Performance\n\nRusty2600's per-frame workload is a fraction of a modern console's — a 2600\nframe is ~262 scanlines × 228 color clocks with a handful of chips, not a\nPPU/APU pipeline juggling thousands of tiles. Real Criterion baselines for\nthe CPU/TIA/RIOT/cart crates live in [`docs/performance.md`](docs/performance.md),\nmeasured with `cargo bench` on a pinned dev host — treat the documented\nnumbers as deltas across changes, not absolute cross-machine guarantees.\n\n---\n\n## Platform Support\n\n| Platform | Status |\n|---|---|\n| Linux (x86_64) | Native binary, CI-built every release |\n| macOS (aarch64) | Native binary, CI-built every release |\n| Windows (x86_64) | Native binary, CI-built every release |\n| WebAssembly | Runs in-browser — [try it live](https://doublegate.github.io/Rusty2600/) |\n| `no_std` embedded target | `rusty2600-core` builds for `thumbv7em-none-eabihf` with `--no-default-features` — a CI gate, not a shipped product |\n\n---\n\n## Documentation\n\n| Doc | Covers |\n|---|---|\n| [`docs/STATUS.md`](docs/STATUS.md) | The single source of truth: current pass counts, board matrix, version policy |\n| [`docs/architecture.md`](docs/architecture.md) | The Bus/scheduler design, the crate graph, the determinism contract |\n| [`docs/scheduler.md`](docs/scheduler.md) | The master-clock lockstep scheduler in detail |\n| [`docs/cpu.md`](docs/cpu.md) / [`docs/tia.md`](docs/tia.md) / [`docs/riot.md`](docs/riot.md) | Per-chip specs |\n| [`docs/cart.md`](docs/cart.md) | The full bankswitch catalogue and Core/Curated/BestEffort tiering |\n| [`docs/thumb.md`](docs/thumb.md) | The ARM7TDMI Thumb-1 interpreter (`rusty2600-thumb`): architecture, scope, deviations from the Go reference |\n| [`docs/movie.md`](docs/movie.md) | The `.r26m` TAS movie format and TAStudio-lite panel: architecture and scope |\n| [`docs/scripting.md`](docs/scripting.md) | The Lua scripting engine (`rusty2600-script`): the `emu` API, `ScriptBus`, determinism gating, scope |\n| [`docs/netplay.md`](docs/netplay.md) | Rollback netplay (`rusty2600-netplay`): the `ggrs` integration, scope, the rollback-desync test |\n| [`docs/mobile.md`](docs/mobile.md) | The mobile UniFFI bridge (`rusty2600-mobile`), the Android app, and the real-emulator verification |\n| [`docs/frontend.md`](docs/frontend.md) | The frontend crate's shape and behavior |\n| [`docs/testing-strategy.md`](docs/testing-strategy.md) | The layered accuracy-oracle methodology |\n| [`docs/performance.md`](docs/performance.md) | Measured Criterion baselines and profiling guidance |\n| [`docs/compatibility.md`](docs/compatibility.md) | Per-game/board compatibility notes and open questions |\n| [`docs/adr/`](docs/adr/) | Numbered architectural decision records |\n| [`to-dos/ROADMAP.md`](to-dos/ROADMAP.md) | The full phase/sprint plan through `v2.0.0` |\n| [`CHANGELOG.md`](CHANGELOG.md) | What actually shipped in each release |\n\n---\n\n## Contributing\n\nContributions are welcome — see [`CONTRIBUTING.md`](CONTRIBUTING.md) for the\nworkflow, commit-message conventions, and the pre-tag quality gate\n(`cargo fmt`, `cargo test --workspace`, `cargo clippy -- -D warnings`, the\n`no_std` build). Never commit commercial ROMs; the `commercial-roms` feature\nand `tests/roms/external/` exist precisely so local dumps stay local.\n\n## License\n\nRusty2600 is dual-licensed under **MIT OR Apache-2.0**. See\n[`LICENSE-MIT`](LICENSE-MIT) and [`LICENSE-APACHE`](LICENSE-APACHE).\n\n## Acknowledgments\n\nRusty2600's differential-oracle workflow leans on two open-source\nreferences studied (never vendored — both are GPL-licensed, incompatible\nwith this project's MIT/Apache-2.0 dual license) for behavioral\ncross-checking: [Stella](https://stella-emu.github.io/) (the canonical C++\nAtari 2600 emulator) and [Gopher2600](https://github.com/JetSetIlly/Gopher2600)\n(a Go implementation used as a headless differential-testing harness). Test\nROMs are drawn from Klaus Dormann's public-domain 6502 functional/decimal\ntest suites and the trimmed SingleStepTests `65x02` corpus.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoublegate%2Frusty2600","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdoublegate%2Frusty2600","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoublegate%2Frusty2600/lists"}