Skip to content

Latest commit

 

History

History
352 lines (302 loc) · 31.8 KB

File metadata and controls

352 lines (302 loc) · 31.8 KB

RustyN64 — STATUS (single source of truth)

This file is authoritative for per-suite pass counts, the board matrix, the chip→crate map, and version policy. Everything else defers to it.

Current release: v0.8.0 "Breadth" — this commit is the v0.8.0 release; the v0.8.0 tag is cut from it on merge to main. (v0.4.1 was a documentation-only patch over v0.4.0 "Rasterizer".)

Phases 1, 2, 3, 4, 5, 6, and 7 are complete. Phase 7 is the accuracy battery and the commercial-corpus breadth pass: the battery reports a real 56 probes / 100% across two Angrylion oracle suites, every residual R-1…R-23 carries a ledger disposition, and 29 of 66 staged commercial titles render through their own graphics microcode — Super Mario 64 draws its title screen. The two defects that unblocked that were in the CPU and the PIF, not the RDP. Phase 6 is frontend integration — the egui shell now presents the real machine (VI scan-out, AI audio drain, SI controller input), plus save-states / rewind / run-ahead (all frontend-side and off by default, ADR 0004) and a wasm browser entry point. It is the first playable release, with playability honestly scoped:

Criterion Result Reproduce
Native picture + sound + control on a real ROM (Phase 6 committable gate) met — committed homebrew ROMs show a real rendered frame through the LLE VI scan-out (real_rom_frame, a verified golden), play deterministic PCM through the AI (audio_play_rom), and read the pad through the SI joybus; the frontend presents Bus::scanout / the AI drain / SI input, not a test pattern cargo test -p rustyn64-test-harness --test real_rom_frame --test audio_play_rom
Save-state restore continues bit-identically (Phase 6 capstone) met — the whole System is serde-serializable; a two-run trace compare (tests/savestate.rs) proves snapshot→continue→restore→continue is bit-identical on a homebrew ROM (committable) and on a booted commercial ROM (full RSP/RDP/AI/cart machine, local-only). Rewind and run-ahead are byte-identical to a plain run_frame when off cargo test -p rustyn64-test-harness --test savestate
A commercial ROM is playable natively with a picture (VERSION-PLAN's literal cut criterion) partially met (ledger R-18)superseded 2026-07-29: this row previously said a commercial title "scans out no frame", which is now false. Paper Mario renders a real frame through the full LLE path (retail HLE boot -> game code -> its microcode on the LLE RSP -> DPC seam -> LLE RDP -> Bus::scanout_scaled), committed at screenshots/paper-mario-first-commercial-frame.png — 87 distinct RGBA5551 values, fully lit on both scan-out paths, held frames 120-270. So the pipeline is not the gap. MET 2026-07-29 — Super Mario 64 renders its TITLE SCREEN (screenshots/super-mario-64-title.png): Mario's head, textured cap, over the tiled SUPER MARIO 64 background, 125,278 RDP commands, 138,474/148,125 px lit at 625x237, viewed and confirmed. The two defects that blocked it were not in the RDP: an interrupt across an ERET was charged to the ERET, livelocking the CPU (#189), and the PIF answered as a connected controller on all four joybus channels, so osContInit saw four pads on a one-pad console and SM64 halted in its own assert (#190). Banjo-Kazooie also renders real 3D geometry (0 -> 133,625 commands) with an open color-cast issue. Corpus census, 2026-07-29: 29 of 66 staged titles render (>1000 RDP commands AND >1000 lit pixels) — Mario Kart 64, Star Fox 64, Wave Race 64, GoldenEye 007, Blast Corps, Bomberman 64/Hero, Perfect Dark, Kirby 64, Ocarina of Time, Super Smash Bros., Resident Evil 2, Turok, Mario Golf and others. 24 issue zero RDP commands (Banjo-Tooie, Conker, Donkey Kong 64, Diddy Kong Racing, F-Zero X, Jet Force Gemini, the Star Wars titles, 1080 ...) and the remainder issue commands but scan out nothing. What remains is per-title coverage, not the pipeline. The census requires both terms deliberately: Rayman 2 and Namco Museum 64 report zero RDP commands with 123,540 and 137,681 lit pixels — uninitialized RDRAM scanned out, which a lit-pixel-only metric would have counted as successes (ledger R-18). The earlier text on this row (Paper Mario as the first frame; SM64 emitting none) is retained above as the historical record. v0.7.0 shipped on the demonstrated-playable path, not a faked commercial pass cargo test -p rustyn64-test-harness --release --test commercial_boot -- --ignored (local)

The earlier phases' exit criteria all remain met (unchanged oracle results). Phase 5, for reference, delivers the cartridge + I/O boundary — the PI bus (BSD domain timing), the SI joybus, all four save backends, the CIC handshake, and two boot paths: the copyright-clean HLE boot (default, CI-able) and a faithful real-PIF boot that runs the console's real IPL1/IPL2 from the PIF ROM and verifies the IPL2 checksum against the CIC (off by default, local-only, never CI-gated — the PIF ROM is copyrighted). Its committable gate is met; its commercial-boot capstone is characterized honestly:

Criterion Result Reproduce
n64-systemtest cart/PIF/SI improvement (Phase 5 committable gate) met — the cart/PIF/SI/boot subsystem drops the suite-wide failing count 93 → 90; Phase-1 categories stay Failed: 0 cargo test -p rustyn64-test-harness --release --test systemtest -- --ignored
Save round-trips + a commercial cartridge boots and executes (Phase 5 capstone) met (as restated) — each of the four save backends round-trips byte-for-byte, and every save-type representative (6102/6103/6105 CICs) boots through the real IPL1→IPL2→IPL3 chain to game execution in RDRAM, IPL2's checksum matching the documented CIC value. Reaching a rendered title frame remains open per-title (ledger R-18) and is outside the cart boundary (ADR 0003) — though a commercial cartridge does now render (Paper Mario, 2026-07-29), so the gap is coverage, not the pipeline cargo test -p rustyn64-test-harness --release --test commercial_boot -- --ignored (local; gitignored corpus)

The eight earlier exit criteria (two per phase, 1–4) are unchanged oracle results. Phase 4's two:

Criterion Result Reproduce
The real RSP audio microcode produces PCM (Phase 4) met — the vendored libdragon mixer (rsp_mixer.S) runs its resampling/volume/mixing DSP on the LLE RSP and DMAs back a golden-pinned, deterministic mixed 16-bit stereo PCM buffer; no RSP gaps cargo test -p rustyn64-test-harness --test mixer_microcode
A real ROM plays audio through the AI (Phase 4) met — a committed bare-metal ROM CPU-feeds a PCM buffer and programs the AI, which DMAs it out; the emitted stream matches byte-for-byte, deterministically, with the AI interrupt firing cargo test -p rustyn64-test-harness --test audio_play_rom

The earlier phases' criteria (unchanged):

Criterion Result Reproduce
Conformance suite bit-matches Angrylion (Phase 3 criterion 1) met — 164 committed .rvec vectors (FILL / scissor / shaded / textured triangles, combiner, blender, dither, alpha-compare, coverage, copy texrects) replay byte-exact vs the Angrylion oracle; the seeded fuzzer found and fixed R-3 and R-15, and R-13 (textured triangles) is resolved cargo test -p rustyn64-test-harness --test rdp_conformance
A real ROM renders a stable golden frame (Phase 3 criterion 2, T-33-006) met — a committed license-clean homebrew ROM boots on the VR4300, CPU-fills a framebuffer, and the VI scans it out to a verified 32×24 golden frame, bit-identical across two boots cargo test -p rustyn64-test-harness --test real_rom_frame
n64-systemtest Failed: 0 (CPU/COP0/TLB/COP1) met — 0 of 917 tests fail in those categories; 90 fail suite-wide (down from 93 after the Phase 5 cart/PIF/SI work), the remainder MI/RDP (Phase 3+) and cart corners cargo test -p rustyn64-test-harness --release --test systemtest -- --ignored
n64-systemtest Failed: 0 (RSP category, Phase 2) met — across 917 tests started, 0 RSP-prefixed failures (the suite-wide total, of which the RSP category was the bulk, fell from 413 to 90); the full VU ISA, load/store, reserved opcodes, BREAK semantics, and the DPC registers landed in #41–#44 cargo test -p rustyn64-test-harness --release --test systemtest -- --ignorednow asserted by rsp_categories_report_no_failures, which reports 0 failing across 224 RSP tests started. Until that test existed this row rested on a manual per-test dump, and the Phase 1 assertion excludes RSP / SP (with its trailing space) by design, so nothing would have caught a regression
Real graphics microcode emits an RDP command list (Phase 2 criterion 2) met — libdragon's combined RSPQ+rdpq blob boots, dispatches an rdpq overlay command to its resident handler, and emits an RDP command (bytes DMA'd to RDRAM + DP_END advanced through the DPC seam) cargo test -p rustyn64-test-harness --test microcode
CPU golden-log 0-diff met — retired-instruction stream identical to ares from the ELF entry cargo test -p rustyn64-test-harness --release --test golden_log -- --ignored

The VR4300 is complete: the canonical 187.5 MHz clock (ADR 0006), the five-stage pipeline (ADR 0007), MIPS III including the 64-bit forms, COP0, the TLB and micro-ITLB, the exception model, interrupts, the primary I- and D-caches, the privilege-aware segment map, Status.RE, and COP1 on a soft-float core.

Phase 2 (v0.3.0) — both exit criteria met; released as v0.3.0. The RSP-category Failed: 0 criterion is met (above). The second — a real graphics microcode boots and emits a plausible RDP command list — is met too: libdragon's real combined RSPQ+rdpq microcode (vendored, third_party/ libdragon-rsp/) boots to its idle break (T-24-002), processes a DMA'd command queue (T-24-003 foundation), and an rdpq overlay command (RDPQCmd_Passthrough8) is dispatched to its resident handler and emits an RDP command — the 8 command bytes are DMA'd to an RDRAM output buffer and DP_END is advanced through the DPC seam (T-24-003). Witnessed non-vacuously by tests/microcode.rs::the_microcode_emits_an_rdp_command_through_the_dpc_seam. The LLE RDP rasterizer (Phase 3) and cart/PIF (Phase 5) are both complete, and the frontend shell (Phase 6, v0.7.0) is now complete too; the next phase is the accuracy battery (Phase 7, v0.8.0). See to-dos/ROADMAP.md.

Read this before trusting any green checkmark: CI passing means the workspace compiles and its 803 tests pass. The CPU, the RSP, the LLE RDP, the AI, and the cartridge boot now genuinely execute — that is real, and proven by the oracle criteria above, not a self-assessment. The honest remaining gap is that a commercial ROM boots and executes but does not yet reach a rendered frame for most titles (ledger R-18 — though Paper Mario does render, so the gap is per-title coverage rather than the pipeline); the playable path is demonstrated on homebrew. Where a subsystem is still a stub it is a TODO(T-XXX-NN) comment inside a no-op body that compiles and returns, not a todo!() panic, so nothing fails loudly — a green cargo test still is not proof a subsystem works. And of the test-ROM corpora below, only a few ROMs are executed by a wired gate; the rest are staged only. Availability of an oracle is not the same as a wired gate.

What works today

  • The Cargo workspace: all rustyn64-* crates build; cargo test --workspace passes 803 tests.
  • rustyn64-core: the Bus (owns RDRAM + every chip + the RCP/MI register state), and the canonical 187.5 MHz scheduler (ADR 0006) — master_ticks is the only incremented counter; CPU (÷2), RCP (÷3) and COP0 Count (÷4) positions are derived accessors. Seeded per-domain power-on phase offsets; reset re-derives the same phase. Pinned by a residue-invariant test in the default test path.
  • rustyn64-cpu: a five-stage pipeline (IC/RF/EX/DC/WB) of four inter-stage latches advanced one PClock per step in reverse stage order, with the operand bypass network, the imprecise load-delay interlock, delay slots and branch-likely nullification, the MIPS III integer set (including the 64-bit D* forms and the unaligned LWL/LWR/LDL/LDR family), the documented errata reproduced-not-corrected, and a SysAD transaction model that cannot complete inside its address phase.
  • rustyn64-rsp and rustyn64-rdp execute: the RSP runs real microcode (scalar + full vector unit) and the RDP rasterizes the command list through the texture / combiner / blender / coverage pipeline with VI scan-out. rustyn64-audio (AI) implements the interface (Phase 4, Sprint 1) — the register block at 0x0450_0000, the two-deep DMA FIFO, the derived DAC rate, the interrupt-on-start, and the delayed-carry bug. The real libdragon mixer microcode (rsp_mixer.S) now runs on the LLE RSP and produces a verified mixed PCM buffer (Phase 4, Sprint 2), and a real bare-metal ROM plays PCM through the AI end to end. The frontend audio drain + resampler landed in Phase 6 (v0.7.0), so the AI path is complete.
  • rustyn64-cart: real ROM-format detection + byte-order normalization (.z64/.n64/.v64), header parse, the SaveType/Cic/RomFormat enums, and (Phase 5, v0.6.0) the PI/SI DMA, the CIC handshake, all four save backends including the FlashRAM command machine, and both HLE and real-PIF boot.
  • rustyn64-test-harness: the golden-log differ, run_until_complete, the accuracy scorer, and the frame comparator — all present. The golden source exists (the CPU golden-log 0-diff vs ares is met); the probe battery is now real (AccuracyScorer::default_battery) — it scores every committed Angrylion conformance vector, RDP and VI, so its expected values are the oracle's, never our own output.
  • The chip stack is #![no_std] + alloc and cross-compiles to thumbv7em-none-eabihf; only the frontend carries std + unsafe.

Host performance

Full evidence, method, and every ruled-out approach: docs/performance.md, which is authoritative here. This is the summary.

build frame FPS what it is
default (cycle-accurate) ~103 ms ~10 the five-stage pipeline, lockstep scheduler
fast-exec + fast-scheduler ~65 ms ~15.3 instruction-granular CPU + bailout scheduler, both by ADR (0011/0013)

Super Mario 64 on examples/frame_bench.rs; the environment is tabled in docs/performance.md §Measured (2026-07-30). 60 FPS is 16.67 ms — a 3.92x gap.

The optimization program closed on 2026-08-01. Shipped: the dirty-region RDRAM upload to the GPU backend (2.54%), a read_u32 RDRAM fast path (1.32%), 2x/4x/8x GPU internal render (quality only, no FPS cost), and the work counters that sized the rest. Do not add those percentages together — they were measured on different configurations.

Both large remaining levers are declined on their own arithmetic, each with a merged ADR recording the decision rather than leaving it to be re-argued:

lever ceiling decision
RSP vector-unit SIMD 1.056x ADR 0016 — recommended against; the crate stays #![forbid(unsafe_code)]
CPU recompiler 1.26–1.40x ADR 0017 — fails its own 1.5x stage-2 gate

So 60 FPS is not reachable from this execution model, and that is a measured statement, not a deferral: no single bucket is 84% of a frame, a perfect fast-scheduler caps at 2.15x, and the two levers above are what remain. Both gates are judgments the maintainer can move; the arithmetic under them is in docs/performance.md §Where the optimization program ended.

Project infrastructure

Distinct from emulation progress: the scaffolding around the code, and where it actually stands.

Area State
Repository github.com/doublegate/RustyN64, public. Version-controlled since 2026-07-19; before that the tree had no git history of its own.
CI Green, verified. All jobs pass on ubuntu/macOS/windows: setup, test (fmt + clippy + test + no_std), rustdoc (-D warnings, an independent job so a doc break cannot ride in behind a green test job), test-roms, no-commercial-roms, wasm-bindgen-pin. Split light/full: the test-roms job and the macOS/Windows matrix run only on push-to-main, the merge queue, release/* PRs, dispatch, and a weekly cron.
Docs site Livehttps://doublegate.github.io/RustyN64/. rustdoc publishes to /api/; / is reserved for the Phase 6 wasm demo and currently redirects.
Release release.yml builds all three targets, packages archives with licenses, generates SHA256SUMS, and publishes on a v* tag. Guarded so the tag must match the workspace version. Exercised for real: v0.1.0 through v0.8.0 are all tagged and released (v0.2.0 onward published checksummed three-target binaries).
wasm Compiles for wasm32-unknown-unknown and has a browser entry point (#[wasm_bindgen(start)] in crates/rustyn64-frontend/src/wasm.rs, web/index.html); trunk build produces a 2D-canvas demo that boots a homebrew ROM and blits the VI scan-out. The full in-browser winit/wgpu/egui shell is roadmap.
Hardware reference n64brew_wiki/ — offline mirror of the N64brew Wiki (324 pages, 96 media, gitignored). Rebuild with scripts/mirror_n64brew_wiki.py.
Reference emulators ref-proj/ — 11 study clones (ares, cen64, gopher64, simple64, parallel-rdp/rsp, angrylion, n64-systemtest, n64-tests, libdragon, PeterLemon). Licenses vary and several forbid copying — read ref-proj/README.md first.

Test-ROM corpora

Full provenance and license rules in tests/roms/README.md.

Corpus License Tier Staged
n64-systemtest MIT committed 1 ROM, 2.7 MB — built from source
krom (PeterLemon) Unlicense external 196 ROMs, 182 MB
dillon-n64-tests none external (run-only) 26 ROMs, 38 MB
240p Test Suite GPL-2.0-or-later external 1 ROM, 12 MB — built from source
commercial copyrighted external (never committed) 66 ROMs, 1.5 GB

Commercial ROMs are blocked by three independent guards (.gitignore, scripts/check_no_roms.sh pre-commit, and the no-commercial-roms CI job); only tests/roms/n64-systemtest/ is allowlisted, and a committed ROM must ship its upstream LICENSE beside it.

Three gates execute real results today. basic.z64 from the dillon-n64-tests corpus runs end to end, judged by its completion protocol (T-11-006). The n64-systemtest ROM runs under the committed --test systemtest runner and reports a real count (Phase 1 categories Failed: 0; RSP category Failed: 0; 90 suite-wide). The golden-log gate (--test golden_log) replays 50,027 retired records at 0 diff against ares. A fourth, the synthetic visual golden (--test golden_frame, T-31-005), executes the FILL → VI scan-out path against a committed frame hash. The accuracy battery now executes (AccuracyScorer::default_battery, 56 probes, 100% — see the table above); the rest of the corpus (the real-ROM krom/240p visual goldens and the commercial ROMs) is staged only — an oracle on disk that no gate executes yet.

What is stubbed (the roadmap)

Subsystem State Phase
VR4300 integer core, pipeline, delay slots, errata, SysAD done (Sprint 1) Phase 1
VR4300 COP0, TLB, exception model done (Sprint 2) Phase 1
VR4300 COP1 (FPU) doneFailed: 0 on the COP1 category; every COP1 operation including SQRT (funct 4, pipeline::fp_sqrt on the soft-float core) is implemented Phase 1 (Sprint 3)
CPU golden-log 0-diff done (T-HARNESS-01) — tests/golden/n64-systemtest.log, captured from ares at the ELF entry; gate is --test golden_log Phase 1
VR4300 I/D caches done (T-11-003) — tags, data, all CACHE ops; DMA coherency outstanding Phase 1
RSP scalar unit + SP interface implemented (T-21-002/004/005) — the SU executes, BREAK halts (incl. in a taken branch's delay slot), DMA and the register file work. Spec docs/rsp.md; regressions in su::tests and n64-systemtest RSP BREAK/SP … Phase 2
RSP vector unit (COP2, accumulator, VRCP/VRSQ) implemented — the full VU: multiplies, accumulating forms, add/sub/carry, compares, the clip compares (VCL/VCH/VCR), VMRG/VRND/VMULQ/VMACQ, the reciprocals, the whole vector load/store family, and the reserved "VZERO" opcodes. Spec docs/rsp.md; regressions in vu's compare_tests/clip_tests/vzero_tests/… and the n64-systemtest RSP category Phase 2
RDP DPC command registers implementedDPC_START/END/CURRENT/STATUS at 0x0410_0000, the START_VALID double-latch + FREEZE; driven both by the CPU and by the RSP microcode's COP0 c8c15 (routed via StepResult::dp_writeBus::rsp_tickRdp::dpc_write, the RSP not being allowed to name Rdp). The rasterizer behind them is implemented (Phase 3). Provenance N64brew Reality Display Processor/Interface; spec docs/rdp.md; regressions in rustyn64-rdp tests + n64-systemtest RSP STATUS: start-valid + microcode::…emits_an_rdp_command… Phase 2 / Phase 3

What remains in COP1. The register file (FR views), the control registers, the data moves, S/D ADD/SUB/MUL/DIV, ABS/MOV/NEG, the compares, the conversions, and the BC1F/BC1T/BC1FL/BC1TL FP branches all decode and execute, and the COP1 category of n64-systemtest passes Failed: 0 (reproduce: cargo test -p rustyn64-test-harness --release --test systemtest -- --ignored). BC1 was the last decoded-but-no-op hazard and is now resolved (ledger R-2 / C-25). SQRT (funct 4) is implemented too (pipeline::fp_sqrtsoftfloat::sqrt, tested in fpr.rs and softfloat.rs), so no COP1 operation is unwired — the COP1 set is complete.

What is done: the unmaskable unimplemented-operation cause (bit 17) is produced for subnormal operands and results, for FS = 1 with underflow or inexact enabled, for MSB-clear NaN operands, and for out-of-range integer conversions (ledger C-13); the arithmetic runs on a soft-float core (crates/rustyn64-cpu/src/softfloat.rs) that produces exact IEEE flags and honors all four FCSR.RM modes, verified bit-for-bit against Rust's native operators over 100,000 cases; enabled FP traps raise Exception::FloatingPoint, leave fd unwritten, do not accumulate the sticky Flags, and do not retire; and the compares and conversions decode and execute — all sixteen C.cond.fmt tests pass outright. NaN classification follows the VR4300's inverted convention (ledger C-12), not IEEE-754:2008.

The decoded-but-no-op hazard, for the record (every COP1 op is now wired, so this is history, not a live gap). The conversions and the C.cond.fmt compares were implemented in fpu.rs yet unreachable, and ABS, MOV and NEG before them; SQRT (funct 4) was the last, wired to pipeline::fp_sqrt in T-13-005. MOV alone cost ~100 failures, because a decoded-but-no-op instruction is invisible to cargo test and the compiler emits one at every FP call boundary. That pattern has cost two separate investigations; when adding a decode arm, enumerate the neighboring funct space rather than only the encoding that prompted the change. | RDP LLE (software reference rasterizer) + VI scan-out | done — texture / combiner / blender / coverage pipeline; 164 conformance vectors bit-match Angrylion; a real ROM renders a golden frame (T-33-006) | Phase 3 | | AI audio DMA double-buffer | done — registers, FIFO, derived DAC rate, IRQ-on-start, delayed-carry bug (Sprint 1); the real mixer microcode produces PCM on the RSP (Sprint 2); the frontend drain + resampler landed in Phase 6 | Phase 4 | | PI/SI DMA, PIF/CIC boot, FlashRAM machine, saves | done (v0.6.0) — PI/SI DMA, the CIC handshake, all four save backends incl. the FlashRAM command machine, and both HLE and real-PIF boot | Phase 5 | | Frontend egui shell | done (v0.7.0) — presents the real machine (VI scan-out, AI drain, SI input) with save-states / rewind / run-ahead; a wasm browser demo | Phase 6 | | Accuracy battery / breadth / reach | battery wired — it scores the 54 committed Angrylion vectors, RDP + VI (100%); the commercial-corpus breadth and the reach features are Phases 7–8 | Phases 7–8 |

Chip → crate map

Crate Chip / role Spec doc
rustyn64-cpu NEC VR4300 (MIPS III, TLB, FPU, SysAD) docs/cpu.md
rustyn64-rsp RSP (SU + VU, DMEM/IMEM, microcode) docs/rsp.md
rustyn64-rdp RDP rasterizer + VI scan-out docs/rdp.md
rustyn64-audio AI DAC + sample DMA docs/audio.md
rustyn64-cart PI cart + PIF/CIC + SI + saves docs/cart.md
rustyn64-core Bus + scheduler (ADR 0006 canonical 187.5 MHz clock) docs/scheduler.md
rustyn64-frontend egui/wgpu/cpal/winit shell (bin rustyn64) docs/frontend.md
rustyn64-test-harness golden-log + accuracy + frame comparators docs/testing-strategy.md
rustyn64-netplay rollback netplay (frontend-side) docs/frontend.md
rustyn64-cheevos RetroAchievements FFI (later, off by default)

Accuracy

The first gate now reports a real number. basic.z64 from Dillon's n64-tests runs end to end and passes all five of its hardware-verified cases — delay-slot semantics, J in a delay slot, BEQL nullification, BNEL nullification, and LWU+DADDI. 59 instructions retired, 129 master ticks (T-11-006).

That is a small number of tests, but it is the first time this emulator has executed a ROM at all, and it independently validates the delay-slot and branch-likely work against something other than our own expectations.

The ADR 0004 determinism contract is now exercised (T-11-007) rather than merely written down: same seed + ROM produces a bit-identical machine across repeated runs, different seeds produce different machines so the check is not vacuous, reset is reproducible, and a source-level guard rejects wall-clock, OS entropy, threads and unordered collections anywhere in the core.

Gate Oracle available? Status
Dillon basic.z64 (control flow) yes — external tier PASSING — 5/5
Determinism (ADR 0004) n/a — self-checking PASSING — exercised, not just specified
CPU/RSP golden-log (reference trace) yestests/golden/n64-systemtest.log, captured from a patched ares MET: 0 diff over 50,027 retired records
n64-systemtest, CPU/COP0/TLB/COP1 categories (Phase 1's criterion) yes — ROM committed, and the runner with it MET: Failed: 0, across 917 tests started. Reproduce with cargo test -p rustyn64-test-harness --release --test systemtest -- --ignored. 90 assertions still fail suite-wide, down from 413 (and from 93 before the Phase 5 cart/PIF/SI work); none are RSP-prefixed (the RSP category is Phase 2's criterion and is now 0), leaving the RDP rasterizer (Phase 3), the MI's RDRAM repeat mode, and the remaining cart/PIF corners
n64-systemtest, RSP category (Phase 2's criterion) yes — same runner MET: Failed: 0 across 917 tests started — every RSP-prefixed test passes (verified by dumping per-test failures; 0 begin with RSP). The full VU ISA, vector load/store, reserved opcodes, BREAK-in-delay-slot, and the DPC registers landed in #41–#44
ParaLLEl-RDP fuzz suite (RDP bit-exactness) source cloned, suite not set up not started
Accuracy battery (AccuracyScorer::default_battery) yes — 56 probes across two oracle suites: 43 Angrylion RDP rasterizer vectors + 13 Angrylion VI scan-out vectors (expected values are the oracle's, never our own output; RDP probes are byte-for-byte, VI probes are RGB-only since the 4th byte is coverage) 100% (56/56) — asserted by default_battery_matches_the_oracle; both suites are asserted to contribute, and an empty battery now scores 0%, not a vacuous 100%
Visual golden / screenshots yes — krom + 240p + commercial staged first frame MET (T-31-005) — a synthetic RDP FILL list rendered through the full command-decode → FILL → VI scan-out path is pinned byte-exact against a committed golden hash (--test golden_frame). Real-ROM krom/240p goldens await cartridge boot (Phase 5). A commercial-cartridge frame is now capturedscreenshots/paper-mario-first-commercial-frame.png (R-18) — as a viewed artifact, not yet a byte-pinned golden

The distinction matters: "oracle available" means the ROM is on disk; it says nothing about whether the emulator can execute it. Both must be true before a gate reports a real number — true today for basic.z64, n64-systemtest, the golden log, the synthetic golden_frame, and the first real-ROM visual golden (real_rom_frame.rs, a homebrew ROM that CPU-renders a frame through the VI), the accuracy battery (56 probes, 100%), and — since 2026-07-29 — an RDP-driven commercial-ROM frame: Super Mario 64 renders its title screen through the full LLE path (screenshots/super-mario-64-title.png, 125,278 RDP commands). The last two were listed here as not yet true; both now are.

See docs/testing-strategy.md for the oracle and the five test layers.

Cart model matrix

Tiered (Core / Curated / BestEffort) under an honesty gate: NO. The N64 has one cart model parameterized by save type + CIC + region, not hundreds of mappers — so there is no board tiering and no honesty-gate test (ADR 0003). The accuracy oracle is n64-systemtest pass/fail + the RDP fuzz suite, not a tier matrix.

Save-type coverage target (per-game DB resolved): EEPROM 4k/16k, SRAM, FlashRAM, Controller Pak (docs/cart.md). All five backends have regression ROMs staged under tests/roms/external/commercial/, one folder per backend, with save types resolved by MD5 against the mupen64plus catalog rather than guessed. All four backends are implemented and round-trip byte-for-byte (Phase 5, v0.6.0); the save-type DB lookup itself (identity only) is a Phase-7 item (T-CART-02).

Version policy

  • Start at v0.1.0; this is a clean N64 project. Additive features land behind default-off flags so the shipped / native / no_std / wasm builds stay byte-identical with the flags off.
  • Do NOT import RustyNES engine-lineage "v2.0" anchors as RustyN64 releases.
  • Three different things get called "finer timing" and must never be conflated in release notes or docs, coarse to fine:
    1. The canonical 187.5 MHz master clock (ADR 0006) — CPU every 2 ticks, RCP every 3. Implemented (T-11-001). ADR 0001's 93.75 MHz tick with its 3:2 fractional accumulator is gone from the tree.
    2. The SysAD command/data split at SClock, 62.5 MHz (ADR 0007). Note this is coarser than one PClock, so it is not sub-cycle resolution. Modeled (T-11-008) — the transaction exists and cannot complete in its address phase, but the scheduler does not yet step the RCP between phases (Sprint 2).
    3. Resolution finer than one PClock — the deferred ADR 0005 refactor. Does not exist, is not scheduled, and is the one expected to break byte-identity and save-state compatibility.
  • v1.0.0 is the production cut (Phases 1–7 complete + the engineering rungs; README/CHANGELOG/docs/STATUS in sync; release matrix + Pages green). Phase 8 (reach — netplay, achievements, TAS, scripting, shaders) is deliberately NOT in the v1.0.0 gate — it is v1.1.0 onward (VERSION-PLAN §Post-v1.0). See to-dos/ROADMAP.md. Of those release-readiness items, Pages is already green and the release matrix has run for real: v0.1.0 through v0.8.0 are all tagged, and release.yml has published checksummed binaries across the three-target matrix (v0.2.0 onward).