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

Codegen: skip OAM clear loop in handlers that don't draw

The OAM clear at frame-handler entry is only needed if the
handler actually modifies OAM — handlers that never call \`draw\`
don't need to reset the shadow buffer at all, since the previous
handler's contents are still correct (or the startup zero-fill
still applies). Adds a \`function_draws_sprites\` helper that
walks the IR ops once and emits the clear loop only when at least
one \`DrawSprite\` exists.

Handlers like a title screen that doesn't render sprites pay zero
cycles for the OAM clear now.

https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
This commit is contained in:
Claude 2026-04-12 18:06:02 +00:00
parent 28cb739840
commit 629e596ec7
No known key found for this signature in database

View file

@ -356,12 +356,15 @@ impl<'a> IrCodeGen<'a> {
self.emit_label(&format!("__ir_fn_{}", func.name));
// At the start of every frame handler, clear the OAM shadow
// buffer so stale sprites from the previous frame (or from a
// different state's handler) don't linger on screen. We set
// the Y position byte of every OAM entry to $FE (off-screen).
// The actual drawing code overwrites the slots it needs.
if self.in_frame_handler {
// At the start of a frame handler that actually draws
// sprites, clear the OAM shadow buffer so stale sprites from
// the previous frame (or from a different state's handler)
// don't linger on screen. We set the Y position byte of every
// OAM entry to $FE (off-screen) and the `draw` code
// overwrites the slots it needs. Handlers that never call
// `draw` skip the clear entirely — the NMI handler's DMA
// copies whatever's in $0200 unchanged.
if self.in_frame_handler && function_draws_sprites(func) {
let clear_loop = format!("__ir_oam_clear_{}", func.name);
self.emit(LDX, AM::Immediate(0));
self.emit(LDA, AM::Immediate(0xFE));
@ -800,6 +803,16 @@ fn substitute_asm_vars<F: Fn(&str) -> Option<u16>>(body: &str, resolver: F) -> S
out
}
/// True if the given IR function contains at least one
/// `DrawSprite` op. Used by the frame-handler OAM clear to skip
/// the clear loop when a handler doesn't actually draw anything.
fn function_draws_sprites(func: &IrFunction) -> bool {
func.blocks
.iter()
.flat_map(|b| &b.ops)
.any(|op| matches!(op, IrOp::DrawSprite { .. }))
}
/// Scan the IR functions for all scanline handlers (named
/// `{state}_scanline_{line}`) and return them in IR function order.
fn collect_scanline_handlers(ir: &IrProgram) -> Vec<(String, u8)> {