1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-08 17:06:04 +00:00
nescript/examples/scanline_split.ne

62 lines
1.7 KiB
Text
Raw Normal View History

examples: add 5 new programs covering match/loop/logic/bitwise/scanline Fills the biggest feature-coverage gaps in the existing example set: - match_demo.ne — match statement over a Screen enum, driving a title / playing / paused / game-over flow with a debounced controller. - loop_break_continue.ne — `loop { ... }` with `break` and `continue`, scanning an enemy array for the first hit. - logic_ops.ne — keyword-based `and` / `or` / `not` gating movement and scoring on alive/paused flags. - bitwise_ops.ne — packed status-byte flags with `&` / `|` / `^` / `>>` plus a health-bar render loop. - scanline_split.ne — MMC3 `on scanline(120)` handler rewriting the scroll register mid-frame for a classic status-bar split. All 14 examples (9 existing + 5 new) pass the jsnes smoke test (`14/14 ROMs rendered successfully`) and still pass `cargo fmt`, `cargo clippy -D warnings`, and `cargo test`. Known limitations surfaced while authoring these examples, to be fixed in follow-up commits: 1. Array-literal global initializers (`var xs: u8[4] = [1,2,3,4]`) are silently dropped by `lower_program` — `eval_const` returns None for `Expr::ArrayLiteral` and no synthetic per-element init code is emitted. Affects `arrays_and_functions`, `structs_enums_for`, `loop_break_continue`, and any future array-using example. Arrays effectively boot at all-zero. 2. `draw` inside a loop body reuses one static OAM slot — `next_oam_slot` increments at IR-codegen time rather than at runtime, so N iterations all write to the same 4-byte OAM entry. Affects `arrays_and_functions`, `structs_enums_for`, `bitwise_ops` (health pips), and any loop that wants to render per-iteration sprites. Both bugs are latent and didn't surface until I tried to write examples that exercise the relevant features — the existing integration tests only check iNES header structure, and the jsnes smoke test's "at least one sprite rendered" bar is satisfied by one sprite even when several were intended. https://claude.ai/code/session_014Z5y3Q9krLcAxYpZQJhZ5V
2026-04-12 19:05:18 +00:00
// Scanline Split — demonstrates MMC3's per-scanline IRQ via an
// `on scanline(N)` handler. The handler runs at PPU scanline 120,
// roughly mid-screen, and changes the horizontal scroll register
// so the top half and bottom half appear to scroll independently.
// This is the classic "status bar + parallax" trick used in many
// NES games.
//
// Build: cargo run -- build examples/scanline_split.ne
game "Scanline Split" {
mapper: MMC3
mirroring: horizontal
}
var top_scroll: u8 = 0
var bottom_scroll: u8 = 0
var px: u8 = 120
var py: u8 = 160
state Main {
on enter {
top_scroll = 0
bottom_scroll = 0
px = 120
py = 160
}
on frame {
// Drift the top layer left and the bottom layer right so
// the split is easy to see when the example runs.
top_scroll += 1
bottom_scroll -= 1
// Player on the bottom half.
if button.right { px += 2 }
if button.left { px -= 2 }
if button.up { py -= 2 }
if button.down { py += 2 }
// Top-half banner.
draw Banner at: (40, 24)
// Bottom-half player.
draw Player at: (px, py)
// Set scroll for the TOP half. The scanline handler will
// overwrite this partway through the frame for the bottom
// half. Without a handler call the PPU would use this one
// value for the entire visible screen.
scroll(top_scroll, 0)
}
// Fires ~halfway down the visible screen. MMC3's scanline IRQ
// counts rendered lines and fires when the reload value
// elapses; the compiler emits the reload + acknowledge glue.
on scanline(120) {
scroll(bottom_scroll, 0)
}
}
start Main