Repository navigation
RustyNES v3.4.0 — "Waypoint" (authenticated netplay and three- and four-player desktop sessions, RetroAchievements hardcore to its full block list, the hosted services as code, about half the window-thread CPU, egui 0.37, and a MiSTer core whose headline build is off-die, with save states and rewind and Phase F3a's boards)
LatestRustyNES v3.4.0 — "Waypoint"
MiSTer users: the core named "RustyNES" now needs an SDRAM add-on. From this release the updater installs the off-die build as RustyNES; it keeps cartridge memory in the SDRAM module. A board without one should choose RustyNES_Lite from the core menu, the on-die build. Both share the games/RustyNES folder and its saves. No hardware has run either bitstream, and the SDRAM pin constraints are still provisional.
Netplay is authenticated in v3.4.0: every native datagram carries a per-session HMAC tag, which protects a session against off-path forgery and replay (not against someone who observes the handshake), and the rollback window is bounded and no longer re-simulates frames whose predictions held. The desktop plays three and four players over that mesh. RetroAchievements hardcore follows RetroAchievements' full block list on desktop and mobile, native achievements can unlock again, and offline unlocks are sent when the connection returns. The hosted lobby, TURN credentials and browser achievements proxy are in the repository as a Cloudflare Worker, not yet deployed (v3.4.1). The desktop and browser use about half the CPU on their window thread, and the frontend moves to egui 0.37. On the MiSTer core the off-die build becomes the headline, with save states and rewind held to the oracle cycle for cycle, a slot-scheduled SDRAM arbiter, and MMC2/MMC4, the FME-7 with 5B audio, VRC2/VRC4 and the Zapper. Netplay with v3.2.x and v3.3.x peers is refused.
Breaking changes
- Netplay protocol 9 (
"RNE9"): every native netplay datagram carries an HMAC-SHA-256 tag under a per-session key, so a datagram from anyone without the key is refused before it is parsed, and a replay is refused. This is protection against off-path forgery. It does not encrypt the session, and someone who observes the handshake can derive the key. A v3.2.x or v3.3.x peer is refused with a reason (the older peer times out). The signalling room protocol is unchanged. rustynes-netplayAPI:NetMessage::Syncgainskey_share,Rosterentries carry a key share, andInputAcknames its player; newauthmodule,UdpTransport::enable_auth,UdpMeshTransport::new_sealed,RollbackSession::retained,impl Transport for Box<T>.- Movies and save states are not affected:
EMULATION_EPOCHstays 5, since nothing in this release changes a frame, a sample or a bus cycle.
Added
- Hosted netplay and achievements services, as code (
services/hosted/): a Cloudflare Worker with a room lobby, short-lived TURN credentials and the browser RetroAchievements proxy. Not deployed in this release; v3.4.1 deploys it. Until then no build points at a hosted service, and netplay works as before over direct IP, LAN and your own signalling server. - A privacy policy at https://doublegate.github.io/RustyNES/privacy/, linked from the README and the apps.
- Three and four players on desktop netplay over the authenticated mesh with the Four Score. Direct IP and LAN only; mobile follows later.
Changed
- RetroAchievements hardcore follows RetroAchievements' full block list on desktop and mobile: frame advance and slowdown, Game Genie and raw cheats, Lua scripts, movie playback and import, TAStudio, the debugger and memory windows, restoring achievement progress, and state loading are refused in hardcore; fast forward and movie recording are allowed. Switching hardcore on power-cycles a running game. The User-Agent follows RetroAchievements' documented shape.
- The netplay rollback window: a confirmation whose predictions held replays nothing (it re-simulated several frames per tick before), a misprediction replays from the mispredicted frame, every per-frame store is bounded, and the state digest is about 15 times faster. One tick of a latent two-peer session: 20.78 ms to 8.1-8.9 ms.
- egui 0.37, from crates.io again (the vendored egui-winit is removed); the emoji font set is kept. egui 0.37 costs about 10% more instructions per shell frame than 0.36, about 3 µs; the text-tessellation part is reported upstream as emilk/egui#8731.
Fixed
- About half the CPU on the frontend's window thread (-46% and -48%, measured live in two before/after pairs; frame rate and pacing unchanged). The check that skips re-uploading an unchanged frame hashed it byte by byte (about 206 µs a redraw, to save a 6 µs copy) and now compares it exactly; the performance statistics were sorted every frame with nothing reading them, and are now built only when shown or logged.
- Restoring a save state no longer recomputes the audio resampler's filter table, which run-ahead did every frame: about 1.5% fewer instructions on the emulation thread with run-ahead 1. Audio and states are byte-identical.
- Native RetroAchievements achievements can unlock (rcheevos checked memory from the network callback, where no reader was installed, and disabled every achievement), and an unlock earned offline is sent when the connection returns.
- The Datach cartridge's own EEPROM is kept in the battery save (mapper 157).
- Netplay: a three- or four-player peer that missed early inputs no longer stalls; a joiner refuses a malformed roster or one not from its host; a second value for a confirmed input is ignored.
- The performance log's egui column now records the egui pass in the normal view;
rustynes-apu'sstdfeature builds on its own (found by a new weekly sweep of every feature pair); the iOS About screen states the right licence.
The MiSTer core
- Release candidate, not hardware-verified. No hardware has run either bitstream.
- Two builds: the off-die headline (needs the SDRAM add-on) and the on-die lite build. MiSTer's updater installs them from the RustyNES_MiSTer repository's
releases/asRustyNES_<date>.rbfandRustyNES_Lite_<date>.rbf; the files attached below carry the same bytes under version names. - Save states and rewind: four slots in the framework's DDR3 region (OSD, Alt+F1-F4 to save, F1-F4 to load) and rewind on Backspace. Every console register is in a scan chain and every memory is reached through a port it already had; a save, a console reset, a pattern over every register and memory byte, and a load are held to the oracle's golden cycle for cycle on 11 stimuli on-die and 10 off-die. A save stalls the picture for about 6 ms on-die and 24 ms off-die. Each state carries a fingerprint of the scan chain's layout: a state survives a rebuild that keeps the layout and is refused by one that changes it. The rewind ring is emptied by a manual load and by loading another game. The HPS side (slot files loaded and written back) is modelled from the framework's documentation only.
- The off-die SDRAM arbiter schedules around the console: worst CHR fetch 18 cycles against a 22-cycle budget (was 25), PRG 20 against 24, and no scheduled request meets a refresh, across the whole off-die ladder.
- New boards: MMC2 and MMC4 (9, 10), the Sunsoft FME-7 with the 5B's audio (69), Konami VRC2 and VRC4 (21, 22, 23, 25, every wiring, with the VRC4 IRQ). The Zapper on port 2, aimed with the mouse.
- One remaining off-die difference, attributed: Ganbare Goemon Gaiden changes a CHR bank in the middle of a background fetch, two master clocks before the PPU latches the byte; block RAM answers in time and SDRAM cannot, so eight pixels at the right edge of line 131 show the old bank. What the board does is not established.
- A battery save is read once per mount (it was read twice), found by a new gate that simulates the top level through the framework's real
hps_io. - Two known save-state defects, deferred to v3.4.1. A ROM download that starts during the few milliseconds of a manual save can leave that slot holding a mix of old and new data that a later load accepts, and one that starts during a load or rewind can let a single byte reach PRG-RAM after the engine has released it (the new game's save then overwrites it). Each needs a ROM download to begin while the save, load or rewind is still in flight: a save takes 5.9 ms on-die and 24.1 ms off-die, a load about 9.0 and 27.2 ms.
- The off-die build's PRG crossing is constrained as designed. The SDRAM bridge's PRG byte reaches the CPU, which loads it only on its per-cycle enable, and the PRG budget reserves the last four SDRAM cycles before that edge for the crossing. The timing constraint now says so for that path alone. Without it the off-die build closed at no seed. If a PRG read ever arrived later than its budget on hardware, the CPU would read a stale byte, which the budget gate rules out on every simulated off-die run.
- The oracle pin moves to v3.4.0 (this release's merge): all 969 golden artifacts from 216 stems reproduce byte for byte (v3.3.0: 848 from 189).
- The co-simulation ladder: 307 passed, 0 failed, 4 expected failures on-die and 311 / 0 / 5 off-die (v3.3.0: 260 / 0 / 3 and 263 / 0 / 3), each one run of a frozen tree, nothing skipped. The new expected failures are the Sunsoft 5B's timed test on both builds (the oracle's 5B departs from its NESdev page, so that gate grades the page) and Goemon Gaiden off-die.
- New bitstreams at fitter seed 2, chosen from eight seeds swept on build date 261018: the on-die build's binding margin chooses, and the off-die build must close. Each was compiled twice to the same bytes:
- off-die
RustyNES_MiSTer-v3.4.0.rbf(the headline), md58f2f1961286fcd202a1d42a4d15bfd22(+0.429 ns setup, +0.078 ns hold). The SDRAM read capture has +0.429 ns setup and +1.189 ns hold over all four timing corners, assuming zero board delay; - on-die
RustyNES_MiSTer-v3.4.0-lite.rbf, md5ad415b8ffdd376b34e8adba879558a13(+0.105 / +0.117 ns). It uses 88% of the device's logic (63% at v3.3.0), which is why v3.5.0 decides first whether the largest boards ship off-die only.
- off-die
- The release script refused these bitstreams and they ship as built, by the maintainer's decision.
release-rbf.shstopped on two cosmetic findings in the RTL the bitstreams were built from: a Quartus width warning atrtl/cart/cart.svline 1376 (the MMC2/MMC4 CHR slots, an index sized from its loop bound, which writes exactly the slots intended), and the Zapper aim's ninth bit, constant because the aim is clamped below 256. Both are fixed in v3.4.1. Its other checks passed when run by hand: the Fitter footer, timing at all four corners, the SDRAM capture, the compile dates and the file names. Both bitstreams were downloaded back from both releases and md5-verified.
Verification
cargo test --workspace --features test-roms --no-fail-fast: 3,547 passed, 0 failed, 13 ignored across 172 test binaries; withcommercial-romsadded, 3,798 passed, 0 failed, 18 ignored across 182, the commercial suites at 60/0, 169/0 and 6/0 (atab6c3b79). AccuracyCoin 146/146, nestest 0-diff, and the epoch fingerprint holds at epoch 5 (epoch_fingerprint, 2/0): nothing in this release moves a frame, a sample or a bus cycle.- Frame time against v3.3.0: the egui merge's one alarming reading (+66%) was host contention. Four
ab_check.shruns (another session held one core busy, so the quiet threshold was raised; two aligned-layout runs read, which agree within 0.25 points) find mixed, small differences:flowing_palette_fastabout 1.7% slower,nestest_fastabout 0.6% faster, the non-fast variants within 1.3%. Recorded indocs/performance.md, not attributed. The CI frame-time gate passed.
Install
- Download the pre-built binaries for Linux, macOS, and Windows below.
- The MiSTer bitstreams are attached below as a release candidate, not hardware-verified:
RustyNES_MiSTer-v3.4.0.rbf(off-die, needs SDRAM) andRustyNES_MiSTer-v3.4.0-lite.rbf(on-die). - 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.