// AVM ISA v1 — Scala Port (Strict Functional Execution) package avm object AVM { val Q16Scale: Int = 65536 def q16Mul(a: Int, b: Int): Int = ((a.toLong * b.toLong) / Q16Scale).toInt def q16Div(a: Int, b: Int): Int = { if (b == 0) throw new ArithmeticException("div by zero") ((a.toLong * Q16Scale) / b).toInt } // ── Types ─────────────────────────────────────── sealed trait ValType case object Q0 extends ValType case object Q16 extends ValType case object BoolT extends ValType case class AvmVal(ty: ValType, raw: Int) object AvmVal { def q16(x: Int): AvmVal = AvmVal(Q16, x) def bool(x: Boolean): AvmVal = AvmVal(BoolT, if (x) 1 else 0) } // ── Primitives ────────────────────────────────── sealed trait Prim case object AddQ16 extends Prim; case object SubQ16 extends Prim case object MulQ16 extends Prim; case object DivQ16 extends Prim case object LtQ16 extends Prim; case object EqQ16 extends Prim case object And extends Prim; case object Or extends Prim; case object Not extends Prim def execPrim(p: Prim, a: AvmVal, b: Option[AvmVal]): AvmVal = (p, b) match { case (AddQ16, Some(bv)) => AvmVal.q16(a.raw + bv.raw) case (SubQ16, Some(bv)) => AvmVal.q16(a.raw - bv.raw) case (MulQ16, Some(bv)) => AvmVal.q16(q16Mul(a.raw, bv.raw)) case (DivQ16, Some(bv)) => AvmVal.q16(q16Div(a.raw, bv.raw)) case (LtQ16, Some(bv)) => AvmVal.bool(a.raw < bv.raw) case (EqQ16, Some(bv)) => AvmVal.bool(a.raw == bv.raw) case (And, Some(bv)) => AvmVal.bool(a.raw != 0 && bv.raw != 0) case (Or, Some(bv)) => AvmVal.bool(a.raw != 0 || bv.raw != 0) case (Not, _) => AvmVal.bool(a.raw == 0) case _ => throw new RuntimeException("type mismatch") } // ── Instructions ──────────────────────────────── sealed trait Instr case class PushQ16(x: Int) extends Instr case class PushBool(x: Boolean) extends Instr case object Pop extends Instr; case object Dup extends Instr case object Swap extends Instr case class Load(i: Int) extends Instr; case class Store(i: Int) extends Instr case class Jump(t: Int) extends Instr; case class JumpIf(t: Int) extends Instr case class PrimOp(p: Prim) extends Instr; case object Halt extends Instr // ── State ─────────────────────────────────────── case class State(pc: Int, stack: List[AvmVal], locals: Vector[Option[AvmVal]], halted: Boolean) object State { def apply(nLocals: Int = 0): State = State(0, Nil, Vector.fill(nLocals)(None), false) } // ── Step ──────────────────────────────────────── def step(s: State, prog: Vector[Instr]): State = { if (s.halted) throw new RuntimeException("halted") if (s.pc < 0 || s.pc >= prog.length) return s.copy(halted = true) prog(s.pc) match { case PushQ16(x) => s.copy(pc = s.pc + 1, stack = AvmVal.q16(x) :: s.stack) case PushBool(x) => s.copy(pc = s.pc + 1, stack = AvmVal.bool(x) :: s.stack) case Pop => s.copy(pc = s.pc + 1, stack = s.stack.tail) case Dup => s.copy(pc = s.pc + 1, stack = s.stack.head :: s.stack) case Swap => val a :: b :: rest = s.stack s.copy(pc = s.pc + 1, stack = b :: a :: rest) case Load(i) => val v = s.locals(i).getOrElse(throw new RuntimeException("missing local")) s.copy(pc = s.pc + 1, stack = v :: s.stack) case Store(i) => s.copy(pc = s.pc + 1, locals = s.locals.updated(i, Some(s.stack.head)), stack = s.stack.tail) case Jump(t) => s.copy(pc = t) case JumpIf(t) => if (s.stack.head.raw != 0) s.copy(pc = t, stack = s.stack.tail) else s.copy(pc = s.pc + 1, stack = s.stack.tail) case PrimOp(p) => val arity = if (p == Not) 1 else 2 val b = if (arity >= 2) Some(s.stack.head) else None val rest = if (arity >= 2) s.stack.tail else s.stack val a = rest.head val result = execPrim(p, a, b) s.copy(pc = s.pc + 1, stack = result :: rest.tail) case Halt => s.copy(halted = true) } } def run(init: State, prog: Vector[Instr], fuel: Int): State = { var s = init for (_ <- 0 until fuel if !s.halted) s = step(s, prog) s } }