1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-11 18:20:47 +00:00

debug: add debug.frame_overrun_count() and debug.frame_overran() builtins

The frame-overrun counter at $07FF was previously only readable
via `peek(0x07FF)`, which forces every program that wants to
guard against missed frames to know the magic address. This adds
two named query expressions:

- `debug.frame_overrun_count()` — cumulative miss count since reset
- `debug.frame_overran()` — sticky bit cleared by the next wait_frame,
  so `debug.assert(not debug.frame_overran())` catches a miss in the
  previous window without waiting for the counter to roll over.

The sticky bit lives at $07FE alongside the existing counter and
is set inside the same NMI-time overrun branch. Release builds
emit none of the runtime side: the NMI handler still skips both
writes, the codegen `wait_frame` only clears $07FE in debug mode,
and committed example ROMs stay byte-identical.

The new expression form parses through `parse_primary`'s `KwDebug`
arm, so the existing `debug.log(...)` / `debug.assert(...)`
*statement* parser stays untouched. The analyzer rejects unknown
methods with E0201 and stray arguments with E0203 so typos don't
silently compile to a zero load.

https://claude.ai/code/session_01KEczoNUX3WmcFLfq6iAQxB
This commit is contained in:
Claude 2026-04-15 01:57:45 +00:00
parent bf7819ca0f
commit d4e613fb7c
No known key found for this signature in database
9 changed files with 401 additions and 7 deletions

View file

@ -1090,6 +1090,29 @@ impl LoweringContext {
// For now, just evaluate the inner expression (truncation/extension is a no-op on 8-bit)
self.lower_expr(inner)
}
Expr::DebugCall(method, _args, _) => {
// The analyzer already validated the method name and
// argument count, so we can dispatch on the method
// name directly. Both currently-supported methods
// map to a Peek of a runtime address: the codegen
// strips the read out and substitutes a constant
// zero in release builds, so the builtin disappears
// from non-debug ROMs.
let t = self.fresh_temp();
let addr: u16 = match method.as_str() {
"frame_overrun_count" => 0x07FF,
"frame_overran" => 0x07FE,
// Should be unreachable post-analyzer, but emit
// a zero rather than panicking so a parser test
// that bypasses the analyzer still produces IR.
_ => {
self.emit(IrOp::LoadImm(t, 0));
return t;
}
};
self.emit(IrOp::Peek(t, addr));
t
}
}
}