Skip to content

RustyNES v3.6.1 — "Threshold" (every dependency at its newest, the MMC5's fetch window ended where rendering ends, and MiSTer save states tied to their region)

Choose a tag to compare

@github-actions github-actions released this 05 Nov 10:18
bdd8f59

RustyNES v3.6.1 — "Threshold"

A patch release on v3.6.0 "Octave": every dependency at its newest compatible release in one change, the MMC5's background-fetch window ended where rendering ends in both cores, the MMC5 split's first two tiles of a frame taken from the right row, and a TAStudio project bounded by its decoded size; on the MiSTer core, save states tied to the region they were made in and the MMC5 on PAL and Dendy. Movies and netplay peers from v3.6.0 are refused (emulation epoch 9; v3.6.0 recorded 8), with a message naming both epochs; save states from v3.6.0 load, and netplay protocol 9 and .rnm format 7 are unchanged.

Fixed

  • The MMC5's fetch window ends where rendering ends. Since v3.5.1 the MMC5 applies its split-screen bank and its extended-attribute banks only to background fetches made while the PPU renders. The window in which a fetch counted as one opened at the start of each rendered line but closed only at vertical blank, so a $2007 read of CHR on post-render line 240, with rendering still on and nothing being fetched, still took the split's bank. And a $2002 read on the exact dot vertical blank begins, which suppresses that frame's VBL flag and NMI, also stopped the PPU telling the mapper vertical blank had begun, so the window stayed open through that whole blank. The window now also closes on the MMC5's own end-of-frame rule, three CPU cycles without a PPU read (a few dots into line 240), while rendering is on, and the PPU reports vertical blank on every frame. Turning rendering off mid-frame leaves the window as it was, so a game that blanks part of a frame draws exactly as before, and turning it back on restarts the count. Both cores.
  • The MMC5 split's first two tiles of each frame. Line 0's first two tiles are fetched on the pre-render line, and the PPU handed the split that line's scanline number, 261 (311 on PAL and Dendy), where it meant to hand it 0: its check for the pre-render line looked for a negative scanline, which the PPU never holds. So those two tiles came from the wrong row of the split. They now take the row $5201 scrolls to, as the NESdev page's split counter, which "resets to the vertical scrolling value in $5201 each v-blank", requires. The MiSTer core's new MMC5 stimulus found it: the FPGA core was already right on NTSC.
  • A TAStudio project is bounded by what it decodes to. A .rnmproj has been limited to 1 GiB on disk since v3.5.1, but a frame record of 5 bytes on disk becomes a full input record in memory, and every branch carries its own copy of the input log, so a valid file near the limit could ask for several GiB. Before anything is decoded, the whole file is now checked against a 1 GiB budget: every input log, every marker, and each branch with its saved state; a project over it is refused with a message and the editor is left as it was. Saving makes the same check, so a project the app could not reopen is never written over a good one. A million-frame project, more than four and a half hours at 60 fps, opens.

Dependencies

  • Every ecosystem moved to its newest compatible release in one change, replacing seven open Dependabot pull requests. Rust: rmcp 3.8 (the MCP server), rusqlite 0.41 (bundled SQLite for Lua's key-value store), toml 1.2, wasm-bindgen 0.2.130 (the WebAssembly build's tool pin moved with it), and the mobile bridge's random seed source on getrandom 0.4, plus a lockfile refresh (tokio 1.54, zip 9.3, ureq 3.5 and others). Android: the Android Gradle Plugin 9.5.0, Gradle 9.8.2 and the Cast framework 23.0.0. The hosted services: wrangler 4.157 and vitest 5.1. CI: two GitHub Actions. The linters behind the pre-commit hooks (markdownlint-cli 0.50.0, ruff 0.17.3) were each run over the whole repository before their pins moved, and passed with nothing changed.
  • Found on the way: one lint failure in the Lua SQLite tests, which no CI job lints with that feature on; fixed.

What the MMC5 fixes change in recorded play

  • No golden, snapshot or epoch fingerprint moved: the full suite with the test ROMs and the local commercial corpus passes 4,048 tests (v3.6.0: 4,030), the 1,116-dump boot coverage and Uchuu Keibitai SDF, the one commercial game that uses the split, included.
  • The emulation epoch rises to 9 anyway, as v3.5.1's did for the same shape of change. Software that splits the first two columns with $5201 set now draws a different frame, and software that reads CHR through $2007 on line 240 now reads a different value, so a v3.6.0 movie of it would replay differently with no warning. With the epoch raised it is refused with the reason instead. Re-record v3.6.0 movies under v3.6.1, and keep both netplay peers on the same release.

The MiSTer core

  • A save state is tied to its region. The state's header now records the region the console ran as, and a state saved under PAL is refused under NTSC or Dendy, with the reason, instead of restoring a PAL console's frame counters and sound periods into an NTSC one. A region change while a save or a rewind capture is in progress abandons it, as loading a new game does, instead of storing a state the region's reset was clearing; a manual save abandoned that way empties its slot, and the rewind history is emptied. Save states from v3.6.0 are refused on this core: the header's format changed, and with it the layout fingerprint.
  • The MMC5 on PAL and Dendy. The core's MMC5 took NTSC's pre-render line, 261, on every region. On PAL and Dendy the pre-render line is 311, so a split frame's first two tiles came from the wrong row, and a $2007 read late in PAL's vertical blank took the split's bank. It now takes the region's line.
  • The fetch window's end, as in the emulator, in the RTL; with three new MMC5 stimuli, on NTSC, PAL and Dendy, whose goldens come from this release.
  • The fitter's settings were measured, not assumed: the comparison of Quartus optimisation modes behind the bitstreams' settings, and the seed it chose, are in the MiSTer core's v3.6.1 notes.
  • New bitstreams, compiled from the MiSTer core's release tree, which pins this release's merge commit, and attached here and to the MiSTer core's v3.6.1 release (added 2026-11-05, after this release was published): RustyNES_MiSTer-v3.6.1.rbf (off-die, md5 261c7636e10130faedf39a8df1fefff0) and RustyNES_MiSTer-v3.6.1-lite.rbf (on-die, md5 7f82507c934fd8570e5b784f8c73cfbe). Their seed, timing margins and ladders (on-die 353 passed, off-die 410 passed, none failed) are in its notes, which also list three save-engine races on a region change, reported in review and not yet confirmed, to be checked in v3.6.2.
  • Release candidate, not hardware-verified. No hardware has run any bitstream.

Verification

  • Each fix pinned by a test that fails first and is made to fail again by a mutation that undoes it: the decode budget (five tests, five mutants), the MMC5 window (three tests, three mutants), the PPU's vertical-blank notice and its pre-render split row (one test and one mutant each). On the MiSTer core: 12 of 12 MMC5 mutants caught, the region refusal caught in the save engine's test and through the full HPS harness, and the region-change abandonment caught there too.
  • The full suite with test ROMs and the local commercial corpus: 4,048 passed, 0 failed, 20 ignored. clippy clean for the workspace, every frontend feature set and all three WebAssembly builds; cargo deny, the no_std core build, the WebAssembly bundle build and the hosted services' type check and 132 tests pass.
  • The release audits (anchors, state prose, notes rendering, the libretro .info, the contribution checklist) pass on the bumped tree.

Install

  • Download the pre-built binaries for Linux, macOS, and Windows below.
  • The MiSTer bitstreams, a release candidate and not hardware-verified, are attached below: copy RustyNES_MiSTer-v3.6.1.rbf (off-die, needs the SDRAM add-on) or RustyNES_MiSTer-v3.6.1-lite.rbf (on-die) to the MiSTer's _Console folder.
  • 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.