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

4 commits

Author SHA1 Message Date
Claude
b6e4c368e1
peephole: step past non-A ops in remove_dead_loads
`remove_dead_loads` now scans past opcodes that touch neither A nor
the flags an LDA sets, so a redundant LDA gets caught by its
successor's overwrite even when an index load or counter bump sits
between them. The extension covers LDX/LDY/INX/INY/DEX/DEY and the
flag ops (CLC/SEC/CLI/SEI/CLD/SED/CLV) alongside the INC/DEC/STX/STY
opcodes the pass already stepped past.

The highest-leverage case is the shape every single-tile `draw`
emits. After copy propagation and dead-store elimination do their
work, the stream reads:

    LDA #<y>      ; stray producer, value never consumed
    LDY oam_cursor
    LDA #<y>      ; real load before STA
    STA $0200,Y

The first LDA was surviving because the pass bailed on the LDY.
With the step-past, it drops. One LDA gone per draw, 2 bytes each.

Measured LDA-count reduction on committed examples:

  platformer  242 → 221   (-21, -8.7 %)
  war         785 → 754   (-31, -4.0 %)
  pong        843 → 827   (-16, -1.9 %)

**Audio goldens.** The cycle savings shift the main-loop/NMI boundary
in audio-emitting programs, which re-times which frame each SFX
trigger lands in. Six audio hashes re-baseline as a result:
audio_demo, friendly_assets, noise_triangle_sfx, platformer, pong,
war. All 50 PNG goldens, the platformer/war/pong demo gifs, and
every non-audio program stay byte-identical. The re-baselined
output is still sample-accurate; what changed is the first-SFX
offset within the captured 132 084-sample window. This is the
audio-shift tradeoff documented in future-work.

Two new peephole unit tests lock in the behaviour:
- `dead_load_elim_steps_past_ldx_ldy` — the DrawSprite shape folds.
- `dead_load_elim_preserves_lda_when_used_by_shift` — a subsequent
  ASL on A keeps the LDA alive across an intervening LDY.

Also updates future-work.md to reflect the shipped change and the
remaining register-allocator wins worth chasing next.
2026-04-19 01:43:58 +00:00
Claude
1c00d20121
runtime: reset PPU scroll after VRAM buffer drain
Each $2006 write inside __vram_buf_drain updates the PPU's `t`
(scroll) register, so leaving it pointing at the last buffer
entry's address shifted the next frame's rendering up/right by
however many cells we wrote past $2000. Reset by writing $00 to
$2006 twice (clears `t` and resets the write-toggle to high)
followed by $00 to $2005 twice (zero X/Y scroll). The HUD demo
golden flips from "smileys offset by ~16px" to the intended
red bar with white hearts and a yellow score digit.

https://claude.ai/code/session_01F7dHsgh7UX7SAK3wZ7JiKc
2026-04-18 22:57:46 +00:00
Claude
a806bfd3bd
hud_demo: declare proper digit + heart CHR so the HUD is readable
The previous version of hud_demo passed `score & 0x0F` and tile
index `1` (= Heart) to nt_set / nt_fill_h, but the demo had no
Heart sprite declared and tile 1 in CHR was uninitialized garbage.
The result was a screen of blue smileys with a tiny red strip in
the corner — the buffer mechanism worked, but the visual gave no
sense that anything HUD-shaped was happening.

This commit makes the HUD actually look like a HUD:

- 12 sprite declarations (Bar, Heart, Digit0..9, Ball) that the
  compiler lays into CHR at known tile indices in declaration
  order. Tile-index constants (`BAR_TILE`, `HEART_TILE`,
  `DIGIT_BASE`) match that order so the call sites can use names
  instead of magic numbers.
- bg1 palette restructured to `[red, white, yellow]` so pixel-art
  characters resolve to visible colours: `#` = red (background
  fill), `%` = white (heart shape), `@` = yellow (digit strokes).
- Background pre-paints row 1 with the solid `Bar` (red) tile
  via a `legend { "B": 1 }` entry, giving the HUD a uniform red
  canvas for individual cell writes to land on.
- Eight `nt_attr` calls at startup paint the entire top metatile
  row (4 rows × 32 cols) with sub-palette 1 so the HUD chrome
  reads as visually distinct from the playfield.

The result at frame 180 is unmistakably HUD-shaped: a yellow-on-
red status bar at the top of the screen above blue playfield with
a yellow ball bouncing around. Per-frame cost still scales with
what changed — `last_score` / `last_lives` shadow-compares mean
the buffer stays empty on the ~58 of 60 frames where nothing
ticks.

Tests: 758 pass. Clippy clean. 48/48 emulator goldens match.
2026-04-18 22:12:43 +00:00
Claude
854b61ea1e
docs + example: HUD demo and language-guide VRAM buffer section
Follow-up to 807c9c7 (the VRAM update buffer core). Adds the
realistic-HUD example the core was missing, plus a language-guide
section that explains when and how to use the three buffer
intrinsics.

**examples/hud_demo.ne**

A bouncing-ball playfield with a classic status bar across the
top:
- 5-cell lives indicator that ticks down once per second and
  resets at zero, drawn via `nt_fill_h` (plus a second
  `nt_fill_h` to erase the stale tail).
- Score counter at the right edge that bumps on every wall
  bounce, drawn via `nt_set`.
- One-shot `nt_attr` call on the first frame flipping the
  top-left metatile group to sub-palette 1 (the red HUD
  palette) so the UI chrome reads as distinct from the
  playfield.

The demo's point is the `last_score != score` / `last_lives !=
lives` shadow-compare pattern: on the ~58-of-60 frames where
nothing changed, the buffer stays empty and drain work is zero.
That's the whole reason the VRAM buffer exists — per-frame cost
scales with what moved, not with HUD complexity. Committed
`.nes` + pixel/audio goldens.

**docs/language-guide.md**

New "VRAM Update Buffer" section between "Hardware Intrinsics"
and "Inline Assembly". Covers:
- Why user code can't just poke `$2006` / `$2007` directly.
- The three intrinsics + their coordinate systems (cell, not
  pixel).
- The HUD pattern with a ready-to-paste code snippet and a
  pointer at `examples/hud_demo.ne`.
- A per-entry budget table + worked 1000-cycle drain example
  against the ~2273-cycle vblank budget.
- Known limits: horizontal-only, no overflow check,
  no coalescing — all already tracked under `future-work.md` §G.

**examples/README.md**

`vram_buffer_demo.ne` reframed as the minimal test-case exercise
it actually is, with a pointer at `hud_demo.ne` for the realistic
pattern. New table row for `hud_demo.ne`.

All 758 tests pass. Clippy clean. 48/48 emulator goldens match.
2026-04-18 21:34:44 +00:00