Releases: doublegate/RustySNES
Release list
v1.11.0 "Podium"
Seventh release of the RustyNES-parity roadmap: RetroAchievements never loaded a game.
Fixed
- RetroAchievements never actually loaded a game. No code path ever called
RaClient::begin_load_game— login worked,CheevosState::do_frameran every
emulated frame, andAchievementTriggeredevents were wired all the way to
status-bar toasts, but with no game ever identified/loaded intorc_client,
there was no achievement set to evaluate memory against, so achievements
could never actually trigger.CheevosState::load_game/unload_gamenow
wrap the missing calls, invoked fromapp.rs'sMenuAction::OpenRom/
CloseRomhandlers (a no-op unless a user is logged in); apoll()-drained
toast surfaces success/failure so the fix is observably verifiable, not just
type-checked. This is the actual prerequisite bug blocking hardcore mode,
leaderboards, and rich presence from meaning anything — found while scoping
those features for this release.
Deferred (honestly scoped, not silently dropped)
- Splitting a new
rustysnes-rasession/UI crate out offrontend/src/cheevos.rs's
informal state, hardcore mode gating rewind/save-load/cheats/TAS, and
leaderboard/rich-presence UI are all real, substantial features that were
meaningless without the game-load fix above landing first. Pushed to a
later, explicitly-scoped release. Seeto-dos/VERSION-PLAN.md'sv1.11.0
section. - A ROM loaded via the CLI at startup, followed by a later login through the
Tools window, is not retroactively announced torc_client— the common
path (launch, log in, then open a ROM via the File menu) is unaffected. See
cheevos.rs's module doc.
v1.10.0 "Atelier"
Sixth release of the RustyNES-parity roadmap: HD-pack emu-thread wiring.
Fixed
- HD texture packs (
v1.3.0) were never wired into theemu-threadbuild —
app.rs's synchronous render path composited an active pack via
hd_compositor::compositebefore its owndrop(emu), but the threaded build's
emu_thread::drive_onehad no equivalent step, so a threaded build with a pack
selected silently rendered the native (uncomposited) framebuffer
(docs/frontend.md's documented scope cut, closed here).drive_one's
plain-frame (run-ahead-disabled) branch now composites before publishing to
PresentBuffer; the common no-pack-active case stays exactly as fast as before
(a cheaphd_pack_name()&selfpre-check, no extra allocation) since the real
compositing cost only applies once a pack is actually selected. Found in review
(#90): the lock is now released before thePresentBuffercopy in this branch
too, matching the run-ahead branch's existingdrop(emu)-before-publish pattern.
Deferred (honestly scoped, not silently dropped)
- The in-app HD-pack Builder GUI (browsing the live
TileTagstream,
assigning replacement PNGs, writingpack.toml+ assets) needs a new
core-side "reconstruct RGBA pixels for a given tile hash" API that doesn't
exist yet — the tile-identity hash doesn't reverse to a VRAM location, so
authoring support is a genuinely separate, substantial piece of work from the
emu-threadwiring fix above. Pushed to a later, explicitly-scoped release.
Seeto-dos/VERSION-PLAN.md'sv1.10.0section. - HD-pack compositing is deliberately NOT applied to
emu-thread's run-ahead
branch. Found in review (#90):step_with_run_ahead's returned frame is a
PEEKED frame (captured, then rolled back soemu's persisted state only
advances by one real frame), butEmuCore::hd_pack_composite_inputsreads
Ppu::tile_tags()fromemu's CURRENT (post-rollback) state — a different
frame than the peeked bytes. Compositing with a mismatched(fb, tags)pair
would silently apply replacement tiles keyed to the wrong frame, corrupting
the picture rather than just showing native art, so this rung skips
compositing there instead. The same desync already exists, unfixed, in
app.rs's synchronous render path (pre-existing since run-ahead and
HD-pack were first combined; not introduced by this release, not touched by
it either) — both are tracked together as av1.10.x/later follow-up in
to-dos/VERSION-PLAN.md. Run-ahead and HD-pack are each off by default, so
this only affects the narrow case where a user has both features enabled
simultaneously.
v1.9.0 "Marionette"
Fifth release of the RustyNES-parity roadmap: Lua scripting bus-widening.
Added
- Lua scripting: full-bus reads —
rustysnes-script'semu.read(addr)now reaches
[Bus::peek] (the full 24-bit bus: WRAM, cart ROM/SRAM; I/O register space still reads back as
0,Bus::peek's own documented behavior, matching the debugger's Memory panel), widened from
[Bus::peek_wram] (WRAM-only).emu.write(addr, val)stays scoped to
[Bus::poke_wram] (WRAM only) — a side-effect-free "poke" has no clean semantic for register
space (a real PPU/APU/DMA register write has hardware side effects a silent poke can't model
without either faking them or breaking the determinism contract), so widening reads while
keeping writes WRAM-scoped is a deliberate, asymmetric choice, not an oversight.
Deferred (honestly scoped, not silently dropped)
- A wasm
piccoloLua backend (scripting is currently native-only,mlua) and TAStudio-style
piano-roll movie editing are both substantial standalone efforts, comparable in size to a full
release rung on their own — pushed to a later, explicitly-scoped release rather than folded into
this one. Seeto-dos/VERSION-PLAN.md'sv1.9.0section.
v1.8.0 "Tracepoint"
Fourth release of the RustyNES-parity roadmap: debugger depth II.
Added
- Memory Compare panel — captures a baseline snapshot of the Memory panel's current window
and diffs it against the live window on every frame, showing only the rows that changed
(before -> afterhex). Flags a mismatch instead of a misleading diff if the window has
scrolled since the baseline was captured (no scroll control exists yet — same gap the Memory
panel itself carries). - Docs panel — an in-app SNES-terminology glossary (
docs/glossary.md, embedded via
include_str!, ~3KB) for quick lookup mid-session, plus a link to the fullMkDocshandbook
(v1.6.0 "Lighthouse"). Deliberately scoped to the glossary alone, not the full 10-50KB
subsystem-spec docs, to keep wasm size impact negligible (verified: +2KB gzip, still ~2.05 MiB
under the 5 MiB budget).
Deferred (honestly scoped, not silently dropped)
- A call-stack view, an instruction/event trace buffer, and an inline 65816 assembler all need
new core-side instrumentation (tracking call/return events or recording a trace log as they
happen — not inferable from a point-in-time memory snapshot the way this rung's two panels are).
A larger cross-crate change than this rung's frontend-only scope; tracked as follow-up work. - A dedicated per-coprocessor-type register panel (DSP-2/4, S-DD1, CX4, OBC1, ST018, S-RTC beyond
the SA-1/GSU state the existing Cart panel already shows) needs newBoard-trait debug-state
accessors — also deferred.
v1.7.1
Patch release: a single user-reported bugfix, no new scope.
Fixed
- The wasm demo's canvas rendered at a smaller, fixed 2x scale instead of the 3x
INITIAL_SCALEnative launches at.App::create_windowspecial-casedwasm32to a hardcoded
(512.0, 448.0)LogicalSize, deferring toweb/index.html's own CSS (512x448) — but
winit's web backend actually resizes the attached<canvas>to match the requested inner size,
overriding that CSS regardless. RustyNES's owncreate_windowrequestsNES_W * INITIAL_SCALE
unconditionally (native and wasm alike), which is why its own demo already rendered at 3x — a
user comparing the two demos side by side noticed the size difference.web/index.html's CSS
updated to896x728(the new pre-JS fallback appearance, matching the actual chrome-padded 3x
size) so there's no flash of the old size before winit applies the real one. Found in review
(#82): the fallback CSS's fixed height paired withmax-width: 96vwwould have distorted the
canvas on narrow viewports — switched toaspect-ratio+height: autoinstead.
v1.7.0 "Telemetry"
Third release of the RustyNES-parity roadmap.
Added
- Debugger foundation — the 4-panel debugger overlay (previously inline in
ui_shell.rs,
~600 lines) moved into a dedicateddebugger/module (mod.rs+cpu_panel.rs/ppu_panel.rs/
apu_panel.rs/cart_panel.rs/watch_panel.rs) — a pure structural extraction, zero behavior
change, that later debugger-depth rungs (v1.8.0onward) plug new panels into.lib.rs's
stale "the deep debugger panels are still TODO stubs" doc comment corrected (the panels have
existed sincev0.8.0; this rung gives them a real module). - Memory panel — the Watch panel (renamed "Memory/Watch" in the panel selector) gained a
read-only hex dump of a 512-byte window of WRAM/cart space (DebugSnapshot::memory_window,
read via the same non-intrusiveBus::peekthe disassembler already uses; I/O register space
reads back as00rather than a live register value —Bus::peekintentionally doesn't model
registers, so this is a memory dump, not a register viewer). Fixed at$7E0000(WRAM bank 0)
by default — no UI scroll control yet (EmuCore::set_debug_memory_scrollexists for a future
one to call), the same honestly-tracked gap the existing VRAM viewer already carries. Write
support and a RAM-search tool are explicitly not included in this rung — deferred, not
overclaimed.
Fixed
- The workspace version was stuck at
1.4.0sincev1.5.0—env!("CARGO_PKG_VERSION")
feeds the egui Help window's version label and the CLI's--versionoutput (including on the
deployed GitHub Pages wasm demo), so both silently under-reported the running version through
thev1.5.0/v1.6.0releases. Every priorchore(release)commit back tov0.7.0bumped
[workspace.package] version(and each crate's own pinnedversionfield) as part of the
closeout — a step that isn't spelled out indocs/adr/0007's decision list and got missed
starting atv1.5.0. Reported by a user testing the live demo; bumped to1.7.0across the
workspace and all 11 non-workspace-inherited crates, and this ceremony gap is now called out
explicitly into-dos/VERSION-PLAN.md's standing release checklist. - Findings from review (#80): the memory panel's doc comment, panel label, and CHANGELOG
entry all overclaimed "full 24-bit CPU bus" without noting thatBus::peekreturns0for
I/O register space — corrected in all three places.
v1.6.0 "Lighthouse"
Second release of the RustyNES-parity roadmap.
Added
- Documentation site + PWA + accuracy ledger — a Material for MkDocs handbook (
mkdocs.yml)
is now published athttps://doublegate.github.io/RustySNES/docs/, alongside the wasm demo
(/) and rustdoc (/api/), replacingpages.ymlwith a combinedweb.ymlthat also enforces
the existing<5MiBgzip wasm size budget (scripts/wasm_size_budget.sh) on every PR. The wasm
demo gained PWA/offline support (manifest.webmanifest, a stale-while-revalidatesw.js
service worker, a realicon.svg). Newdocs/accuracy-ledger.mdmaps every known
approximation/divergence to an explicit disposition (Remediated / No-stricter-oracle-available /
Deferred / Out-of-scope), the "why" companion todocs/STATUS.md's pass-count dashboard.
docs/DOCUMENTATION_INDEX.mdrefreshed (was still stampedv0.4.0, referenced a nonexistent
SALVAGE_MANIFEST.md).
Fixed (caught in PR review, #78)
web.yml'scancel-in-progresswas inverted relative to its own comment — PR size-budget
checks could be cancelled mid-flight while stalemainpushes weren't; corrected to cancel on
pushonly.sw.js's fetch handler could resolverespondWith()toundefinedon a truly offline first
visit (network fails and nothing is cached yet); now falls back to a synthetic503response.
v1.5.0 "Bedrock"
First release of the RustyNES-parity roadmap: closes the gap between this project's own
feature/UX/accuracy maturity and its sibling NES emulator RustyNES, tracked in lockstep rather
than a frozen snapshot. This rung is CI safety net only — see to-dos/VERSION-PLAN.md's
"RustyNES-parity ladder" section for the full v1.5.0-v1.19.0 plan.
Added
- CI safety net —
cargo test --workspacenow runs on every PR/push tomain(new
test-lightjob), not only on a tagged release. A newchanges/setupjob pair computes a
light-vs-full run mode per push (mirroring RustyNES's own pattern), andfull-test/no_std/
benchnow also run on every push tomain(previously tag-only), plus a weekly drift-net cron
and manual dispatch. A newci-successjob is the one stable required-check name; branch
protection onmainnow requires it. Seedocs/adr/0011. - A shared
.github/actions/rust-setupcomposite action factors the pinned toolchain version and
cache-key convention out ofci.yml/pages.ymlinto one place. to-dos/LOCKSTEP-CHECKLIST.md— the process for re-checking RustyNES's own continuing
development before scoping each subsequent rung in the parity ladder.
Fixed (caught in PR review, #76)
- The
rust-setupcomposite action pinneddtolnay/rust-toolchain@master(a floating ref);
changed to@1.96, matching whatci.yml's jobs already used before this release. - The composite action's Linux frontend dependency list was missing
libxkbcommon-x11-dev
(present inCONTRIBUTING.md's documented list but never actually installed by the old inline
per-job steps this action replaces) — added, andCONTRIBUTING.mdreconciled to match exactly.
v1.4.0 "Convergence"
Added
- Window Size presets (native only) — View → Window Size offers 1x/2x/3x/4x (100%-400%) of
the SNES native resolution, matching RustyNES; the app now launches at 3x by default instead of
a fixed 512x448 window. - Libretro peripheral negotiation —
rustysnes-libretronow offers Mouse (both ports) and
Super Multitap / Super Scope (port 2) viaRETRO_ENVIRONMENT_SET_CONTROLLER_INFO, mirroring
bsnes's own libretro core's per-port device menu. emu-threadmechanical re-sync — cheats, watchpoints, breakpoints, port2-peripheral
selection, and per-voice audio mutes now apply in the threaded build too (previously only the
synchronous drive path saw these changes).emu-threadrun-ahead + netplay-aware pause — run-ahead now runs on the emu thread via
crate::rewind::step_with_run_ahead, only when actually configured (matching the synchronous
path's ownrun_ahead > 0branch, avoiding an avoidable per-frame allocation in the common
disabled case — caught in PR review); netplay now actually functions underemu-thread(its
NetplayState::drivecall was previously dead code there, so netplay was silently
non-functional in threaded builds), pausing the emu thread TOCTOU-safely via a new
EmuControl::netplay_pausedflag re-checked under the sharedEmuCorelock.PresentBuffer
now carries the framebuffer's(width, height)alongside its bytes, and the present path tracks
the dims that actually match its staging buffer (Active::present_dims) rather than the emu's
live (possibly-moved-on) resolution, so a run-ahead-peeked frame can never publish bytes for one
resolution against dims from another (also caught in review).emu-threadis now clippy- and
test-gated in CI for the first time (previously referenced only in a comment). Movies, Lua
scripting, RetroAchievements, and rewind-recording remain intentionally unported toemu-thread
— confirmed via RustyNES's own reference implementation, which doesn't port these to its thread
either.
Fixed
- Fullscreen crash on monitors wider/taller than 2048px —
Gfxrequested
wgpu::Limits::downlevel_webgl2_defaults()unconditionally on every target, capping
max_texture_dimension_2dat 2048 even on native GPUs that support far more. Fullscreening on
e.g. a 3440x1368 ultrawide madeSurface::configurereceive an out-of-range request and
panic/abort (wgpu::Surface::configurehas no recoverable error path here). Native now requests
downlevel_defaults()and both targets call.using_resolution(adapter.limits()), raising the
floor preset to match the real adapter; the granted limit is tracked at runtime and enforced
everywhere the old hardcoded 2048 constant was. - Open bus during DMA/HDMA transfers (the "Speedy Gonzales stage 6-1" mechanism) — DMA/HDMA
reads now update the open-bus latch, matching real hardware; writes deliberately do not, per a
direct cross-check against ares' and bsnes' ownCPU::ChannelDMA implementation.
superfx_boots_live_and_deterministic's 24 golden hashes were re-blessed with that citation
trail as justification — seedocs/scheduler.md§Open bus via DMA/HDMA.
v1.3.0 "Palimpsest"
Oracle/golden suites: all held, no regressions. The full workspace test suite (455 tests,
44 suites) plus the full --features test-roms ROM-oracle battery (28 tests, 17 suites via
-p rustysnes-test-harness --release) are green; no golden hash changed. fmt/clippy clean
across every feature combination this feature touches (default, hd-pack, full,
emu-thread,hd-pack, plus the pre-existing debug-hooks/scripting/cheats/netplay/
retroachievements lanes); no_std build clean; both wasm32 frontends (wasm-winit default,
wasm-canvas) build clean via real trunk build --release runs; rustysnes-libretro builds
clean.
Added
- HD texture packs (
hd-packfeature, off by default) — replace individual 8×8 tiles with
higher-resolution PNG art while the accuracy-critical core stays completely pack-agnostic
(docs/adr/0010).rustysnes-ppucomputes a palette-inclusive XXH3-64 tile-identity hash
(hdtag::hash_tile, hashed into a fixed stack buffer — no heap allocation on the rendering hot
path) and records it per composited pixel into a write-onlyPpu::tile_tags()side-buffer,
populated only whenPpu::set_hd_pack_tagging(true)is on; leaving it at its defaultfalse
is proven byte-identical to every prior release
(hd_pack_tagging_toggle_does_not_alter_framebuffer_output), and the whole mechanism compiles
out entirely — not just runtime-disabled — when thehd-packfeature is off. The frontend owns
everything pack-specific: a versionedpack.tomlmanifest + PNG loader (crate::hd_pack,
pure-Rustpngdecode, path-traversal-safe, duplicate-hash-rejecting), a pure CPU compositor
(crate::hd_compositor::composite, fully unit-testable without a GPU adapter), a Settings →
Video pack selector (dynamicComboBox, populated per-ROM via the same SHA-256 identity
save-states already use), andconfig.video.hd_pack_namepersistence with automatic
re-selection on ROM load. The compositor is wired into the live wgpu present path:Gfx's
previously fixedMAX_W × MAX_Hstreaming texture now grows on demand
(Gfx::ensure_texture_capacity, capped at this device's actual downlevel-WebGL2
max_texture_dimension_2d) to fit the composited output at a fixed 2× upscale, with the
no-pack-active path staying pixel-identical to before (the texture never grows past its
original allocation unless a pack is actually active). Not yet built, honestly tracked:
a user-configurable upscale factor (fixed at 2× for now) andemu-thread-build compositing
(that build's framebuffer arrives via a lock-free handoff with no equivalentTileTagchannel
yet). Seedocs/ppu.md§HD texture packTileTagrecording hook,docs/frontend.md§HD
texture packs, anddocs/adr/0010.
Process note: all three feature PRs (#66, #67, #68) went through the full branch → CI →
automated bot review → fix → reply → resolve → green → squash-merge ceremony. Real findings
addressed along the way: a heap allocation on the PPU rendering hot path, a path-traversal
vulnerability in the pack loader, a memory-pre-allocation DoS vector sized off an untrusted PNG
header, an integer-overflow risk in the compositor's coordinate math, a stale-tile-tags bug when
tagging was toggled off mid-session, an active-pack-cleared-on-a-failed-ROM-load bug, a
redundant per-frame ROM re-hash, and a texture-capacity cap that was enforced by the caller but
not the function itself. Two Gemini suggestions were investigated and found to not actually
compile as proposed (a borrow-checker conflict in the Settings pack-selector closure) — verified
by trying them, not just trusting the diff, and documented with the reasoning inline.
Files changed: 15 files across 3 PRs — crates/rustysnes-ppu/src/hdtag.rs (new),
crates/rustysnes-ppu/src/{lib,render}.rs, crates/rustysnes-frontend/src/hd_pack.rs (new),
crates/rustysnes-frontend/src/hd_compositor.rs (new), crates/rustysnes-frontend/src/{emu, gfx,app,config,ui_shell,cli,save_states}.rs, crates/rustysnes-core/Cargo.toml (new hd-pack
feature propagation), docs/{ppu,frontend}.md, docs/adr/0010-hd-texture-pack-system.md (new).
Testing evidence: cargo test --workspace (455 tests, 44 suites), cargo test -p rustysnes-test-harness --features test-roms --release (28 tests, 17 suites, zero regressions),
cargo clippy --workspace --all-targets -- -D warnings across every feature combination this
work touches, cargo fmt --all --check, RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps (both default and --features hd-pack), the no_std gate, two real trunk build --release runs (wasm-winit default + wasm-canvas), and cargo build -p rustysnes-libretro.
Manual verification: real headless (xvfb-run) launches of the native binary against a staged
ROM — with no pack configured (unaffected path), with a real generated pack at the default 2×
scale, and with the scale temporarily forced to 3× specifically to exercise the texture-growth
path — all ran clean with no panics or wgpu validation errors.