diff --git a/.github/release-notes/v2.2.5.md b/.github/release-notes/v2.2.5.md new file mode 100644 index 00000000..9014cc44 --- /dev/null +++ b/.github/release-notes/v2.2.5.md @@ -0,0 +1,107 @@ +# RustyNES v2.2.5 — "Colophon" (provenance, licensing, and documentation integrity) + +A **provenance, licensing, and documentation-integrity** release. It was prompted +by community review (NESdev forums) of the project's licensing and AI-assisted +origins, and it corrects how RustyNES *describes its own provenance* — in source +comments, in `NOTICE`, and in the docs — so the record matches what the code +actually is. + +**Zero emulation-core behavior changes.** The deterministic `#![no_std]` chip stack, +save-state / TAS / netplay formats, and every golden vector are byte-identical to +v2.2.4 by construction, so **AccuracyCoin holds 141/141 (100.00%)** and nestest is +0-diff. This was re-verified after every change (AccuracyCoin full + run-ahead, +the nestest golden log, the dual-path differential net, and the snapshot-schema +audit), and the whole tree passes `cargo fmt`, `clippy -D warnings` (workspace + +feature combos), `rustdoc -D warnings`, markdownlint, and the `no_std` +cross-compile. + +## Why this release exists + +A reviewer pointed out — correctly — that a number of in-source comments described +implementations as "ports" of other emulators, including GPL-licensed ones, and +that the project's AI-assisted nature was not disclosed. Rather than argue the +point, we audited the entire tree and fixed it. The short version: + +- The chip, mapper, and peripheral **behaviors** are implemented from public + hardware documentation (the NESdev wiki, published datasheets, the documented + 6502 unofficial-opcode behavior) and pinned to public test ROMs. Where a + reference emulator was consulted, it was used as a **behavioral oracle** — to + observe and cross-check documented behavior — not as a source of copied code. +- Some comments had mischaracterized that relationship as "ported from X." Those + were reworded to state what actually happened. **No GPL-licensed emulator source + is incorporated into RustyNES.** + +## Licensing & provenance + +- **In-source "port" comments corrected** across the CPU (unstable-store opcodes), + PPU (sprite-evaluation and OAM models), APU, and numerous mapper register + decoders — reframed from "ported from Mesen2 / puNES" (GPLv3 / GPLv2) to + independent implementations of publicly-documented hardware behavior, + cross-checked against reference emulators as oracles. +- **`NOTICE` rewritten** to: + - disclose the behavioral-oracle use of GPL-licensed emulators + (Mesen2/MesenCE, higan, **GeraNES**, ares, FCEUX, Nestopia UE, puNES) with **no + code incorporated**; + - attribute the genuinely incorporated permissive components — **emu2413** + (Mitsutaka Okazaki, MIT), **TriCNES** (Chris Siebert, MIT), and **rcheevos** + (RetroAchievements.org, MIT) — with their copyright notices and the MIT text; + - attribute the bundled fonts (**Font Awesome**; **Press Start 2P** / OFL) and + the bundled **test ROMs** (AccuracyCoin MIT, Damian Yerrick Holy Mapperel zlib, + blargg / kevtris public domain); + - credit the CRT-shader / NTSC-filter **visual influences** as independent + reimplementations. +- **GeraNES (GPL-3.0-only) disclosed** — it was cited as a reference across ~58 + files but was previously absent from `NOTICE`. +- **CRT shaders and NTSC filters** (`crt_royale`, `crt_guest`, `megatron`, the + Bisqwit and EMMIR NTSC filters) were reviewed at source level. Each is a + single-pass shader built on RustyNES's own uniform/pipeline conventions and is + structurally incompatible with being a translation of the upstream *multi-pass* + shader source; copyright protects code expression, not a visual look or a + rendering technique, so these are independent reimplementations. Comments were + reworded from "port / condensation of X" accordingly, and the one comment + claiming tables were "ported verbatim from Bisqwit's C" was corrected — those + tables encode the two-level NES composite signal documented at the NESdev wiki. +- **`blip.rs`** no longer mislabels `blip_buf` as BSD/MIT (it is LGPL-2.1+); the + file is an independent band-limited-step (BLEP) implementation and now says so. + +## Documentation + +- **New `docs/originality-and-provenance.md`** — a candid account of where RustyNES + advances, diverges from, or independently re-derives NES emulation technique + (the one-clock timebase, the transistor-literal octal-latch fetch, the + machine-checked accuracy-honesty gates, the determinism contract, measured-and- + rejected optimizations), the development timeline, an oracle-versus-port + classification of every reference, and the full license posture — written to be + honest rather than triumphal, including that the project is heavily AI-assisted. +- **README** — added an AI-assistance disclosure; removed a comparison graphic that + contained inaccurate details; corrected a mislabeled "sub-cycle accuracy in + action" screenshot caption (it was an early-development image); toned down + overstated language; and synced the Acknowledgments with `NOTICE`. +- **`tests/roms/LICENSES.md`** — fixed a false exclusion claim (four Holy Mapperel + mapper ROMs stated as excluded are in fact committed), a stale crate path, and + the AccuracyCoin sub-test count, and added blanket coverage for the committed + directories not individually tabulated (328 committed `.nes` total, none + commercial). + +## Assets + +- **Press Start 2P OFL text** added to the Android app assets. The font shipped in + the Android app without the SIL Open Font License text that OFL 1.1 requires + travel with the font; the desktop and iOS builds already carried it. + +## Compatibility + +No format, save-state, or behavior changes. `.rns` save-states, `.rnm` movies, and +netplay replays are byte-identical to v2.2.4. The libretro core's +`display_version` advances to `v2.2.5`; there are no other functional changes to +the RetroArch integration. + +## Verification + +- **AccuracyCoin: 141/141 (100.00%)** — full suite and through run-ahead. +- **nestest: 0-diff** against the Nintendulator golden log. +- **Dual-path differential net** and **snapshot-schema audit** green. +- `cargo fmt --check`, `cargo clippy --workspace --all-targets -- -D warnings` + (plus the `retroachievements` / `scripting` / `hd-pack` feature combos), + `RUSTDOCFLAGS="-D warnings" cargo doc`, markdownlint, and the `no_std` + `thumbv7em-none-eabihf` cross-compile all pass. diff --git a/.gitignore b/.gitignore index dd292533..05fb22a9 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ /target/ /target/doc/ /target/criterion/ +# Nested per-crate target/ dirs (build output; the root /target/ rule is anchored). +crates/*/target/ **/*.rs.bk *.bench # This workspace ships a binary app — commit the lock (re-include it). diff --git a/AGENTS.md b/AGENTS.md index cb2066ca..1f4eebdc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,7 +27,7 @@ RustyNES is a cycle-accurate Nintendo Entertainment System emulator written in pure Rust. The accuracy bar is Mesen2 / higan / ares: tight lockstep scheduling at PPU-dot resolution on a master-clock-precise timebase, sub-instruction PPU events visible to subsequent CPU code, and a lookup-table non-linear audio mixer with band-limited synthesis. The frontend is pure Rust (`winit` + `wgpu` + `cpal` + `egui`). -**Current release: v2.2.4 "Cartridge"** (2026-07-24) — a **libretro / RetroArch distribution** cut whose purpose is that the RustyNES core **builds and installs cleanly through the Libretro buildbot** () for in-RetroArch use. **Zero emulation-core changes** — the deterministic `#![no_std]` chip stack, save-state / TAS / netplay formats, and every golden vector are byte-identical to v2.2.3, so **AccuracyCoin holds 141/141 (100.00%)** and nestest is 0-diff by construction. `crates/rustynes-libretro` wraps `rustynes-core`, so it inherits every v2.2.3 change automatically (the fast-dot-path default; the `PPU_SNAPSHOT_VERSION` 8 / APU v4 save-state schema, transparent because `get_serialize_size` / `on_serialize` size and emit the *current* snapshot via `Nes::snapshot_core_into` rather than a fixed layout; the `Mapper::mix_audio` i32 widening; the Zapper model; the `mNNN_` mapper rename), and both buildbot cross-ABIs the CI early-warning gate models — `x86_64-pc-windows-gnu` and `aarch64-linux-android` — `cargo check --release -p rustynes-libretro` clean. The concrete change is a **`rustynes_libretro.info` metadata correction**: **`disk_control` `false` → `true`** (the real fix — the FDS multi-side Disk Control interface has been wired since the buildbot recipe landed but was advertised as absent, hiding multi-disk FDS swapping from RetroArch's Quick Menu), `display_version` `v1.0.0` → `v2.2.4`, and the description mapper count `168` → `172`. Libretro **core options** (region / overscan / palette / accuracy toggles) remain unexposed — `core_options = "false"` is accurate, a documented future enhancement rather than a v2.2.4 gap. The Antigravity PR reviewer standardization onto the shared template rides along. On top of **v2.2.3 "Datum"** (2026-07-23) — a **performance and accuracy-closure patch**, the product of a measure-first appraisal that profiled the emulator and acted on what the profile showed rather than on intuition. **Performance:** the specialized PPU fast dot path is promoted to the **default** and exposed to users for the first time — `Nes::set_fast_dotloop` had **no caller outside the core**, so a **−11.3%** frame-time win (fresh clean-host Criterion, reproducing v2.1.8's +12.3% by a different method; differential-tested bit-identical every frame since v2.1.8) shipped switched off and unreachable; release builds now ship **PGO-optimized** Linux binaries when the existing >3%-and-byte-identical gate passes; and CI gained a same-runner **relative** frame-time regression gate, closing a hole where a 2.5x slowdown passed the deliberately-loose absolute ceiling. **Two optimizations were measured and REJECTED** and are documented with their numbers per `docs/performance.md`'s convention — P3 (`emit_pixel` bounds-check elision) made the shipped default *slower* (+4.32% / +3.35% on the `_fast` workloads, p ≤ 0.02), and P4 (`cpu_clock`) found both textbook optimizations already implemented with the one remaining lever capped at **≤1.9%**. **Accuracy:** the **last two Holy Mapperel residuals are closed**, so all 17 ROMs report `detail=0000` (was 15/17) — MMC1's two software WRAM write-protect layers (`$E000` bit 4 + SNROM's CHR-register layer, gated on `chr_is_ram`) and FME-7's open bus on the RAM-selected-but-disabled window, both routed through the trait's existing `cpu_read_unmapped` contract. MMC1 is the change Holy Mapperel's README calls a game-compatibility hazard (FCEUX / PowerPak omit it), so it was validated before landing: **60/60** commercial ROMs including seven battery-backed MMC1 saves, plus **138/138** extended. The **Sunsoft 5B absolute level** is calibrated against Mesen2, which required widening `Mapper::mix_audio` to `i32` (the correct full-scale 5B tone `1882 * 18.471 = 34,761` does not fit `i16`). A **save-state schema gap** is fixed — `PPU_SNAPSHOT_VERSION` **8** carrying the sprite-eval FSM + OAM data-bus state, plus an APU **v4** tail — which is what made AccuracyCoin report **141/141 through run-ahead** as well as without it; a new standing field-vs-schema audit found it and the two APU gaps mechanically. A **Zapper beam-relative light model** lands opt-in / default-off (no pass-fail light-gun ROM exists to adjudicate it). **AccuracyCoin holds 141/141 (100.00%)**, nestest 0-diff. Also: the eleven `sprintN.rs` mapper modules (27,631 lines, ~110 boards) are renamed for the boards they emulate with `mNNN_` mapper-number prefixes, proven content-preserving by a byte-for-byte item comparison (930 items, 0 altered) and an identical 172-ID dispatch table. +**Current release: v2.2.5 "Colophon"** (2026-08-03) — a **provenance, licensing, and documentation-integrity** release with **zero emulation-core behavior changes** (so **AccuracyCoin holds 141/141 (100.00%)** and nestest is 0-diff by construction). It reworded in-source comments that had mischaracterized publicly-documented hardware-behavior implementations as "ports of" copyleft emulators (Mesen2 GPLv3, puNES GPLv2) into the accurate oracle framing; rewrote `NOTICE` to disclose the behavioral-oracle use of GPL emulators (Mesen2/MesenCE, higan, GeraNES, ares, FCEUX, Nestopia, puNES — no code incorporated) and to attribute the genuinely-incorporated permissive components (emu2413, TriCNES, rcheevos — all MIT), the bundled fonts and test ROMs, and the CRT-shader/NTSC-filter visual influences as independent reimplementations; disclosed **GeraNES (GPL-3.0-only)** as an oracle; added `docs/originality-and-provenance.md`; and added an AI-assistance disclosure to the README (removing a misleading comparison graphic and fixing a mislabeled screenshot caption). On top of **v2.2.4 "Cartridge"** (2026-07-24) — a **libretro / RetroArch distribution** cut whose purpose is that the RustyNES core **builds and installs cleanly through the Libretro buildbot** () for in-RetroArch use. **Zero emulation-core changes** — the deterministic `#![no_std]` chip stack, save-state / TAS / netplay formats, and every golden vector are byte-identical to v2.2.3, so **AccuracyCoin holds 141/141 (100.00%)** and nestest is 0-diff by construction. `crates/rustynes-libretro` wraps `rustynes-core`, so it inherits every v2.2.3 change automatically (the fast-dot-path default; the `PPU_SNAPSHOT_VERSION` 8 / APU v4 save-state schema, transparent because `get_serialize_size` / `on_serialize` size and emit the *current* snapshot via `Nes::snapshot_core_into` rather than a fixed layout; the `Mapper::mix_audio` i32 widening; the Zapper model; the `mNNN_` mapper rename), and both buildbot cross-ABIs the CI early-warning gate models — `x86_64-pc-windows-gnu` and `aarch64-linux-android` — `cargo check --release -p rustynes-libretro` clean. The concrete change is a **`rustynes_libretro.info` metadata correction**: **`disk_control` `false` → `true`** (the real fix — the FDS multi-side Disk Control interface has been wired since the buildbot recipe landed but was advertised as absent, hiding multi-disk FDS swapping from RetroArch's Quick Menu), `display_version` `v1.0.0` → `v2.2.4`, and the description mapper count `168` → `172`. Libretro **core options** (region / overscan / palette / accuracy toggles) remain unexposed — `core_options = "false"` is accurate, a documented future enhancement rather than a v2.2.4 gap. The Antigravity PR reviewer standardization onto the shared template rides along. On top of **v2.2.3 "Datum"** (2026-07-23) — a **performance and accuracy-closure patch**, the product of a measure-first appraisal that profiled the emulator and acted on what the profile showed rather than on intuition. **Performance:** the specialized PPU fast dot path is promoted to the **default** and exposed to users for the first time — `Nes::set_fast_dotloop` had **no caller outside the core**, so a **−11.3%** frame-time win (fresh clean-host Criterion, reproducing v2.1.8's +12.3% by a different method; differential-tested bit-identical every frame since v2.1.8) shipped switched off and unreachable; release builds now ship **PGO-optimized** Linux binaries when the existing >3%-and-byte-identical gate passes; and CI gained a same-runner **relative** frame-time regression gate, closing a hole where a 2.5x slowdown passed the deliberately-loose absolute ceiling. **Two optimizations were measured and REJECTED** and are documented with their numbers per `docs/performance.md`'s convention — P3 (`emit_pixel` bounds-check elision) made the shipped default *slower* (+4.32% / +3.35% on the `_fast` workloads, p ≤ 0.02), and P4 (`cpu_clock`) found both textbook optimizations already implemented with the one remaining lever capped at **≤1.9%**. **Accuracy:** the **last two Holy Mapperel residuals are closed**, so all 17 ROMs report `detail=0000` (was 15/17) — MMC1's two software WRAM write-protect layers (`$E000` bit 4 + SNROM's CHR-register layer, gated on `chr_is_ram`) and FME-7's open bus on the RAM-selected-but-disabled window, both routed through the trait's existing `cpu_read_unmapped` contract. MMC1 is the change Holy Mapperel's README calls a game-compatibility hazard (FCEUX / PowerPak omit it), so it was validated before landing: **60/60** commercial ROMs including seven battery-backed MMC1 saves, plus **138/138** extended. The **Sunsoft 5B absolute level** is calibrated against Mesen2, which required widening `Mapper::mix_audio` to `i32` (the correct full-scale 5B tone `1882 * 18.471 = 34,761` does not fit `i16`). A **save-state schema gap** is fixed — `PPU_SNAPSHOT_VERSION` **8** carrying the sprite-eval FSM + OAM data-bus state, plus an APU **v4** tail — which is what made AccuracyCoin report **141/141 through run-ahead** as well as without it; a new standing field-vs-schema audit found it and the two APU gaps mechanically. A **Zapper beam-relative light model** lands opt-in / default-off (no pass-fail light-gun ROM exists to adjudicate it). **AccuracyCoin holds 141/141 (100.00%)**, nestest 0-diff. Also: the eleven `sprintN.rs` mapper modules (27,631 lines, ~110 boards) are renamed for the boards they emulate with `mNNN_` mapper-number prefixes, proven content-preserving by a byte-for-byte item comparison (930 items, 0 altered) and an identical 172-ID dispatch table. The prior release, **v2.2.2 "Conduit"** (2026-07-21), was a **build, distribution, and CI-integrity patch**: the **libretro buildbot recipe from 1 of 10 jobs green to all ten building** (the last step before RustyNES lands in RetroArch's built-in core downloader), a **GitHub Actions supply-chain hardening** pass (`persist-credentials: false` on all 19 checkouts, a fail-closed release-tag check via `git/matching-refs`, `dtolnay/rust-toolchain` SHA-pinned off `@master`), and the toolchain **collapsed to one pinned source of truth** — no toolchain version literal anywhere under `.github/` and **no `nightly` on any build path**. **Zero emulation-core changes**, so AccuracyCoin held 141/141 by construction. Its one behavioural improvement in a shipped artifact: the libretro **tvOS** core built with `panic = "abort"` like every other platform. @@ -185,7 +185,7 @@ These cross-cutting decisions span multiple files. Reading individual chip docs - `ref-docs/` is immutable. Research updates go in dated supplemental files. - ADRs go in `docs/adr/` (Michael Nygard format). - `rustynes-core` re-exports the public types from the chip crates; downstream consumers (`rustynes-frontend`, `rustynes-test-harness`) should depend on `rustynes-core` rather than the chip crates directly. -- When relabeling old engine "v2.x" narrative for users, present it as upstream lineage/history — **never as a current RustyNES release version.** The current release is **v2.2.4 "Cartridge"** (2026-07-24, a libretro/RetroArch distribution cut — zero emulation-core changes so AccuracyCoin holds 141/141 by construction; the libretro core is confirmed up-to-date with all recent changes and builds for the buildbot ABIs [`x86_64-pc-windows-gnu`, `aarch64-linux-android`], and `rustynes_libretro.info` is corrected: `disk_control` false→true [the FDS Disk Control interface was wired but advertised absent], `display_version` v1.0.0→v2.2.4, mapper count 168→172; core options remain a documented future enhancement; the Antigravity reviewer standardization rides along), on top of **v2.2.3 "Datum"** (2026-07-23, a performance and accuracy-closure patch — the fast PPU dot path promoted to default and exposed, PGO binaries shipped on the release path, a same-runner relative frame-time CI gate, the last two Holy Mapperel residuals closed [MMC1 WRAM write-protect + FME-7 open bus, all 17 ROMs now `detail=0000`], the Sunsoft 5B level calibrated with `Mapper::mix_audio` widened to i32, a save-state schema gap fixed at `PPU_SNAPSHOT_VERSION` 8 + an APU v4 tail, an opt-in Zapper beam-relative light model, and the eleven `sprintN.rs` mapper modules renamed to `mNNN_.rs`; two optimizations measured and REJECTED and documented as such; AccuracyCoin 141/141 — on top of **v2.2.2 "Conduit"** [2026-07-21, a build/distribution/CI-integrity patch — the libretro buildbot recipe taken from 1 of 10 jobs green to all ten building, a GitHub Actions supply-chain hardening pass, and the toolchain collapsed to one pinned source of truth with no `nightly` on any build path; zero emulation-core changes], itself on **v2.2.1** [2026-07-15, a housekeeping patch: dev-tooling archival, a zero-source-change dependency consolidation, and a gitignored FDS test-corpus addition], itself on **v2.2.0 "Capstone"** [2026-07-12], the milestone cut that closes the v2.1.5 → v2.2.0 "deepen the existing project" run — its two remaining marquees the netplay matchmaking / lobby stack and the FDS medium model, atop a peripherals + quality/security pass (Famicom `$4016`-bit-2 microphone + 3×3-aperture Zapper; cargo-fuzz targets 3 → 8 finding + fixing two `Movie::deserialize` OOM-DoS paths; a read-only Tools → ROM Info browser); every change additive or default-off, AccuracyCoin 141/141) on the v2.0.0 "Timebase" one-clock / every-cycle-bus-access scheduler rewrite + Vs. `DualSystem` dual-console support. The v2.0.x "Harbor" mobile-finalization train (v2.0.1→v2.0.9) and the entire v2.1.x "Fathom" line (v2.1.0→v2.1.10) plus the v2.2.0 "Capstone" milestone have all shipped — the run's steps being v2.1.5 "Vernier" (regression-net & residual) → v2.1.6 "Timbre" (expansion-audio fidelity) → v2.1.7 "Stepping" (opt-in PPU/2A03 die-revisions + power-on RAM/palette models; the DMA "unexpected read" frontier a documented no-op on every oracle, ADR 0033) → v2.1.8 "Tempo" (a default-OFF fast PPU dot path + SIMD blitter + wasm size pass) → v2.1.9 "Aperture" (a marquee CRT shader stack + raw NTSC composite signal-decode + GIF/WAV capture + palette editor) → v2.1.10 "Loom" (TAStudio greenzone + Lua API breadth + browser-RA auth-proxy deploy stack + Vs. `DualSystem` libretro presentation) → v2.2.0 "Capstone" (the milestone cut closing the run) → v2.2.1 (housekeeping) → **v2.2.2 "Conduit"** the build/distribution/CI-integrity patch (the prior release) — preceded by v1.10.0 "Arcade" the native Libretro / RetroArch core, the v1.9.0→v1.9.9 iOS TestFlight train, the v1.8.0→v1.8.9 "Android" train, and the desktop-feature lineage v1.1.0→v1.7.1, all on the v1.0.0 production core (see the top "Current release" block + `docs/STATUS.md`). **Never claim any version *later* than v2.2.4 is released** — in particular the joint mobile app-store launch (Google Play + Apple App Store + AltStore PAL + F-Droid) is the future **v2.3.0** (NOT v2.1.0 or v2.2.0 — the entire v2.1.x line and the v2.2.0 "Capstone" milestone have all already shipped, closing the "deepen the existing project" run; the store launch moved out to v2.3.0 — see `to-dos/ROADMAP.md`). Two distinct "v2.0"s exist and must not be conflated, **both now shipped, at different times, for different reasons**: the **engine-lineage v2.0** master-clock work shipped as the **v1.0.0** production core (2026-06-13) — it was the *only* scheduler through v1.10.0. RustyNES's own **v2.0.0 "Timebase"** release (2026-07-03) is a *different* milestone that *replaces* that same dot-lockstep scheduler outright: the **one-clock + every-cycle-bus-access collapse** (a single canonical cycle counter + a split-around-the-access `start_cycle`/`end_cycle` PPU catch-up, mirroring Mesen2's structure), full Vs. `DualSystem` dual-console emulation (core-and-harness-only; frontend wiring deferred), and the breaking save-state / cross-version changes it entailed (ADR 0002 / ADR 0028 / ADR 0029) — the one release that broke byte-identity / save-state compatibility, by design. The R1/R2 hard-tier MMC3 IRQ-timing residual was investigated under a bounded-effort campaign and is by-design-deferred beyond v2.0.0, not closed — see ADR 0002's decision-update section for the mechanism-level finding. +- When relabeling old engine "v2.x" narrative for users, present it as upstream lineage/history — **never as a current RustyNES release version.** The current release is **v2.2.5 "Colophon"** (2026-08-03, a provenance/licensing/documentation-integrity release — zero emulation-core behavior changes so AccuracyCoin holds 141/141 by construction; `NOTICE` rewritten for full attribution + GPL-oracle disclosure + GeraNES, in-source "port" comments reworded to the oracle framing, the CRT-shader/NTSC provenance reworded to independent reimplementations, `docs/originality-and-provenance.md` added, README AI-assistance disclosure), on top of **v2.2.4 "Cartridge"** (2026-07-24, a libretro/RetroArch distribution cut — zero emulation-core changes so AccuracyCoin holds 141/141 by construction; the libretro core is confirmed up-to-date with all recent changes and builds for the buildbot ABIs [`x86_64-pc-windows-gnu`, `aarch64-linux-android`], and `rustynes_libretro.info` is corrected: `disk_control` false→true [the FDS Disk Control interface was wired but advertised absent], `display_version` v1.0.0→v2.2.4, mapper count 168→172; core options remain a documented future enhancement; the Antigravity reviewer standardization rides along), on top of **v2.2.3 "Datum"** (2026-07-23, a performance and accuracy-closure patch — the fast PPU dot path promoted to default and exposed, PGO binaries shipped on the release path, a same-runner relative frame-time CI gate, the last two Holy Mapperel residuals closed [MMC1 WRAM write-protect + FME-7 open bus, all 17 ROMs now `detail=0000`], the Sunsoft 5B level calibrated with `Mapper::mix_audio` widened to i32, a save-state schema gap fixed at `PPU_SNAPSHOT_VERSION` 8 + an APU v4 tail, an opt-in Zapper beam-relative light model, and the eleven `sprintN.rs` mapper modules renamed to `mNNN_.rs`; two optimizations measured and REJECTED and documented as such; AccuracyCoin 141/141 — on top of **v2.2.2 "Conduit"** [2026-07-21, a build/distribution/CI-integrity patch — the libretro buildbot recipe taken from 1 of 10 jobs green to all ten building, a GitHub Actions supply-chain hardening pass, and the toolchain collapsed to one pinned source of truth with no `nightly` on any build path; zero emulation-core changes], itself on **v2.2.1** [2026-07-15, a housekeeping patch: dev-tooling archival, a zero-source-change dependency consolidation, and a gitignored FDS test-corpus addition], itself on **v2.2.0 "Capstone"** [2026-07-12], the milestone cut that closes the v2.1.5 → v2.2.0 "deepen the existing project" run — its two remaining marquees the netplay matchmaking / lobby stack and the FDS medium model, atop a peripherals + quality/security pass (Famicom `$4016`-bit-2 microphone + 3×3-aperture Zapper; cargo-fuzz targets 3 → 8 finding + fixing two `Movie::deserialize` OOM-DoS paths; a read-only Tools → ROM Info browser); every change additive or default-off, AccuracyCoin 141/141) on the v2.0.0 "Timebase" one-clock / every-cycle-bus-access scheduler rewrite + Vs. `DualSystem` dual-console support. The v2.0.x "Harbor" mobile-finalization train (v2.0.1→v2.0.9) and the entire v2.1.x "Fathom" line (v2.1.0→v2.1.10) plus the v2.2.0 "Capstone" milestone have all shipped — the run's steps being v2.1.5 "Vernier" (regression-net & residual) → v2.1.6 "Timbre" (expansion-audio fidelity) → v2.1.7 "Stepping" (opt-in PPU/2A03 die-revisions + power-on RAM/palette models; the DMA "unexpected read" frontier a documented no-op on every oracle, ADR 0033) → v2.1.8 "Tempo" (a default-OFF fast PPU dot path + SIMD blitter + wasm size pass) → v2.1.9 "Aperture" (a marquee CRT shader stack + raw NTSC composite signal-decode + GIF/WAV capture + palette editor) → v2.1.10 "Loom" (TAStudio greenzone + Lua API breadth + browser-RA auth-proxy deploy stack + Vs. `DualSystem` libretro presentation) → v2.2.0 "Capstone" (the milestone cut closing the run) → v2.2.1 (housekeeping) → **v2.2.2 "Conduit"** the build/distribution/CI-integrity patch (the prior release) — preceded by v1.10.0 "Arcade" the native Libretro / RetroArch core, the v1.9.0→v1.9.9 iOS TestFlight train, the v1.8.0→v1.8.9 "Android" train, and the desktop-feature lineage v1.1.0→v1.7.1, all on the v1.0.0 production core (see the top "Current release" block + `docs/STATUS.md`). **Never claim any version *later* than v2.2.5 is released** — in particular the joint mobile app-store launch (Google Play + Apple App Store + AltStore PAL + F-Droid) is the future **v2.3.0** (NOT v2.1.0 or v2.2.0 — the entire v2.1.x line and the v2.2.0 "Capstone" milestone have all already shipped, closing the "deepen the existing project" run; the store launch moved out to v2.3.0 — see `to-dos/ROADMAP.md`). Two distinct "v2.0"s exist and must not be conflated, **both now shipped, at different times, for different reasons**: the **engine-lineage v2.0** master-clock work shipped as the **v1.0.0** production core (2026-06-13) — it was the *only* scheduler through v1.10.0. RustyNES's own **v2.0.0 "Timebase"** release (2026-07-03) is a *different* milestone that *replaces* that same dot-lockstep scheduler outright: the **one-clock + every-cycle-bus-access collapse** (a single canonical cycle counter + a split-around-the-access `start_cycle`/`end_cycle` PPU catch-up, mirroring Mesen2's structure), full Vs. `DualSystem` dual-console emulation (core-and-harness-only; frontend wiring deferred), and the breaking save-state / cross-version changes it entailed (ADR 0002 / ADR 0028 / ADR 0029) — the one release that broke byte-identity / save-state compatibility, by design. The R1/R2 hard-tier MMC3 IRQ-timing residual was investigated under a bounded-effort campaign and is by-design-deferred beyond v2.0.0, not closed — see ADR 0002's decision-update section for the mechanism-level finding. - **Forward plans + roadmap live in `to-dos/`.** `to-dos/ROADMAP.md` (updated in #129) is the planning entry point and frames the release line + "the path to v2.0.0 and beyond"; `to-dos/plans/` holds the per-release plan docs (through `v1.7.0-forge-plan.md` on `main`, plus the staged-forward `v1.8.0-android-plan.md` / `v1.9.0-ios-plan.md` / `v2.0.0-master-clock-plan.md`) + the `to-dos/plans/engine-lineage/` history archive + a `to-dos/plans/research/` reference-mining archive. - The v1.0.0 release + GitHub Pages/CI + post-release record is in `docs/v1.0.0-synthesis-handoff-2026-06-13.md` — read it before touching CI, Pages, or release tooling. Full per-release history is in `CHANGELOG.md`. - **Markdownlint is a CI gate** (pre-commit, pinned `markdownlint-cli v0.39.0`). The local `markdownlint` binary is a newer version that reports rules v0.39.0 lacks (e.g. MD060) — those are NOT gated; verify with `pre-commit run markdownlint --all-files`, not the bare binary. `.markdownlint.json` keeps `MD013`/`MD033`/`MD041` disabled by design (long technical tables, the README HTML banner/``, the HTML-led README). `.markdownlintignore` exempts `ref-docs/`, `ref-proj/`, the vendored `tricnes/` + upstream READMEs, and the frozen `docs/archive/` + `to-dos/archive/` trees — don't lint or reformat those. diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c25205e..0a29037f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,62 @@ cycle-accurate core later replaced. ## [Unreleased] +## [2.2.5] - 2026-08-03 - "Colophon" (provenance, licensing, and documentation integrity) + +A **provenance, licensing, and documentation-integrity** release, prompted by +community review of the project's licensing and AI-assisted origins. **Zero +emulation-core behavior changes**, so **AccuracyCoin holds 141/141 (100.00%)**, nestest is +0-diff, and the `#![no_std]` chip stack, save-state / TAS / netplay formats, and +every golden vector are byte-identical to v2.2.4 by construction. + +### Changed + +- **In-source "port" comments corrected.** A full-tree audit found comments that + described implementations of publicly-documented hardware behavior (the CPU + unstable-store opcodes, the PPU sprite-evaluation / OAM models, and numerous + mapper register decoders) as "ports of" copyleft emulators (Mesen2 — GPLv3; + puNES — GPLv2). Those behaviors are implemented from the NESdev wiki, published + datasheets, and the documented 6502 behavior, and were cross-checked against + reference emulators as *oracles*; the comments were reworded to say so. No + GPL-licensed emulator source is incorporated. +- **CRT shaders & NTSC filters reworded.** `crt_royale` / `crt_guest` / `megatron` + and the Bisqwit / EMMIR NTSC filters were reviewed at source level and reframed + from "port / condensation of X" to independent single-pass reimplementations of + the *look and technique* (copyright protects code expression, not a visual look); + no upstream shader source is incorporated. The comment claiming tables were + "ported verbatim from Bisqwit's C" was corrected — those tables encode the + NESdev-documented NES composite signal. +- **`blip.rs`** no longer mislabels `blip_buf` as BSD/MIT (it is LGPL-2.1+); the + file is an independent BLEP implementation and now says so. +- **README** toned down and corrected: added an AI-assistance disclosure, removed + a comparison graphic with inaccurate details, fixed a mislabeled + ("sub-cycle accuracy") screenshot caption, and synced Acknowledgments with + `NOTICE`. + +### Added + +- **`NOTICE` rewritten** to disclose the behavioral-oracle use of GPL emulators + (Mesen2/MesenCE, higan, **GeraNES**, ares, FCEUX, Nestopia UE, puNES — no code + incorporated), attribute the incorporated permissive components (emu2413, + TriCNES, rcheevos — all MIT, with the MIT text), the bundled fonts (Font Awesome; + Press Start 2P / OFL) and test ROMs, and credit the CRT-shader / NTSC-filter + visual influences as independent reimplementations. GeraNES (GPL-3.0-only), cited + across ~58 files, was previously undisclosed. +- **New `docs/originality-and-provenance.md`** — an honest account of where + RustyNES advances, diverges from, or independently re-derives NES emulation + technique, its development timeline, and its full license posture (including that + the project is heavily AI-assisted). +- **Press Start 2P OFL text** added to the Android app assets (it shipped without + the required OFL text; desktop and iOS already carried it). + +### Fixed + +- **`tests/roms/LICENSES.md`** — a false exclusion claim (four Holy Mapperel mapper + ROMs stated as excluded were in fact committed), a stale crate path, and the + AccuracyCoin sub-test count; and added blanket coverage for the committed + directories not individually tabulated (328 committed `.nes` total, none + commercial). + ## [2.2.4] - 2026-07-24 - "Cartridge" (libretro core builds/installs for RetroArch) A **libretro / RetroArch distribution** cut. Its purpose is that the RustyNES diff --git a/Cargo.lock b/Cargo.lock index 922924d4..f08364f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4290,7 +4290,7 @@ dependencies = [ [[package]] name = "rustynes-android" -version = "2.2.4" +version = "2.2.5" dependencies = [ "android-activity", "android_logger", @@ -4308,7 +4308,7 @@ dependencies = [ [[package]] name = "rustynes-apu" -version = "2.2.4" +version = "2.2.5" dependencies = [ "bitflags 2.13.1", "criterion", @@ -4321,7 +4321,7 @@ dependencies = [ [[package]] name = "rustynes-cheevos" -version = "2.2.4" +version = "2.2.5" dependencies = [ "cc", "ureq", @@ -4329,7 +4329,7 @@ dependencies = [ [[package]] name = "rustynes-core" -version = "2.2.4" +version = "2.2.5" dependencies = [ "bitflags 2.13.1", "criterion", @@ -4346,7 +4346,7 @@ dependencies = [ [[package]] name = "rustynes-cpu" -version = "2.2.4" +version = "2.2.5" dependencies = [ "bitflags 2.13.1", "criterion", @@ -4357,7 +4357,7 @@ dependencies = [ [[package]] name = "rustynes-frontend" -version = "2.2.4" +version = "2.2.5" dependencies = [ "anstyle", "arboard", @@ -4411,11 +4411,11 @@ dependencies = [ [[package]] name = "rustynes-gfx-shaders" -version = "2.2.4" +version = "2.2.5" [[package]] name = "rustynes-hdpack" -version = "2.2.4" +version = "2.2.5" dependencies = [ "lewton", "png", @@ -4426,7 +4426,7 @@ dependencies = [ [[package]] name = "rustynes-ios" -version = "2.2.4" +version = "2.2.5" dependencies = [ "bytemuck", "cpal", @@ -4440,7 +4440,7 @@ dependencies = [ [[package]] name = "rustynes-libretro" -version = "2.2.4" +version = "2.2.5" dependencies = [ "libc", "rust-libretro", @@ -4449,7 +4449,7 @@ dependencies = [ [[package]] name = "rustynes-mappers" -version = "2.2.4" +version = "2.2.5" dependencies = [ "bitflags 2.13.1", "criterion", @@ -4461,7 +4461,7 @@ dependencies = [ [[package]] name = "rustynes-mobile" -version = "2.2.4" +version = "2.2.5" dependencies = [ "rustynes-core", "rustynes-hdpack", @@ -4476,14 +4476,14 @@ dependencies = [ [[package]] name = "rustynes-monetization" -version = "2.2.4" +version = "2.2.5" dependencies = [ "uniffi", ] [[package]] name = "rustynes-netplay" -version = "2.2.4" +version = "2.2.5" dependencies = [ "futures-util", "js-sys", @@ -4499,7 +4499,7 @@ dependencies = [ [[package]] name = "rustynes-ppu" -version = "2.2.4" +version = "2.2.5" dependencies = [ "bitflags 2.13.1", "criterion", @@ -4511,14 +4511,14 @@ dependencies = [ [[package]] name = "rustynes-ra" -version = "2.2.4" +version = "2.2.5" dependencies = [ "rustynes-cheevos", ] [[package]] name = "rustynes-script" -version = "2.2.4" +version = "2.2.5" dependencies = [ "mlua", "piccolo", @@ -4529,7 +4529,7 @@ dependencies = [ [[package]] name = "rustynes-test-harness" -version = "2.2.4" +version = "2.2.5" dependencies = [ "insta", "png", diff --git a/Cargo.toml b/Cargo.toml index d3d85516..6daaefe5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ members = [ default-members = ["crates/rustynes-libretro"] [workspace.package] -version = "2.2.4" +version = "2.2.5" edition = "2024" rust-version = "1.96" license = "MIT OR Apache-2.0" diff --git a/NOTICE b/NOTICE index 998741e8..d93fded2 100644 --- a/NOTICE +++ b/NOTICE @@ -15,8 +15,134 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +-------------------------------------------------------------------------------- +Hardware documentation +-------------------------------------------------------------------------------- + This project draws on the Nintendo Entertainment System hardware reverse- engineering work documented at the NESdev community wiki (https://www.nesdev.org/wiki/) and the Visual 6502 / Visual 2C02 projects (http://www.visual6502.org/). No code from those projects is incorporated; the documentation is referenced for hardware behavior specification. + +The chip, mapper, and peripheral behaviors implemented in RustyNES are written +from that public hardware documentation (the NESdev wiki, Disch's mapper +write-ups, Brad Taylor's 2C02 technical reference, published Xicor/Intersil I2C +serial-EEPROM and Yamaha YM2413 datasheets, and the documented 6502 unofficial- +opcode behavior) and pinned against public, permissively-licensed test ROMs +(see tests/roms/LICENSES.md). + +-------------------------------------------------------------------------------- +Reference emulators (behavioral oracles only -- no code incorporated) +-------------------------------------------------------------------------------- + +During development, several existing NES emulators were consulted as behavioral +oracles / accuracy references -- that is, to observe and cross-check documented +hardware behavior when reconciling ambiguous test-ROM results. Among these are +Mesen2 and MesenCE (GPLv3), higan (GPLv3), GeraNES (GPLv3), ares (ISC), and +FCEUX and Nestopia UE and puNES (GPLv2). NO SOURCE +CODE from any GPL-licensed emulator is incorporated into RustyNES; where an +in-source comment references one of these projects it does so only to record +that its runtime behavior was used as a cross-check for a behavior RustyNES +implements independently from the hardware documentation above. + +-------------------------------------------------------------------------------- +Incorporated third-party components (permissively licensed) +-------------------------------------------------------------------------------- + +The following third-party works ARE incorporated (as a Rust port or as vendored +source) under their permissive licenses. Their copyright notices and the MIT +permission notice are reproduced below. + +* emu2413 v1.5.9 -- Yamaha YM2413 (OPLL) FM synthesizer, used for VRC7 audio. + Rust port in crates/rustynes-apu/src/opll.rs. + https://github.com/digital-sound-antiques/emu2413 + Copyright (c) 2020 Mitsutaka Okazaki + +* TriCNES -- transistor-level NES emulator by the AccuracyCoin author; its + PPU address/data-multiplex (ALE / octal-latch), OAM-corruption, and per-cycle + DMA-dispatch models are ported into RustyNES (crates/rustynes-ppu, + rustynes-cpu, rustynes-core), and its full source is vendored as a golden + oracle at crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/. + https://github.com/100thCoin/TriCNES (commit 9199870) + Copyright (c) 2025 Chris Siebert + +* rcheevos v12.3.0 -- RetroAchievements client runtime, vendored at + crates/rustynes-cheevos/vendor/rcheevos/ (compiled only under the optional + `retroachievements` feature). + https://github.com/RetroAchievements/rcheevos + Copyright (c) 2018 RetroAchievements.org + +The Font Awesome Free glyphs bundled with the frontend are covered by their own +license at crates/rustynes-frontend/assets/fonts/LICENSE-FontAwesome.txt. + +All three components above (emu2413, TriCNES, rcheevos) are distributed under the +MIT License: + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +-------------------------------------------------------------------------------- +Bundled fonts +-------------------------------------------------------------------------------- + +* Font Awesome Free -- Fonticons, Inc. -- see + crates/rustynes-frontend/assets/fonts/LICENSE-FontAwesome.txt. +* Press Start 2P -- CodeMan38 -- SIL Open Font License 1.1. The OFL text ships + alongside each copy of the font: assets/RustyNES_Icon/OFL.txt (desktop), + ios/RustyNES/Fonts/PressStart2P-OFL.txt (iOS), and + android/app/src/main/assets/PressStart2P-OFL.txt (Android). + +-------------------------------------------------------------------------------- +Visual influences (independently reimplemented -- no code incorporated) +-------------------------------------------------------------------------------- + +RustyNES's optional CRT shader stack (crates/rustynes-gfx-shaders/) and NTSC +filters (crates/rustynes-frontend/src/ntsc_bisqwit.rs, ntsc_lmp88959.rs) are +independent, single-pass WGSL / Rust reimplementations of the *visual looks and +techniques* of the projects below. No source code from any of them is +incorporated: copyright protects code expression, not a visual look or a +rendering technique, and these are from-scratch shaders on RustyNES's own +uniform / pipeline conventions. They are credited here as the visual influences +they reproduce: + +* CRT-Royale -- TroggleMonkey -- GPLv2+ (libretro) -- CRT phosphor/scanline look +* crt-guest-advanced / guest-dr-venom -- guest.r -- GPL-family (libretro) +* Sony Megatron Colour Video Monitor -- MajorPainInTheCactus -- GPL-family (libretro) +* NTSC-CRT -- EMMIR (LMP88959) -- free use, credit appreciated -- + https://github.com/LMP88959/NTSC-CRT +* Bisqwit's NES composite-NTSC model -- the underlying two-level NES composite + signal is the hardware model documented at the NESdev wiki ("NTSC video"); the + RustyNES tables encode that documented model. + +-------------------------------------------------------------------------------- +Bundled test ROMs +-------------------------------------------------------------------------------- + +The ROMs committed under tests/roms/ are public-domain or permissively-licensed +homebrew test programs, catalogued per-author with their individual licenses in +tests/roms/LICENSES.md. No commercial Nintendo software is bundled. The +permissive works whose licenses require their notices be preserved include: + +* AccuracyCoin -- Chris Siebert (100thCoin) -- MIT -- the upstream MIT LICENSE is + vendored at tests/roms/accuracycoin/LICENSE. +* Holy Mapperel and other Damian Yerrick test ROMs -- zlib -- the upstream + notices are preserved with the ROMs (see tests/roms/LICENSES.md). + +blargg's and kevtris's suites are public domain. See tests/roms/LICENSES.md for +the full per-ROM provenance. diff --git a/README.md b/README.md index e1e627e1..dfbb07f3 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

