1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-09 01:16:12 +00:00
nescript/examples
Claude e8d602c1bc
codereview: address six findings from a fresh review pass
A focused review of the branch surfaced two correctness bugs and
four important polish items in the new features. None of the
existing example goldens shift — every fix is gated on conditions
that don't fire in the committed examples.

**debug.frame_overran() never reset between frames in implicit
wait_frame programs.** The IR-level WaitFrame op cleared $07FE,
but the implicit main-loop flag-clear that runs between dispatch
iterations only cleared ZP_FRAME_FLAG. A program whose
`on frame { ... }` body had no explicit `wait_frame` would latch
$07FE to 1 on the first miss and never reset, breaking
`debug.assert(not debug.frame_overran())` guards. The dispatch
loop now also clears $07FE in debug builds, mirroring the
WaitFrame path. New regression test asserts the main loop emits
exactly one STA $07FE in a no-wait_frame debug build.

**Metasprite base-tile resolution silently miscompiled for
`@chr` / `@binary` sprites.** The IR lowering walks
`program.sprites` to compute base tile indices but assumes
1 tile per non-Inline source, while the real asset resolver
reads the file. The analyzer now hard-rejects the combination
with a clear "use inline pixels" hint instead of letting it
compile to a visual glitch. New analyzer test
`analyze_metasprite_with_external_chr_sprite_errors` covers it.

**next_sprite_tile capping silently allowed CHR overlap.** The
pipeline used `.min(255)` which would let a background tile
overwrite a sprite tile when the sprite range filled the
pattern table. Now hard-errors via CompileError::AssetResolution
when the sprite range >= 256 *and* the program declares any
`@nametable(...)` background. Inline backgrounds aren't affected.

**Linker silently truncated background CHR overflow.** The
`if end <= chr.len()` guard at the CHR copy site dropped any
auto-CHR bytes that would have run past the pattern table.
Replaced with a debug assertion since the resolver should
have caught it upstream — defense in depth.

**Stale comment in nested_structs.ne** said struct literals
"don't accept array fields yet" while the example itself
demonstrates inline array fields working through
`expand_struct_literal_init`. Comment updated.

**Misleading sentinel comment in audio.rs** described the pitch
envelope's trailing zero as a runtime sentinel; in practice the
volume tick `JMP`s to `__audio_sfx_done` first and the pitch
update block never reads the trailing byte. Rewrote the comment
to clarify it's padding for predictable blob length.

Also tidies up two minor items the reviewer flagged:

- `flatten_struct_fields` rebuilt the `struct_sizes` HashMap on
  every leaf field; hoisted the snapshot to the function entry.
- Integration tests called `resolve_backgrounds(..., 0)` (the new
  `next_sprite_tile` parameter); changed to `1` so a future
  PNG-nametable test fixture won't accidentally overwrite the
  runtime smiley at tile 0.

