mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 08:55:38 +00:00
113 lines
3.1 KiB
Text
113 lines
3.1 KiB
Text
|
|
// MMC3 Per-State Scanline Split — proves the compiler's per-state
|
||
|
|
// IRQ dispatch and reload logic. Two states each own their own
|
||
|
|
// `on scanline(N)` handler at a different scanline; pressing START
|
||
|
|
// transitions between them. Because the MMC3 IRQ latch is
|
||
|
|
// reloaded from the *current* state's scanline each frame (via
|
||
|
|
// the `__ir_mmc3_reload` helper), the visible split line moves
|
||
|
|
// when the state changes.
|
||
|
|
//
|
||
|
|
// What this exercises end-to-end:
|
||
|
|
// - MMC3 scanline IRQ firing at the latched line
|
||
|
|
// - `__ir_mmc3_reload` walking the dispatch table to pick the
|
||
|
|
// latch value for the new state after a transition
|
||
|
|
// - `__irq_user` dispatching to the right per-state handler
|
||
|
|
// - scroll writes from inside an IRQ handler landing before the
|
||
|
|
// PPU renders the next visible scanline
|
||
|
|
//
|
||
|
|
// Controls:
|
||
|
|
// START — toggle between Upper-split and Lower-split states
|
||
|
|
//
|
||
|
|
// Build: cargo run -- build examples/mmc3_per_state_split.ne
|
||
|
|
|
||
|
|
game "MMC3 Split" {
|
||
|
|
mapper: MMC3
|
||
|
|
mirroring: horizontal
|
||
|
|
}
|
||
|
|
|
||
|
|
// Scroll values for the two halves of the screen. `frame_scroll`
|
||
|
|
// drifts every frame so the split is easy to see in motion — the
|
||
|
|
// top half scrolls right, the bottom scrolls left.
|
||
|
|
var top_scroll: u8 = 0
|
||
|
|
var bottom_scroll: u8 = 0
|
||
|
|
|
||
|
|
// Tiny debouncer so one press of START doesn't cycle through the
|
||
|
|
// states multiple times per frame.
|
||
|
|
var debounce: u8 = 0
|
||
|
|
|
||
|
|
// Shared drift counter — primarily for observability (the split
|
||
|
|
// animation works from `top_scroll` / `bottom_scroll` directly).
|
||
|
|
var _frame_counter: u8 = 0
|
||
|
|
|
||
|
|
state Upper {
|
||
|
|
on enter {
|
||
|
|
// When we arrive, reset the scroll so the split is easy
|
||
|
|
// to see from the first frame onward.
|
||
|
|
top_scroll = 0
|
||
|
|
bottom_scroll = 0
|
||
|
|
}
|
||
|
|
|
||
|
|
on frame {
|
||
|
|
_frame_counter += 1
|
||
|
|
top_scroll += 1
|
||
|
|
bottom_scroll -= 1
|
||
|
|
|
||
|
|
// Initial scroll for the TOP half. The scanline handler
|
||
|
|
// below will rewrite scroll midway through the frame.
|
||
|
|
scroll(top_scroll, 0)
|
||
|
|
|
||
|
|
// Draw a marker at the top half and a player sprite in
|
||
|
|
// the bottom half so the split position is visually
|
||
|
|
// obvious.
|
||
|
|
draw Marker at: (40, 40)
|
||
|
|
draw Player at: (120, 140)
|
||
|
|
|
||
|
|
if debounce > 0 {
|
||
|
|
debounce -= 1
|
||
|
|
}
|
||
|
|
if button.start and debounce == 0 {
|
||
|
|
transition Lower
|
||
|
|
debounce = 30
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Split at line 80 — the top 80 rows use `top_scroll`, the
|
||
|
|
// rest use `bottom_scroll`.
|
||
|
|
on scanline(80) {
|
||
|
|
scroll(bottom_scroll, 0)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
state Lower {
|
||
|
|
on enter {
|
||
|
|
top_scroll = 0
|
||
|
|
bottom_scroll = 0
|
||
|
|
}
|
||
|
|
|
||
|
|
on frame {
|
||
|
|
_frame_counter += 1
|
||
|
|
top_scroll += 1
|
||
|
|
bottom_scroll -= 1
|
||
|
|
|
||
|
|
scroll(top_scroll, 0)
|
||
|
|
|
||
|
|
draw Marker at: (40, 40)
|
||
|
|
draw Player at: (120, 140)
|
||
|
|
|
||
|
|
if debounce > 0 {
|
||
|
|
debounce -= 1
|
||
|
|
}
|
||
|
|
if button.start and debounce == 0 {
|
||
|
|
transition Upper
|
||
|
|
debounce = 30
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Split at line 160 — lower split, so the top 160 rows use
|
||
|
|
// `top_scroll` and only the last ~80 rows use `bottom_scroll`.
|
||
|
|
on scanline(160) {
|
||
|
|
scroll(bottom_scroll, 0)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
start Upper
|