Skip to content

Frontend Architecture

DoubleGate edited this page Jul 8, 2026 · 1 revision

Frontend Architecture

rusty2600-frontend is the egui shell that turns the accurate core into a playable app: winit for windowing, wgpu for rendering, cpal for audio output, and egui for the UI, all pure Rust.

The always-on egui shell

Unlike a bare present-a-texture window, egui runs every frame: a persistent menu bar and status bar frame the emulated display, with toggleable debugger panels layered on top. The shell never holds the emulator lock inside the egui closure — menu interactions return a MenuAction that gets dispatched after the egui pass completes, keeping the UI and the emulation state cleanly separated.

2600-specific display

The TIA has no chip-owned framebuffer — see TIA — so the frontend composes the displayed image from the scanline buffer the core produces each frame, applying the region-appropriate NTSC/PAL/SECAM palette (palette is data, never a build-time fork).

Shader stack

A composable post-process pipeline Gfx::present chains after the base blit, arbitrary-length (not a fixed slot count), byte-identical to the base image when the stack is empty:

  • CRT scanline — the original built-in pass.
  • Composite artifact / NTSC composite YIQ decode — a real YIQ demodulation pass (not just a blur approximation), referencing the same class of algorithm used by well-regarded NTSC-accurate emulators. This is genuinely more load-bearing for the 2600 than for most consoles: many 2600 titles deliberately exploit NTSC composite artifact-color dithering for extra apparent hues, and the TIA core already emits a raw palette-index byte per dot, a natural fit for this pass.
  • hqNx / xBRZ upscaling — standard public-domain pixel-art upscaling algorithms, ported to WGSL.
  • A constrained RetroArch .slangp/.cgp preset importer — maps known shader-name stems to Rusty2600's own built-in passes and reports "unsupported" rather than silently dropping anything it can't map; deliberately not a full GLSL-to-WGSL transpiler.

All passes are output-only and frontend-only — the core stays byte-identical whether or not any shader is active.

The debugger (debug-hooks feature, default-on for native)

A real, live debugger, not a stub: 6507/TIA/RIOT/Memory panels, breakpoints, step/continue, a side-effect-free Bus::peek/peek_range (so inspecting memory never perturbs emulation), and a standalone disassembler. Additional panels layered on over time: watch expressions, call stack, an event log, a player/missile/ball (PMB) visualizer, a memory-compare tool, an access-count heatmap, a RetroAchievements panel, a TAStudio piano-roll editor, and a Lua console (see Scripting-Engine). Toggle the whole overlay with the backquote key.

debug-hooks is wasm-safe alongside the wasm-winit build feature (see Building-from-Source) as of v2.9.0 — the core CPU/TIA/RIOT/Memory panels and nearly everything else work in-browser, with one native-only exception (a TAStudio action).

RetroAchievements (retroachievements feature, off by default)

rusty2600-cheevos vendors the rcheevos C library and wires a safe RaClient into the frontend: per-frame achievement tracking, hardcore mode, and a RetroAchievements menu. Hardcore mode is one of the sources WritesLocked gates script/debug writes against — see Scripting-Engine.

Save-states, rewind, run-ahead

  • Manual save-state slots (v2.4.0) — a numbered-slot picker (8 slots, matched on both desktop and mobile), keyed by a per-ROM content hash so different ROMs' slots never collide. Built on top of the already-real, versioned SaveState format (see Architecture-Decision-Records ADR 0007) — the hard part (correct, versioned encode/decode) was already done by the determinism contract; this is UI/menu/file-layout wiring on top of it.
  • Rewind — a ring buffer of recent save-states, scrubbable from the UI.
  • Run-ahead (0..=4 frames, live via a Settings slider) — speculatively steps the simulation ahead on local input to reduce perceived latency, live via the frontend only; the core simulation loop never knows run-ahead exists.

2600-specific input

Joystick, paddle (analog, RC-circuit-timed to match real hardware), and console-switch (Select/Reset/Color-BW/Difficulty A-B) input are all modeled. A 2600 keyboard controller / Trak-Ball peripheral was researched and deliberately not modeled — genuinely obscure historical peripherals with negligible real-world software support; see Deferred-Features.

wasm

Two mutually-exclusive build features: wasm-winit (the real native UI compiled to WebAssembly, v2.5.0) and wasm-canvas (a simpler canvas-2D fallback, the currently-deployed GitHub Pages build). See Building-from-Source for exactly how to build each. wasm-winit also gained on-screen touch controls, a working in-browser Settings panel, localStorage/IndexedDB persistence, a share-link (?settings= URL query round-trip), and PWA installability across v2.8.0v2.9.0.

See also

Architecture-Overview · Scripting-Engine · Netplay-and-Rollback · Building-from-Source · Deferred-Features

Clone this wiki locally