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 -- --ignored — now 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.
- The Cargo workspace: all
rustyn64-*crates build;cargo test --workspacepasses 803 tests. rustyn64-core: theBus(owns RDRAM + every chip + the RCP/MI register state), and the canonical 187.5 MHz scheduler (ADR 0006) —master_ticksis the only incremented counter; CPU (÷2), RCP (÷3) and COP0Count(÷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-bitD*forms and the unalignedLWL/LWR/LDL/LDRfamily), the documented errata reproduced-not-corrected, and aSysADtransaction model that cannot complete inside its address phase.rustyn64-rspandrustyn64-rdpexecute: 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 at0x0450_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, theSaveType/Cic/RomFormatenums, 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]+allocand cross-compiles tothumbv7em-none-eabihf; only the frontend carriesstd+unsafe.
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.
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 | Live — https://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. |
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.
| 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) | done — Failed: 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 | implemented — DPC_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 c8–c15 (routed via StepResult::dp_write → Bus::rsp_tick → Rdp::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_sqrt → softfloat::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 |
| 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) | — |
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) | yes — tests/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 captured — screenshots/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.
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).
- 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:
- 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.
- 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).
- 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.0throughv0.8.0are all tagged, andrelease.ymlhas published checksummed binaries across the three-target matrix (v0.2.0onward).