1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-07 00:22:50 +00:00
nescript/examples/sprites_and_palettes.ne

70 lines
1.4 KiB
Text
Raw Normal View History

cleanup: fix silent miscompiles and delete dead code exposed by code review Two correctness bugs were silently producing wrong ROMs: - `x << n` / `x >> n` always shifted by 1, regardless of `n`, because the IR lowering for `BinOp::ShiftLeft`/`ShiftRight` hardcoded the count. Now eval_const the RHS into a compile-time count; fall back to a new `IrOp::ShiftLeftVar` / `ShiftRightVar` (runtime loop) when the amount isn't constant. Strength reduction folds the variable form back to a fixed count once the optimizer knows the value. - `x / n` / `x % n` always returned 0, because the lowering emitted `LoadImm(t, 0)` for `BinOp::Div`/`Mod` with a comment saying the runtime call was "TODO for now". Added real `IrOp::Div` and `IrOp::Mod`, wired them through use-counting and DCE, gave codegen `__divide`-based implementations, and taught strength reduction to rewrite power-of-two divisors into shifts and modulo-by-2ⁿ into AND masks. Constant folding now handles `Mul`/`Div`/`Mod`/shifts too, which were previously left for the codegen to emit inefficient software calls. Dead code removed (no backward-compat shims kept): - `src/debug/` entirely. `DebugSymbols`, `SourceMap`, and the Mesen/.sym emitters had no callers outside their own tests; `main.rs` never wrote a symbol file. Documented the intent in `docs/future-work.md` so it comes back intentionally if needed. - `ErrorCode::E0202` (invalid cast) and `E0403` (unreachable state): defined, formatted, and marked `#[allow(dead_code)]` but never emitted. W0104 now carries the unreachable-state semantics too. - `Level::Info`: never constructed. - `load_background` / `set_palette` statements and their `BackgroundDecl` / `PaletteDecl` parser support: parsed and silently dropped by IR lowering (`// TODO: implement in asset pipeline`). Removed keywords, AST variants, parser paths, analyzer arms, and tests. `docs/future-work.md` documents the runtime palette/nametable design for when it comes back. Doc cleanup: - `docs/architecture.md` was describing files that don't exist (`analyzer/types.rs`, `optimizer/const_fold.rs`, `codegen/regalloc.rs`, `rom/header.rs`, `debug/symbols.rs`, …). Rewrote it to match the real flat `mod.rs` + `tests.rs` layout and the real pipeline order. - `docs/future-work.md` was a hybrid of open work and "recently completed" entries that duplicated the active stubs at the top of the file. Collapsed to just the gaps that are actually still open. - `README.md` claimed Mesen symbol export and 210 tests; updated both. - `docs/language-guide.md` and `spec.md` described `palette` decls, `set_palette` / `load_background`, `debug.overlay`, and error codes that were never emitted. Trimmed. - Stale comments on `Statement::Play`/`StartMusic`/`StopMusic` claimed the audio subsystem was "a no-op at codegen time". Tests: - Regression tests for every fix above (`lower_shift_left_with_literal _count_uses_that_count`, `lower_shift_right_with_variable_count _uses_runtime_variant`, `lower_divide_emits_div_op_not_load_imm _zero`, `lower_modulo_emits_mod_op_not_load_imm_zero`, `strength_reduce_div_by_power_of_two`, `strength_reduce_mod_by _power_of_two`, `strength_reduce_shift_var_with_constant_amount`). - Renamed the `program_with_sprites_and_palette` integration test (which was exercising the now-removed `load_background`/`set_palette`) to `program_with_inline_sprite_chr`. `examples/sprites_and_palettes.ne` lost its `palette`/`set_palette` usage. Nothing in the emulator test presses A, so the headless jsnes render shouldn't move, but the golden may need regeneration via `UPDATE_GOLDENS=1` if it does. https://claude.ai/code/session_012fKB251HvEUQwG3tizFyqt
2026-04-13 02:47:37 +00:00
// Sprites and Scroll — demonstrates M3/M4 asset features.
//
cleanup: fix silent miscompiles and delete dead code exposed by code review Two correctness bugs were silently producing wrong ROMs: - `x << n` / `x >> n` always shifted by 1, regardless of `n`, because the IR lowering for `BinOp::ShiftLeft`/`ShiftRight` hardcoded the count. Now eval_const the RHS into a compile-time count; fall back to a new `IrOp::ShiftLeftVar` / `ShiftRightVar` (runtime loop) when the amount isn't constant. Strength reduction folds the variable form back to a fixed count once the optimizer knows the value. - `x / n` / `x % n` always returned 0, because the lowering emitted `LoadImm(t, 0)` for `BinOp::Div`/`Mod` with a comment saying the runtime call was "TODO for now". Added real `IrOp::Div` and `IrOp::Mod`, wired them through use-counting and DCE, gave codegen `__divide`-based implementations, and taught strength reduction to rewrite power-of-two divisors into shifts and modulo-by-2ⁿ into AND masks. Constant folding now handles `Mul`/`Div`/`Mod`/shifts too, which were previously left for the codegen to emit inefficient software calls. Dead code removed (no backward-compat shims kept): - `src/debug/` entirely. `DebugSymbols`, `SourceMap`, and the Mesen/.sym emitters had no callers outside their own tests; `main.rs` never wrote a symbol file. Documented the intent in `docs/future-work.md` so it comes back intentionally if needed. - `ErrorCode::E0202` (invalid cast) and `E0403` (unreachable state): defined, formatted, and marked `#[allow(dead_code)]` but never emitted. W0104 now carries the unreachable-state semantics too. - `Level::Info`: never constructed. - `load_background` / `set_palette` statements and their `BackgroundDecl` / `PaletteDecl` parser support: parsed and silently dropped by IR lowering (`// TODO: implement in asset pipeline`). Removed keywords, AST variants, parser paths, analyzer arms, and tests. `docs/future-work.md` documents the runtime palette/nametable design for when it comes back. Doc cleanup: - `docs/architecture.md` was describing files that don't exist (`analyzer/types.rs`, `optimizer/const_fold.rs`, `codegen/regalloc.rs`, `rom/header.rs`, `debug/symbols.rs`, …). Rewrote it to match the real flat `mod.rs` + `tests.rs` layout and the real pipeline order. - `docs/future-work.md` was a hybrid of open work and "recently completed" entries that duplicated the active stubs at the top of the file. Collapsed to just the gaps that are actually still open. - `README.md` claimed Mesen symbol export and 210 tests; updated both. - `docs/language-guide.md` and `spec.md` described `palette` decls, `set_palette` / `load_background`, `debug.overlay`, and error codes that were never emitted. Trimmed. - Stale comments on `Statement::Play`/`StartMusic`/`StopMusic` claimed the audio subsystem was "a no-op at codegen time". Tests: - Regression tests for every fix above (`lower_shift_left_with_literal _count_uses_that_count`, `lower_shift_right_with_variable_count _uses_runtime_variant`, `lower_divide_emits_div_op_not_load_imm _zero`, `lower_modulo_emits_mod_op_not_load_imm_zero`, `strength_reduce_div_by_power_of_two`, `strength_reduce_mod_by _power_of_two`, `strength_reduce_shift_var_with_constant_amount`). - Renamed the `program_with_sprites_and_palette` integration test (which was exercising the now-removed `load_background`/`set_palette`) to `program_with_inline_sprite_chr`. `examples/sprites_and_palettes.ne` lost its `palette`/`set_palette` usage. Nothing in the emulator test presses A, so the headless jsnes render shouldn't move, but the golden may need regeneration via `UPDATE_GOLDENS=1` if it does. https://claude.ai/code/session_012fKB251HvEUQwG3tizFyqt
2026-04-13 02:47:37 +00:00
// Shows: sprite declarations with inline CHR data, type casting,
// PPU scroll writes.
//
// Build: cargo run -- build examples/sprites_and_palettes.ne
game "Asset Demo" {
mapper: NROM
}
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
// Define a sprite with ASCII pixel art. Each character maps to a
// 2-bit palette index: `.` = 0 (transparent), `#` = 1, `%` = 2,
// `@` = 3. The parser handles the 2-bitplane CHR encoding, so we
// never touch hex bytes by hand.
//
// Arrow — a right-facing arrow in palette-index 1.
sprite Arrow {
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
pixels: [
"...##...",
"...###..",
"#######.",
"########",
"########",
"#######.",
"...###..",
"...##..."
]
}
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
// Heart — a full-colour heart in palette-index 3 (the brightest
// shade, `@`).
sprite Heart {
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
pixels: [
".@@..@@.",
"@@@@@@@@",
"@@@@@@@@",
"@@@@@@@@",
".@@@@@@.",
"..@@@@..",
"...@@...",
"........"
]
}
var px: u8 = 128
var py: u8 = 120
var scroll_x: u8 = 0
on frame {
// Movement
if button.right { px += 2 }
if button.left { px -= 2 }
if button.down { py += 2 }
if button.up { py -= 2 }
// Scroll background
scroll_x += 1
scroll(scroll_x, 0)
// Type cast demo
var wide: u16 = px as u16
// Draw sprites
draw Arrow at: (px, py)
draw Heart at: (px, py - 16)
}
start Main