- Build Status License: MIT OR Apache-2.0 Version Rust: 1.96
+ Build Status License: MIT OR Apache-2.0 Version Rust: 1.96
AccuracyCoin nestest Try in browser
Platform

@@ -24,6 +24,16 @@ scheduling at PPU-dot resolution on a master-clock-precise timebase — clearing two newest upstream PPU tests, "ALE + Read" and "Hybrid Addresses", via the promoted 2-cycle-ALE fetch model — ADR 0030.) +> **Development note — AI-assisted:** RustyNES is heavily AI-assisted software, +> built with LLM tooling under a human-directed, test-driven workflow (public +> test ROMs as the oracle, a `no_std` core, and continuous CI). See +> [`docs/originality-and-provenance.md`](docs/originality-and-provenance.md) for +> what that means for originality and licensing, and the +> [Acknowledgments](#acknowledgments) for the references and components it builds +> on. Accuracy claims are meant to be *checked* by running the public suites, not +> taken on faith; comparisons to other emulators are comparisons, not a claim of +> being "better." + Beyond reference accuracy, RustyNES is a complete, modern emulation platform: **172 mapper families** covering the vast majority of the commercial library (plus a UNIF `.unf` cartridge loader), the full **Famicom Disk System** (real-BIOS boot with a @@ -676,17 +686,25 @@ Read" and "Hybrid Addresses", now pass on the shipped default. > headline unit-test number. When a doc and a passing test ROM disagree, **the ROM > wins** — that is the project's definition of "cycle-accurate." -

- RustyNES Emulator Accuracy Comparison -

+RustyNES's accuracy claims are meant to be *checked*, not taken on faith: run the +public suites yourself (AccuracyCoin, nestest, blargg, Holy Mapperel — see +[Compatibility & Accuracy](#compatibility-and-accuracy)). Any comparison to +another emulator is exactly that — a comparison against a reference RustyNES was +measured against (e.g. Mesen2 / higan / ares — see the [Acknowledgments](#acknowledgments)) — +and is **not** a claim that RustyNES is "better." For an honest +account of where the project advances, diverges from, or independently re-derives +NES emulation technique (and its license posture), see +[`docs/originality-and-provenance.md`](docs/originality-and-provenance.md). -### Sub-cycle accuracy in action +### Super Mario Bros. on RustyNES -The screenshot below shows Super Mario Bros. at first light — correct background -rendering, palette, and timing straight from the master-clock scheduler. +The screenshot below is an early-milestone image — Super Mario Bros. at "first +light," among the first commercial titles to render during development. It +predates much of the current accuracy work and is kept as a representative +gameplay shot, not a claim about any particular sub-system.

- Super Mario Bros. first light on RustyNES + Super Mario Bros. running on RustyNES

--- @@ -775,34 +793,46 @@ and the Material-for-MkDocs documentation handbook at ## Current Release -RustyNES's current release is **v2.2.4 "Cartridge"**, a **libretro / RetroArch -distribution** cut. Its purpose is that the RustyNES core builds and installs -cleanly through the Libretro buildbot -([git.libretro.com/libretro/RustyNES](https://git.libretro.com/libretro/RustyNES)) -so RetroArch users can pull it from the in-app core downloader. - -**Zero emulation-core changes**, so **AccuracyCoin holds 141/141 (100.00%)**, -nestest is 0-diff, and the `#![no_std]` chip stack, save-state / TAS / netplay -formats, and every golden vector are byte-identical to v2.2.3 by construction. -`crates/rustynes-libretro` wraps `rustynes-core`, so it inherits every v2.2.3 -change automatically (the fast dot path default; the `PPU_SNAPSHOT_VERSION` 8 / -APU v4 save-state schema, handled transparently because the serialize path sizes -and emits the *current* snapshot via `Nes::snapshot_core_into`; the -`Mapper::mix_audio` i32 widening; the Zapper model; the `mNNN_` mapper rename), -and both buildbot cross-ABIs the CI gate models — `x86_64-pc-windows-gnu` and -`aarch64-linux-android` — build clean. - -The concrete work is a **`rustynes_libretro.info` metadata correction** (the file -RetroArch's core downloader reads): **`disk_control` `false` → `true`** — the -real fix, since the FDS multi-side Disk Control interface has been wired since -the buildbot recipe landed but was advertised as absent, hiding multi-disk FDS -swapping from RetroArch's Quick Menu; plus `display_version` `v1.0.0` → `v2.2.4` -and the mapper count `168` → `172`. Libretro **core options** (region / overscan -/ palette / accuracy toggles) remain a documented future enhancement -(`core_options = "false"` is accurate, not stale). The Antigravity PR reviewer -standardization onto the shared template rides along. - -It follows **v2.2.3 "Datum"**, a performance and accuracy-closure patch: the +RustyNES's current release is **v2.2.5 "Colophon"**, a **provenance, licensing, +and documentation-integrity** release. It carries **zero emulation-core +behavior changes** — so **AccuracyCoin holds 141/141 (100.00%)**, nestest is 0-diff, and +the `#![no_std]` chip stack, save-state / TAS / netplay formats, and every golden +vector are byte-identical to v2.2.4 by construction. Instead it corrects how the +project *describes its own provenance*, prompted by community review: + +- A full-tree audit reworded in-source comments that had mischaracterized + implementations of publicly-documented hardware behavior as "ports of" copyleft + emulators (Mesen2, puNES). Those behaviors are implemented from the NESdev wiki, + datasheets, and the documented 6502 behavior, and cross-checked against + reference emulators as *oracles* — the comments now say exactly that. +- `NOTICE` was rewritten to disclose that GPL-licensed emulators (Mesen2/MesenCE, + higan, GeraNES, ares, FCEUX, Nestopia UE, puNES) were used only as behavioral + oracles (no code incorporated); to attribute the genuinely incorporated + permissive components (emu2413, TriCNES, rcheevos — all MIT), the bundled fonts, + and the bundled test ROMs; and to credit the CRT-shader / NTSC-filter *visual + influences* (CRT-Royale, crt-guest-advanced, Sony Megatron, EMMIR's NTSC-CRT, + Bisqwit) as independent reimplementations. +- The CRT shaders and NTSC filters were reviewed at source level and reworded from + "port / condensation of X" to independent single-pass reimplementations of the + *look and technique* — copyright protects code expression, not a visual look, and + no upstream shader source is incorporated. +- A new [`docs/originality-and-provenance.md`](docs/originality-and-provenance.md) + gives an honest account of where RustyNES advances, diverges from, or + independently re-derives NES emulation technique, and its license posture — + including that the project is heavily AI-assisted. +- The README (this file), `tests/roms/LICENSES.md`, and related docs were corrected + for accuracy: an AI-assistance disclosure was added, a comparison graphic with + inaccurate details was removed, and a mislabeled screenshot caption was fixed. + +It follows **v2.2.4 "Cartridge"**, a **libretro / RetroArch distribution** cut so +RetroArch users can pull the core from the in-app downloader via the Libretro +buildbot ([git.libretro.com/libretro/RustyNES](https://git.libretro.com/libretro/RustyNES)); +its concrete work was a `rustynes_libretro.info` metadata correction +(**`disk_control` `false` → `true`**, exposing multi-disk FDS swapping in +RetroArch's Quick Menu; `display_version` `v1.0.0` → `v2.2.4`; mapper count +`168` → `172`), with zero emulation-core changes. + +Earlier, **v2.2.3 "Datum"**, a performance and accuracy-closure patch: the specialized PPU fast dot path (**−11.3%** on rendering-heavy content, differential-tested bit-identical since v2.1.8) promoted to the **default** and exposed to users for the first time; PGO-optimized Linux release binaries when @@ -959,6 +989,19 @@ RustyNES stands on the shoulders of giants: - **[RetroAchievements](https://retroachievements.org/)** and the **[`rcheevos`](https://github.com/RetroAchievements/rcheevos)** library that powers the achievement integration. +- **[emu2413](https://github.com/digital-sound-antiques/emu2413)** (Mitsutaka + Okazaki, MIT) — the YM2413 / OPLL model behind VRC7 audio — and + **[TriCNES](https://github.com/100thCoin/TriCNES)** (Chris Siebert, MIT), the + transistor-level emulator whose PPU / DMA models RustyNES ports and also uses as + a golden oracle. **GeraNES**, FCEUX, Nestopia UE, and puNES served as additional + behavioral oracles. +- The community CRT shaders and NTSC filters whose *looks* RustyNES independently + reimplements — **CRT-Royale** (TroggleMonkey), **crt-guest-advanced** (guest.r), + **Sony Megatron** (MajorPainInTheCactus), + **[NTSC-CRT](https://github.com/LMP88959/NTSC-CRT)** (EMMIR), and **Bisqwit**'s + NES composite model — plus the **Press Start 2P** (OFL) and **Font Awesome** + fonts. Full attribution and the complete license posture are in + [`NOTICE`](NOTICE). --- @@ -971,7 +1014,7 @@ If you use RustyNES in academic research, please cite: author = {RustyNES Contributors}, title = {RustyNES: A Cycle-Accurate NES Emulator in Rust}, year = {2026}, - version = {2.2.0}, + version = {2.2.5}, url = {https://github.com/doublegate/RustyNES}, note = {Cycle-accurate NES emulator on a master-clock-precise scheduler; AccuracyCoin 100\% (141/141), nestest 0-diff; 172 mapper families, diff --git a/android/app/src/main/assets/PressStart2P-OFL.txt b/android/app/src/main/assets/PressStart2P-OFL.txt new file mode 100644 index 00000000..d836cf59 --- /dev/null +++ b/android/app/src/main/assets/PressStart2P-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2012 The Press Start 2P Project Authors (cody@zone38.net), with Reserved Font Name "Press Start 2P". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/crates/rustynes-apu/src/blip.rs b/crates/rustynes-apu/src/blip.rs index 9a33ac80..448b563f 100644 --- a/crates/rustynes-apu/src/blip.rs +++ b/crates/rustynes-apu/src/blip.rs @@ -6,8 +6,11 @@ //! per-CPU-cycle mixer output and produces band-limited samples at the //! host audio rate (default 44.1 kHz). //! -//! The technique is the same one used by Shay Green's `blip_buf` (BSD/MIT) -//! and Mesen2's mixer: +//! The technique is band-limited step (BLEP) synthesis — the same general +//! approach popularized by Shay Green's `blip_buf` and used by many emulators. +//! This is an independent implementation (our polyphase kernel in +//! [`crate::blip_kernel`] uses a finer 32-phase resolution than `blip_buf`); no +//! `blip_buf` code is incorporated: //! //! - Pre-compute a polyphase windowed-sinc kernel ([`crate::blip_kernel`]) //! keyed by `PHASES = 32` sub-output-sample fractional offsets, with diff --git a/crates/rustynes-apu/src/opll.rs b/crates/rustynes-apu/src/opll.rs index c7b8b220..9647e96e 100644 --- a/crates/rustynes-apu/src/opll.rs +++ b/crates/rustynes-apu/src/opll.rs @@ -14,16 +14,16 @@ //! //! # Algorithmic reference //! -//! - `/home/parobek/Code/OSS_Public-Projects/RustyNES/ref-proj/Mesen2/Core/Shared/Utilities/emu2413.{h,cpp}` -//! — the canonical C source (Mesen2 vendors it verbatim from upstream) +//! - `emu2413 v1.5.9` (, +//! MIT, Mitsutaka Okazaki) — the canonical upstream MIT C source //! - nesdev wiki `VRC7_audio.md` — register surface + chip-level behaviour //! - nesdev wiki `User_Ben_Boldt_YM2413_Patches.md` — patch ROM analysis //! //! # License posture //! -//! emu2413 is MIT-licensed at upstream; this Rust port is a clean-room -//! reimplementation guided by the C source's algorithm. We preserve -//! the upstream MIT notice in `NOTICE` at the repo root (see ADR-0005). +//! emu2413 is MIT-licensed at upstream; this is a pure-Rust port of that C +//! source, distributed under the same MIT license. We preserve the upstream +//! MIT notice in `NOTICE` at the repo root (see ADR-0006). //! //! # Determinism //! diff --git a/crates/rustynes-cpu/src/cpu.rs b/crates/rustynes-cpu/src/cpu.rs index 07565c69..9a792769 100644 --- a/crates/rustynes-cpu/src/cpu.rs +++ b/crates/rustynes-cpu/src/cpu.rs @@ -865,9 +865,16 @@ impl Cpu { /// SH* unstable-store family helper (`SHA / SHX / SHY / SHS / TAS`, /// opcodes `$9F / $93 / $9E / $9C / $9B`). /// - /// Faithful port of Mesen2's `SyaSxaAxa` (`Core/NES/NesCpu.h` lines - /// 716-745). Implements the canonical 6502-derivative - /// unstable-store algorithm: + /// Implements the canonical 6502 unstable-store (SH*) algorithm as + /// documented by the `NESdev` community (the "unstable"/"highbyte" store + /// opcodes: `value AND (high-byte-of-address + 1)`, with the RDY/DMA + /// quirk) and pinned bit-for-bit by `AccuracyCoin`'s "Unofficial + /// Instructions: SH*" sub-test. This is an independent Rust + /// implementation of that documented behavior — the DMC-DMA + /// interruption detection below uses the emulator's own bus cycle-count + /// machinery. (Behavior cross-checked against reference emulators as + /// accuracy oracles; no third-party emulator code is incorporated.) + /// The algorithm: /// /// 1. Compute the page-crossed flag against `base + index_reg`. /// 2. Perform a dummy read at the **unfixed** address diff --git a/crates/rustynes-frontend/src/debugger/source_map.rs b/crates/rustynes-frontend/src/debugger/source_map.rs index 07c42e6d..e9790c38 100644 --- a/crates/rustynes-frontend/src/debugger/source_map.rs +++ b/crates/rustynes-frontend/src/debugger/source_map.rs @@ -22,7 +22,8 @@ //! For every `line` record we resolve each referenced span to its CPU address //! range and record `address → (file, line)` for every byte in range. Lines //! with no spans (e.g. macro / comment lines) carry no address and are skipped. -//! This mirrors Mesen2's `DbgImporter`/`NesDbgImporter`. +//! This is an independent importer for the same ca65/cc65 `.dbg` debug-info file +//! format that other emulators' symbol importers also read. //! //! ## Output-only //! diff --git a/crates/rustynes-frontend/src/ntsc_bisqwit.rs b/crates/rustynes-frontend/src/ntsc_bisqwit.rs index ba13eabb..1cc058a4 100644 --- a/crates/rustynes-frontend/src/ntsc_bisqwit.rs +++ b/crates/rustynes-frontend/src/ntsc_bisqwit.rs @@ -1,8 +1,8 @@ #![allow( clippy::too_many_arguments, clippy::doc_markdown, - // Numeric tables are ported verbatim from Bisqwit's C; the integer casts - // are intentional truncation (matching the `(int)` / `(int8_t)` casts). + // Numeric tables encode the documented NES composite-signal levels; the + // integer casts are intentional truncation (matching the fixed-point model). clippy::cast_possible_truncation, clippy::cast_precision_loss, clippy::cast_sign_loss, @@ -12,9 +12,11 @@ //! True composite NES_NTSC filter — Bisqwit's algorithm on the GPU (T-110-A1, //! stage 2/2). //! -//! Unlike the simplified [`crate::ntsc`] blur, this is a faithful port of -//! Bisqwit's `nes_ntsc`-style composite model (as implemented by Mesen2's -//! `BisqwitNtscFilter`): it reconstructs the analog luma+chroma **signal** from +//! Unlike the simplified [`crate::ntsc`] blur, this is an independent +//! implementation of the Bisqwit-style NES composite model — the two-level NES +//! composite signal documented at the NESdev wiki ("NTSC video") page +//! (cross-checked against reference emulators as oracles; no code incorporated): +//! it reconstructs the analog luma+chroma **signal** from //! the PPU's per-pixel palette index, then demodulates it back to RGB with a //! windowed Y/I/Q filter. The genuine NTSC artifacts (chroma dot-crawl, colour //! fringing on vertical edges, the diagonal "checkerboard" on saturated hues) @@ -203,7 +205,7 @@ var SIGNAL_HIGH: array = array({signal_high}); var SINE: array = array({sine}); var EMPHASIS: array = array({emphasis}); -// Base YIQ matrix scalars (Bisqwit / Mesen). The live contrast / saturation +// Base YIQ matrix scalars (the standard FCC NTSC YIQ->RGB matrix). The live contrast / saturation // knobs scale these per frame; at knob = 0 the integer matrix below equals the // old baked Y/IR/QR/... constants exactly (verified in f32). const CONTRAST_BASE: f32 = 167941.0; diff --git a/crates/rustynes-frontend/src/ntsc_lmp88959.rs b/crates/rustynes-frontend/src/ntsc_lmp88959.rs index 88bf0f91..64052cb3 100644 --- a/crates/rustynes-frontend/src/ntsc_lmp88959.rs +++ b/crates/rustynes-frontend/src/ntsc_lmp88959.rs @@ -2,9 +2,11 @@ //! LMP88959-style composite NTSC/PAL filter — wgsl post-pass (v1.6.0 "Studio" I1). //! -//! A self-contained composite-NTSC look modelled on EMMIR's well-known -//! `NTSC-CRT` / `LMP88959` algorithm (a single-pass encode-then-decode of the -//! RGB image through a simulated composite signal). Unlike the Bisqwit +//! A self-contained composite-NTSC look — an independent implementation modelled +//! on the technique of EMMIR's (LMP88959) well-known `NTSC-CRT` project +//! (; free use, credit appreciated; no +//! code incorporated) — a single-pass encode-then-decode of the RGB image +//! through a simulated composite signal. Unlike the Bisqwit //! [`crate::ntsc_bisqwit`] filter — which consumes the `R16Uint` palette-index //! texture and must be the *first* pass — this one is a pure **RGBA post-pass**: //! it samples the already-rendered framebuffer, so it composes anywhere in the diff --git a/crates/rustynes-gfx-shaders/src/bisqwit.wgsl b/crates/rustynes-gfx-shaders/src/bisqwit.wgsl index b3180976..d33620f8 100644 --- a/crates/rustynes-gfx-shaders/src/bisqwit.wgsl +++ b/crates/rustynes-gfx-shaders/src/bisqwit.wgsl @@ -17,7 +17,7 @@ var SIGNAL_HIGH: array = array(38, 38, 38, 38, 38, var SINE: array = array(0, 3, 6, 8, 6, 3, 0, -3, -6, -8, -6, -4, 0, 4, 6, 8, 6, 3, 0, -3, -6, -8, -6, -4, 0, 4, 6); var EMPHASIS: array = array(0, 63, 1008, 1023, 3843, 3903, 4083, 4095); -// Base YIQ matrix scalars (Bisqwit / Mesen). The live contrast / saturation +// Base YIQ matrix scalars (the standard FCC NTSC YIQ->RGB matrix). The live contrast / saturation // knobs scale these per frame; at knob = 0 the integer matrix below equals the // old baked Y/IR/QR/... constants exactly (verified in f32). const CONTRAST_BASE: f32 = 167941.0; diff --git a/crates/rustynes-gfx-shaders/src/crt_guest.wgsl b/crates/rustynes-gfx-shaders/src/crt_guest.wgsl index e1a447a0..a7e9ae7f 100644 --- a/crates/rustynes-gfx-shaders/src/crt_guest.wgsl +++ b/crates/rustynes-gfx-shaders/src/crt_guest.wgsl @@ -1,9 +1,11 @@ -// crt-guest-advanced / guest-dr-venom (single-pass WGSL port) — v2.1.9 (B6). +// crt-guest-advanced-style look (single-pass WGSL) — v2.1.9 (B6). // -// A single-pass condensation of guest.r's crt-guest-advanced / guest-dr-venom -// libretro slang shaders. Those are a large multi-pass stack (linearize, two -// blur passes for glow + halation, the scanline/mask pass, an AfterGlow pass); -// this port keeps guest's characteristic look in one fragment shader: +// An independent single-pass reimplementation of the *look* popularized by +// guest.r's crt-guest-advanced / guest-dr-venom libretro shaders (GPL-family). +// No upstream shader source is used or incorporated: those are a large +// multi-pass stack (linearize, two blur passes for glow + halation, the +// scanline/mask pass, an AfterGlow pass); this is a from-scratch single +// fragment shader implementing comparable, widely-documented techniques: // // * A sharp horizontal beam profile (guest's "beam shape" — a configurable // scan-width with a controllable inner/outer falloff) rather than the pure diff --git a/crates/rustynes-gfx-shaders/src/crt_royale.wgsl b/crates/rustynes-gfx-shaders/src/crt_royale.wgsl index 6126a18c..345cb480 100644 --- a/crates/rustynes-gfx-shaders/src/crt_royale.wgsl +++ b/crates/rustynes-gfx-shaders/src/crt_royale.wgsl @@ -1,11 +1,12 @@ -// CRT-Royale (single-pass WGSL port) — v2.1.9 "Presentation & Signal" (B6). +// CRT-Royale-style look (single-pass WGSL) — v2.1.9 "Presentation & Signal" (B6). // -// A faithful *single-pass* condensation of TroggleMonkey's libretro CRT-Royale -// slang preset. The reference is a multi-pass pipeline (bloom/blur passes + -// scanline + phosphor mask + halation + geometry); this port folds its core -// perceptual model into one fullscreen fragment shader so it slots into the -// existing RustyNES post-pass pipeline (same rect/crop letterbox convention as -// CRT_WGSL). It keeps CRT-Royale's defining pieces: +// An independent single-pass reimplementation of the *perceptual model* made +// famous by TroggleMonkey's libretro CRT-Royale (GPLv2+). No CRT-Royale source +// is used or incorporated: that reference is a large multi-pass pipeline +// (bloom/blur passes + scanline + phosphor mask + halation + geometry), whereas +// this is a from-scratch single fullscreen fragment shader on RustyNES's own +// rect/crop uniform convention (same as CRT_WGSL), implementing the standard, +// widely-documented CRT-simulation techniques that produce a comparable look: // // * Gaussian scanline beam in gamma-linear space (per-source-row beam with a // configurable standard deviation, so bright rows bloom wider than dark diff --git a/crates/rustynes-gfx-shaders/src/crt_stack.rs b/crates/rustynes-gfx-shaders/src/crt_stack.rs index d089c49b..6b9d5bb7 100644 --- a/crates/rustynes-gfx-shaders/src/crt_stack.rs +++ b/crates/rustynes-gfx-shaders/src/crt_stack.rs @@ -14,20 +14,21 @@ //! These are opt-in: the shipped default presentation (plain blit / the existing //! CRT) is unchanged, so the default framebuffer stays byte-identical. -/// CRT-Royale — single-pass WGSL port. +/// CRT-Royale-style look — independent single-pass WGSL reimplementation. /// /// Gaussian luminance-scaled beam, selectable phosphor mask, gamma-correct /// scanlines, barrel curvature. See the file header for the model and the /// shared CRT-stack uniform layout. pub const CRT_ROYALE_WGSL: &str = include_str!("crt_royale.wgsl"); -/// crt-guest-advanced / guest-dr-venom — single-pass WGSL port (power-shaped -/// beam, halation glow, selectable mask, curvature). +/// crt-guest-advanced-style look — independent single-pass WGSL reimplementation +/// (power-shaped beam, halation glow, selectable mask, curvature). pub const CRT_GUEST_WGSL: &str = include_str!("crt_guest.wgsl"); -/// Sony Megatron — single-pass WGSL port (per-subpixel phosphor lighting, -/// selectable mask, gamma-correct beam, an HDR headroom hook with SDR Reinhard -/// tone-map fallback). +/// Sony-Megatron-style look — independent single-pass WGSL reimplementation. +/// +/// Per-subpixel phosphor lighting, selectable mask, gamma-correct beam, an HDR +/// headroom hook with SDR Reinhard tone-map fallback. pub const MEGATRON_WGSL: &str = include_str!("megatron.wgsl"); /// Raw NTSC signal-decode pass (P4). diff --git a/crates/rustynes-gfx-shaders/src/lib.rs b/crates/rustynes-gfx-shaders/src/lib.rs index 2ff64fb1..84c3f451 100644 --- a/crates/rustynes-gfx-shaders/src/lib.rs +++ b/crates/rustynes-gfx-shaders/src/lib.rs @@ -245,8 +245,10 @@ fn fs_main(in: VsOut) -> @location(0) vec4 { } "; -/// The Bisqwit composite NES NTSC post-pass (a faithful port of Bisqwit's -/// `nes_ntsc` signal model). +/// The Bisqwit-style composite NES NTSC post-pass. +/// +/// An independent implementation of the NES composite signal model documented at +/// the NESdev wiki ("NTSC video"); no third-party emulator code is incorporated. /// /// Unlike CRT/LMP it samples the **palette-index** framebuffer as an `R16Uint` /// texture (`@group(0) @binding(0) idx_tex`), not the RGBA, plus the per-frame NTSC diff --git a/crates/rustynes-gfx-shaders/src/megatron.wgsl b/crates/rustynes-gfx-shaders/src/megatron.wgsl index 0e9935cd..6fd917cb 100644 --- a/crates/rustynes-gfx-shaders/src/megatron.wgsl +++ b/crates/rustynes-gfx-shaders/src/megatron.wgsl @@ -1,11 +1,13 @@ -// Sony Megatron (single-pass WGSL port) — v2.1.9 "Presentation & Signal" (B6). +// Sony-Megatron-style look (single-pass WGSL) — v2.1.9 "Presentation & Signal" (B6). // -// A port of MajorPainInTheCactus's "Sony Megatron Colour Video Monitor" slang -// shader. Megatron's defining idea is a physically-scaled phosphor subpixel +// An independent reimplementation of the *approach* of MajorPainInTheCactus's +// "Sony Megatron Colour Video Monitor" libretro shader (GPL-family); no upstream +// shader source is used or incorporated. Megatron's defining idea is a +// physically-scaled phosphor subpixel // model driven for HDR displays: it lights individual R/G/B phosphors within a // selectable mask and scales brightness to an absolute nits target so an HDR // swapchain reproduces CRT peak brightness. WGSL/wgpu here targets an SDR -// swapchain by default, so this port keeps Megatron's *structure* — per-subpixel +// swapchain by default, so this reimplementation keeps Megatron's *structure* — per-subpixel // phosphor lighting, mask selection, gamma-correct scanline beam, and an // exposed peak/paper-white ratio — but tone-maps the result back into [0,1] // with a Reinhard curve (`aux.w` controls the HDR headroom the tone-map diff --git a/crates/rustynes-libretro/rustynes_libretro.info b/crates/rustynes-libretro/rustynes_libretro.info index 58bc8f5b..a31bc986 100644 --- a/crates/rustynes-libretro/rustynes_libretro.info +++ b/crates/rustynes-libretro/rustynes_libretro.info @@ -5,7 +5,7 @@ supported_extensions = "nes|fds" corename = "RustyNES" license = "MIT OR Apache-2.0" permissions = "" -display_version = "v2.2.4" +display_version = "v2.2.5" categories = "Emulator" # Hardware Information diff --git a/crates/rustynes-mappers/src/fds.rs b/crates/rustynes-mappers/src/fds.rs index 2f2419a0..2671c72a 100644 --- a/crates/rustynes-mappers/src/fds.rs +++ b/crates/rustynes-mappers/src/fds.rs @@ -196,7 +196,9 @@ pub const HEAD_SEEK_BYTES_PER_CYCLE: u32 = 8; /// long so the BIOS re-read loop always observes the not-ready -> ready edge. pub const HEAD_SEEK_SETTLE_CYCLES: u32 = 512; -/// Per-game FDS timing quirk, modelled on `puNES` `fds.c`'s per-CRC drive table. +/// Per-game FDS timing quirk: a per-CRC drive-timing table (the concept +/// cross-checked against `puNES` as an oracle; no third-party emulator code is +/// incorporated). /// /// A small, additive set of knobs keyed off the disk-image CRC-32 (see /// [`quirk_for_crc`]). Most titles run on the nominal timing and have no entry; @@ -238,7 +240,7 @@ pub fn quirk_for_crc(crc: u32) -> FdsQuirk { // position model (the [`HEAD_RESEEK_CYCLES`] re-seek window opened on every // motor-restart rewind) is what actually closes the Kid Icarus side-B // post-registration replay — that fix is title-independent and needs no - // table entry. This table is the puNES-`fds.c`-style *framework* for the + // table entry. This table is a per-CRC *framework* (of the kind puNES uses) for the // residual minority of titles whose replay loop wants extra not-ready slack // beyond the nominal window. // diff --git a/crates/rustynes-mappers/src/kaiser.rs b/crates/rustynes-mappers/src/kaiser.rs index e43e481b..2d35169d 100644 --- a/crates/rustynes-mappers/src/kaiser.rs +++ b/crates/rustynes-mappers/src/kaiser.rs @@ -606,7 +606,9 @@ kaiser_ctor!( // Per-1 KiB CHR low/high registers ($B000-$E00C), a CHR-RAM escape (CHR reg // value 4/5 + a force-ROM toggle on slot 0 via $88/$C8), two 8 KiB PRG selects // ($8010/$A010), $9400 mirroring, and a /114-scaled CPU-cycle IRQ ($F000 etc.). -// Ported from Mesen2 Waixing/Mapper253.h. +// Register map per the NESdev wiki mapper-253 documentation (cross-checked +// against reference emulators as accuracy oracles; no third-party emulator +// code is incorporated). // =========================================================================== #[cfg(test)] diff --git a/crates/rustynes-mappers/src/lib.rs b/crates/rustynes-mappers/src/lib.rs index 4936a20c..28436656 100644 --- a/crates/rustynes-mappers/src/lib.rs +++ b/crates/rustynes-mappers/src/lib.rs @@ -1164,8 +1164,9 @@ pub fn parse(bytes: &[u8]) -> Result<(Cartridge, Box), RomError> { // --- v1.6.0 "Studio" Workstream E, best-effort (Tier-2): J.Y. Company // ASIC. One silicon implementation behind three iNES mapper numbers; // 90 inhibits the ROM-nametable / extended-mirroring feature, 209 - // register-enables it, 211 forces it on. Ported from the nesdev - // "J.Y. Company ASIC" page + Mesen2 `JyCompany`. Register-decode + + // register-enables it, 211 forces it on. Implemented from the nesdev + // "J.Y. Company ASIC" page (cross-checked against Mesen2 as an oracle). + // Register-decode + // save-state unit-tested only, NOT accuracy-gated (`tier.rs`). 90 => Box::new( JyAsic::new(prg_rom, chr_rom, h.mirroring, JyBoard::M90) diff --git a/crates/rustynes-mappers/src/m016_bandai_fcg.rs b/crates/rustynes-mappers/src/m016_bandai_fcg.rs index 1150c5f2..888dc3af 100644 --- a/crates/rustynes-mappers/src/m016_bandai_fcg.rs +++ b/crates/rustynes-mappers/src/m016_bandai_fcg.rs @@ -36,8 +36,10 @@ //! # EEPROM //! //! An I²C state machine ([`Eeprom`]) for the X24C01 (159) / 24C02 (16) is -//! implemented below — a faithful port of the Mesen2 `Eeprom24C01` / -//! `Eeprom24C02` models. It clocks bits on the SCL **rising** edge and +//! implemented below — an independent state machine for the Xicor/Intersil +//! X24C01 (mapper 159) / 24C02 (mapper 16) serial EEPROMs, written from the +//! published I2C serial-EEPROM datasheet protocol. It clocks bits on the SCL +//! **rising** edge and //! advances the mode/ACK handshake on the **falling** edge, detects //! START/STOP as SDA transitions while SCL is held high, and honors the two //! chips' differing bit order (X24C01 LSB-first, 24C02 MSB-first) and @@ -114,8 +116,9 @@ impl FcgVariant { /// Serial I²C EEPROM (X24C01 / 24C02) state machine. /// -/// Faithful port of the Mesen2 `Eeprom24C01` / `Eeprom24C02` models -/// (`ref-proj/Mesen2/Core/NES/Mappers/Bandai/`). The protocol is driven on +/// An independent I2C state machine for the X24C01 / 24C02 serial EEPROMs, +/// implemented from the published I2C serial-EEPROM datasheet protocol. The +/// protocol is driven on /// **both** SCL edges: bits are clocked on the rising edge, and the /// mode/ACK handshake advances on the falling edge — exactly how the boards /// drive the line. START / STOP are detected as SDA transitions while SCL is diff --git a/crates/rustynes-mappers/src/m035_jy_asic.rs b/crates/rustynes-mappers/src/m035_jy_asic.rs index 54b8c66d..d98f97ac 100644 --- a/crates/rustynes-mappers/src/m035_jy_asic.rs +++ b/crates/rustynes-mappers/src/m035_jy_asic.rs @@ -299,15 +299,16 @@ impl JyAsic { /// Apply the PRG bank-number reversal used by PRG mode 3 (`$D000` bits /// 0-1 == 3). /// - /// The wiki describes this as "bank numbers bits 0-6 reversed". This is a - /// verbatim port of Mesen2's `InvertPrgBits`, which reverses the three - /// outer bit pairs (0<->6, 1<->5, 2<->4) and notably does **not** carry - /// bit 3 through: a faithful "reverse a 7-bit field" would leave the - /// centre bit (3) in place, but neither Mesen2 nor Disch's original - /// writeup preserves it, so this port drops it to match the accuracy - /// reference bit-for-bit (no known game distinguishes the two; the JY - /// ASIC is BestEffort tier). If a future test ROM proves bit 3 must be - /// preserved, OR `reg & 0x08` back into the result here. + /// The NESdev wiki / Disch's JY-ASIC writeup describe this as "bank + /// numbers bits 0-6 reversed". Following that documentation we reverse the + /// three outer bit pairs (0<->6, 1<->5, 2<->4) and notably do **not** carry + /// bit 3 through: a literal "reverse a 7-bit field" would leave the centre + /// bit (3) in place, but Disch's writeup does not preserve it, so we drop it + /// to match the documented hardware bit-for-bit (no known game distinguishes + /// the two; the JY ASIC is BestEffort tier). If a future test ROM proves bit + /// 3 must be preserved, OR `reg & 0x08` back into the result here. (Behavior + /// cross-checked against reference emulators as accuracy oracles; no + /// third-party emulator code is incorporated.) const fn invert_prg_bits(reg: u8, invert: bool) -> u8 { if invert { (reg & 0x01) << 6 diff --git a/crates/rustynes-mappers/src/m069_sunsoft_fme7.rs b/crates/rustynes-mappers/src/m069_sunsoft_fme7.rs index 5063ef10..84058c4b 100644 --- a/crates/rustynes-mappers/src/m069_sunsoft_fme7.rs +++ b/crates/rustynes-mappers/src/m069_sunsoft_fme7.rs @@ -112,10 +112,11 @@ const SUNSOFT5B_DC_BIAS: i32 = 0; /// lets the shape stay pinned by its own unit test while the level is pinned /// by a ROM oracle. /// -/// **Target, derived from Mesen2 (the project's accuracy bar) rather than from -/// our own prior numbers.** In `NesSoundMixer::GetOutputVolume` a full-volume +/// **Target, calibrated against Mesen2 (the project's accuracy bar) as an +/// oracle rather than against our own prior numbers.** Using the standard +/// blargg nonlinear-mixer approximation (nesdev "APU Mixer"), a full-volume /// 2A03 square is `(95.88 * 5000) / (8128/15 + 100) = 746.9` units, and the 5B -/// is summed with weight `* 15` over `Sunsoft5bAudio::_volumeLut` +/// is summed with weight `* 15` over the documented 5B log-DAC volume table /// (`= (uint8_t)1.1885^(2i)`, so `LUT[12] = 63`, `LUT[15] = 177`). The /// `db_5b` ROM compares a **volume-12** 5B square against that square: /// diff --git a/crates/rustynes-mappers/src/m176_bmc_fk23c.rs b/crates/rustynes-mappers/src/m176_bmc_fk23c.rs index ef1e0ac9..f2bf5edf 100644 --- a/crates/rustynes-mappers/src/m176_bmc_fk23c.rs +++ b/crates/rustynes-mappers/src/m176_bmc_fk23c.rs @@ -95,7 +95,9 @@ fn check_prg(prg: &[u8], id: u16) -> Result<(), MapperError> { // an outer-bank / extended-MMC3 / CNROM-CHR mode. This is the // register-decode-faithful BestEffort port: the MMC3 PRG/CHR layout plus the // FK23C $5000 banking modes (0-2 MMC3, 3 = 32 KiB, 4 = whole-256 KiB) and the -// $5001/$5002 outer PRG/CHR base bits. Ported from Mesen2 Waixing/Fk23C.h. +// $5001/$5002 outer PRG/CHR base bits. Register map per the NESdev wiki FK23C / +// mapper-176 documentation (cross-checked against reference emulators as +// accuracy oracles; no third-party emulator code is incorporated). // =========================================================================== /// Waixing FK23C 8/16 Mbit BMC ASIC (mapper 176). @@ -541,11 +543,12 @@ pub fn new_m176( // =========================================================================== // Coolboy (mapper 268) — COOLBOY / MINDKIDS MMC3-clone. // -// An MMC3 core wrapped by four $6000-$7FFF outer-bank registers (_exRegs[0..3]) -// that supply PRG/CHR base bits + a wider/narrower mask + an extended-bank mode -// (_exRegs[3] & 0x10). This is the register-decode-faithful BestEffort port of -// the FCEUX/Mesen2 banking transforms. Ported from -// Mesen2 Mmc3Variants/MMC3_Coolboy.h. +// An MMC3 core wrapped by four $6000-$7FFF outer-bank registers that supply +// PRG/CHR base bits + a wider/narrower mask + an extended-bank mode. The +// COOLBOY/MINDKIDS banking transforms are a register-decode BestEffort model +// implemented from the nesdev wiki COOLBOY / mapper-268 board notes +// (cross-checked against FCEUX/Mesen2 as behavioral oracles; no third-party +// emulator code is incorporated). // =========================================================================== #[cfg(test)] diff --git a/crates/rustynes-mappers/src/m268_bmc_coolboy.rs b/crates/rustynes-mappers/src/m268_bmc_coolboy.rs index 7f89899c..a5244f0b 100644 --- a/crates/rustynes-mappers/src/m268_bmc_coolboy.rs +++ b/crates/rustynes-mappers/src/m268_bmc_coolboy.rs @@ -447,7 +447,9 @@ pub fn new_m268( // // A plain MMC3 core with a PRG-A19/A20 outer bank from the high two bits of // $8001 (captured when the selected register is < 6), forced into the top of -// the address space. CHR is RAM. Ported from Mesen2 Sachen/Sachen9602.h. +// the address space. CHR is RAM. Register map per the NESdev wiki CoolBoy / +// mapper-268 documentation (cross-checked against reference emulators as +// accuracy oracles; no third-party emulator code is incorporated). // =========================================================================== #[cfg(test)] diff --git a/crates/rustynes-mappers/src/m513_sachen_9602.rs b/crates/rustynes-mappers/src/m513_sachen_9602.rs index cbfdc193..af97b171 100644 --- a/crates/rustynes-mappers/src/m513_sachen_9602.rs +++ b/crates/rustynes-mappers/src/m513_sachen_9602.rs @@ -340,7 +340,9 @@ pub fn new_m513( // =========================================================================== // TxcChip — the TXC protection accumulator (shared by Sachen 3011 / m136). -// Ported from Mesen2 Txc/TxcChip.h (the non-JV001 variant, mask 0x07). +// The non-JV001 variant (mask 0x07), per the NESdev wiki TXC / mapper-136 +// documentation (cross-checked against reference emulators as accuracy oracles; +// no third-party emulator code is incorporated). // =========================================================================== #[cfg(test)] diff --git a/crates/rustynes-mappers/src/mmc3_clones.rs b/crates/rustynes-mappers/src/mmc3_clones.rs index dbcf3604..2e138d41 100644 --- a/crates/rustynes-mappers/src/mmc3_clones.rs +++ b/crates/rustynes-mappers/src/mmc3_clones.rs @@ -776,7 +776,9 @@ clone_ctor!( // 8259C: shift 2, chrOr [1,2,3] (mapper 139) // reg7 bits 1-2 select mirroring (reg7 bit 0 = "simple mode" override). // reg5 selects the 32 KiB PRG bank; reg4 supplies the CHR high bits. -// Ported from Mesen2 Sachen/Sachen8259.h. +// Register map per the NESdev wiki Sachen 8259 (mappers 138/139/141) +// documentation (cross-checked against reference emulators as accuracy oracles; +// no third-party emulator code is incorporated). // =========================================================================== #[cfg(test)] diff --git a/crates/rustynes-mappers/src/multicart_discrete.rs b/crates/rustynes-mappers/src/multicart_discrete.rs index 6f2658ba..fe11a095 100644 --- a/crates/rustynes-mappers/src/multicart_discrete.rs +++ b/crates/rustynes-mappers/src/multicart_discrete.rs @@ -3797,7 +3797,9 @@ pub fn new_m204( // `mode & 0x02` the board is in UNROM-style mode (a switchable $8000 + a fixed // $C000), with a NROM-256 sub-case when `mode & 0x0100`; otherwise both 16 KiB // windows mirror the same NROM bank. `mode & 0x01` flips the mirroring. CHR is a -// single fixed 8 KiB window. Ported from Mesen2 Ntdec/Mapper221.h. +// single fixed 8 KiB window. Register map per the NESdev wiki mapper-299 / +// BMC-11160 documentation (cross-checked against reference emulators as +// accuracy oracles; no third-party emulator code is incorporated). // =========================================================================== /// TXC/BMC-11160 multicart (mapper 299). diff --git a/crates/rustynes-mappers/src/ntdec.rs b/crates/rustynes-mappers/src/ntdec.rs index f97935b1..514341bd 100644 --- a/crates/rustynes-mappers/src/ntdec.rs +++ b/crates/rustynes-mappers/src/ntdec.rs @@ -861,8 +861,10 @@ fn chr_or_ram(chr_rom: Box<[u8]>) -> (Box<[u8]>, bool) { // to the final three banks; $8000 is the one switchable window (register 3). // CHR: 2 KiB pages. Register 0 selects a paired 2 KiB window into the first two // slots ($0000 + $0800), register 1 the third ($1000), register 2 the fourth -// ($1800). Registers live at $6000-$7FFF (addr & 3). Ported from Mesen2 -// Ntdec/NtdecTc112.h. +// ($1800). Registers live at $6000-$7FFF (addr & 3). Register map per the +// NESdev wiki NTDEC TC-112 / mapper-193 documentation (cross-checked against +// reference emulators as accuracy oracles; no third-party emulator code is +// incorporated). // =========================================================================== /// NTDEC TC-112 (mapper 193). @@ -1046,8 +1048,9 @@ pub fn new_m193( // The written *address* low bits select the layout: `bitMask = addr & 0x06` // gives the 16 KiB PRG block, and (when bitMask != 0x06) `addr & 1` picks the // inner half. Both PRG windows ($8000 + $C000) and the 8 KiB CHR window track -// the decoded page; `addr & 0x10` flips the mirroring. Ported from Mesen2 -// Unlicensed/Mapper204.h. +// the decoded page; `addr & 0x10` flips the mirroring. Register map per the +// NESdev wiki mapper-204 documentation (cross-checked against reference +// emulators as accuracy oracles; no third-party emulator code is incorporated). // =========================================================================== /// NTDEC N625092 multicart (mapper 221). @@ -1249,7 +1252,9 @@ pub fn new_m221( // // One value-decoded $8000-$FFFF register: bits 4-6 select a 32 KiB PRG bank, // the 8 KiB CHR bank is `(bank << 2) | (value & 0x03)`, and bit 7 flips the -// mirroring (set => vertical). Ported from Mesen2 Txc/Bmc11160.h. +// mirroring (set => vertical). Register map per the NESdev wiki mapper-299 / +// BMC-11160 documentation (cross-checked against reference emulators as +// accuracy oracles; no third-party emulator code is incorporated). // =========================================================================== #[cfg(test)] diff --git a/crates/rustynes-mappers/src/sachen_discrete.rs b/crates/rustynes-mappers/src/sachen_discrete.rs index 89975e22..f94d6ae4 100644 --- a/crates/rustynes-mappers/src/sachen_discrete.rs +++ b/crates/rustynes-mappers/src/sachen_discrete.rs @@ -445,7 +445,9 @@ impl Mapper for Sachen146 { /// The TXC JV001 scrambling-accumulator chip (mapper 147). Distinct from the /// non-JV001 `TxcChip` in `txc.rs` (different register/output bit positions). -/// Ported bit-for-bit from puNES `JV001.c` / `mapper_147.c`. +/// The JV001 pre/post-scramble is a fixed hardware bit-permutation, implemented +/// from the nesdev wiki mapper-147 board notes and cross-checked against `puNES` +/// as a behavioral oracle (no third-party emulator code is incorporated). #[derive(Clone, Copy)] struct Jv001Chip { accumulator: u8, @@ -1458,7 +1460,8 @@ mod tests { #[test] fn m147_jv001_protection_read_and_bank_decode() { - // Ported from puNES JV001.c / mapper_147.c. The board pre-scrambles + // JV001 scramble per the nesdev wiki mapper-147 board notes (cross-checked + // against puNES as an oracle). The board pre-scrambles // writes ((v&3)<<6)|((v&0xFC)>>2) and post-scrambles reads // ((v&0x3F)<<2)|((v&0xC0)>>6); the chip resets with invert=0xFF. let mut m = diff --git a/crates/rustynes-mappers/src/unif.rs b/crates/rustynes-mappers/src/unif.rs index ec279da4..410d5664 100644 --- a/crates/rustynes-mappers/src/unif.rs +++ b/crates/rustynes-mappers/src/unif.rs @@ -118,9 +118,10 @@ pub fn board_to_mapper(board: &str) -> Option { None } -/// Exact (already-uppercased) board-name lookup. Ported from the -/// `UNIF_BOARD_MAP` in `scripts/coverage/coverage.py` (Mesen2 + puNES, checked -/// vs `docs/mappers.md`). +/// Exact (already-uppercased) board-name lookup. This board-name -> mapper-number +/// table is factual UNIF board-naming data compiled from `docs/mappers.md` and +/// the nesdev UNIF board list (cross-checked against `Mesen2` / `puNES` as +/// oracles; no third-party emulator code is incorporated). // Arms are grouped by vendor (Nintendo / Konami / Bandai / Sachen / ...) for // provenance and readability; some distinct board families intentionally share // a mapper id (e.g. several boards resolve to MMC3 = 4), so identical-body arms @@ -229,7 +230,7 @@ fn lookup_board(b: &str) -> Option { "RET-CUFROM" => 29, // --- v1.8.9 "Backlog" beta.6 UNIF board-map breadth: well-known board // names mapping to families RustyNES already implements. Cross-checked - // against Mesen2 `UnifLoader.cpp` + FCEUX `unif.cpp`. + // against Mesen2 / FCEUX as behavioral oracles (no code incorporated). // NTDEC / TXC / discrete BMC families. "11160" => 299, "N625092" => 221, diff --git a/crates/rustynes-ppu/src/palette_gen.rs b/crates/rustynes-ppu/src/palette_gen.rs index dada13ea..4c7a6a1a 100644 --- a/crates/rustynes-ppu/src/palette_gen.rs +++ b/crates/rustynes-ppu/src/palette_gen.rs @@ -3,7 +3,8 @@ //! The hand-authored [`crate::NES_PALETTE`] is one artist's calibration of a //! Sony PVM reference. This module instead *synthesizes* the 64-entry base //! palette from a model of the 2C02's composite-video output, following the -//! Bisqwit / ares (`fc/ppu/color.cpp`) approach: for each of the 64 colors, +//! Bisqwit composite-palette method published on the nesdev wiki: for each of +//! the 64 colors, //! integrate the PPU's two-level chroma square wave over the 12 subcarrier //! phases of one pixel, demodulate to YIQ, and convert to RGB through the FCC //! matrix with a gamma correction. The result is deterministic, parameterized @@ -28,9 +29,10 @@ //! ## Model reference //! //! The waveform constants (the eight composite voltage levels, the -//! sync/black/white references, and the FCC YIQ→RGB matrix) are Bisqwit's +//! sync/black/white references, and the FCC YIQ→RGB matrix) are from Bisqwit's //! canonical NES palette generator as published on the nesdev wiki ("NTSC -//! video"); ares' `PPU::Color` uses the same integration. The `hue` parameter +//! video") and cross-checked against ares as a behavioral oracle. The `hue` +//! parameter //! is a global tint in subcarrier-phase units (each unit = 30°); grays are //! hue-independent because a constant signal integrates to zero chroma. diff --git a/crates/rustynes-ppu/src/ppu.rs b/crates/rustynes-ppu/src/ppu.rs index 488022ee..98c32209 100644 --- a/crates/rustynes-ppu/src/ppu.rs +++ b/crates/rustynes-ppu/src/ppu.rs @@ -744,27 +744,29 @@ pub struct Ppu { /// if its y is in range, else NO sprite-zero is detected. pub(crate) sprite_eval_first_iter: bool, - /// v2.0 Tier 1.2 — isolated OAM-data-bus model (parallel port of Mesen2's - /// `ProcessSpriteEvaluation` + `_oamCopybuffer`). These fields exist ONLY - /// under `ppu-oam-data-bus` and are read solely by `$2004` during - /// rendering — the rendering / sprite-zero / overflow / MMC3 sprite-fetch - /// FSM uses `secondary_oam` + `sprite_eval_*` + `spr_*`, all untouched. - /// `oam_bus_copybuffer` mirrors `_oamCopybuffer` (the value `$2004` - /// returns while the screen is drawn). + /// v2.0 Tier 1.2 — isolated OAM-data-bus model of the `NESdev`-documented PPU + /// sprite-evaluation datapath (`NESdev` wiki "PPU sprite evaluation"). These + /// fields exist ONLY under `ppu-oam-data-bus` and are read solely by `$2004` + /// during rendering — the rendering / sprite-zero / overflow / MMC3 + /// sprite-fetch FSM uses `secondary_oam` + `sprite_eval_*` + `spr_*`, all + /// untouched. `oam_bus_copybuffer` is the value `$2004` returns while the + /// screen is drawn (the byte currently on the OAM data bus). (Behavior + /// cross-checked against reference emulators as accuracy oracles; no + /// third-party emulator code is incorporated.) pub(crate) oam_bus_copybuffer: u8, - /// Parallel secondary OAM (`_secondarySpriteRam`) for the bus model only. + /// Parallel secondary OAM (the 32-byte sprite line buffer) for the bus model only. pub(crate) oam_bus_secondary: [u8; 32], - /// `_spriteAddrH` (the eval pointer's sprite index, 0..=63). + /// Eval-pointer sprite index (0..=63) — which of the 64 primary sprites is examined. pub(crate) oam_bus_addr_h: u8, - /// `_spriteAddrL` (the eval pointer's byte-in-sprite, 0..=3). + /// Eval-pointer byte-in-sprite (0..=3) — Y / tile / attr / X. pub(crate) oam_bus_addr_l: u8, - /// `_secondaryOamAddr` (write index into the parallel secondary OAM). + /// Write index into the parallel secondary OAM. pub(crate) oam_bus_secondary_addr: u8, - /// `_oamCopyDone` (primary OAM fully scanned / wrapped). + /// Primary OAM fully scanned / wrapped for this scanline. pub(crate) oam_bus_copy_done: bool, - /// `_spriteInRange` (currently copying an in-range sprite). + /// Currently copying an in-range sprite. pub(crate) oam_bus_sprite_in_range: bool, - /// `_overflowBugCounter` (the 8-sprite-overflow PPU-bug countdown). + /// The 8-sprite-overflow PPU-bug countdown. pub(crate) oam_bus_overflow_counter: u8, /// OAM-corruption model — faithful port of `TriCNES`'s eval-pointer @@ -1446,8 +1448,9 @@ impl Ppu { /// v2.1.4 F2.3 — OAM-read decay hook. Call **immediately before** reading /// `oam[addr]` at every primary-OAM read site (the `$2004` read and both - /// sprite-evaluation read paths). Faithful port of Mesen2's `ReadSpriteRam` - /// (`Core/NES/NesPpu.cpp`): + /// sprite-evaluation read paths). Implements the `NESdev`-documented OAM DRAM + /// decay-on-read behavior (`NESdev` wiki "PPU OAM" — sprite RAM is dynamic and + /// its cells decay; a read recharges the touched row): /// /// - If the model is inactive (disabled or PAL), this is a no-op — `oam` and /// the timestamps are left untouched, so the read is byte-identical to stock. @@ -1458,7 +1461,7 @@ impl Ppu { /// (sprAddr & 0xE3) : sprAddr` (the attribute byte keeps only its implemented /// bits; the others read back their own low address) and leave the stale /// timestamp (so the row keeps reading decayed until a write refreshes it, - /// exactly like Mesen2). + /// matching the documented decay behavior). /// /// The subsequent `oam[addr]` read then returns the (possibly decayed) byte. #[inline] @@ -1488,8 +1491,9 @@ impl Ppu { } /// v2.1.4 F2.3 — OAM-write decay hook. Call **after** writing `oam[addr]` at - /// every primary-OAM write site (`$2004` / OAM DMA). Faithful port of Mesen2's - /// `WriteSpriteRam`: a write recharges the row's DRAM cells, so refresh the + /// every primary-OAM write site (`$2004` / OAM DMA). Implements the documented + /// OAM DRAM decay-on-write refresh (`NESdev` wiki "PPU OAM"): a write recharges + /// the row's DRAM cells, so refresh the /// row's last-touch timestamp. Inactive (disabled or PAL) ⇒ no-op, so the write /// path is byte-identical to stock at the default. #[inline] @@ -3925,7 +3929,7 @@ impl Ppu { // (F1.1): with rendering DISABLED and the VRAM address `v` pointing // into palette space ($3F00-$3FFF), the palette's shared address line // is driven by `v`, so hardware outputs the color at `v & 0x1F` - // INSTEAD of the backdrop (NESdev "PPU palettes"; Mesen2 `NesPpu.cpp` + // INSTEAD of the backdrop (`NESdev` "PPU palettes"; Mesen2 `NesPpu.cpp` // / ares output stage). This is a DISPLAY artifact only — palette RAM // is not mutated. It cannot fire while rendering is enabled: there // the fetch pipeline owns `v` and this branch means a transparent @@ -4149,10 +4153,13 @@ impl Ppu { /// v2.0 Tier 1.2 — per-dot driver for the isolated OAM-data-bus model. /// - /// A faithful, side-effect-free port of Mesen2's - /// `NesPpu::ProcessSpriteEvaluation` (`NesPpu.cpp:1015-1141`, default - /// config — `EnablePpuSpriteEvalBug` off) plus the cycle-321 copybuffer - /// reset (`NesPpu.cpp:945-951`). It maintains ONLY `oam_bus_copybuffer` + + /// A side-effect-free model of the `NESdev`-documented PPU sprite-evaluation + /// sequence (`NESdev` wiki "PPU sprite evaluation" + "PPU rendering"): + /// secondary-OAM clear (dots 1-64), evaluation (65-256), and sprite fetch + /// (257-320) in the default configuration (the optional OAMADDR sprite-eval + /// corruption glitch disabled; the 8-sprite overflow bug is still modeled), + /// plus the cycle-321 copy-buffer reset. It maintains ONLY + /// `oam_bus_copybuffer` + /// the parallel `oam_bus_secondary`; it reads primary `oam` read-only and /// NEVER touches the real sprite-eval / overflow / sprite-zero state (so /// the existing rendering FSM is unperturbed — `$2004` reads are the sole @@ -4166,7 +4173,7 @@ impl Ppu { 8 }; // Y-test reference: the scanline being evaluated (sprites render on - // scanline+1). Mesen uses `_scanline` directly here. + // scanline+1). let scan = self.scanline; if cycle == 0 { @@ -5036,7 +5043,7 @@ mod tests { // space ($3F00-$3FFF), the palette's shared address input is driven by `v`, // so the PPU outputs the color at `v & 0x1F` INSTEAD of the universal // backdrop ($3F00). This is a display artifact only — palette RAM is never - // mutated, and rendering-enabled output is unchanged. See NESdev "PPU + // mutated, and rendering-enabled output is unchanged. See `NESdev` "PPU // palettes"; mirrors Mesen2 `NesPpu.cpp` / ares output-stage behavior. #[test] fn palette_backdrop_override_when_rendering_disabled() { diff --git a/crates/rustynes-test-harness/src/bin/pgo_trainer.rs b/crates/rustynes-test-harness/src/bin/pgo_trainer.rs index 2e01cb80..b373a2a2 100644 --- a/crates/rustynes-test-harness/src/bin/pgo_trainer.rs +++ b/crates/rustynes-test-harness/src/bin/pgo_trainer.rs @@ -1,9 +1,9 @@ //! v2.8.0 Phase 4 — the PGO training workload (see `scripts/pgo/run.sh`). //! -//! Adapted from Mesen2's `PGOHelper`: sweep a ROM corpus at maximum speed +//! An independent PGO training harness: sweep a ROM corpus at maximum speed //! (no limiter, no audio device, no display) with scripted input that //! pushes games past their title screens (Start held on a 4-of-7-frame -//! cycle — Mesen2's exact trick — plus a rotating d-pad/A mix so movement +//! cycle — a common title-skip trick — plus a rotating d-pad/A mix so movement //! and collision code paths get profiled too). //! //! Corpus: a committed CC0/MIT/zlib spread covering the hot configurations diff --git a/crates/rustynes-test-harness/target/cpu_boot_trace/mmc3_test_2_4_ours.bin b/crates/rustynes-test-harness/target/cpu_boot_trace/mmc3_test_2_4_ours.bin deleted file mode 100644 index b2351f23..00000000 Binary files a/crates/rustynes-test-harness/target/cpu_boot_trace/mmc3_test_2_4_ours.bin and /dev/null differ diff --git a/crates/rustynes-test-harness/target/cpu_boot_trace/mmc3_test_2_4_ours.preview.csv b/crates/rustynes-test-harness/target/cpu_boot_trace/mmc3_test_2_4_ours.preview.csv deleted file mode 100644 index 534b5eae..00000000 --- a/crates/rustynes-test-harness/target/cpu_boot_trace/mmc3_test_2_4_ours.preview.csv +++ /dev/null @@ -1,501 +0,0 @@ -cycle,frame,scanline,dot,pc,a,x,y,p,s,opcode,op1,op2,flags -8,1,0,25,EA5F,00,00,00,24,FD,78,4C,FA,00 -10,1,0,31,EA60,00,00,00,24,FD,4C,FA,EE,00 -13,1,0,40,EEFA,00,00,00,24,FD,A9,00,8D,00 -15,1,0,46,EEFC,00,00,00,26,FD,8D,00,20,00 -19,1,0,58,EEFF,00,00,00,26,FD,8D,01,20,00 -23,1,0,70,EF02,00,00,00,26,FD,4C,63,EA,00 -26,1,0,79,EA63,00,00,00,26,FD,78,D8,A2,00 -28,1,0,85,EA64,00,00,00,26,FD,D8,A2,FF,00 -30,1,0,91,EA65,00,00,00,26,FD,A2,FF,9A,00 -32,1,0,97,EA67,00,FF,00,A4,FD,9A,E8,8E,00 -34,1,0,103,EA68,00,FF,00,A4,FF,E8,8E,00,00 -36,1,0,109,EA69,00,00,00,26,FF,8E,00,20,00 -40,1,0,121,EA6C,00,00,00,26,FF,20,7E,EA,00 -46,1,0,139,EA7E,00,00,00,26,FD,20,36,EB,00 -52,1,0,157,EB36,00,00,00,26,FB,A9,00,A0,00 -54,1,0,163,EB38,00,00,00,26,FB,A0,20,A2,00 -56,1,0,169,EB3A,00,00,20,24,FB,A2,01,84,00 -58,1,0,175,EB3C,00,01,20,24,FB,84,0E,86,00 -61,1,0,184,EB3E,00,01,20,24,FB,86,0F,A0,00 -64,1,0,193,EB40,00,01,20,24,FB,A0,E0,A2,00 -66,1,0,199,EB42,00,01,E0,A4,FB,A2,FF,91,00 -68,1,0,205,EB44,00,FF,E0,A4,FB,91,0E,C8,00 -74,1,0,223,EB46,00,FF,E0,A4,FB,C8,D0,FB,00 -76,1,0,229,EB47,00,FF,E1,A4,FB,D0,FB,E6,00 -79,1,0,238,EB44,00,FF,E1,A4,FB,91,0E,C8,00 -85,1,0,256,EB46,00,FF,E1,A4,FB,C8,D0,FB,00 -87,1,0,262,EB47,00,FF,E2,A4,FB,D0,FB,E6,00 -90,1,0,271,EB44,00,FF,E2,A4,FB,91,0E,C8,00 -96,1,0,289,EB46,00,FF,E2,A4,FB,C8,D0,FB,00 -98,1,0,295,EB47,00,FF,E3,A4,FB,D0,FB,E6,00 -101,1,0,304,EB44,00,FF,E3,A4,FB,91,0E,C8,00 -107,1,0,322,EB46,00,FF,E3,A4,FB,C8,D0,FB,00 -109,1,0,328,EB47,00,FF,E4,A4,FB,D0,FB,E6,00 -112,1,0,337,EB44,00,FF,E4,A4,FB,91,0E,C8,00 -118,1,1,14,EB46,00,FF,E4,A4,FB,C8,D0,FB,00 -120,1,1,20,EB47,00,FF,E5,A4,FB,D0,FB,E6,00 -123,1,1,29,EB44,00,FF,E5,A4,FB,91,0E,C8,00 -129,1,1,47,EB46,00,FF,E5,A4,FB,C8,D0,FB,00 -131,1,1,53,EB47,00,FF,E6,A4,FB,D0,FB,E6,00 -134,1,1,62,EB44,00,FF,E6,A4,FB,91,0E,C8,00 -140,1,1,80,EB46,00,FF,E6,A4,FB,C8,D0,FB,00 -142,1,1,86,EB47,00,FF,E7,A4,FB,D0,FB,E6,00 -145,1,1,95,EB44,00,FF,E7,A4,FB,91,0E,C8,00 -151,1,1,113,EB46,00,FF,E7,A4,FB,C8,D0,FB,00 -153,1,1,119,EB47,00,FF,E8,A4,FB,D0,FB,E6,00 -156,1,1,128,EB44,00,FF,E8,A4,FB,91,0E,C8,00 -162,1,1,146,EB46,00,FF,E8,A4,FB,C8,D0,FB,00 -164,1,1,152,EB47,00,FF,E9,A4,FB,D0,FB,E6,00 -167,1,1,161,EB44,00,FF,E9,A4,FB,91,0E,C8,00 -173,1,1,179,EB46,00,FF,E9,A4,FB,C8,D0,FB,00 -175,1,1,185,EB47,00,FF,EA,A4,FB,D0,FB,E6,00 -178,1,1,194,EB44,00,FF,EA,A4,FB,91,0E,C8,00 -184,1,1,212,EB46,00,FF,EA,A4,FB,C8,D0,FB,00 -186,1,1,218,EB47,00,FF,EB,A4,FB,D0,FB,E6,00 -189,1,1,227,EB44,00,FF,EB,A4,FB,91,0E,C8,00 -195,1,1,245,EB46,00,FF,EB,A4,FB,C8,D0,FB,00 -197,1,1,251,EB47,00,FF,EC,A4,FB,D0,FB,E6,00 -200,1,1,260,EB44,00,FF,EC,A4,FB,91,0E,C8,00 -206,1,1,278,EB46,00,FF,EC,A4,FB,C8,D0,FB,00 -208,1,1,284,EB47,00,FF,ED,A4,FB,D0,FB,E6,00 -211,1,1,293,EB44,00,FF,ED,A4,FB,91,0E,C8,00 -217,1,1,311,EB46,00,FF,ED,A4,FB,C8,D0,FB,00 -219,1,1,317,EB47,00,FF,EE,A4,FB,D0,FB,E6,00 -222,1,1,326,EB44,00,FF,EE,A4,FB,91,0E,C8,00 -228,1,2,3,EB46,00,FF,EE,A4,FB,C8,D0,FB,00 -230,1,2,9,EB47,00,FF,EF,A4,FB,D0,FB,E6,00 -233,1,2,18,EB44,00,FF,EF,A4,FB,91,0E,C8,00 -239,1,2,36,EB46,00,FF,EF,A4,FB,C8,D0,FB,00 -241,1,2,42,EB47,00,FF,F0,A4,FB,D0,FB,E6,00 -244,1,2,51,EB44,00,FF,F0,A4,FB,91,0E,C8,00 -250,1,2,69,EB46,00,FF,F0,A4,FB,C8,D0,FB,00 -252,1,2,75,EB47,00,FF,F1,A4,FB,D0,FB,E6,00 -255,1,2,84,EB44,00,FF,F1,A4,FB,91,0E,C8,00 -261,1,2,102,EB46,00,FF,F1,A4,FB,C8,D0,FB,00 -263,1,2,108,EB47,00,FF,F2,A4,FB,D0,FB,E6,00 -266,1,2,117,EB44,00,FF,F2,A4,FB,91,0E,C8,00 -272,1,2,135,EB46,00,FF,F2,A4,FB,C8,D0,FB,00 -274,1,2,141,EB47,00,FF,F3,A4,FB,D0,FB,E6,00 -277,1,2,150,EB44,00,FF,F3,A4,FB,91,0E,C8,00 -283,1,2,168,EB46,00,FF,F3,A4,FB,C8,D0,FB,00 -285,1,2,174,EB47,00,FF,F4,A4,FB,D0,FB,E6,00 -288,1,2,183,EB44,00,FF,F4,A4,FB,91,0E,C8,00 -294,1,2,201,EB46,00,FF,F4,A4,FB,C8,D0,FB,00 -296,1,2,207,EB47,00,FF,F5,A4,FB,D0,FB,E6,00 -299,1,2,216,EB44,00,FF,F5,A4,FB,91,0E,C8,00 -305,1,2,234,EB46,00,FF,F5,A4,FB,C8,D0,FB,00 -307,1,2,240,EB47,00,FF,F6,A4,FB,D0,FB,E6,00 -310,1,2,249,EB44,00,FF,F6,A4,FB,91,0E,C8,00 -316,1,2,267,EB46,00,FF,F6,A4,FB,C8,D0,FB,00 -318,1,2,273,EB47,00,FF,F7,A4,FB,D0,FB,E6,00 -321,1,2,282,EB44,00,FF,F7,A4,FB,91,0E,C8,00 -327,1,2,300,EB46,00,FF,F7,A4,FB,C8,D0,FB,00 -329,1,2,306,EB47,00,FF,F8,A4,FB,D0,FB,E6,00 -332,1,2,315,EB44,00,FF,F8,A4,FB,91,0E,C8,00 -338,1,2,333,EB46,00,FF,F8,A4,FB,C8,D0,FB,00 -340,1,2,339,EB47,00,FF,F9,A4,FB,D0,FB,E6,00 -343,1,3,7,EB44,00,FF,F9,A4,FB,91,0E,C8,00 -349,1,3,25,EB46,00,FF,F9,A4,FB,C8,D0,FB,00 -351,1,3,31,EB47,00,FF,FA,A4,FB,D0,FB,E6,00 -354,1,3,40,EB44,00,FF,FA,A4,FB,91,0E,C8,00 -360,1,3,58,EB46,00,FF,FA,A4,FB,C8,D0,FB,00 -362,1,3,64,EB47,00,FF,FB,A4,FB,D0,FB,E6,00 -365,1,3,73,EB44,00,FF,FB,A4,FB,91,0E,C8,00 -371,1,3,91,EB46,00,FF,FB,A4,FB,C8,D0,FB,00 -373,1,3,97,EB47,00,FF,FC,A4,FB,D0,FB,E6,00 -376,1,3,106,EB44,00,FF,FC,A4,FB,91,0E,C8,00 -382,1,3,124,EB46,00,FF,FC,A4,FB,C8,D0,FB,00 -384,1,3,130,EB47,00,FF,FD,A4,FB,D0,FB,E6,00 -387,1,3,139,EB44,00,FF,FD,A4,FB,91,0E,C8,00 -393,1,3,157,EB46,00,FF,FD,A4,FB,C8,D0,FB,00 -395,1,3,163,EB47,00,FF,FE,A4,FB,D0,FB,E6,00 -398,1,3,172,EB44,00,FF,FE,A4,FB,91,0E,C8,00 -404,1,3,190,EB46,00,FF,FE,A4,FB,C8,D0,FB,00 -406,1,3,196,EB47,00,FF,FF,A4,FB,D0,FB,E6,00 -409,1,3,205,EB44,00,FF,FF,A4,FB,91,0E,C8,00 -415,1,3,223,EB46,00,FF,FF,A4,FB,C8,D0,FB,00 -417,1,3,229,EB47,00,FF,00,26,FB,D0,FB,E6,00 -419,1,3,235,EB49,00,FF,00,26,FB,E6,0F,E8,00 -424,1,3,250,EB4B,00,FF,00,24,FB,E8,D0,F6,00 -426,1,3,256,EB4C,00,00,00,26,FB,D0,F6,A0,00 -428,1,3,262,EB4E,00,00,00,26,FB,A0,00,A2,00 -430,1,3,268,EB50,00,00,00,26,FB,A2,02,84,00 -432,1,3,274,EB52,00,02,00,24,FB,84,0E,86,00 -435,1,3,283,EB54,00,02,00,24,FB,86,0F,A0,00 -438,1,3,292,EB56,00,02,00,24,FB,A0,24,A2,00 -440,1,3,298,EB58,00,02,24,24,FB,A2,FA,91,00 -442,1,3,304,EB5A,00,FA,24,A4,FB,91,0E,C8,00 -448,1,3,322,EB5C,00,FA,24,A4,FB,C8,D0,FB,00 -450,1,3,328,EB5D,00,FA,25,24,FB,D0,FB,E6,00 -453,1,3,337,EB5A,00,FA,25,24,FB,91,0E,C8,00 -459,1,4,14,EB5C,00,FA,25,24,FB,C8,D0,FB,00 -461,1,4,20,EB5D,00,FA,26,24,FB,D0,FB,E6,00 -464,1,4,29,EB5A,00,FA,26,24,FB,91,0E,C8,00 -470,1,4,47,EB5C,00,FA,26,24,FB,C8,D0,FB,00 -472,1,4,53,EB5D,00,FA,27,24,FB,D0,FB,E6,00 -475,1,4,62,EB5A,00,FA,27,24,FB,91,0E,C8,00 -481,1,4,80,EB5C,00,FA,27,24,FB,C8,D0,FB,00 -483,1,4,86,EB5D,00,FA,28,24,FB,D0,FB,E6,00 -486,1,4,95,EB5A,00,FA,28,24,FB,91,0E,C8,00 -492,1,4,113,EB5C,00,FA,28,24,FB,C8,D0,FB,00 -494,1,4,119,EB5D,00,FA,29,24,FB,D0,FB,E6,00 -497,1,4,128,EB5A,00,FA,29,24,FB,91,0E,C8,00 -503,1,4,146,EB5C,00,FA,29,24,FB,C8,D0,FB,00 -505,1,4,152,EB5D,00,FA,2A,24,FB,D0,FB,E6,00 -508,1,4,161,EB5A,00,FA,2A,24,FB,91,0E,C8,00 -514,1,4,179,EB5C,00,FA,2A,24,FB,C8,D0,FB,00 -516,1,4,185,EB5D,00,FA,2B,24,FB,D0,FB,E6,00 -519,1,4,194,EB5A,00,FA,2B,24,FB,91,0E,C8,00 -525,1,4,212,EB5C,00,FA,2B,24,FB,C8,D0,FB,00 -527,1,4,218,EB5D,00,FA,2C,24,FB,D0,FB,E6,00 -530,1,4,227,EB5A,00,FA,2C,24,FB,91,0E,C8,00 -536,1,4,245,EB5C,00,FA,2C,24,FB,C8,D0,FB,00 -538,1,4,251,EB5D,00,FA,2D,24,FB,D0,FB,E6,00 -541,1,4,260,EB5A,00,FA,2D,24,FB,91,0E,C8,00 -547,1,4,278,EB5C,00,FA,2D,24,FB,C8,D0,FB,00 -549,1,4,284,EB5D,00,FA,2E,24,FB,D0,FB,E6,00 -552,1,4,293,EB5A,00,FA,2E,24,FB,91,0E,C8,00 -558,1,4,311,EB5C,00,FA,2E,24,FB,C8,D0,FB,00 -560,1,4,317,EB5D,00,FA,2F,24,FB,D0,FB,E6,00 -563,1,4,326,EB5A,00,FA,2F,24,FB,91,0E,C8,00 -569,1,5,3,EB5C,00,FA,2F,24,FB,C8,D0,FB,00 -571,1,5,9,EB5D,00,FA,30,24,FB,D0,FB,E6,00 -574,1,5,18,EB5A,00,FA,30,24,FB,91,0E,C8,00 -580,1,5,36,EB5C,00,FA,30,24,FB,C8,D0,FB,00 -582,1,5,42,EB5D,00,FA,31,24,FB,D0,FB,E6,00 -585,1,5,51,EB5A,00,FA,31,24,FB,91,0E,C8,00 -591,1,5,69,EB5C,00,FA,31,24,FB,C8,D0,FB,00 -593,1,5,75,EB5D,00,FA,32,24,FB,D0,FB,E6,00 -596,1,5,84,EB5A,00,FA,32,24,FB,91,0E,C8,00 -602,1,5,102,EB5C,00,FA,32,24,FB,C8,D0,FB,00 -604,1,5,108,EB5D,00,FA,33,24,FB,D0,FB,E6,00 -607,1,5,117,EB5A,00,FA,33,24,FB,91,0E,C8,00 -613,1,5,135,EB5C,00,FA,33,24,FB,C8,D0,FB,00 -615,1,5,141,EB5D,00,FA,34,24,FB,D0,FB,E6,00 -618,1,5,150,EB5A,00,FA,34,24,FB,91,0E,C8,00 -624,1,5,168,EB5C,00,FA,34,24,FB,C8,D0,FB,00 -626,1,5,174,EB5D,00,FA,35,24,FB,D0,FB,E6,00 -629,1,5,183,EB5A,00,FA,35,24,FB,91,0E,C8,00 -635,1,5,201,EB5C,00,FA,35,24,FB,C8,D0,FB,00 -637,1,5,207,EB5D,00,FA,36,24,FB,D0,FB,E6,00 -640,1,5,216,EB5A,00,FA,36,24,FB,91,0E,C8,00 -646,1,5,234,EB5C,00,FA,36,24,FB,C8,D0,FB,00 -648,1,5,240,EB5D,00,FA,37,24,FB,D0,FB,E6,00 -651,1,5,249,EB5A,00,FA,37,24,FB,91,0E,C8,00 -657,1,5,267,EB5C,00,FA,37,24,FB,C8,D0,FB,00 -659,1,5,273,EB5D,00,FA,38,24,FB,D0,FB,E6,00 -662,1,5,282,EB5A,00,FA,38,24,FB,91,0E,C8,00 -668,1,5,300,EB5C,00,FA,38,24,FB,C8,D0,FB,00 -670,1,5,306,EB5D,00,FA,39,24,FB,D0,FB,E6,00 -673,1,5,315,EB5A,00,FA,39,24,FB,91,0E,C8,00 -679,1,5,333,EB5C,00,FA,39,24,FB,C8,D0,FB,00 -681,1,5,339,EB5D,00,FA,3A,24,FB,D0,FB,E6,00 -684,1,6,7,EB5A,00,FA,3A,24,FB,91,0E,C8,00 -690,1,6,25,EB5C,00,FA,3A,24,FB,C8,D0,FB,00 -692,1,6,31,EB5D,00,FA,3B,24,FB,D0,FB,E6,00 -695,1,6,40,EB5A,00,FA,3B,24,FB,91,0E,C8,00 -701,1,6,58,EB5C,00,FA,3B,24,FB,C8,D0,FB,00 -703,1,6,64,EB5D,00,FA,3C,24,FB,D0,FB,E6,00 -706,1,6,73,EB5A,00,FA,3C,24,FB,91,0E,C8,00 -712,1,6,91,EB5C,00,FA,3C,24,FB,C8,D0,FB,00 -714,1,6,97,EB5D,00,FA,3D,24,FB,D0,FB,E6,00 -717,1,6,106,EB5A,00,FA,3D,24,FB,91,0E,C8,00 -723,1,6,124,EB5C,00,FA,3D,24,FB,C8,D0,FB,00 -725,1,6,130,EB5D,00,FA,3E,24,FB,D0,FB,E6,00 -728,1,6,139,EB5A,00,FA,3E,24,FB,91,0E,C8,00 -734,1,6,157,EB5C,00,FA,3E,24,FB,C8,D0,FB,00 -736,1,6,163,EB5D,00,FA,3F,24,FB,D0,FB,E6,00 -739,1,6,172,EB5A,00,FA,3F,24,FB,91,0E,C8,00 -745,1,6,190,EB5C,00,FA,3F,24,FB,C8,D0,FB,00 -747,1,6,196,EB5D,00,FA,40,24,FB,D0,FB,E6,00 -750,1,6,205,EB5A,00,FA,40,24,FB,91,0E,C8,00 -756,1,6,223,EB5C,00,FA,40,24,FB,C8,D0,FB,00 -758,1,6,229,EB5D,00,FA,41,24,FB,D0,FB,E6,00 -761,1,6,238,EB5A,00,FA,41,24,FB,91,0E,C8,00 -767,1,6,256,EB5C,00,FA,41,24,FB,C8,D0,FB,00 -769,1,6,262,EB5D,00,FA,42,24,FB,D0,FB,E6,00 -772,1,6,271,EB5A,00,FA,42,24,FB,91,0E,C8,00 -778,1,6,289,EB5C,00,FA,42,24,FB,C8,D0,FB,00 -780,1,6,295,EB5D,00,FA,43,24,FB,D0,FB,E6,00 -783,1,6,304,EB5A,00,FA,43,24,FB,91,0E,C8,00 -789,1,6,322,EB5C,00,FA,43,24,FB,C8,D0,FB,00 -791,1,6,328,EB5D,00,FA,44,24,FB,D0,FB,E6,00 -794,1,6,337,EB5A,00,FA,44,24,FB,91,0E,C8,00 -800,1,7,14,EB5C,00,FA,44,24,FB,C8,D0,FB,00 -802,1,7,20,EB5D,00,FA,45,24,FB,D0,FB,E6,00 -805,1,7,29,EB5A,00,FA,45,24,FB,91,0E,C8,00 -811,1,7,47,EB5C,00,FA,45,24,FB,C8,D0,FB,00 -813,1,7,53,EB5D,00,FA,46,24,FB,D0,FB,E6,00 -816,1,7,62,EB5A,00,FA,46,24,FB,91,0E,C8,00 -822,1,7,80,EB5C,00,FA,46,24,FB,C8,D0,FB,00 -824,1,7,86,EB5D,00,FA,47,24,FB,D0,FB,E6,00 -827,1,7,95,EB5A,00,FA,47,24,FB,91,0E,C8,00 -833,1,7,113,EB5C,00,FA,47,24,FB,C8,D0,FB,00 -835,1,7,119,EB5D,00,FA,48,24,FB,D0,FB,E6,00 -838,1,7,128,EB5A,00,FA,48,24,FB,91,0E,C8,00 -844,1,7,146,EB5C,00,FA,48,24,FB,C8,D0,FB,00 -846,1,7,152,EB5D,00,FA,49,24,FB,D0,FB,E6,00 -849,1,7,161,EB5A,00,FA,49,24,FB,91,0E,C8,00 -855,1,7,179,EB5C,00,FA,49,24,FB,C8,D0,FB,00 -857,1,7,185,EB5D,00,FA,4A,24,FB,D0,FB,E6,00 -860,1,7,194,EB5A,00,FA,4A,24,FB,91,0E,C8,00 -866,1,7,212,EB5C,00,FA,4A,24,FB,C8,D0,FB,00 -868,1,7,218,EB5D,00,FA,4B,24,FB,D0,FB,E6,00 -871,1,7,227,EB5A,00,FA,4B,24,FB,91,0E,C8,00 -877,1,7,245,EB5C,00,FA,4B,24,FB,C8,D0,FB,00 -879,1,7,251,EB5D,00,FA,4C,24,FB,D0,FB,E6,00 -882,1,7,260,EB5A,00,FA,4C,24,FB,91,0E,C8,00 -888,1,7,278,EB5C,00,FA,4C,24,FB,C8,D0,FB,00 -890,1,7,284,EB5D,00,FA,4D,24,FB,D0,FB,E6,00 -893,1,7,293,EB5A,00,FA,4D,24,FB,91,0E,C8,00 -899,1,7,311,EB5C,00,FA,4D,24,FB,C8,D0,FB,00 -901,1,7,317,EB5D,00,FA,4E,24,FB,D0,FB,E6,00 -904,1,7,326,EB5A,00,FA,4E,24,FB,91,0E,C8,00 -910,1,8,3,EB5C,00,FA,4E,24,FB,C8,D0,FB,00 -912,1,8,9,EB5D,00,FA,4F,24,FB,D0,FB,E6,00 -915,1,8,18,EB5A,00,FA,4F,24,FB,91,0E,C8,00 -921,1,8,36,EB5C,00,FA,4F,24,FB,C8,D0,FB,00 -923,1,8,42,EB5D,00,FA,50,24,FB,D0,FB,E6,00 -926,1,8,51,EB5A,00,FA,50,24,FB,91,0E,C8,00 -932,1,8,69,EB5C,00,FA,50,24,FB,C8,D0,FB,00 -934,1,8,75,EB5D,00,FA,51,24,FB,D0,FB,E6,00 -937,1,8,84,EB5A,00,FA,51,24,FB,91,0E,C8,00 -943,1,8,102,EB5C,00,FA,51,24,FB,C8,D0,FB,00 -945,1,8,108,EB5D,00,FA,52,24,FB,D0,FB,E6,00 -948,1,8,117,EB5A,00,FA,52,24,FB,91,0E,C8,00 -954,1,8,135,EB5C,00,FA,52,24,FB,C8,D0,FB,00 -956,1,8,141,EB5D,00,FA,53,24,FB,D0,FB,E6,00 -959,1,8,150,EB5A,00,FA,53,24,FB,91,0E,C8,00 -965,1,8,168,EB5C,00,FA,53,24,FB,C8,D0,FB,00 -967,1,8,174,EB5D,00,FA,54,24,FB,D0,FB,E6,00 -970,1,8,183,EB5A,00,FA,54,24,FB,91,0E,C8,00 -976,1,8,201,EB5C,00,FA,54,24,FB,C8,D0,FB,00 -978,1,8,207,EB5D,00,FA,55,24,FB,D0,FB,E6,00 -981,1,8,216,EB5A,00,FA,55,24,FB,91,0E,C8,00 -987,1,8,234,EB5C,00,FA,55,24,FB,C8,D0,FB,00 -989,1,8,240,EB5D,00,FA,56,24,FB,D0,FB,E6,00 -992,1,8,249,EB5A,00,FA,56,24,FB,91,0E,C8,00 -998,1,8,267,EB5C,00,FA,56,24,FB,C8,D0,FB,00 -1000,1,8,273,EB5D,00,FA,57,24,FB,D0,FB,E6,00 -1003,1,8,282,EB5A,00,FA,57,24,FB,91,0E,C8,00 -1009,1,8,300,EB5C,00,FA,57,24,FB,C8,D0,FB,00 -1011,1,8,306,EB5D,00,FA,58,24,FB,D0,FB,E6,00 -1014,1,8,315,EB5A,00,FA,58,24,FB,91,0E,C8,00 -1020,1,8,333,EB5C,00,FA,58,24,FB,C8,D0,FB,00 -1022,1,8,339,EB5D,00,FA,59,24,FB,D0,FB,E6,00 -1025,1,9,7,EB5A,00,FA,59,24,FB,91,0E,C8,00 -1031,1,9,25,EB5C,00,FA,59,24,FB,C8,D0,FB,00 -1033,1,9,31,EB5D,00,FA,5A,24,FB,D0,FB,E6,00 -1036,1,9,40,EB5A,00,FA,5A,24,FB,91,0E,C8,00 -1042,1,9,58,EB5C,00,FA,5A,24,FB,C8,D0,FB,00 -1044,1,9,64,EB5D,00,FA,5B,24,FB,D0,FB,E6,00 -1047,1,9,73,EB5A,00,FA,5B,24,FB,91,0E,C8,00 -1053,1,9,91,EB5C,00,FA,5B,24,FB,C8,D0,FB,00 -1055,1,9,97,EB5D,00,FA,5C,24,FB,D0,FB,E6,00 -1058,1,9,106,EB5A,00,FA,5C,24,FB,91,0E,C8,00 -1064,1,9,124,EB5C,00,FA,5C,24,FB,C8,D0,FB,00 -1066,1,9,130,EB5D,00,FA,5D,24,FB,D0,FB,E6,00 -1069,1,9,139,EB5A,00,FA,5D,24,FB,91,0E,C8,00 -1075,1,9,157,EB5C,00,FA,5D,24,FB,C8,D0,FB,00 -1077,1,9,163,EB5D,00,FA,5E,24,FB,D0,FB,E6,00 -1080,1,9,172,EB5A,00,FA,5E,24,FB,91,0E,C8,00 -1086,1,9,190,EB5C,00,FA,5E,24,FB,C8,D0,FB,00 -1088,1,9,196,EB5D,00,FA,5F,24,FB,D0,FB,E6,00 -1091,1,9,205,EB5A,00,FA,5F,24,FB,91,0E,C8,00 -1097,1,9,223,EB5C,00,FA,5F,24,FB,C8,D0,FB,00 -1099,1,9,229,EB5D,00,FA,60,24,FB,D0,FB,E6,00 -1102,1,9,238,EB5A,00,FA,60,24,FB,91,0E,C8,00 -1108,1,9,256,EB5C,00,FA,60,24,FB,C8,D0,FB,00 -1110,1,9,262,EB5D,00,FA,61,24,FB,D0,FB,E6,00 -1113,1,9,271,EB5A,00,FA,61,24,FB,91,0E,C8,00 -1119,1,9,289,EB5C,00,FA,61,24,FB,C8,D0,FB,00 -1121,1,9,295,EB5D,00,FA,62,24,FB,D0,FB,E6,00 -1124,1,9,304,EB5A,00,FA,62,24,FB,91,0E,C8,00 -1130,1,9,322,EB5C,00,FA,62,24,FB,C8,D0,FB,00 -1132,1,9,328,EB5D,00,FA,63,24,FB,D0,FB,E6,00 -1135,1,9,337,EB5A,00,FA,63,24,FB,91,0E,C8,00 -1141,1,10,14,EB5C,00,FA,63,24,FB,C8,D0,FB,00 -1143,1,10,20,EB5D,00,FA,64,24,FB,D0,FB,E6,00 -1146,1,10,29,EB5A,00,FA,64,24,FB,91,0E,C8,00 -1152,1,10,47,EB5C,00,FA,64,24,FB,C8,D0,FB,00 -1154,1,10,53,EB5D,00,FA,65,24,FB,D0,FB,E6,00 -1157,1,10,62,EB5A,00,FA,65,24,FB,91,0E,C8,00 -1163,1,10,80,EB5C,00,FA,65,24,FB,C8,D0,FB,00 -1165,1,10,86,EB5D,00,FA,66,24,FB,D0,FB,E6,00 -1168,1,10,95,EB5A,00,FA,66,24,FB,91,0E,C8,00 -1174,1,10,113,EB5C,00,FA,66,24,FB,C8,D0,FB,00 -1176,1,10,119,EB5D,00,FA,67,24,FB,D0,FB,E6,00 -1179,1,10,128,EB5A,00,FA,67,24,FB,91,0E,C8,00 -1185,1,10,146,EB5C,00,FA,67,24,FB,C8,D0,FB,00 -1187,1,10,152,EB5D,00,FA,68,24,FB,D0,FB,E6,00 -1190,1,10,161,EB5A,00,FA,68,24,FB,91,0E,C8,00 -1196,1,10,179,EB5C,00,FA,68,24,FB,C8,D0,FB,00 -1198,1,10,185,EB5D,00,FA,69,24,FB,D0,FB,E6,00 -1201,1,10,194,EB5A,00,FA,69,24,FB,91,0E,C8,00 -1207,1,10,212,EB5C,00,FA,69,24,FB,C8,D0,FB,00 -1209,1,10,218,EB5D,00,FA,6A,24,FB,D0,FB,E6,00 -1212,1,10,227,EB5A,00,FA,6A,24,FB,91,0E,C8,00 -1218,1,10,245,EB5C,00,FA,6A,24,FB,C8,D0,FB,00 -1220,1,10,251,EB5D,00,FA,6B,24,FB,D0,FB,E6,00 -1223,1,10,260,EB5A,00,FA,6B,24,FB,91,0E,C8,00 -1229,1,10,278,EB5C,00,FA,6B,24,FB,C8,D0,FB,00 -1231,1,10,284,EB5D,00,FA,6C,24,FB,D0,FB,E6,00 -1234,1,10,293,EB5A,00,FA,6C,24,FB,91,0E,C8,00 -1240,1,10,311,EB5C,00,FA,6C,24,FB,C8,D0,FB,00 -1242,1,10,317,EB5D,00,FA,6D,24,FB,D0,FB,E6,00 -1245,1,10,326,EB5A,00,FA,6D,24,FB,91,0E,C8,00 -1251,1,11,3,EB5C,00,FA,6D,24,FB,C8,D0,FB,00 -1253,1,11,9,EB5D,00,FA,6E,24,FB,D0,FB,E6,00 -1256,1,11,18,EB5A,00,FA,6E,24,FB,91,0E,C8,00 -1262,1,11,36,EB5C,00,FA,6E,24,FB,C8,D0,FB,00 -1264,1,11,42,EB5D,00,FA,6F,24,FB,D0,FB,E6,00 -1267,1,11,51,EB5A,00,FA,6F,24,FB,91,0E,C8,00 -1273,1,11,69,EB5C,00,FA,6F,24,FB,C8,D0,FB,00 -1275,1,11,75,EB5D,00,FA,70,24,FB,D0,FB,E6,00 -1278,1,11,84,EB5A,00,FA,70,24,FB,91,0E,C8,00 -1284,1,11,102,EB5C,00,FA,70,24,FB,C8,D0,FB,00 -1286,1,11,108,EB5D,00,FA,71,24,FB,D0,FB,E6,00 -1289,1,11,117,EB5A,00,FA,71,24,FB,91,0E,C8,00 -1295,1,11,135,EB5C,00,FA,71,24,FB,C8,D0,FB,00 -1297,1,11,141,EB5D,00,FA,72,24,FB,D0,FB,E6,00 -1300,1,11,150,EB5A,00,FA,72,24,FB,91,0E,C8,00 -1306,1,11,168,EB5C,00,FA,72,24,FB,C8,D0,FB,00 -1308,1,11,174,EB5D,00,FA,73,24,FB,D0,FB,E6,00 -1311,1,11,183,EB5A,00,FA,73,24,FB,91,0E,C8,00 -1317,1,11,201,EB5C,00,FA,73,24,FB,C8,D0,FB,00 -1319,1,11,207,EB5D,00,FA,74,24,FB,D0,FB,E6,00 -1322,1,11,216,EB5A,00,FA,74,24,FB,91,0E,C8,00 -1328,1,11,234,EB5C,00,FA,74,24,FB,C8,D0,FB,00 -1330,1,11,240,EB5D,00,FA,75,24,FB,D0,FB,E6,00 -1333,1,11,249,EB5A,00,FA,75,24,FB,91,0E,C8,00 -1339,1,11,267,EB5C,00,FA,75,24,FB,C8,D0,FB,00 -1341,1,11,273,EB5D,00,FA,76,24,FB,D0,FB,E6,00 -1344,1,11,282,EB5A,00,FA,76,24,FB,91,0E,C8,00 -1350,1,11,300,EB5C,00,FA,76,24,FB,C8,D0,FB,00 -1352,1,11,306,EB5D,00,FA,77,24,FB,D0,FB,E6,00 -1355,1,11,315,EB5A,00,FA,77,24,FB,91,0E,C8,00 -1361,1,11,333,EB5C,00,FA,77,24,FB,C8,D0,FB,00 -1363,1,11,339,EB5D,00,FA,78,24,FB,D0,FB,E6,00 -1366,1,12,7,EB5A,00,FA,78,24,FB,91,0E,C8,00 -1372,1,12,25,EB5C,00,FA,78,24,FB,C8,D0,FB,00 -1374,1,12,31,EB5D,00,FA,79,24,FB,D0,FB,E6,00 -1377,1,12,40,EB5A,00,FA,79,24,FB,91,0E,C8,00 -1383,1,12,58,EB5C,00,FA,79,24,FB,C8,D0,FB,00 -1385,1,12,64,EB5D,00,FA,7A,24,FB,D0,FB,E6,00 -1388,1,12,73,EB5A,00,FA,7A,24,FB,91,0E,C8,00 -1394,1,12,91,EB5C,00,FA,7A,24,FB,C8,D0,FB,00 -1396,1,12,97,EB5D,00,FA,7B,24,FB,D0,FB,E6,00 -1399,1,12,106,EB5A,00,FA,7B,24,FB,91,0E,C8,00 -1405,1,12,124,EB5C,00,FA,7B,24,FB,C8,D0,FB,00 -1407,1,12,130,EB5D,00,FA,7C,24,FB,D0,FB,E6,00 -1410,1,12,139,EB5A,00,FA,7C,24,FB,91,0E,C8,00 -1416,1,12,157,EB5C,00,FA,7C,24,FB,C8,D0,FB,00 -1418,1,12,163,EB5D,00,FA,7D,24,FB,D0,FB,E6,00 -1421,1,12,172,EB5A,00,FA,7D,24,FB,91,0E,C8,00 -1427,1,12,190,EB5C,00,FA,7D,24,FB,C8,D0,FB,00 -1429,1,12,196,EB5D,00,FA,7E,24,FB,D0,FB,E6,00 -1432,1,12,205,EB5A,00,FA,7E,24,FB,91,0E,C8,00 -1438,1,12,223,EB5C,00,FA,7E,24,FB,C8,D0,FB,00 -1440,1,12,229,EB5D,00,FA,7F,24,FB,D0,FB,E6,00 -1443,1,12,238,EB5A,00,FA,7F,24,FB,91,0E,C8,00 -1449,1,12,256,EB5C,00,FA,7F,24,FB,C8,D0,FB,00 -1451,1,12,262,EB5D,00,FA,80,A4,FB,D0,FB,E6,00 -1454,1,12,271,EB5A,00,FA,80,A4,FB,91,0E,C8,00 -1460,1,12,289,EB5C,00,FA,80,A4,FB,C8,D0,FB,00 -1462,1,12,295,EB5D,00,FA,81,A4,FB,D0,FB,E6,00 -1465,1,12,304,EB5A,00,FA,81,A4,FB,91,0E,C8,00 -1471,1,12,322,EB5C,00,FA,81,A4,FB,C8,D0,FB,00 -1473,1,12,328,EB5D,00,FA,82,A4,FB,D0,FB,E6,00 -1476,1,12,337,EB5A,00,FA,82,A4,FB,91,0E,C8,00 -1482,1,13,14,EB5C,00,FA,82,A4,FB,C8,D0,FB,00 -1484,1,13,20,EB5D,00,FA,83,A4,FB,D0,FB,E6,00 -1487,1,13,29,EB5A,00,FA,83,A4,FB,91,0E,C8,00 -1493,1,13,47,EB5C,00,FA,83,A4,FB,C8,D0,FB,00 -1495,1,13,53,EB5D,00,FA,84,A4,FB,D0,FB,E6,00 -1498,1,13,62,EB5A,00,FA,84,A4,FB,91,0E,C8,00 -1504,1,13,80,EB5C,00,FA,84,A4,FB,C8,D0,FB,00 -1506,1,13,86,EB5D,00,FA,85,A4,FB,D0,FB,E6,00 -1509,1,13,95,EB5A,00,FA,85,A4,FB,91,0E,C8,00 -1515,1,13,113,EB5C,00,FA,85,A4,FB,C8,D0,FB,00 -1517,1,13,119,EB5D,00,FA,86,A4,FB,D0,FB,E6,00 -1520,1,13,128,EB5A,00,FA,86,A4,FB,91,0E,C8,00 -1526,1,13,146,EB5C,00,FA,86,A4,FB,C8,D0,FB,00 -1528,1,13,152,EB5D,00,FA,87,A4,FB,D0,FB,E6,00 -1531,1,13,161,EB5A,00,FA,87,A4,FB,91,0E,C8,00 -1537,1,13,179,EB5C,00,FA,87,A4,FB,C8,D0,FB,00 -1539,1,13,185,EB5D,00,FA,88,A4,FB,D0,FB,E6,00 -1542,1,13,194,EB5A,00,FA,88,A4,FB,91,0E,C8,00 -1548,1,13,212,EB5C,00,FA,88,A4,FB,C8,D0,FB,00 -1550,1,13,218,EB5D,00,FA,89,A4,FB,D0,FB,E6,00 -1553,1,13,227,EB5A,00,FA,89,A4,FB,91,0E,C8,00 -1559,1,13,245,EB5C,00,FA,89,A4,FB,C8,D0,FB,00 -1561,1,13,251,EB5D,00,FA,8A,A4,FB,D0,FB,E6,00 -1564,1,13,260,EB5A,00,FA,8A,A4,FB,91,0E,C8,00 -1570,1,13,278,EB5C,00,FA,8A,A4,FB,C8,D0,FB,00 -1572,1,13,284,EB5D,00,FA,8B,A4,FB,D0,FB,E6,00 -1575,1,13,293,EB5A,00,FA,8B,A4,FB,91,0E,C8,00 -1581,1,13,311,EB5C,00,FA,8B,A4,FB,C8,D0,FB,00 -1583,1,13,317,EB5D,00,FA,8C,A4,FB,D0,FB,E6,00 -1586,1,13,326,EB5A,00,FA,8C,A4,FB,91,0E,C8,00 -1592,1,14,3,EB5C,00,FA,8C,A4,FB,C8,D0,FB,00 -1594,1,14,9,EB5D,00,FA,8D,A4,FB,D0,FB,E6,00 -1597,1,14,18,EB5A,00,FA,8D,A4,FB,91,0E,C8,00 -1603,1,14,36,EB5C,00,FA,8D,A4,FB,C8,D0,FB,00 -1605,1,14,42,EB5D,00,FA,8E,A4,FB,D0,FB,E6,00 -1608,1,14,51,EB5A,00,FA,8E,A4,FB,91,0E,C8,00 -1614,1,14,69,EB5C,00,FA,8E,A4,FB,C8,D0,FB,00 -1616,1,14,75,EB5D,00,FA,8F,A4,FB,D0,FB,E6,00 -1619,1,14,84,EB5A,00,FA,8F,A4,FB,91,0E,C8,00 -1625,1,14,102,EB5C,00,FA,8F,A4,FB,C8,D0,FB,00 -1627,1,14,108,EB5D,00,FA,90,A4,FB,D0,FB,E6,00 -1630,1,14,117,EB5A,00,FA,90,A4,FB,91,0E,C8,00 -1636,1,14,135,EB5C,00,FA,90,A4,FB,C8,D0,FB,00 -1638,1,14,141,EB5D,00,FA,91,A4,FB,D0,FB,E6,00 -1641,1,14,150,EB5A,00,FA,91,A4,FB,91,0E,C8,00 -1647,1,14,168,EB5C,00,FA,91,A4,FB,C8,D0,FB,00 -1649,1,14,174,EB5D,00,FA,92,A4,FB,D0,FB,E6,00 -1652,1,14,183,EB5A,00,FA,92,A4,FB,91,0E,C8,00 -1658,1,14,201,EB5C,00,FA,92,A4,FB,C8,D0,FB,00 -1660,1,14,207,EB5D,00,FA,93,A4,FB,D0,FB,E6,00 -1663,1,14,216,EB5A,00,FA,93,A4,FB,91,0E,C8,00 -1669,1,14,234,EB5C,00,FA,93,A4,FB,C8,D0,FB,00 -1671,1,14,240,EB5D,00,FA,94,A4,FB,D0,FB,E6,00 -1674,1,14,249,EB5A,00,FA,94,A4,FB,91,0E,C8,00 -1680,1,14,267,EB5C,00,FA,94,A4,FB,C8,D0,FB,00 -1682,1,14,273,EB5D,00,FA,95,A4,FB,D0,FB,E6,00 -1685,1,14,282,EB5A,00,FA,95,A4,FB,91,0E,C8,00 -1691,1,14,300,EB5C,00,FA,95,A4,FB,C8,D0,FB,00 -1693,1,14,306,EB5D,00,FA,96,A4,FB,D0,FB,E6,00 -1696,1,14,315,EB5A,00,FA,96,A4,FB,91,0E,C8,00 -1702,1,14,333,EB5C,00,FA,96,A4,FB,C8,D0,FB,00 -1704,1,14,339,EB5D,00,FA,97,A4,FB,D0,FB,E6,00 -1707,1,15,7,EB5A,00,FA,97,A4,FB,91,0E,C8,00 -1713,1,15,25,EB5C,00,FA,97,A4,FB,C8,D0,FB,00 -1715,1,15,31,EB5D,00,FA,98,A4,FB,D0,FB,E6,00 -1718,1,15,40,EB5A,00,FA,98,A4,FB,91,0E,C8,00 -1724,1,15,58,EB5C,00,FA,98,A4,FB,C8,D0,FB,00 -1726,1,15,64,EB5D,00,FA,99,A4,FB,D0,FB,E6,00 -1729,1,15,73,EB5A,00,FA,99,A4,FB,91,0E,C8,00 -1735,1,15,91,EB5C,00,FA,99,A4,FB,C8,D0,FB,00 -1737,1,15,97,EB5D,00,FA,9A,A4,FB,D0,FB,E6,00 -1740,1,15,106,EB5A,00,FA,9A,A4,FB,91,0E,C8,00 -1746,1,15,124,EB5C,00,FA,9A,A4,FB,C8,D0,FB,00 -1748,1,15,130,EB5D,00,FA,9B,A4,FB,D0,FB,E6,00 -1751,1,15,139,EB5A,00,FA,9B,A4,FB,91,0E,C8,00 -1757,1,15,157,EB5C,00,FA,9B,A4,FB,C8,D0,FB,00 -1759,1,15,163,EB5D,00,FA,9C,A4,FB,D0,FB,E6,00 -1762,1,15,172,EB5A,00,FA,9C,A4,FB,91,0E,C8,00 -1768,1,15,190,EB5C,00,FA,9C,A4,FB,C8,D0,FB,00 -1770,1,15,196,EB5D,00,FA,9D,A4,FB,D0,FB,E6,00 -1773,1,15,205,EB5A,00,FA,9D,A4,FB,91,0E,C8,00 -1779,1,15,223,EB5C,00,FA,9D,A4,FB,C8,D0,FB,00 -1781,1,15,229,EB5D,00,FA,9E,A4,FB,D0,FB,E6,00 -1784,1,15,238,EB5A,00,FA,9E,A4,FB,91,0E,C8,00 -1790,1,15,256,EB5C,00,FA,9E,A4,FB,C8,D0,FB,00 -1792,1,15,262,EB5D,00,FA,9F,A4,FB,D0,FB,E6,00 -1795,1,15,271,EB5A,00,FA,9F,A4,FB,91,0E,C8,00 -1801,1,15,289,EB5C,00,FA,9F,A4,FB,C8,D0,FB,00 -1803,1,15,295,EB5D,00,FA,A0,A4,FB,D0,FB,E6,00 -1806,1,15,304,EB5A,00,FA,A0,A4,FB,91,0E,C8,00 -1812,1,15,322,EB5C,00,FA,A0,A4,FB,C8,D0,FB,00 diff --git a/docs/STATUS.md b/docs/STATUS.md index 9ed61da9..7e922049 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -1,6 +1,20 @@ # RustyNES — Project Status Matrix -> **Current release: v2.2.4** (2026-07-24) — **"Cartridge"**, a **libretro / +> **Current release: v2.2.5** (2026-08-03) — **"Colophon"**, a **provenance, +> licensing, and documentation-integrity** release with **zero emulation-core +> behavior changes** (so **AccuracyCoin holds 141/141 (100.00%)**, nestest 0-diff, and the +> `#![no_std]` chip stack, save-state / TAS / netplay formats, and every golden +> vector are byte-identical to v2.2.4 by construction). It reworded in-source +> comments that had mischaracterized publicly-documented hardware-behavior +> implementations as "ports of" copyleft emulators (Mesen2, puNES); rewrote +> `NOTICE` to disclose the behavioral-oracle use of GPL emulators (no code +> incorporated) and to attribute the incorporated permissive components (emu2413, +> TriCNES, rcheevos — MIT), the bundled fonts and test ROMs, and the CRT-shader / +> NTSC-filter visual influences as independent reimplementations; added +> `docs/originality-and-provenance.md`; and added an AI-assistance disclosure to +> the README (with a misleading comparison graphic removed and a mislabeled +> screenshot caption fixed). On top of **v2.2.4** (2026-07-24) — **"Cartridge"**, +> a **libretro / > RetroArch distribution** cut whose purpose is that the RustyNES core builds and > installs cleanly through the Libretro buildbot > () for in-RetroArch use. **Zero diff --git a/docs/originality-and-provenance.md b/docs/originality-and-provenance.md new file mode 100644 index 00000000..f51aea30 --- /dev/null +++ b/docs/originality-and-provenance.md @@ -0,0 +1,420 @@ +# Engineering Originality and Provenance + +This document explains where RustyNES advances, diverges from, or independently +re-derives NES emulation technique; how the project was actually built (research +first, test-driven, measured); and how it treats the licenses of the reference +emulators and test ROMs consulted during development. + +It is written to be **honest rather than triumphal**. RustyNES is not a clean-room +project that never looked at prior art, and it is not a fork or a translation of +another emulator either. It is an independent implementation whose *architecture* +and *engineering method* are its own, and which incorporates a small number of +clearly-attributed components from permissively-licensed projects while using +copyleft-licensed emulators only as behavioral oracles. The sections below spell +out exactly which is which, with file-level and ADR-level citations so the claims +can be checked against the tree. + +Authoritative companions to this document: `docs/STATUS.md` (per-suite pass +counts and the mapper matrix), `CHANGELOG.md` (user-visible history), `docs/adr/` +(the decision record), `NOTICE` (the legal attribution file), and +`tests/roms/LICENSES.md` (test-ROM provenance). + +--- + +## 1. Thesis: an independent build with attributed borrowings + +The honest claim RustyNES can make is not "no line resembles any other emulator." +It is this: + +- **The architecture is original.** The scheduler substrate, the ownership model, + the crate/dependency topology, the determinism contract, the accuracy-honesty + gates, and the save-state schema discipline are RustyNES's own design decisions, + recorded as ADRs and implemented in its own `#![no_std]` Rust idiom. +- **The engineering method is original and auditable.** Behaviors are implemented + from public hardware documentation, pinned to public test ROMs first, and every + performance change is measured — including the ones that were measured and + *rejected*. The discipline is machine-checked in CI, not asserted in prose. +- **Specific algorithms are deliberately, transparently borrowed** from + permissively-licensed projects (TriCNES, emu2413, rcheevos), each attributed in + source and in `NOTICE` under its MIT license. +- **Copyleft-licensed emulators were used only as oracles** — to observe and + cross-check documented hardware behavior — never as a source of copied code. + +Put differently: RustyNES's originality lives less in any single novel algorithm +(most hardware behaviors are, by definition, shared by every accurate emulator) +and more in the *system* that produces and guarantees that accuracy. That is the +claim the rest of this document substantiates. + +**A note on AI assistance.** RustyNES is heavily AI-assisted software: much of it +was produced with LLM tooling under a human-directed, test-driven workflow, with +public test ROMs as the oracle, a `no_std` core as a hard baseline, and continuous +CI as the gate. That is disclosed plainly here and in the README because it belongs +in an honest provenance record — and because the licensing lapses this document +corrects (comments that called hardware-behavior implementations "ports" of +copyleft emulators) are exactly the kind of mistake AI-assisted authoring is prone +to. The remedy is the same either way: audit against the sources, attribute +accurately, and let the machine-checked gates — not the prose — carry the accuracy +claims. + +**Not a superiority claim.** Nothing here asserts that RustyNES is "better" than +the emulators that came before it. Where this document compares RustyNES to a +reference, the comparison is exactly that — a comparison against a project RustyNES +was measured against — and every accuracy figure is independently checkable by +running the public suites (see the README Acknowledgments for the references and +components the project builds on). + +--- + +## 2. Where RustyNES advances or diverges from prior art + +Each subsection names the mechanism, the measurable result where one exists, the +governing ADR, and — where relevant — the specific reference emulator RustyNES +agrees or disagrees with. + +### 2.1 The one-clock, every-cycle-bus-access timebase (ADR 0029) + +Most NES emulators either batch subsystem work per scanline/instruction (fast, +less accurate) or run a multi-counter dot-lockstep (accurate, complex). RustyNES's +v2.0.0 "Timebase" rewrite collapses scheduling to a **single canonical cycle +counter** in which *every* CPU cycle is a real bus access, and PPU catch-up is +split around that access via paired `start_cycle` / `end_cycle` hooks. This makes +sub-instruction PPU state visible to the very next CPU read without per-quirk +patches — mid-scanline scroll writes, a sprite-zero hit at a precise dot, an MMC3 +IRQ at PPU dot 260 all fall out of the model rather than being special-cased. + +The structural choice mirrors Mesen2's cycle-stepped approach conceptually, but +the implementation, the counter model, and the split-around-access hook design are +RustyNES's own (`crates/rustynes-core`, `docs/scheduler.md`). It is a deliberate +MAJOR-boundary change: the old five-counter dot-lockstep scheduler was retired +outright, and the save-state / movie formats broke by design (see 2.9 and ADR +0028). See ADR 0029 for the full rationale. + +### 2.2 The 2-cycle-ALE octal-latch PPU fetch: an independent, transistor-literal model (ADR 0030) + +This is a clear example of independent, evidence-led accuracy work. The PPU +multiplexes its low VRAM address pins with the data pins; an external +74LS373-class octal latch captures the low address bits on the address-latch-enable +(ALE) half of each two-cycle VRAM access, and the PPU drives only the high bits on +the read half. When those halves desync (a mid-fetch `$2006` update, or a `$2007` +read overlapping the fetch cadence), the PPU reads a "hybrid" address it never +coherently drove. + +Two AccuracyCoin tests ("ALE + Read", `$0491`; "Hybrid Addresses", `$0492`) +exercise exactly this, and RustyNES passes both by modeling the octal latch +explicitly. The instructive part is *how the references differ* (ADR 0030): +Mesen2 also passes these tests, but via a persistent internal bus-address +abstraction rather than a literal latch; higan and ares, by contrast, genuinely +fail them (higan blocks `$2007` during rendering and models no bus latch; ares +does not implement the `$2006` hybrid corruption). RustyNES deliberately took the +transistor-literal modeling approach of TriCNES — the die-level emulator by the +AccuracyCoin author — over the higher-level abstraction, because a physical +octal-latch model is what makes the hybrid-address cases fall out of the design +rather than being special-cased. It promoted the 2-cycle-ALE fetch to the +unconditional default in v2.0.3 (both prior experimental flags retired). See ADR +0030 for the campaign audit. This is independent modeling, not copying: RustyNES +re-derived the physical mechanism from die-level evidence, converging with some +references and diverging from others on the strength of the hardware model rather +than by following any single one of them. + +### 2.3 The sprite-evaluation FSM and OAM data bus (ADR 0034) + +RustyNES models the PPU's sprite-evaluation datapath as an explicit per-dot state +machine (secondary-OAM clear at dots 1-64, evaluation at 65-256, sprite fetch at +257-320) plus an isolated OAM-data-bus model that reproduces what `$2004` returns +while the screen is drawn. A standing field-vs-schema audit (2.4) found that this +FSM state and the OAM data-bus latch were not fully serialized, which is what let +AccuracyCoin regress under run-ahead; serializing them (PPU snapshot version 8) +restored a full pass through run-ahead as well as without it. The model is +implemented from the NESdev-documented sprite-evaluation sequence; see ADR 0034. + +### 2.4 Machine-checked accuracy honesty: mapper tiering and schema audits (ADR 0011) + +Rather than claim uniform accuracy, RustyNES classifies every mapper family into +**Core / Curated / BestEffort** tiers and enforces, via a CI honesty gate, that +the suite cannot advertise support or accuracy it does not actually verify against +a test ROM or oracle. As of the v2.2.x line this covers 172 mapper families across +the three tiers (see `docs/STATUS.md` for the current split and the authoritative +counts). A second machine check, `snapshot_schema_audit`, parses the emulator's +live struct fields and fails the build if any new stateful field is not covered by +the save-state schema — the mechanism that mechanically surfaced the gap in 2.3. +Honesty here is a build gate, not a promise. See ADR 0011. + +### 2.5 Determinism as a hard contract (the `#![no_std]` core) + +The chip stack (`rustynes-{cpu,ppu,apu,mappers,core}`) is `#![no_std]` + +`extern crate alloc`, with a strictly one-directional dependency graph in which the +Bus owns all mutable subsystems and each chip borrows the narrowest trait it needs. +The contract is exact: same seed + ROM + input sequence yields a bit-identical +framebuffer and audio stream. Power-on CPU/PPU phase alignment is drawn from a +seeded PRNG and preserved across reset, save-state, TAS replay, and netplay +rollback. Wall-clock, OS RNG, thread scheduling, and unordered-map iteration are +kept out of the core by construction. This is what makes the entire test and +regression apparatus meaningful, and it is enforced by the `no_std` cross-compile +job (`thumbv7em-none-eabihf`, no default features) in CI. See +`docs/architecture.md`. + +### 2.6 Measure-first performance, including documented rejections + +RustyNES treats performance as an accuracy-subordinate, evidence-gated activity: a +change is adopted only if it is Criterion-stable above a threshold **and** proven +byte-identical by the differential net, and it is documented in `docs/performance.md` +*whether or not it cleared the bar*. Concrete outcomes: + +- The specialized fast PPU dot path was measured at roughly **-11.3%** frame time + on a rendering-heavy workload (clean-host Criterion, v2.2.3), differential-tested + bit-identical every frame, and only then promoted to the default and exposed to + users. +- Two optimizations were **measured and rejected with their numbers**: an + `emit_pixel` bounds-check elision made the shipped default *slower* + (+4.32% / +3.35% on the fast workloads, p <= 0.02), and a `cpu_clock` + micro-optimization was capped at <= 1.9% with the textbook wins already in place. +- Release builds ship PGO-optimized Linux binaries only when the >3%-and-byte- + identical gate passes; a same-runner relative frame-time regression gate closes a + hole the deliberately-loose absolute ceiling left open. + +Publishing rejected optimizations with p-values is unusual and is itself a form of +originality: the record shows the discipline, not just the wins. See +`docs/performance.md`. + +### 2.7 Signal-level video and expansion-audio calibration + +RustyNES includes a raw NTSC composite signal-decode path (`rustynes-ppu::raw_signal`) +feeding a naga-validated WGSL CRT-shader stack, and a decibel oracle that asserts +measured expansion-audio channel levels against hardware / Mesen2 targets (which, +for the Sunsoft 5B, required widening the mapper audio-mix path to `i32` to +represent full-scale tone without overflow). The base 2A03 NTSC output remains +byte-identical across these additions. See `docs/performance.md`, `docs/ppu-2c02.md`, +and the audio expansion oracle in `crates/rustynes-test-harness`. + +### 2.8 Rollback netplay kept out of the deterministic core + +Netplay's dynamic rate control, run-ahead, and snapshot-restore orchestration live +entirely in the frontend; the core's synthesis never sees them. This is what lets +the same deterministic core serve save-states, TAS replay, and rollback netplay +without any of them perturbing byte-identity. Keeping timing jitter and rate +control at the frontend boundary — never in the core — is a deliberate ownership +decision (`docs/frontend.md`, `docs/architecture.md`). + +### 2.9 Explicit, versioned save-state schema (ADR 0028) + +Save-state and movie formats carry explicit version epochs. A pre-v2.0.0 slot +fails to load with a clear error rather than silently misinterpreting stale bytes, +and additive schema growth (e.g. the PPU snapshot version 8 tail in 2.3) upconverts +older blobs where compatible. The one intentional format break is the v2.0.0 +MAJOR boundary; see ADR 0028. + +--- + +## 3. How the project was built + +RustyNES did not begin as a copy to be modified. Its development record shows a +research-first, test-driven, verify-last cadence, and — importantly for the "not a +port" claim — the emulation core was **replaced wholesale** partway through the +project rather than incrementally grown from a single seed. + +**Research before code.** The `ref-docs/` tree holds an immutable hardware and +emulation reference corpus (a 60-plus-source research report plus a set of +emulator technical studies). Behaviors were specified against this documentation +and against public test ROMs before implementation. Corrections to the corpus land +as new dated supplements, never in-place rewrites, so the research record stays +auditable. + +**Test-as-spec.** For accuracy work the failing test-ROM expectation is pinned +first, then code is written until it passes; where the prose docs and a passing +test ROM disagree, the ROM wins and the docs are corrected. The suites in +`tests/roms/` (blargg, kevtris, mmc3_test_2, AccuracyCoin, and others) are treated +as the closed-form definition of "cycle-accurate." + +**A documented lineage, honestly labeled.** The current core is a synthesis, cut +as v1.0.0 on 2026-06-13 (`docs/v1.0.0-synthesis-handoff-2026-06-13.md`), that +replaced the earlier v0.8.x emulation core with a cycle-accurate engine developed +through documentary stages v0.9.0-v0.9.7. Two cautions are recorded so the history +is not misread: + +- The engine lineage carries its own internal "v1.x / v2.x" accuracy milestones + that are *not* RustyNES release versions; they are folded into the v0.9.x stages + and shipped as the v1.0.0 production core. +- Consequently, **two distinct "v2.0"s exist and must not be conflated**: the + engine-lineage master-clock work (which shipped *as* the v1.0.0 core), and + RustyNES's own **v2.0.0 "Timebase"** release (2026-07-03), which *replaces* that + same dot-lockstep scheduler with the one-clock model of 2.1. + +**Then continuous, gated deepening.** After v1.0.0 came the platform ports +(Android, iOS, the libretro/RetroArch core), the v2.0.0 Timebase rewrite, and the +v2.1.x "Fathom" accuracy line capped by the v2.2.0 "Capstone" milestone — each +release additive or default-off on the shipped core, verified NTSC-byte-identical +(AccuracyCoin 141/141) except where a break was explicitly announced (v2.0.0). The +decision record for all of this is `docs/adr/` (0001 through 0034 as of writing), +backed by over a hundred implementation-audit logs under `docs/audit/` (about +113 at time of writing). The +current release is v2.2.5 "Colophon" (this release); `docs/STATUS.md` is the source of truth for +per-suite counts. + +--- + +## 4. Independence: oracle versus port + +The distinction that matters for the "not just a port" question is **how** each +reference was used. RustyNES's sources fall into three categories, and the source +tree is written so a reader can tell which applies at any given site. + +1. **Implemented from public hardware documentation.** The overwhelming majority + of chip, mapper, and peripheral behavior is written from the NESdev wiki, + Disch's mapper write-ups, published datasheets (e.g. the Xicor/Intersil I2C + serial EEPROMs, the Yamaha YM2413), the documented 6502 unofficial-opcode + behavior, and the Visual 6502 / Visual 2C02 die studies — then pinned to public + test ROMs. Hardware behavior is factual; every accurate emulator necessarily + agrees on it. +2. **Ported from a permissively-licensed project, with attribution.** A small, + named set of components is genuinely incorporated as a Rust port under a + compatible (MIT) license — principally TriCNES (the PPU address/data-multiplex + and OAM-corruption models; see `crates/rustynes-ppu/src/ppu.rs`), the emu2413 + OPLL synthesizer for VRC7 audio, and the rcheevos RetroAchievements runtime. + Each carries an in-source attribution and a `NOTICE` entry (Section 5.3). +3. **Consulted only as a behavioral oracle.** Copyleft-licensed emulators + (Mesen2/MesenCE and higan and GeraNES under GPLv3; FCEUX, Nestopia UE, and + puNES under GPLv2) — plus ares (ISC) — were run to observe and cross-check + documented behavior when test-ROM results were ambiguous. No code from any of + them is incorporated. + +The octal-latch work in 2.2 illustrates the difference between categories 2 and 3: +RustyNES took TriCNES's transistor-literal *modeling approach* for the ALE fetch +(a permissively-licensed influence) while treating Mesen2, higan, and ares purely +as oracles to check the result — passing `$0491` / `$0492` where higan and ares +fail, and by a more physical model than Mesen2's abstraction. That is independent +modeling, not copying. + +**A note on the provenance record.** The in-source provenance comments were +audited to make sure they accurately reflect the categories above. A number of +comments in the shipping crates had described hardware-behavior implementations +(CPU unstable stores, the PPU sprite-evaluation and OAM models, and numerous +mapper register decoders) as "ports of" a copyleft reference — Mesen2 (GPLv3), or +FCEUX / puNES (GPLv2) — which overstated the relationship for behaviors that are, +in fact, implemented from public hardware documentation. Those comments were +corrected to cite the public hardware source and to record the copyleft emulator +as a behavioral cross-check rather than a code source; GeraNES (GPLv3) was added +to the disclosed oracle set; and `NOTICE` was extended to state the oracle-versus- +incorporated posture explicitly and to reproduce the MIT notices for the +incorporated components (Section 5.3). These corrections changed only comments and +the attribution file; the emulator's behavior is byte-identical, re-verified +against AccuracyCoin (141/141, including run-ahead), the nestest golden log +(0-diff), and the dual-path differential net. The video shader stack and the +NTSC-decode filters are a separate provenance matter, addressed in Section 5.6. + +--- + +## 5. License compliance + +### 5.1 RustyNES's own license + +RustyNES is dual-licensed **MIT OR Apache-2.0** (author: DoubleGate), the +conventional permissive dual-license for the Rust ecosystem. This choice is +deliberately compatible with the permissively-licensed components it incorporates +and deliberately does *not* subject the project to the copyleft terms of the +reference emulators it merely consulted. + +### 5.2 Reference emulators: oracle use, not code reuse + +The projects below were used only as behavioral oracles / accuracy references. No +source code from any of them is incorporated into RustyNES; this is stated in +`NOTICE` and reflected in the in-source comments (Section 4). + +| Reference emulator | License | Use in RustyNES | +| --- | --- | --- | +| Mesen2 / MesenCE | GPLv3 | Behavioral oracle / accuracy cross-check only | +| higan | GPLv3 | Accuracy reference for scheduler structure | +| ares | ISC | Accuracy reference for scheduler structure | +| GeraNES | GPLv3 | Behavioral oracle / cross-check for several mapper boards | +| FCEUX | GPLv2 | Behavioral oracle for legacy-compat behaviors | +| Nestopia UE | GPLv2 | Behavioral oracle | +| puNES | GPLv2 | Behavioral oracle | + +Using a GPL-licensed program to *observe* hardware behavior, and then implementing +that publicly-documented behavior independently, does not create a derivative work +of that program. The point of the Section 4 audit was to make the source comments +say precisely that, so nothing in the tree could be read as claiming a copyleft +source was translated into this permissive project. + +### 5.3 Incorporated third-party components (permissive) + +These works are genuinely incorporated and are attributed in `NOTICE` with their +copyright notices and the MIT permission text: + +| Component | License | Copyright | Where | +| --- | --- | --- | --- | +| emu2413 v1.5.9 | MIT | 2020 Mitsutaka Okazaki | `crates/rustynes-apu/src/opll.rs` (Rust port; VRC7 audio, ADR 0006) | +| TriCNES (commit 9199870) | MIT | 2025 Chris Siebert | `crates/rustynes-{ppu,cpu,core}` (ported models) + vendored golden oracle | +| rcheevos v12.3.0 | MIT | 2018 RetroAchievements.org | `crates/rustynes-cheevos/vendor/rcheevos/` (optional `retroachievements` feature) | +| Font Awesome Free | its own license | Fonticons, Inc. | `crates/rustynes-frontend/assets/fonts/` (bundled glyphs) | + +The emu2413 port is a pure-Rust port of the upstream MIT C source (ADR 0006), +distributed under that MIT license; the upstream MIT notice is now reproduced in +`NOTICE` as that file's own comment claims. TriCNES is both a ported source (its ALE/octal-latch, +OAM-corruption, and DMA-dispatch models) and a vendored golden oracle for the +tests it grounds. rcheevos is compiled only when the RetroAchievements feature is +enabled and keeps its own in-tree `LICENSE`. + +### 5.4 Test ROMs + +Every ROM committed under `tests/roms/` is a public-domain work released +specifically for validating NES emulators, catalogued per-author in +`tests/roms/LICENSES.md` (blargg's suites, kevtris/AccuracyCoin material, and +others). **No commercial Nintendo software is bundled**, and none ever should be; +users who want to test against commercial dumps they own place them in the +gitignored `tests/roms/external/`. The AccuracyCoin battery itself is MIT-licensed +(Chris Siebert / 100thCoin). + +### 5.5 Vendored and immutable trees + +RustyNES vendors several third-party source trees whose value depends on their +being byte-identical to upstream (the TriCNES golden oracle, the rcheevos runtime, +upstream test-ROM READMEs, and the `ref-docs/` / `ref-proj/` reference material). +These are protected from accidental reformatting: `.markdownlintignore` exempts +them from markdown linting, a shared `exclude` anchor in the pre-commit +configuration keeps the whitespace-rewriting hooks off content the project did not +author, and `ref-proj/` is gitignored while `ref-docs/` is treated as immutable +(corrections land as dated supplements). This preserves both the integrity of the +oracles and the upstream provenance of the vendored code. + +### 5.6 Video shaders and NTSC-decode filters + +The optional CRT shader stack (`crates/rustynes-gfx-shaders/`) and the NTSC-decode +filters (`crates/rustynes-frontend/src/ntsc_bisqwit.rs`, `ntsc_lmp88959.rs`) +reproduce the *look* of well-known community shaders and filters — CRT-Royale +(TroggleMonkey, GPLv2+), crt-guest-advanced (guest.r), Sony Megatron +(MajorPainInTheCactus), Bisqwit's NES composite model, and EMMIR's NTSC-CRT +(permissive). These were reviewed at the source level. Each is a single +fullscreen pass built on RustyNES's own uniform / pipeline conventions and is +structurally incompatible with being a translation of the upstream *multi-pass* +shader source. Because copyright protects code expression — not a visual look or +a rendering technique — these are independent reimplementations, not derivative +works of the upstream code, even where an upstream is copyleft; no upstream +shader source is incorporated. The one comment that had implied otherwise (an +NTSC filter reading "ported verbatim from Bisqwit's C ... as implemented by +Mesen2") was corrected: those tables encode the two-level NES composite signal +documented at the NESdev wiki ("NTSC video") — a hardware model, not copied code. +The in-source comments were reworded accordingly, and `NOTICE` now credits each +project as a "visual influence, independently reimplemented (no code +incorporated)". All of these features are optional and default-off; none affects +the deterministic emulation core, its `AccuracyCoin` results, or the base NTSC +framebuffer, which are unchanged. + +--- + +## 6. Conclusion + +RustyNES is an independent emulator, not a port. Its scheduler, ownership model, +determinism contract, accuracy-honesty gates, and measured-performance discipline +are its own, recorded as ADRs and enforced in CI rather than asserted. Where it +borrows, it borrows narrowly and openly, under compatible permissive licenses, +with attribution in both source and `NOTICE`. Where it consulted copyleft +references, it used them as oracles to check publicly-documented hardware behavior, +and — as the octal-latch case shows — it was willing to disagree with a leading +reference when the transistor-level evidence pointed the other way. + +The strongest evidence for originality is not any single clever routine; it is the +system that surrounds every routine: research before code, a failing test pinned +first, a hard byte-identity contract, honesty gates that fail the build rather than +the reader, and a decision record that documents the rejections alongside the wins. +That system is what makes RustyNES's accuracy claims checkable — and it is what +this project built for itself. diff --git a/tests/roms/LICENSES.md b/tests/roms/LICENSES.md index 2868ab67..6b93a468 100644 --- a/tests/roms/LICENSES.md +++ b/tests/roms/LICENSES.md @@ -117,10 +117,11 @@ These ROMs are cartridge-PCB-assembly tests that detect the mapper via mirroring tests, then size PRG/CHR and exercise bank reachability. Output is **visual** (on-screen text + Morse-coded audio beeps), not the blargg `$6000` status protocol — so the integration tests in -`crates/nes-test-harness/tests/holy_mapperel.rs` are smoke gates. +`crates/rustynes-test-harness/tests/holy_mapperel.rs` are smoke gates. -We exclude `M28*`, `M78.3*`, `M118*`, `M180*` because the project does -not implement those mappers (per `docs/STATUS.md` §"Mapper coverage"). +The Holy Mapperel ROMs for mappers 28, 78.3, 118, and 180 are also committed +under `extra/mappers/` (same Damian Yerrick zlib license) for manual +investigation; see "Additional committed test-ROM directories" below. ## DPCM Letterbox (Damian Yerrick / Tepples) @@ -153,7 +154,9 @@ deterministic frame-hash visual smoke (no input) in | `AccuracyCoin/sub-tests/frame-counter-irq.nes` | derived from `AccuracyCoin.asm` (suite 13 / test 2 — `TEST_FrameCounterIRQ`) | NROM (0) | derivative of Chris Siebert | MIT (inherits) | | `AccuracyCoin/sub-tests/apu-reg-activation.nes` | derived from `AccuracyCoin.asm` (suite 13 / test 6 — `TEST_APURegActivation`) | NROM (0) | derivative of Chris Siebert | MIT (inherits) | -The four sub-test ROMs under `AccuracyCoin/sub-tests/` are derivative +The sub-test ROMs under `AccuracyCoin/sub-tests/` (26 in total; the four +with dedicated Rust regression tests are tabulated above, and the rest follow +the identical build + MIT-inheritance pattern) are derivative works produced by patching the upstream `AccuracyCoin.asm` source to jump directly into a single target test at boot (bypassing both the menu-screen and the full-battery loop). They are built by @@ -163,7 +166,7 @@ script; the patched `AutomaticallyRunEveryTestInROM` routine is streamlined to "set Y=suite_idx, X=test_idx, JSR RunTest, halt" and the boot path's `InfiniteLoop` spin is redirected to enter that wrapper immediately. Each sub-test ROM reaches its target test by frame ~30 on -RustyNES (verified via `crates/nes-test-harness/src/bin/ +RustyNES (verified via `crates/rustynes-test-harness/src/bin/ validate_sub_test_rom.rs`), unblocking the Session-22 Mesen2 wall-time oracle blocker for the v1.0.0-final Phase 3 / Phase 4 work. @@ -172,15 +175,12 @@ fetched 2026-05-10). Upstream `LICENSE` file is the MIT License ("Copyright (c) 2025 Chris Siebert"); the full text is vendored alongside the .nes file. -AccuracyCoin is a single-NROM-cartridge battery of ~139 NES accuracy -tests. The ROM is **interactive** — pass/fail results are reported -visually (on-screen "PASS"/"FAIL" + hex error codes) and the user -navigates the test menu with D-Pad + A + Start. There is no `$6000` -status protocol, so the integration test in -`crates/nes-test-harness/tests/accuracycoin.rs` is a boot-without-crash -smoke gate only. v1.0.0 will need a pixel-decoding harness to extract -the pass rate (currently un-measured; the ≥ 90% bar is documented in -`docs/STATUS.md` §"Version policy"). +AccuracyCoin is a single-NROM-cartridge battery of NES accuracy tests. The ROM is +**interactive** — pass/fail results are reported visually (on-screen "PASS"/"FAIL" +plus hex error codes) with no `$6000` status protocol. The integration test in +`crates/rustynes-test-harness/tests/accuracycoin.rs` decodes the per-test result +state from RAM and asserts the measured pass rate, which RustyNES holds at +**141/141 (100.00%)** (see `docs/STATUS.md`). ## "full palette" ROMs @@ -255,6 +255,31 @@ quirk that affects mappers (like MMC3) that depend on A12 for IRQ counter clocking. The MMC1 path is the control case (no A12-based IRQ). Sourced from `MMC1_A12/` in `nes-test-roms`. +## Additional committed test-ROM directories (blanket coverage) + +Beyond the individually-tabulated suites above, the repository commits the +directories below under `tests/roms/`. All are public-domain or permissive +homebrew from the same authors and aggregator already documented in this file; +none is commercial software. + +- `nes-test-roms/` (89 `.nes` committed): the committed subset of the working + clone of `christopherpow/nes-test-roms`, the public-domain/permissive aggregator + named in this file's header. Each suite retains its own author and license (blargg, + kevtris, tepples, Damian Yerrick, NewRisingSun, bbbradsmith, and others); the + suites RustyNES actively gates are tabulated in the sections above. +- `extra/` (56 `.nes`: `apu/` 19, `cpu/` 3, `mappers/` 23, `ppu/` 11): a curated + overflow of the same suites — additional blargg APU/CPU/PPU tests (public + domain), Damian Yerrick Holy Mapperel variants (zlib, including the mapper 28 + / 78.3 / 118 / 180 boards), and related homebrew — kept out of the + actively-gated set but retained for manual investigation. +- `AccuracyCoin/sub-tests/` (26 `.nes`): the boot-into-one-test derivatives of + `AccuracyCoin.asm` described above, all MIT (inheriting upstream). + +The authoritative running total is 328 committed `.nes` files under +`tests/roms/` (per `git ls-files`; excluding the gitignored `tests/roms/external/` +and any untracked clone contents); no commercial +ROM is among them. + ## Notes - The `tests/roms/external/` directory is gitignored and reserved for diff --git a/tests/roms/README.md b/tests/roms/README.md index 72c303a1..47de29af 100644 --- a/tests/roms/README.md +++ b/tests/roms/README.md @@ -7,7 +7,7 @@ under a committed corpus is under a public-domain / CC0 / MIT / BSD / zlib / equivalently-permissive license. Full provenance and licensing is in [`LICENSES.md`](./LICENSES.md). -The integration tests in `crates/nes-test-harness/tests/` consume these +The integration tests in `crates/rustynes-test-harness/tests/` consume these files directly. The full workspace test count (510 + 6 `#[ignore]`'d expected-fails across 34 suites with `--features test-roms`) is gated on this corpus. @@ -22,7 +22,7 @@ on this corpus. | [`holy_mapperel/`](./holy_mapperel/) | Damian Yerrick's "Holy Mapperel" cartridge-PCB-assembly tests (mapper-detection + bank-reachability). 17 ROMs covering mappers 0, 1, 2, 3, 4, 7, 9, 10, 34, 66, 69. | Damian Yerrick / tepples | zlib | | [`mmc5/`](./mmc5/) | MMC5 (mapper 5) accuracy suite from `christopherpow/nes-test-roms`: split-screen, ExRAM modes, scanline IRQ. | Various (aggregator) | Public domain | | [`accuracycoin/`](./accuracycoin/) | Chris Siebert's 144-test single-NROM AccuracyCoin battery — the **single source of truth** for the v0.9.x → v1.0.0 quality bar. | Chris Siebert (100thCoin) | MIT | -| [`AccuracyCoin/`](./AccuracyCoin/) | The upstream `SOURCE_CATALOG.tsv` (144 test-name catalog parsed by the RAM-direct decoder) plus a copy of `AccuracyCoin.nes` for symmetry. The test catalog is `include_str!`ed by `nes-test-harness::accuracy_coin_catalog`. | Chris Siebert | MIT | +| [`AccuracyCoin/`](./AccuracyCoin/) | The upstream `SOURCE_CATALOG.tsv` (144 test-name catalog parsed by the RAM-direct decoder) plus a copy of `AccuracyCoin.nes` for symmetry. The test catalog is `include_str!`ed by `rustynes-test-harness::accuracy_coin_catalog`. | Chris Siebert | MIT | | [`audio-tests/`](./audio-tests/) | Brad Smith (`bbbradsmith`)'s `nes-audio-tests` corpus — expansion-audio relative-loudness comparisons, VRC7 / N163 / FME-7 / MMC5 audio quirks, APU DAC linearity. Covers mappers 5, 19, 24, 26, 69, 85. | Brad Smith | "Freely redistributed and modified for any purpose" (effectively PD) | | [`m22/`](./m22/) | NewRisingSun's VRC2 (mapper 22) CHR-banking smoke test. | NewRisingSun (aggregated in `christopherpow/nes-test-roms`) | Public domain (aggregator) | | [`mmc1_a12/`](./mmc1_a12/) | tepples's MMC1 + PPU A12 transition test (control case for the MMC3 A12-IRQ axis). | tepples (aggregated) | Public domain (aggregator) | @@ -85,10 +85,10 @@ cargo test --workspace cargo test --workspace --features test-roms # Single corpus: -cargo test -p nes-test-harness --features test-roms blargg -cargo test -p nes-test-harness --features test-roms mmc3 -cargo test -p nes-test-harness --features test-roms accuracy_coin -cargo test -p nes-test-harness --features test-roms,commercial-roms external_real_games +cargo test -p rustynes-test-harness --features test-roms blargg +cargo test -p rustynes-test-harness --features test-roms mmc3 +cargo test -p rustynes-test-harness --features test-roms accuracy_coin +cargo test -p rustynes-test-harness --features test-roms,commercial-roms external_real_games ``` The `commercial-roms` feature is gated separately precisely because it diff --git a/to-dos/ROADMAP.md b/to-dos/ROADMAP.md index 156be199..785c72ea 100644 --- a/to-dos/ROADMAP.md +++ b/to-dos/ROADMAP.md @@ -18,13 +18,17 @@ engine + the ported desktop-UX shell + production polish) → **`v1.1.0` (+ the `v1.4.1` patch) **→ `v1.5.0` "Lens" → `v1.6.0` "Studio" → `v1.7.0` "Forge"** (+ the `v1.7.1` patch) **→ `v1.8.0` … `v1.8.9` "Atlas"** (the Android platform train) **→ `v1.9.0` … `v1.9.9` "Workshop"** (the iOS/iPadOS TestFlight -train) **→ `v1.10.0` "Arcade"** (the native Libretro core) — the additive, -off-by-default feature/platform releases on that core, of which **`v1.10.0` -"Arcade" is the current shipped tag**. The forward path then lands the real -**RustyNES `v2.0.0` "Timebase"** (the one-clock/every-cycle-bus-access -scheduler collapse, ADR 0002/0029) — **code-complete on `main` as of -2026-07-03, tag pending** — then the **v2.0.1 → v2.1.0** mobile-finalization -train that launches the Android + iOS apps jointly at **v2.1.0**, and beyond. +train) **→ `v1.10.0` "Arcade"** (the native Libretro core) — all additive, +off-by-default feature/platform releases on the v1.0.0 core. The forward path +then landed the breaking **RustyNES `v2.0.0` "Timebase"** (the +one-clock/every-cycle-bus-access scheduler collapse, ADR 0002/0029, shipped +2026-07-03), the **v2.0.1 → v2.0.9 "Harbor"** mobile-finalization train, the +**v2.1.0 → v2.1.10 "Fathom"** accuracy line, the **v2.2.0 "Capstone"** milestone +that closed the "deepen the existing project" run, and the maintenance / +distribution / provenance patches **v2.2.1 → v2.2.5** — of which **`v2.2.5` +"Colophon" (provenance, licensing, and documentation integrity) is the current +shipped tag**. The **joint Google Play + Apple App Store + AltStore PAL + F-Droid +launch** (activating `rustynes-monetization`) is the future **`v2.3.0`**. Where the detailed sections below carry the inbound engine's own `v1.x`/`v2.x` tags, read them as upstream engine history (its v2.0–v2.8 line), which maps onto the integration stages roughly as: engine v1.0.0 → RustyNES v0.9.0;