mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 08:55:38 +00:00
Five language features and optimizations from the planned-work backlog: - **Minimal audio driver**: `play`/`start_music`/`stop_music` now generate APU pulse-1/pulse-2 writes from a builtin SFX/music name table, and the NMI handler gains a `JSR __audio_tick` splice (via the linker's `__audio_used` marker lookup) that ages an SFX countdown counter and mutes pulse 1 when the tone expires. Programs that never trigger audio pay zero ROM cost. - **u16 arithmetic and comparisons**: new IR ops `LoadVarHi`, `StoreVarHi`, `Add16`, `Sub16`, and six `Cmp*16` variants. The lowering context tracks variable types via the analyzer's symbol table and routes expressions through the 8-bit or 16-bit path based on operand width. Add16 emits `CLC;ADC;ADC` with carry propagating naturally into the high byte; compares dispatch high-byte-first with a short-circuit low-byte fallback. Fixes a silent miscompile where `big += 1` on a u16 var only incremented the low byte. - **Multi-scanline handlers per state**: `gen_scanline_irq` now dispatches on `(current_state, ZP_SCANLINE_STEP)` and reloads the MMC3 counter with the delta to the next scanline in the same state. `gen_scanline_reload` resets the step counter at the top of each NMI so a state with multiple handlers fires them in ascending line order. Previously only the first handler per state ever fired. - **IR temp slot recycling**: `build_use_counts` pre-scans each function to count per-temp uses; `retire_op_sources` decrements the counts after each op and pushes dead slots back onto `free_slots` for later allocation. `bitwise_ops.ne` used to crash (debug) or miscompile (release) once it hit 128 concurrent temps; with recycling the same function now uses ~4 slots instead of 136. - **INC/DEC peephole fold + improved dead-load elimination**: `fold_inc_dec` collapses `LDA addr; CLC; ADC #1; STA addr` into a single `INC addr` (and the SEC/SBC variant into `DEC addr`), saving 5 bytes and 5 cycles per increment. The fold is suppressed when the next instruction reads carry. `remove_dead_loads` now walks past INC/DEC/STX/STY (which don't touch A) to find the actual next A-use, catching more dead loads. Tests: 331 unit + 39 integration (up from 313 + 37), including new guards for audio, u16, multi-scanline, and slot recycling. https://claude.ai/code/session_01A8qk3gw2jWSzdiXBZPZSFE |
||
|---|---|---|
| .. | ||
| arrays_and_functions.ne | ||
| audio_demo.ne | ||
| bitwise_ops.ne | ||
| bouncing_ball.ne | ||
| coin_cavern.ne | ||
| comparisons.ne | ||
| function_chain.ne | ||
| hello_sprite.ne | ||
| inline_asm_demo.ne | ||
| logic_ops.ne | ||
| loop_break_continue.ne | ||
| match_demo.ne | ||
| mmc1_banked.ne | ||
| mmc3_per_state_split.ne | ||
| README.md | ||
| scanline_split.ne | ||
| sprites_and_palettes.ne | ||
| state_machine.ne | ||
| structs_enums_for.ne | ||
| two_player.ne | ||
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, palettes, scroll, cast | Inline CHR data, palette switching, type casting |
mmc1_banked.ne |
MMC1, banks, multiply | Banked mapper with software multiply |
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