1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-08 00:45:38 +00:00

commit built ROMs alongside .ne sources

The compiler is deterministic: rebuilding any example produces
a byte-identical ROM, verified across all 22 examples and all
four mappers (NROM, MMC1, UxROM, MMC3). That means the .nes
files are reproducible artefacts and can live next to their
sources without drift.

Benefits:

- Users can clone the repo and open any example in an emulator
  without installing a Rust toolchain or running the compiler.
- The emulator harness can trust examples/*.nes directly, so its
  CI job no longer needs a compiler build or a "compile all
  examples" loop — it just boots jsnes against the committed
  ROMs and diffs each against its golden.
- ROM diffs in PRs are now meaningful: "this compiler change
  flipped 17 bytes in hello_sprite.nes" is visible review
  signal, not hidden behind the emulator golden.

Guard rails so the ROMs don't drift from their sources:

- .gitignore no longer excludes *.nes.
- The `examples` CI job rebuilds every .ne into /tmp and fails
  loudly (with a GitHub error annotation pointing at the exact
  cargo command to rerun) if any committed ROM differs.
- scripts/pre-commit does the same check locally.
- CLAUDE.md now states that editing a .ne file requires
  rebuilding its .nes in the same commit, so future agents
  won't miss the invariant.

Total footprint: 22 ROMs, 624 KB (avg 28 KB each — most are
NROM 24 KB; two banked examples are larger).

https://claude.ai/code/session_01BcCcHi6FUmTh8jC7UgkA3A
This commit is contained in:
Claude 2026-04-13 15:12:18 +00:00
parent 43dc1ace49
commit 57faf9e36a
No known key found for this signature in database
26 changed files with 67 additions and 22 deletions

View file

@ -15,6 +15,13 @@ re-derive the project conventions from scratch.
pipeline.
- Examples live in `examples/*.ne`. Every example is expected to compile
cleanly and has a pinned emulator golden — see below.
- **`examples/*.nes` is committed.** The compiler is deterministic
(same source → byte-identical ROM), so the ROMs travel with the
repo. If you edit any `.ne` file you **must** rebuild its `.nes`
in the same commit — CI's `examples` job rebuilds each ROM into a
tmp path and fails if the committed version differs, pointing at
the exact `cargo run -- build examples/<name>.ne` to run. The
pre-commit hook under `scripts/pre-commit` catches this locally.
- `docs/future-work.md` lists the remaining gaps. If you implement
something from that file, update the doc in the same PR.
@ -63,22 +70,30 @@ tests/emulator/
The harness is **separate** from `cargo test`. You have to run it by hand:
```bash
# 1. Build every example first. The harness reads pre-built .nes files
# from examples/ — it will not invoke cargo.
cargo build --release
for f in examples/*.ne; do ./target/release/nescript build "$f"; done
# 2. Install node deps (once per worktree; node_modules/ is gitignored).
# 1. Install node deps (once per worktree; node_modules/ is gitignored).
cd tests/emulator
npm install # or `npm ci` in CI
# 3. Verify every example still matches its golden.
# 2. Verify every committed ROM still matches its golden.
node run_examples.mjs
# → "21/21 ROMs match their goldens" on success
# → "22/22 ROMs match their goldens" on success
# → FAIL / MISS lines + `actual/<name>.png`, `actual/<name>.diff.png`,
# `actual/<name>.wav` written for any ROM that mismatched
```
The harness reads the committed `examples/*.nes` files directly —
since those are rebuilt and diffed by the `examples` CI job, the
emulator job doesn't need a compiler toolchain at all. If you're
iterating on the compiler locally, rebuild the example ROM(s) you
care about before re-running the harness:
```bash
cargo build --release
./target/release/nescript build examples/hello_sprite.ne
# then:
(cd tests/emulator && node run_examples.mjs)
```
### Updating goldens
If a change is supposed to flip goldens (you added a new example, changed