1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-08 00:45:38 +00:00
nescript/examples
Claude e10d09db76
examples/war: redesign card art — opaque bodies, 16x16 pips, checkerboard back
The first-pass card tiles were riddled with palette-0 transparent
pixels that let the felt background bleed through every rank
glyph, small suit, and big pip. At arm's length the cards looked
like they had green specks eating them. This commit rewrites all
of the card art from scratch:

- **Opaque card bodies.** Every pixel inside a card tile is now
  either white (palette 2), red (1), or black (3). No `.` values
  anywhere on a face or back tile. The green felt only shows
  outside the card rectangle, where it should.

- **Readable rank glyphs.** The 13 rank tiles (A, 2-9, 10, J, Q, K)
  are now drawn as bold black strokes on a solid white body. The
  "holes" of the letters (e.g. the triangle inside an "A") are
  white, not transparent.

- **16x16 big pips.** The big centre pip is now a 4-tile (16x16)
  shape split into TL/TR/BL/BR quadrants per suit. Previously it
  was a 2-tile (16x8) half-height strip that looked cramped. The
  TL/TR quadrants kept their existing tile indices (28-35) so the
  shift is local; the new BL/BR quadrants are appended after the
  BIG WAR letters at tiles 88-95 to avoid renumbering the entire
  alphabet / digits / UI tile range.

- **Distinct suit shapes.** Spade is a smooth teardrop with a
  short stem and base; heart is two symmetric lobes with a V
  bottom; diamond is a clean rhombus; club is three circles
  joined over a stem. Side-by-side they are unmistakable.

- **Clean checkerboard card back.** The old card back was a
  diamond lattice that had the same transparent-bleed problem
  and looked noisy anyway. Replaced with a crisp 2-pixel black-
  and-white checkerboard that tiles seamlessly across the card
  back's 16x24 footprint.

- **draw_card_face now emits 6 sprites in a rank/suit + 4-tile
  big-pip layout.** The previous 6-sprite layout was
  `[rank][ssuit] / [pipL][pipR] / [blankL][blankR]`; the new
  one is `[rank][ssuit] / [pipTL][pipTR] / [pipBL][pipBR]` with
  the bottom row carrying the bottom half of the big pip
  instead of being wasted blank tiles.

Also:
- New constants TILE_PIP_TL_BASE / TR / BL / BR replace the old
  TILE_PIP_L_BASE / TILE_PIP_R_BASE in constants.ne.
- Refreshed war.nes and the goldens. Every emulator harness
  test still passes (31/31).

https://claude.ai/code/session_0143dTgh3UeRrtfHgQwzcv5z
2026-04-15 18:31:15 +00:00
..
war examples/war: redesign card art — opaque bodies, 16x16 pips, checkerboard back 2026-04-15 18:31:15 +00:00
arrays_and_functions.ne
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
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
bouncing_ball.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
coin_cavern.ne
coin_cavern.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
comparisons.ne
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
function_chain.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
hello_sprite.ne
hello_sprite.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
inline_asm_demo.ne
inline_asm_demo.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
logic_ops.ne
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
loop_break_continue.nes commit built ROMs alongside .ne sources 2026-04-13 15:12:39 +00:00
match_demo.ne
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
mmc1_banked.nes codegen: support banked → banked cross-bank function calls 2026-04-15 02:37:19 +00:00
mmc3_per_state_split.ne
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 examples/war: polish pass + README entry + plan close-out 2026-04-15 16:08:03 +00:00
scanline_split.ne
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
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
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
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
war.ne examples/war: working end-to-end War card game 2026-04-15 15:22:20 +00:00
war.nes examples/war: redesign card art — opaque bodies, 16x16 pips, checkerboard back 2026-04-15 18:31:15 +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.
war.ne production-quality card game, multi-file source layout A complete port of the card game War, split across examples/war/*.ne files and pulled in via include directives. Title screen with a 0/1/2-player menu (cursor sprite, blinking PRESS A, brisk 4/4 march on pulse 2), a 50-frame deal animation, a deep Playing state with an inner phase machine (P_WAIT_A/P_FLY_A/.../P_WAR_BANNER/P_WAR_BURY/P_CHECK), card-conserving queue-based decks built on a 200-iteration random-swap shuffle, a "WAR!" tie-break that buries 3+1 face-down cards per player and plays a noise-channel thump per bury, and a victory screen with the builtin fanfare. The first NEScript example to use a top-level file as a thin shell that includes ~12 component files; building it surfaced and fixed two compiler bugs (E0506 too-many-params, and the IR-lowering wide_hi leak across functions). The remaining limitations and workarounds are catalogued in war/COMPILER_BUGS.md.

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