1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-08 08:55:38 +00:00
nescript/tests/emulator/goldens
Claude 0602fd9590
analyzer+codegen: lift the 4-param ceiling via a direct-write calling convention
Follow-up to the silent-drop audit. The old ABI passed every
parameter through four fixed zero-page transport slots `$04-$07`,
imposing a hard 4-param cap (E0506) that didn't compose with
structs/arrays/u16s and fell back to "pack args into a global"
workarounds whenever a function needed five things. The transport
scheme also cost every non-leaf call a 4-LDA/STA spill prologue
(~28 cycles, 16 bytes) to copy args out of ZP before the next
nested `JSR` could clobber them.

Replace it with a hybrid convention keyed on leaf-ness:

- **Leaf callees** (no nested `JSR` in body, ≤4 params):
  unchanged. Caller stages args into `$04-$07`; body reads those
  slots directly for its entire lifetime. No prologue copy.
  Fastest path, 3-cycle ZP stores + 3-cycle ZP loads, preserves
  the SHA-256 leaf-primitive optimisation that motivated the
  original fast path.

- **Non-leaf callees** (body contains a nested `JSR`, OR ≥5
  params): direct-write. Caller stages each argument straight
  into the callee's analyzer-allocated parameter RAM slot,
  bypassing the transport slots entirely. No prologue copy on
  the callee side. Saves ~24 cycles and ~16 bytes per call vs
  the old transport-then-spill path, and — crucially — scales
  past 4 params because the per-param slots live wherever the
  analyzer put them rather than in a fixed ZP window.

The analyzer's ceiling moves from 4 to 8. Functions with 5–8
params are silently promoted to the non-leaf convention (even if
their body has no nested `JSR`), which pays the direct-write cost
rather than the prologue-copy cost — still cheaper than the old
ABI. Declarations with 9+ params still emit E0506.

### Implementation

- `function_is_leaf` now also requires `param_count <= 4`.
- `IrCodeGen::new` populates `non_leaf_param_addrs: HashMap<String,
  Vec<u16>>` — for every non-leaf function, the ordered list of
  addresses its parameters occupy. Callers use this to route each
  arg directly to the right slot.
- `IrOp::Call` branches on presence in the map: non-leaf → direct-
  write, leaf (or absent — 0-arg case) → ZP transport.
- `gen_function` no longer emits a prologue. Leaves didn't have
  one; non-leaves had a 4-LDA/STA copy that is now unnecessary
  because args arrive pre-written to the slot.
- The previous `leaf_functions: HashSet<String>` field is
  removed; leaf-ness is now inferred from absence-in-
  `non_leaf_param_addrs` at the call site.

### Tests and regressions

- `eight_param_non_leaf_function_stages_every_arg_at_its_allocated_slot`
  compiles an 8-param function, scans PRG for a distinct
  `LDA #\$NN / STA <addr>` per arg (immediates `0x11..0x88`), and
  asserts that STAs to the `$04-$07` range are strictly fewer
  than 8 — proof the old transport path is gone for this call.
- `non_leaf_call_direct_writes_args_to_callee_param_slots`
  replaces the old `gen_function_prologue_spills_params_to_local_ram`
  test with a dual assertion: (a) no `LDA \$04` prologue at the
  callee entry, and (b) the caller-side STA lands at the
  analyzer-allocated param slot, not at `\$04-\$07`.
- `analyze_rejects_function_with_more_than_4_params` renamed and
  rewritten for the new 8-param cap.
- `feature_canary.ne` gains a 6-param `sum6` call (1+2+3+4+5+6 =
  21) as check 8. The canary stays green (all eight checks
  pass), so the committed golden is unchanged.

### Blast radius

- Six example ROMs change bytes (arrays_and_functions, function_chain,
  mmc1_banked, pong, sha256, war) because their non-leaf call sites
  pick up the shorter staging sequence.
- Pong and war audio hashes refresh (pure layout-timing shift; no
  behavioural change in the 180-frame no-input window). docs/pong.gif
  and docs/war.gif stay byte-identical.
- `examples/function_chain.ne`'s header comment updated to
  document the leaf vs non-leaf split it exercises.
- `docs/language-guide.md` parameter-count section and E0506 entry
  updated to reflect the new rule.

All 720 Rust tests pass; all 35 emulator goldens pass.

