RustyNES v2.9.3 — "Handset" (the old review threads closed, and the mobile run prepared)
RustyNES v2.9.3 — "Handset"
The fourth release of the v2.9.x line that ADR 0041 put before the SuperStation One core. It moves every dependency to its newest release, answers every review thread left open on the project's older pull requests, fixes the ten findings among them that still held, and makes the mobile device run ready to go.
The device run is not in this release. The maintainer moved the mobile device runs, the SuperStation One board session, and the fixes each produces to after v3.0.0 (2026-09-29). Every Swift change since v2.7.4 is therefore still uncompiled, no Android row has run on a device, and no hardware has run any bitstream. ADR 0041 defined v3.0.0 as the hardware-verified core, so its 2026-09-29 amendment records the conflict and leaves what v3.0.0 ships open.
Every dependency at its newest
- egui 0.36 with wgpu 30 and naga 30. The web build needs a fix to
egui-winitthat upstream has merged but not released, so a vendored copy of the published 0.36.2 carries a three-line guard until the next release. - Frames are presented evenly under wgpu 30. On a 120 Hz Wayland desktop the median present interval went from 5.5 to 8.4 ms and the p95 from 15.6 to about 11 ms, in two alternating A/B pairs against v2.9.2. Before, presents came in bunches; now they land one refresh apart.
- rcheevos 12.5.0 for RetroAchievements. Its
time_tfields now use the platform's own size, which fixes the struct layout on 32-bit Android and 32-bit Linux. - UniFFI 0.32.2, Gradle 9.8.0, and every compatible update.
bincodeis removed rather than bumped: nothing used it.
Old review threads, checked against today's code
244 review comments on PRs #7-#97 had never been answered. Each was checked against the current code: 234 were answered and resolved, and these ten still held and are fixed:
- Action 53 multicarts (mapper 28) are rebuilt to the NESdev spec. PRG banking was wrong for every outer bank size above 32 KiB and in both UNROM modes, the CHR bank register and the one-screen select did nothing, and the board powered on at the wrong bank. A unit test checks every mode, outer bank and inner bank against the wiki's table. Old save states still load.
- The ROM header editor writes only the fields you change. It used to re-encode all 16 bytes, which zeroed Vs. UniSystem protection types, the extended console type, bytes 14-15, the NVRAM sizes and anything in iNES 1.0 bytes 8-15. It also saved every mapper from 16 up as the wrong mapper (mapper 66 as 2), since v1.7.0. Loading ROMs was never affected.
- A palette file that cannot be read no longer erases your palette setting, and loading one reads only the 192 bytes it uses.
- NSF bank registers read as open bus, as the NSF spec lists them write-only.
- The Bisqwit NTSC filter no longer darkens the picture edges.
- The debugger's event heatmap keeps its proportions; a NaN audio gain falls back to unity; the
fds_tracediagnostic no longer calls a cut-short block a match; a stale documentation pointer is fixed.
For developers
rustynes_mappers::serialize_headeris deprecated: it zeroes every header bitHeaderhas no field for. Useserialize_header_preserving(header, original_bytes). The old function is removed at v3.0.0 with the other deprecations (ADR 0042).- CI now runs the Android app's Kotlin unit tests (on
mainpushes, and on pull requests that touch the app, its UniFFI bridge or the Android workflow) and compiles the iOS renderer on every pull request that changes code.
The mobile run, ready for later
docs/mobile-v2.9.3-run-sheet.md consolidates every row of the v2.7.4 checklist and v2.9.2's additions, plus rows for this release's wgpu and rcheevos changes. Some of the Android rows were run on an emulator (Android 14): the battery save survives force-stops, audio stops when the screen turns off, paused games stay paused, picture-in-picture keeps playing, and the GPU renderer survives rotation and sleep (A1, A3, A4, A6, A7, D1, and A10 for function). A2 could run on an emulator and did not, and A8 has only a 30-second reading against its one-minute step. Those are emulator results, not device results; the device columns are empty.
Verification
| Check | Result |
|---|---|
cargo test --workspace --features test-roms |
2,887 passed, 0 failed, 20 ignored |
| AccuracyCoin / nestest | 144/144 / 0-diff |
| fmt, clippy (every feature set, wasm), rustdoc, no_std build | clean |
| Android: NDK cross-build, Kotlin unit tests | pass (CI) |
Install
- Download the pre-built binaries for Linux, macOS, and Windows below.
- The MiSTer core bitstreams are attached below:
RustyNES_MiSTer-v2.9.3.rbf(on-die, also asRustyNES_20260928.rbf) andRustyNES_MiSTer-v2.9.3-offdie.rbf. The core is unchanged since v2.9.2 and these are v2.9.2's files, byte for byte (on-die md5c17b0f37d5c49b150c023734b583f468, off-die75ae3a99e840e29675ded9898dae127d), so the MiSTer menu shows their build date, 260928. A fresh compile at seed 2 on 2026-09-29 missed on-die timing by 0.121 ns, because the build date is a design constant, so the timing-clean v2.9.2 pair ships instead. Neither has run on hardware. - The WebAssembly build is live at doublegate.github.io/RustyNES.
- The RetroArch core is in RetroArch's Online Updater on the platforms the libretro buildbot publishes to.
- Licensed under GPL-3.0-or-later.