mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 17:06:04 +00:00
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
80 lines
2 KiB
Text
80 lines
2 KiB
Text
// Logic Ops — demonstrates keyword-based boolean operators
|
|
// (`and`, `or`, `not`) along with comparison operators.
|
|
//
|
|
// The player sprite is drawn only when it's "alive AND unpaused",
|
|
// the score counter freezes when paused OR dead, and movement is
|
|
// allowed when NOT paused.
|
|
//
|
|
// Build: cargo run -- build examples/logic_ops.ne
|
|
|
|
game "Logic Ops" {
|
|
mapper: NROM
|
|
}
|
|
|
|
var alive: bool = true
|
|
var paused: bool = false
|
|
var px: u8 = 100
|
|
var py: u8 = 120
|
|
var score: u8 = 0
|
|
var tick: u8 = 0
|
|
var debounce: u8 = 0
|
|
|
|
on frame {
|
|
if debounce > 0 {
|
|
debounce -= 1
|
|
}
|
|
|
|
// Start toggles pause; B kills the player (both with debounce).
|
|
if button.start and debounce == 0 {
|
|
if paused {
|
|
paused = false
|
|
} else {
|
|
paused = true
|
|
}
|
|
debounce = 20
|
|
}
|
|
if button.b and debounce == 0 {
|
|
alive = false
|
|
debounce = 20
|
|
}
|
|
if button.a and debounce == 0 {
|
|
alive = true
|
|
debounce = 20
|
|
}
|
|
|
|
// Movement is only allowed when alive AND not paused.
|
|
if alive and (not paused) {
|
|
if button.right { px += 1 }
|
|
if button.left { px -= 1 }
|
|
if button.up { py -= 1 }
|
|
if button.down { py += 1 }
|
|
}
|
|
|
|
// Score ticks up 1/frame unless paused or dead. Using OR to
|
|
// short-circuit when either flag blocks scoring.
|
|
tick += 1
|
|
if paused or (not alive) {
|
|
// frozen
|
|
} else {
|
|
if tick >= 30 {
|
|
tick = 0
|
|
if score < 240 {
|
|
score += 1
|
|
}
|
|
}
|
|
}
|
|
|
|
// Only draw the player when alive and unpaused.
|
|
if alive and (not paused) {
|
|
draw Player at: (px, py)
|
|
}
|
|
|
|
// A simple score bar: draw one marker per 30 score points so the
|
|
// effect of the pause/dead gating is visible without text.
|
|
if score >= 30 { draw Pip at: (20, 20) }
|
|
if score >= 60 { draw Pip at: (30, 20) }
|
|
if score >= 90 { draw Pip at: (40, 20) }
|
|
if score >= 120 { draw Pip at: (50, 20) }
|
|
}
|
|
|
|
start Main
|