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:
parent
a45944e0f9
commit
496925344d
7 changed files with 142 additions and 55 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue