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

Language: poke() and peek() hardware intrinsics

Common PPU/APU/mapper access previously required either variable
aliases or inline asm. Now two built-in intrinsics handle the
single-register case directly:

    poke(0x2006, 0x3F)     // STA \$3F, \$2006
    poke(0x2006, 0x00)
    poke(0x2007, 0x0F)
    var status: u8 = peek(0x2002)

- Analyzer: \`poke\` / \`peek\` are recognized as built-in intrinsics
  so they don't require a function declaration. Arity is still
  checked (E0203 on mismatch).
- IR: new \`IrOp::Poke(u16, IrTemp)\` and \`IrOp::Peek(IrTemp, u16)\`
  variants carrying the compile-time constant address.
- IR lowering: recognizes the \`poke\`/\`peek\` call names, evaluates
  the address as a const expression, and emits the intrinsic op.
  Falls back to a regular call if the address isn't a constant.
- IR codegen: emits a single LDA/STA in ZP or absolute mode based
  on whether the address fits in a byte.
- Optimizer: Poke has a source temp (liveness), Peek has a dest
  (new value); both pass through the existing passes.

https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
This commit is contained in:
Claude 2026-04-12 17:40:34 +00:00
parent a45944e0f9
commit 496925344d
No known key found for this signature in database
7 changed files with 142 additions and 55 deletions

View file

@ -411,8 +411,20 @@ impl LoweringContext {
self.emit(IrOp::WaitFrame);
}
Statement::Call(name, args, _) => {
let arg_temps: Vec<_> = args.iter().map(|a| self.lower_expr(a)).collect();
self.emit(IrOp::Call(None, name.clone(), arg_temps));
match name.as_str() {
// Built-in `poke(addr, value)` — write a byte to
// a compile-time-constant address.
"poke" if args.len() == 2 => {
if let Some(addr) = self.eval_const(&args[0]) {
let val = self.lower_expr(&args[1]);
self.emit(IrOp::Poke(addr, val));
}
}
_ => {
let arg_temps: Vec<_> = args.iter().map(|a| self.lower_expr(a)).collect();
self.emit(IrOp::Call(None, name.clone(), arg_temps));
}
}
}
Statement::Scroll(x_expr, y_expr, _) => {
let x = self.lower_expr(x_expr);
@ -768,6 +780,15 @@ impl LoweringContext {
t
}
Expr::Call(name, args, _) => {
// Built-in `peek(addr)` reads a byte from a fixed
// absolute address at compile time.
if name == "peek" && args.len() == 1 {
if let Some(addr) = self.eval_const(&args[0]) {
let t = self.fresh_temp();
self.emit(IrOp::Peek(t, addr));
return t;
}
}
let arg_temps: Vec<_> = args.iter().map(|a| self.lower_expr(a)).collect();
let t = self.fresh_temp();
self.emit(IrOp::Call(Some(t), name.clone(), arg_temps));