Skip to content

RustyNES v2.8.2 — "Solder" (the MiSTer core's on-die RTL, corrected against the oracle and the wiki)

Choose a tag to compare

@github-actions github-actions released this 25 Sep 19:41
432c7e7

RustyNES v2.8.2 — "Solder"

The third release of the v2.8.x line that ADR 0041 put before the SuperStation One core (v3.0.0), and its first RTL release. It works through the MiSTer core's correctness findings from the RTL audit: every row targeted here now has a verdict and evidence in its ledger. Each fix failed a new co-simulation gate before it was made, and each gate catches the fix reverted. The RTL was written from the nesdev wiki and from this emulator's traces; no reference HDL was opened.

For emulator users: one change. MMC1 games no longer lose a mapper reset written on the cycle after another write, the case Shinsenden relies on. Everything else here is the MiSTer core, whose bitstream is attached below. No hardware has run any bitstream; that is v2.9.2's subject.

The emulator was the one that was wrong

The audit said the MiSTer core honoured an MMC1 reset before filtering consecutive writes, where this emulator filtered first, and called the core wrong. The nesdev wiki says the reverse. The MMC1 serial port ignores a data write on the cycle after another write, which is how read-modify-write instructions behave on it, but a write with bit 7 set resets it wherever it lands. The core was right. The emulator ignored the reset too, and that is fixed, with a failing test first. A new core gate runs INC on the serial port at both values that matter and keeps the two implementations agreeing. The ledger gains a verdict for this case, INVERTED: the finding was real, and it was about the other implementation.

Four fixes to the MiSTer core

  • An MMC3 IRQ acknowledge is not lost to a counter clock on the same edge. When a $E000 write and the A12 rise that took the counter to zero landed on one master clock, the counter won: an IRQ stayed pending with IRQs disabled until the next $E000. The console orders the two events, and either order ends with no IRQ pending. A module gate places both on one clock, since on MMC3 IRQ timing this emulator is deliberately not the reference.
  • SNROM's PRG-RAM follows its CHR-bank enable. SNROM boards, among them The Legend of Zelda's, wire CHR A16 to a second PRG-RAM enable. The core ignored it, so RAM a game had disabled stayed readable and writable. The gate diverged on 3,924 of 178,678 cycles before the fix.
  • The triangle and noise drop a length reload that lands on a length clock, as the pulse channels already did. The wiki states the rule for no channel, so the question was put to this emulator's output: a reload placed on the clock cycle is dropped there for every channel.
  • A $2002 read on the vertical-blank dot leaves the value it returned on the data bus. The latch was rebuilt from the registers, which had not set yet, so a following read of a write-only register returned $00 where the console returns $80. Finding that dot needed a loop whose length is coprime with the frame; the first draft could reach only 3 of every 33 dots.

Pulse 1's sweep with negate and shift 0 ($4001 = $08) is now under a gate as well. The core was already right there; the gate guards this emulator's v2.7.0 fix, and the old bug, reintroduced, diverges on every cycle the note should sound.

Not defects as written, and two left open

Two rows closed without a change, and two stay open. The MMC3 "Sharp reload-to-zero" behaviour the audit flagged is the one the core already implements. The $2002 suppression case it describes needs two reads one dot apart, and CPU accesses are three dots apart. Two CPU findings (a spurious last_cycle on branches, a second NMI edge during NMI entry) match the code but show no effect under any gate, so they are left for the v2.9.0 re-audit rather than changed blind.

Verification

Check Result
AccuracyCoin (RAM decoder) 144 / 144
nestest pass
--features test-roms workspace suite 155 suites, 2,756 passed, 0 failed
Co-simulation ladder (tb/regress.sh) 162 passed, 0 failed, 1 expected failure (chrram-fetch, unchanged at 18 fetches); v2.6.23's 155 plus the seven new gates
New gates, each red before its fix cart-mmc3-gate, mapper1snrom067, aputrireload070, apunoisereload071, ppulatch072; mapper1rmw068 and apusweepneg069 guard behaviour the core already had
Mutations each fix reverted fails its own gate; the triangle and noise mutants each fail only their own channel's
Quartus 17.0.2, fitter seed 4 0 errors; worst setup +0.429 ns, worst hold +0.097 ns (was +0.421 / +0.112); 22,997 ALMs, 468 RAM blocks, 33 DSP

Next: v2.8.3, RTL robustness: reset synchronisers and the audit's logic-cost claims, measured in Quartus rather than quoted.

Install

  • Download the pre-built binaries for Linux, macOS, and Windows below.
  • The MiSTer core bitstream, RustyNES_MiSTer-v2.8.2.rbf, is attached below. It has not 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.
  • The audit ledger is docs/audits/rtl-disposition.md, and the plan to-dos/plans/v2.8.2-solder-plan.md.
  • Licensed under GPL-3.0-or-later.