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

IR codegen: clear OAM shadow at frame handler entry

Each frame handler now begins with a short 64-slot loop that
writes \$FE to the Y-position byte of every OAM entry, hiding any
sprites the previous frame drew that the current frame doesn't.
Without this, stopping a \`draw\` call one frame leaves the sprite
lingering at its last position forever.

Cost: ~384 cycles per frame (2.3% of the ~16 666 cycles available
between vblanks). The per-frame \`draw\` calls overwrite the slots
they actually use, so the clear is free at runtime for sprites
that ARE drawn.

https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
This commit is contained in:
Claude 2026-04-12 17:58:36 +00:00
parent d2075bc8f9
commit 2fd3a6e545
No known key found for this signature in database

View file

@ -356,6 +356,24 @@ 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 {
let clear_loop = format!("__ir_oam_clear_{}", func.name);
self.emit(LDX, AM::Immediate(0));
self.emit(LDA, AM::Immediate(0xFE));
self.emit_label(&clear_loop);
self.emit(STA, AM::AbsoluteX(0x0200));
self.emit(INX, AM::Implied);
self.emit(INX, AM::Implied);
self.emit(INX, AM::Implied);
self.emit(INX, AM::Implied);
self.emit(BNE, AM::LabelRelative(clear_loop));
}
for block in &func.blocks {
self.gen_block(block);
}