https://claude.ai/code/session_01KEczoNUX3WmcFLfq6iAQxB
2026-04-15 03:56:42 +00:00
..
arrays_and_functions.ne Add README, LICENSE, examples, fix draw parser lookahead 2026-04-12 00:38:19 +00:00
arrays_and_functions.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
audio_demo.ne examples: adopt the friendly asset syntax 2026-04-13 18:04:21 +00:00
audio_demo.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
auto_chr_background.ne assets: auto-generate CHR data from @nametable() PNG sources 2026-04-15 03:29:58 +00:00
auto_chr_background.nes assets: auto-generate CHR data from @nametable() PNG sources 2026-04-15 03:29:58 +00:00
auto_chr_bg.png assets: auto-generate CHR data from @nametable() PNG sources 2026-04-15 03:29:58 +00:00
bitwise_ops.ne examples: add 5 new programs covering match/loop/logic/bitwise/scanline 2026-04-12 19:05:18 +00:00
bitwise_ops.nes fix(optimizer): preserve cross-block LoadImm uses in const_fold DCE 2026-04-13 16:29:44 +00:00
bouncing_ball.ne Add example builds to CI, document sprite name behavior 2026-04-11 22:55:43 +00:00
bouncing_ball.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
coin_cavern.ne M2: Wire IR pipeline, add Coin Cavern example and integration tests 2026-04-11 23:34:35 +00:00
coin_cavern.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
comparisons.ne examples: 4 new programs covering MMC3 + other e2e gaps 2026-04-12 20:48:31 +00:00
comparisons.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
friendly_assets.ne language: pleasant asset syntax for palettes, CHR, bg, sfx, music 2026-04-13 16:09:53 +00:00
friendly_assets.nes language: pleasant asset syntax for palettes, CHR, bg, sfx, music 2026-04-13 16:09:53 +00:00
function_chain.ne examples: 4 new programs covering MMC3 + other e2e gaps 2026-04-12 20:48:31 +00:00
function_chain.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
hello_sprite.ne Add example builds to CI, document sprite name behavior 2026-04-11 22:55:43 +00:00
hello_sprite.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
inline_asm_demo.ne examples: inline_asm_demo.ne 2026-04-12 18:02:59 +00:00
inline_asm_demo.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
logic_ops.ne examples: add 5 new programs covering match/loop/logic/bitwise/scanline 2026-04-12 19:05:18 +00:00
logic_ops.nes fix(optimizer): preserve cross-block LoadImm uses in const_fold DCE 2026-04-13 16:29:44 +00:00
loop_break_continue.ne examples: add 5 new programs covering match/loop/logic/bitwise/scanline 2026-04-12 19:05:18 +00:00
loop_break_continue.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
match_demo.ne examples: add 5 new programs covering match/loop/logic/bitwise/scanline 2026-04-12 19:05:18 +00:00
match_demo.nes fix(optimizer): preserve cross-block LoadImm uses in const_fold DCE 2026-04-13 16:29:44 +00:00
metasprite_demo.ne parser/lowering: declarative metasprites for multi-tile sprite groups 2026-04-15 03:13:30 +00:00
metasprite_demo.nes parser/lowering: declarative metasprites for multi-tile sprite groups 2026-04-15 03:13:30 +00:00
mmc1_banked.ne Add README, LICENSE, examples, fix draw parser lookahead 2026-04-12 00:38:19 +00:00
mmc1_banked.nes codegen: support banked → banked cross-bank function calls 2026-04-15 02:37:19 +00:00
mmc3_per_state_split.ne examples: 4 new programs covering MMC3 + other e2e gaps 2026-04-12 20:48:31 +00:00
mmc3_per_state_split.nes codegen: support banked → banked cross-bank function calls 2026-04-15 02:37:19 +00:00
nested_structs.ne codereview: address six findings from a fresh review pass 2026-04-15 03:56:42 +00:00
nested_structs.nes analyzer/lowering: support nested struct fields and array struct fields 2026-04-15 02:19:49 +00:00
noise_triangle_sfx.ne audio: triangle and noise sfx channels 2026-04-14 10:42:53 +00:00
noise_triangle_sfx.nes audio: always enable all four tone channels on sfx trigger 2026-04-14 12:09:46 +00:00
palette_and_background.ne examples: adopt the friendly asset syntax 2026-04-13 18:04:21 +00:00
palette_and_background.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
platformer.ne feat(platformer): add stomp-or-die enemy collisions, live HUD, GameOver state 2026-04-13 20:23:07 +00:00
platformer.nes feat(platformer): add stomp-or-die enemy collisions, live HUD, GameOver state 2026-04-13 20:23:07 +00:00
README.md assets: auto-generate CHR data from @nametable() PNG sources 2026-04-15 03:29:58 +00:00
scanline_split.ne examples: add 5 new programs covering match/loop/logic/bitwise/scanline 2026-04-12 19:05:18 +00:00
scanline_split.nes codegen: support banked → banked cross-bank function calls 2026-04-15 02:37:19 +00:00
sfx_pitch_envelope.ne audio: per-frame pitch envelopes for pulse SFX 2026-04-15 02:54:56 +00:00
sfx_pitch_envelope.nes audio: per-frame pitch envelopes for pulse SFX 2026-04-15 02:54:56 +00:00
sprites_and_palettes.ne examples: adopt the friendly asset syntax 2026-04-13 18:04:21 +00:00
sprites_and_palettes.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
state_machine.ne Add README, LICENSE, examples, fix draw parser lookahead 2026-04-12 00:38:19 +00:00
state_machine.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
structs_enums_for.ne analyzer/lowering: support nested struct fields and array struct fields 2026-04-15 02:19:49 +00:00
structs_enums_for.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
two_player.ne examples: 4 new programs covering MMC3 + other e2e gaps 2026-04-12 20:48:31 +00:00
two_player.nes fix(optimizer): preserve cross-block LoadImm uses in const_fold DCE 2026-04-13 16:29:44 +00:00
uxrom_banked.ne examples: add uxrom_banked and jsnes golden 2026-04-13 02:04:25 +00:00
uxrom_banked.nes codegen: support banked → banked cross-bank function calls 2026-04-15 02:37:19 +00:00
uxrom_banked_to_banked.ne codegen: support banked → banked cross-bank function calls 2026-04-15 02:37:19 +00:00
uxrom_banked_to_banked.nes codegen: support banked → banked cross-bank function calls 2026-04-15 02:37:19 +00:00
uxrom_user_banked.ne codegen: user code in switchable banks via cross-bank trampolines 2026-04-14 11:41:20 +00:00
uxrom_user_banked.nes codegen: support banked → banked cross-bank function calls 2026-04-15 02:37:19 +00:00

NEScript Examples

Quick Start

# Build the compiler
cargo build --release

