1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-08 08:55:38 +00:00
nescript/tests/emulator/goldens/sprite_flicker_demo.audio.hash

2 lines
16 B
Text
Raw Normal View History

sprite-per-scanline: add cycle_sprites runtime flicker + debug telemetry W0109 (shipped last commit) catches the 8-sprites-per-scanline hardware limit at compile time for static layouts, but the dynamic case — enemy formations, projectile clusters, animated NPCs where coordinates come from variables — was still silent. This change adds two layers of defense on top of W0109: Layer 2: `cycle_sprites` runtime flicker intrinsic New keyword statement that rotates the OAM DMA start offset one slot per call. When called once per `on frame`, the PPU's sprite evaluation picks up a different subset of the 12+ overlapping sprites each frame, so the permanent-dropout failure mode becomes visible flicker — the classic NES technique used by Gradius, Battletoads, and every shmup. Implementation: - Lexer keyword `KwCycleSprites` and parser production. - AST `Statement::CycleSprites(Span)`. - `IrOp::CycleSprites` lowered by the IR pass. - Codegen emits `LDA $07EF / CLC / ADC #4 / STA $07EF` with natural u8 wrap, plus a one-shot `__sprite_cycle_used` marker label the first time it fires. - Linker detects the marker and switches `gen_nmi` to the cycling variant, which reads the rotating offset from `$07EF` into OAM_ADDR before the DMA instead of writing a literal 0. Programs that don't call `cycle_sprites` skip the marker and get byte-identical ROM output. Layer 3: debug-mode sprite overflow telemetry Mirrors the frame-overrun pair (`debug.frame_overrun_count` / `debug.frame_overran`). In debug builds the NMI handler reads `$2002` at the top of vblank, masks bit 5 (the PPU's sprite overflow flag), and if set bumps a cumulative counter at `$07FD` plus a sticky bit at `$07FC`. The sticky bit clears on every `wait_frame`. New debug builtins: - `debug.sprite_overflow_count()` → u8 peek of $07FD - `debug.sprite_overflow()` → u8 peek of $07FC (sticky bit) The hardware flag has well-known quirks but is correct for the overwhelming majority of cases and costs ~15 cycles per frame to sample. Release builds emit no overflow-check code at all, so the four bytes at `$07EF` / `$07FC`-`$07FD` stay free for user allocation. Related changes: - `gen_nmi` now takes an `NmiOptions` struct. Four bool parameters tripped clippy's `fn_params_excessive_bools`. - CLI `build` now renders analyzer warnings on a successful build. Previously warnings were silently dropped unless the user also ran `nescript check`, which made W0109 effectively invisible to CI and local dev alike. Existing pre-existing W0103 / W0106 warnings on `coin_cavern`, `mmc3_per_state_split`, `sprites_and_palettes` surface too — not regressions, just now visible. New example: `examples/sprite_flicker_demo.ne` Draws 12 sprites into a 4-pixel band, W0109 fires at compile time with nine labels pointing at the offenders, and a `cycle_sprites` call at the end of `on frame` turns the hardware dropout into flicker. The committed emulator golden captures one frame of the cycling pattern (deterministic). Tests: - `runtime::tests::nmi_debug_mode_samples_sprite_overflow` - `runtime::tests::nmi_sprite_cycle_variant_reads_rotating_offset` - `ir_codegen::*::debug_sprite_overflow_count_loads_07fd` - `ir_codegen::*::debug_sprite_overflow_flag_loads_07fc` - `ir_codegen::*::wait_frame_clears_sprite_overflow_sticky_in_debug_mode` - `ir_codegen::*::wait_frame_release_does_not_touch_sprite_overflow_sticky` - `ir_codegen::*::cycle_sprites_emits_marker_and_add4` - `ir_codegen::*::cycle_sprites_marker_dedup_across_multiple_calls` - `ir_codegen::*::program_without_cycle_sprites_emits_no_marker` - `analyzer::*::accepts_debug_sprite_overflow_builtins` - `analyzer::*::rejects_unknown_debug_method_lists_all_four_known_names` - `analyzer::*::accepts_cycle_sprites_statement` Docs: `examples/war/COMPILER_BUGS.md` §4 now describes all three layers (W0109, `cycle_sprites`, debug telemetry) with reasoning for when each applies. `README.md` and `examples/README.md` add the new example to their tables. All 32 emulator goldens still match — the cycling is opt-in and programs that don't call `cycle_sprites` or enable debug mode are byte-identical to the pre-change output. https://claude.ai/code/session_0143dTgh3UeRrtfHgQwzcv5z
2026-04-15 22:07:19 +00:00
a82b6ff5 132084