https://claude.ai/code/session_01AoQ678uVeqpyayvWHpfDhC
2026-04-18 02:34:56 +00:00
..
arrays_and_functions.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
arrays_and_functions.png compiler: fix three scoping bugs; war: revert all local/param workarounds 2026-04-15 20:33:41 +00:00
audio_demo.audio.hash codegen: emit gate markers at end of generate() to protect peephole 2026-04-16 21:31:47 +00:00
audio_demo.png tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
auto_chr_background.audio.hash assets: auto-generate CHR data from @nametable() PNG sources 2026-04-15 03:29:58 +00:00
auto_chr_background.png assets: auto-generate CHR data from @nametable() PNG sources 2026-04-15 03:29:58 +00:00
bitwise_ops.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
bitwise_ops.png tests/emulator: byte-exact golden-image diffs 2026-04-12 21:30:18 +00:00
bouncing_ball.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
bouncing_ball.png tests/emulator: byte-exact golden-image diffs 2026-04-12 21:30:18 +00:00
coin_cavern.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
coin_cavern.png tests/emulator: byte-exact golden-image diffs 2026-04-12 21:30:18 +00:00
comparisons.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
comparisons.png codegen: emit gate markers at end of generate() to protect peephole 2026-04-16 21:31:47 +00:00
feature_canary.audio.hash examples: add feature_canary that turns red on any memory silent-drop regression 2026-04-18 00:14:40 +00:00
feature_canary.png examples: add feature_canary that turns red on any memory silent-drop regression 2026-04-18 00:14:40 +00:00
friendly_assets.audio.hash codegen: emit gate markers at end of generate() to protect peephole 2026-04-16 21:31:47 +00:00
friendly_assets.png language: pleasant asset syntax for palettes, CHR, bg, sfx, music 2026-04-13 16:09:53 +00:00
function_chain.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
function_chain.png tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
hello_sprite.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
hello_sprite.png tests/emulator: byte-exact golden-image diffs 2026-04-12 21:30:18 +00:00
inline_asm_demo.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
inline_asm_demo.png codegen: reuse analyzer's local allocations so inline asm {param} works 2026-04-16 16:03:10 +00:00
logic_ops.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
logic_ops.png tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
loop_break_continue.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
loop_break_continue.png tests/emulator: byte-exact golden-image diffs 2026-04-12 21:30:18 +00:00
match_demo.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
match_demo.png tests/emulator: byte-exact golden-image diffs 2026-04-12 21:30:18 +00:00
metasprite_demo.audio.hash parser/lowering: declarative metasprites for multi-tile sprite groups 2026-04-15 03:13:30 +00:00
metasprite_demo.png parser/lowering: declarative metasprites for multi-tile sprite groups 2026-04-15 03:13:30 +00:00
mmc1_banked.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
mmc1_banked.png tests/emulator: byte-exact golden-image diffs 2026-04-12 21:30:18 +00:00
mmc3_per_state_split.audio.hash main: write the real mapper byte into the iNES header 2026-04-13 01:27:42 +00:00
mmc3_per_state_split.png tests/emulator: byte-exact golden-image diffs 2026-04-12 21:30:18 +00:00
nested_structs.audio.hash analyzer/lowering: support nested struct fields and array struct fields 2026-04-15 02:19:49 +00:00
nested_structs.png analyzer/lowering: support nested struct fields and array struct fields 2026-04-15 02:19:49 +00:00
noise_triangle_sfx.audio.hash codegen: emit gate markers at end of generate() to protect peephole 2026-04-16 21:31:47 +00:00
noise_triangle_sfx.png audio: triangle and noise sfx channels 2026-04-14 10:42:53 +00:00
palette_and_background.audio.hash palette/background: first-class declarations with reset-time load and runtime swaps 2026-04-13 11:11:33 +00:00
palette_and_background.png codegen: emit gate markers at end of generate() to protect peephole 2026-04-16 21:31:47 +00:00
platformer.audio.hash examples: move state-scoped globals to state-local in coin_cavern + platformer 2026-04-17 11:58:02 +00:00
platformer.png feat(platformer): add stomp-or-die enemy collisions, live HUD, GameOver state 2026-04-13 20:23:07 +00:00
pong.audio.hash analyzer+codegen: lift the 4-param ceiling via a direct-write calling convention 2026-04-18 02:34:56 +00:00
pong.png codegen: fuse compare-then-branch to drop boolean materialization 2026-04-16 17:10:02 +00:00
scanline_split.audio.hash main: write the real mapper byte into the iNES header 2026-04-13 01:27:42 +00:00
scanline_split.png tests/emulator: byte-exact golden-image diffs 2026-04-12 21:30:18 +00:00
sfx_pitch_envelope.audio.hash codegen: emit gate markers at end of generate() to protect peephole 2026-04-16 21:31:47 +00:00
sfx_pitch_envelope.png linker: skip default palette + rendering enable for non-visual ROMs 2026-04-16 21:15:08 +00:00
sha256.audio.hash examples/sha256: interactive SHA-256 hasher with on-screen keyboard 2026-04-16 14:02:58 +00:00
sha256.png examples/sha256: interactive SHA-256 hasher with on-screen keyboard 2026-04-16 14:02:58 +00:00
sprite_flicker_demo.audio.hash sprite-per-scanline: add cycle_sprites runtime flicker + debug telemetry 2026-04-15 22:07:19 +00:00
sprite_flicker_demo.png sprite-per-scanline: add cycle_sprites runtime flicker + debug telemetry 2026-04-15 22:07:19 +00:00
sprites_and_palettes.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
sprites_and_palettes.png tests/emulator: byte-exact golden-image diffs 2026-04-12 21:30:18 +00:00
state_machine.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
state_machine.png tests/emulator: byte-exact golden-image diffs 2026-04-12 21:30:18 +00:00
structs_enums_for.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
structs_enums_for.png tests/emulator: byte-exact golden-image diffs 2026-04-12 21:30:18 +00:00
two_player.audio.hash tests/emulator: record audio goldens alongside screenshots 2026-04-12 22:33:48 +00:00
two_player.png tests/emulator: byte-exact golden-image diffs 2026-04-12 21:30:18 +00:00
uxrom_banked.audio.hash examples: add uxrom_banked and jsnes golden 2026-04-13 02:04:25 +00:00
uxrom_banked.png examples: add uxrom_banked and jsnes golden 2026-04-13 02:04:25 +00:00
uxrom_banked_to_banked.audio.hash codegen: support banked → banked cross-bank function calls 2026-04-15 02:37:19 +00:00
uxrom_banked_to_banked.png codegen: support banked → banked cross-bank function calls 2026-04-15 02:37:19 +00:00
uxrom_user_banked.audio.hash codegen: user code in switchable banks via cross-bank trampolines 2026-04-14 11:41:20 +00:00
uxrom_user_banked.png codegen: user code in switchable banks via cross-bank trampolines 2026-04-14 11:41:20 +00:00
war.audio.hash analyzer+codegen: lift the 4-param ceiling via a direct-write calling convention 2026-04-18 02:34:56 +00:00
war.png examples: regenerate ROMs, gifs, and goldens after codegen local fix 2026-04-16 16:12:46 +00:00