1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-09 09:18:01 +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

@ -991,7 +991,9 @@ impl Analyzer {
// `return value` silently — the value is simply discarded.
}
Statement::Call(name, args, span) => {
if self.symbols.contains_key(name) {
if is_intrinsic(name) {
self.check_intrinsic_args(name, args, *span);
} else if self.symbols.contains_key(name) {
self.check_call_signature(name, args, *span);
} else {
self.diagnostics.push(Diagnostic::error(
@ -1336,6 +1338,44 @@ fn is_small_constant(expr: &Expr) -> bool {
matches!(expr, Expr::IntLiteral(_, _))
}
/// True if `name` is a built-in intrinsic function recognized by the
/// compiler. Intrinsics don't need a declaration and may have
/// special codegen (e.g. \`poke\` / \`peek\` write to raw addresses).
fn is_intrinsic(name: &str) -> bool {
matches!(name, "poke" | "peek")
}
impl Analyzer {
/// Validate the arguments to a built-in intrinsic. Emits
/// diagnostics for mismatched arity or non-constant addresses.
fn check_intrinsic_args(&mut self, name: &str, args: &[Expr], span: Span) {
match name {
"poke" => {
if args.len() != 2 {
self.diagnostics.push(Diagnostic::error(
ErrorCode::E0203,
format!(
"`poke` takes exactly 2 arguments (addr, value), got {}",
args.len()
),
span,
));
}
}
"peek" => {
if args.len() != 1 {
self.diagnostics.push(Diagnostic::error(
ErrorCode::E0203,
format!("`peek` takes exactly 1 argument (addr), got {}", args.len()),
span,
));
}
}
_ => {}
}
}
}
/// True if this statement unconditionally ends block execution —
/// subsequent statements in the same block cannot be reached.
fn stmt_is_terminator(stmt: &Statement) -> bool {