1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-08 00:45:38 +00:00
nescript/examples/i16_demo.ne

49 lines
1.4 KiB
Text
Raw Normal View History

compiler: i16 / SRAM saves / inline-asm dot labels / docs Another batch from the cc65/nesdoug catalogue. All gated on parser-level opt-in or default-false attributes so existing programs produce byte-identical ROMs (no committed .nes file changed). **§A — `i16` signed 16-bit type:** - New `KwI16` lexer token, `NesType::I16` AST variant, parser case in `parse_type`. Type-size and integer-type tables treat `i16` like `u16` (2 bytes, integer). - IR lowering accepts `i16` everywhere it accepts `u16` for wide-load / wide-store / widen-narrow paths. - New constant fold for `UnaryOp::Negate(IntLiteral(v))` that emits the wide two's-complement form. Without it, `var vy: i16 = -10` would zero-extend to `$00F6` (= 246) instead of sign-extending to `$FFF6` (= -10). Negative literals now store the right bytes. - Comparisons reuse the existing unsigned 16-bit compare ops (matching the existing `i8` behaviour). Documented in the `NesType::I16` doc comment and in `future-work.md` §A. - Example `examples/i16_demo.ne` with committed golden. - Tests cover the literal-fold sign-extension and end-to-end compile of the example. **§S — SRAM / battery-backed saves:** - New `save { var ... }` top-level block. Lexer + parser opt into a dedicated `KwSave` token. Analyzer allocates save vars from a separate `next_sram_addr` bump pointer starting at `$6000`, capped at `$8000` (8 KB cartridge SRAM window). - Linker reads `analysis.has_battery_saves` and flips iNES byte-6 bit-1 via the new `RomBuilder::set_battery` / `Linker::with_battery` chain. - New `W0111` warning for save-var initializers — SRAM is preserved across power cycles, so an init expression would either silently never run or clobber persisted data on every boot. The warning teaches the user about the magic-byte sentinel pattern. - Struct fields in save blocks are explicitly rejected for now (the field-flattening path uses the main-RAM allocator). - Example `examples/sram_demo.ne` with committed golden, plus 4 integration tests. **§D (partial) — inline-asm `.label:` syntax:** - Codegen-side mangler rewrites `.IDENT` → `__ilab_<N>_IDENT` per inline-asm block, where `<N>` is the call site's monotonic suffix. Two `asm { .loop: ... }` blocks in the same function now coexist without colliding in the linker's label table. - Bounds checks on `.` placement: `$2002` and `name.field` are unaffected; only `.IDENT` in label / branch context triggers the rewrite. Two integration tests pin the uniqueness and dollar-vs-dot disambiguation. **§X follow-up — Mesen trace-log docs:** - New "Debugger-assisted workflows" section in `docs/nes-reference.md` walking through the Mesen / FCEUX log workflows alongside the new `debug_port:` attribute. **Misc:** - `future-work.md` updated to mark the shipped items out of the catalogue and reshuffle the priority ranking. Remaining niche follow-ups (signedness on Cmp16, struct save fields, inline-asm format specifiers) documented inline so future passes know the design. All 757 tests pass. Clippy clean. 46/46 emulator goldens match.
2026-04-18 20:49:06 +00:00
// i16 demo — exercises the signed 16-bit type with negative
// literals, signed velocity, and round-tripping through wide
// arithmetic.
//
// Position is stored as `i16` so negative deltas (the ball moving
// left or up) can be represented directly instead of having to
// shadow them with a sign byte. The lowering folds `-N` literals
// into wide two's-complement constants, so `vy = -10` lands as
// `$FFF6` (=-10) rather than the zero-extended `$00F6` (=246).
game "i16 Demo" {
mapper: NROM
}
var px: i16 = 64
var py: i16 = 80
var vx: i16 = 1
var vy: i16 = -1
var frame: u8 = 0
on frame {
frame += 1
// Bounce off the visible-area edges. Comparisons against the
// small positive bounds use the unsigned 16-bit compare path
// (matching the existing i8 behaviour) — for purely positive
// ranges this matches signed semantics.
px += vx
py += vy
// Reverse vx every 100 frames so we exercise both the
// positive-velocity and negative-velocity arithmetic paths.
if frame == 100 {
frame = 0
vx = vx + vx // double-then-negate would be cleaner once
vx = vx - vx // we add unary `-` on a runtime expression
vx = 1
vy = -1
}
// Clamp position into u8 range for `draw`. The cast truncates
// the high byte; for our 0..255 motion that's the right thing.
var dx: u8 = px as u8
var dy: u8 = py as u8
draw Ball at: (dx, dy)
}
start Main