# Compile all examples
for f in examples/*.ne; do cargo run -- build "$f"; done

# Or compile one
cargo run -- build examples/hello_sprite.ne

Open any .nes file in an NES emulator (Mesen, FCEUX, etc.)

Examples

File Features Description
hello_sprite.ne input, draw Move a sprite with the d-pad
bouncing_ball.ne if/else, variables Auto-bouncing sprite with edge detection
coin_cavern.ne states, functions, constants 3-state game with gravity and coin collection
arrays_and_functions.ne arrays, functions, while Enemy array with collision detection
state_machine.ne on enter/exit, transitions Multi-state flow with timers
sprites_and_palettes.ne sprites, scroll, cast Inline CHR data, PPU scroll writes, type casting
mmc1_banked.ne MMC1, banks, multiply Banked mapper with software multiply
uxrom_user_banked.ne UxROM, bank Foo { fun ... }, cross-bank trampoline First example to put real user code inside a switchable bank. The animation step lives in bank Extras and is invoked from the fixed-bank state handler via a generated __tramp_step_animation stub that selects bank 0, JSRs the body, then restores the fixed bank before returning.
uxrom_banked_to_banked.ne UxROM, banked → banked cross-bank call Two bank Foo { fun ... } blocks: step lives in bank Logic and calls clamp in bank Helpers. The trampoline uses ZP_BANK_CURRENT + PHA/PLA to save and restore the caller's bank, so the same per-callee stub works whether the caller is in the fixed bank or another switchable bank.
palette_and_background.ne palette, background, set_palette, load_background Reset-time initial load plus vblank-safe runtime swaps
auto_chr_background.ne background @nametable(...) with auto-CHR First example to use the @nametable("file.png") shortcut without supplying any matching CHR data. The resolver dedupes the PNG's 8×8 cells, encodes them via the same brightness-bucketing the sprite CHR encoder uses, and slots them into CHR ROM at the next free tile slot. The committed auto_chr_bg.png is a 256×240 grayscale gradient that exercises ~50 unique tiles.
friendly_assets.ne named colours, grouped palette, pixel art, tilemap+legend, palette_map, scalar sfx pitch, note-name music Exercises every "friendlier" asset syntax at once — the palette uses bg0..sp3 + a shared universal:, the sprite is authored as ASCII pixel art, the background uses a legend { ... } + map: tilemap with a palette_map: for attributes, the sfx uses a scalar pitch: + envelope: alias, and the music uses note names (C4, E4 40, rest 10) with a tempo: default.
noise_triangle_sfx.ne channel: noise, channel: triangle on sfx blocks Demonstrates the noise and triangle sfx channels. Declares one noise burst and one triangle bass note, plays each on a timer so the emulator harness captures both the pixel output and the APU state.
sfx_pitch_envelope.ne varying-pitch pulse SFX A 16-frame frequency sweep written as a per-frame pitch: array on a Pulse-1 sfx. The compiler emits a separate __sfx_pitch_<name> blob and gates the audio tick's pitch update path on the __sfx_pitch_used marker, so programs that stick to the scalar pitch: form still get byte-identical ROM output.
metasprite_demo.ne declarative multi-tile sprites A 16×16 hero sprite split into a metasprite Hero { sprite: Hero16, dx: [...], dy: [...], frame: [...] } declaration. draw Hero at: (px, py) then expands to one DrawSprite op per tile in the IR lowering, each with its dx/dy added to the user's anchor point and the frame offset by the underlying sprite's base tile. The codegen needs no metasprite-specific support — it sees N regular draws and the OAM cursor allocator handles the slots.
nested_structs.ne nested struct fields, array struct fields, chained literals Two Hero instances each carry a Vec2 position and a u8[4] inventory. Exercises hero.pos.x chained access, hero.inv[i] array-field access, and chained struct-literal initializers (Hero { pos: Vec2 { x: ..., y: ... }, inv: [...] }).
platformer.ne every subsystem End-to-end side-scrolling demo: custom CHR tileset, full 32×30 nametable with per-region attribute palettes, 2×2 metasprite hero with gravity/jump physics, wrap-around horizontal scrolling, stomp-or-die enemy collisions with a live stomp-count HUD, coin pickups, user-declared SFX + music, and a Title → Playing → GameOver state machine with a proximity-based autopilot so the headless harness cycles through stomp, stomp, die, and retry inside six seconds. Regenerate the tile art with cargo run --bin gen_platformer_tiles.

Emulator Controls

NES Button Typical Key
D-pad Arrow keys
A Z
B X
Start Enter
Select Right Shift

About Sprites

Sprite names in draw Player at: (x, y) are parsed and recorded in the AST. You can define sprites with inline CHR tile data:

sprite Player {
    chr: [0x3C, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3C,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
}

If no matching sprite declaration exists, the draw uses the built-in default tile (a smiley face). See sprites_and_palettes.ne for a full example.

Compiler Commands

# Compile to ROM
cargo run -- build game.ne

# Custom output path
cargo run -- build game.ne --output my_game.nes

# Type-check only
cargo run -- check game.ne

# View generated 6502 assembly
cargo run -- build game.ne --asm-dump

# Debug mode
cargo run -- build game.ne --debug