1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-08 17:06:04 +00:00
Commit graph

2 commits

Author SHA1 Message Date
Claude
ad951a835f
examples: adopt the friendly asset syntax
Rewrites every example with non-trivial asset declarations to use
the pleasant QoL syntax introduced in the previous commit. Every
example still compiles to a byte-identical ROM (verified by a
temp-path diff before committing), so the committed `.nes` files
and the 23 emulator goldens are unchanged.

  * platformer.ne — the centerpiece end-to-end demo:
      - `palette Main` goes to grouped form with a shared
        `universal: 0x22` (sky blue), one shared colour per
        sub-palette, and named NES colours throughout; the
        long-standing `$3F10` mirror-trap warning is now handled
        by the parser and the manual pitfall comment is gone.
      - `sprite Tileset` is 15 tiles of ASCII pixel art instead
        of 240 bytes of inline hex.
      - `background Level` uses a `legend { '.': 15, '#': 9, ... }`
        block plus `map:` strings for the 32×30 nametable, and
        `palette_map:` rows for the attribute table. The map
        reads top-down like the rendered screen.
      - SFX latch-once `pitch: 0x30` scalars + `envelope:` alias.
      - `music Theme` uses note names + `tempo: 10` default.
  * audio_demo.ne — scalar sfx pitches, `envelope:` alias, and a
    note-name `C4, E4, G4, ...` music track.
  * palette_and_background.ne — grouped CoolBlues / WarmReds
    palettes with `universal: black` + named colours, plus
    `legend` + `map:` tilemaps for the two backgrounds.
  * sprites_and_palettes.ne — Arrow and Heart sprites rewritten
    as `pixels:` ASCII art.

Along the way, two small parser extensions support the rewrites:

  - `parse_pixel_art` now accepts `a/b/c` as aliases for `#/%/@`,
    matching the vocabulary every NES editor (and our own
    gen_platformer_tiles.rs generator) uses.
  - `palette_map_to_attrs` allows up to 16 metatile rows (the
    full attribute-table coverage, including the off-screen
    bottom half) and auto-replicates row 14 → row 15 when only
    15 rows are supplied so the visible bottom of the screen
    gets consistent sub-palette assignments by default. The old
    15-row cap couldn't match a hand-packed `0xAA` attribute
    table for the last row; the platformer required this to
    stay byte-identical.

`scripts/gen_platformer_tiles.rs` is updated to emit the new
syntax directly (pixel-art `pixels:` block + `legend`/`map:`/
`palette_map:` for the background), so regenerating the
platformer tiles stays a one-liner.

474 lib tests + 64 integration tests pass (3 new parser tests
for `palette_map:` 15/16/17 rows and the `abc` alias). All 23
emulator goldens still match pixel- and sample-for-sample.

https://claude.ai/code/session_01PzaSFj3VahDzxEYTKCESkz
2026-04-13 18:04:21 +00:00
Claude
688d9afcec
platformer: end-to-end side-scroller demo + three runtime bug fixes
Adds `examples/platformer.ne`, a full side-scrolling game that
exercises nearly every subsystem of the compiler in one program:
custom CHR tileset, 32×30 background nametable with per-region
attribute palettes, 2×2 metasprite hero with gravity/jump physics,
wrap-around horizontal scrolling, moving enemies, coin pickups,
user-declared SFX + music, and a Title → Playing state machine
with autopilot so the headless jsnes harness captures real
gameplay at frame 180. Tile art + nametable are generated by
`scripts/gen_platformer_tiles.rs` (`cargo run --bin gen_platformer_tiles`).

Building this out uncovered three independent runtime bugs that
together made the example render as black-on-black smileys. All
three are fixed in this commit:

1. **`gen_init` enabled sprite rendering before the linker's
   initial palette/background load runs.** The PPU's v-register
   auto-increments on every `$2007` write *during active
   rendering*, so the palette load (32 B) and nametable load
   (1024 B) were scrambled past the first ~72 bytes — every
   existing program with a `background Level { ... }` block was
   silently rendering zero-filled VRAM. Fix: leave `PPU_MASK = 0`
   at the end of `gen_init` and emit a new `gen_enable_rendering`
   call *after* all initial VRAM writes complete.

2. **Audio tick corrupted `ZP_CURRENT_STATE`.** The audio
   driver's period-table lookup reused `$02/$03` as a temporary
   indirect pointer with a comment claiming the slots were free
   because the tick doesn't call mul/div. But `$03` is also
   `ZP_CURRENT_STATE` used by the state dispatch loop, so every
   music note silently overwrote the state index with the high
   byte of `__period_table` (`0xC5` in the platformer ROM),
   wedging the state machine forever. Fix: `gen_nmi` now PHAs
   `$02/$03` on entry and PLA-restores them on exit, and the
   audio tick JSR moves inside that save/restore window (it used
   to be spliced by the linker *before* the register saves, so
   even A/X/Y were technically being trashed pre-save). Only
   `audio_demo`'s audio hash shifts (its note timings move a few
   cycles); every other golden is unchanged.

3. **Sub-palette mirroring footgun.** Writing a 32-byte palette
   blob sequentially causes the sprite sub-palettes' "index 0"
   slots at `$3F10/$3F14/$3F18/$3F1C` to clobber the background
   universal colour at `$3F00/$3F04/$3F08/$3F0C` via NES hardware
   mirroring. The example's palette sets all eight first bytes
   to `$22` (sky blue) for this reason; `docs/future-work.md`
   picks up a TODO to warn on inconsistent first-byte values in
   the analyzer.

Also:

- `docs/platformer.gif` — 6-second recording of the example
  running in jsnes, generated by the new
  `tests/emulator/record_gif.mjs` puppeteer helper (encodes via
  `gifenc`, committed as a dev-dependency under
  `tests/emulator/package.json`).
- README / examples/README tables and the 497-test count are
  updated to cover the new example.

https://claude.ai/code/session_01BcCcHi6FUmTh8jC7UgkA3A
2026-04-13 13:04:26 +00:00