1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-08 17:06:04 +00:00
nescript/examples/README.md
Claude db3a4adc57
codegen: support banked → banked cross-bank function calls
Programs that put functions in switchable banks can now call across
bank boundaries — `bank A { fun step() { helper() } }` where
`helper` lives in `bank B` used to panic in the IR codegen. Three
small pieces unblock it:

1. **Generic trampoline.** `runtime/gen_bank_trampoline` no longer
   takes a `fixed_bank_index` argument. Instead it reads the
   caller's current bank from `ZP_BANK_CURRENT`, pushes it on the
   hardware stack, switches to the target, JSRs the entry, then
   pulls and restores the saved bank. The same per-callee stub
   works for fixed→banked and banked→banked direction; nested
   trampolines compose because each PHA/PLA pair sits inside its
   own JSR/RTS frame. `gen_mapper_init` seeds `ZP_BANK_CURRENT`
   with the fixed bank index for any banked mapper so the very
   first cross-bank call from the fixed bank still restores to
   the fixed bank (matching pre-banked-banked semantics).

2. **Codegen drops the panic.** The `Some(from), Some(to)` arm in
   the call-resolution switch now emits `JSR __tramp_<name>` like
   the fixed→banked case instead of panicking. Banked→fixed calls
   still go direct (the fixed bank is always mapped at $C000).

3. **Bank-namespaced local labels.** Two banks emitting the same
   `__ir_cmp_e_8` would trip the linker's discovery-pass duplicate-
   label check the moment any banked code generated a comparison.
   The new `local_label_suffix` helper prefixes the suffix with the
   current bank name when banked code is being emitted, leaving
   fixed-bank label generation untouched (so existing examples are
   byte-identical apart from the trampoline / init bytes
   themselves).

The new `examples/uxrom_banked_to_banked.ne` demonstrates the path
end-to-end: `bank Logic { fun step() { ... clamp() } }` calls
`bank Helpers { fun clamp() { ... } }` once per frame. The harness
golden is committed alongside it. The five existing banked example
ROMs change byte-for-byte because of the new trampoline shape and
the seed-ZP_BANK_CURRENT init, but their emulator goldens still
match exactly — observable behaviour is unchanged.

https://claude.ai/code/session_01KEczoNUX3WmcFLfq6iAQxB
2026-04-15 02:37:19 +00:00

79 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# NEScript Examples
## Quick Start
```bash
# 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](https://www.mesen.ca/), [FCEUX](https://fceux.com/), 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 |
| `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. |
| `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
```bash
# 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
```