mirror of
https://github.com/allaunthefox/SilverSight.git
synced 2026-08-10 09:10:34 +00:00
Lean (reference), Python, Rust, C, C++, Go, Julia, R, Scala, Fortran, Coq, Octave — all implementing the same AVM ISA v1 specification. Every port implements: - Full type universe: Q0_16, Q16_16, Bool - 11 primitives with floor division (Lean Int.ediv), V6 signed comparison, symmetric clamping [-2147483647, 2147483647] - 12 instruction opcodes with stack depth limit (1024) - Fuel-bounded run loop - Error handling (stack under/overflow, type mismatch, div-by-zero, jump OOB)
135 lines
6.5 KiB
Scala
135 lines
6.5 KiB
Scala
// AVM ISA v1 — Scala Port (Strict Functional Execution)
|
|
package silversight.avm
|
|
|
|
object AVM {
|
|
// ── Constants ──────────────────────────────────────────────────
|
|
val AVMClampMin = -2147483647
|
|
val AVMClampMax = 2147483647
|
|
val AVMQ0Min = -32767
|
|
val AVMQ0Max = 32767
|
|
val Q16Scale = 65536L
|
|
val AVMMaxStack = 1024
|
|
|
|
def avmClamp(x: Long): Int = math.min(AVMClampMax, math.max(AVMClampMin, x.toInt))
|
|
def avmQ0Clamp(x: Long): Int = math.min(AVMQ0Max, math.max(AVMQ0Min, x.toInt))
|
|
|
|
def floorDiv(a: Long, b: Long): Int = {
|
|
if (b == 0) throw new ArithmeticException("division by zero")
|
|
val q = a / b; val r = a % b
|
|
(if (r != 0 && ((a ^ b) < 0)) q - 1 else q).toInt
|
|
}
|
|
|
|
def ltQ16V6(a: Int, b: Int): Boolean = {
|
|
val sa = a < 0; val sb = b < 0
|
|
if (sa != sb) sa else a < b
|
|
}
|
|
|
|
// ── Types ────────────────────────────────────────────────────
|
|
sealed trait Ty
|
|
case object Q0_16 extends Ty; case object Q16_16 extends Ty; case object BoolTy extends Ty
|
|
|
|
case class Val(ty: Ty, i: Int = 0, b: Boolean = false)
|
|
object Val { def q16(x: Int) = Val(Q16_16, i = avmClamp(x))
|
|
def q0(x: Int) = Val(Q0_16, i = avmQ0Clamp(x))
|
|
def bool(x: Boolean) = Val(BoolTy, b = x) }
|
|
|
|
// ── Primitives ──────────────────────────────────────────────
|
|
sealed trait Prim
|
|
case object AddSatQ0 extends Prim; case object SubSatQ0 extends Prim
|
|
case object AddSatQ16 extends Prim; case object SubSatQ16 extends Prim
|
|
case object MulSatQ16 extends Prim; case object DivSatQ16 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 primArity(p: Prim): Int = if (p == Not) 1 else 2
|
|
|
|
def execPrim(p: Prim, a: Val, b: Val): Val = (p, a.ty, b.ty) match {
|
|
case (AddSatQ0, Q0_16, Q0_16) => Val.q0(avmQ0Clamp(a.i.toLong + b.i))
|
|
case (SubSatQ0, Q0_16, Q0_16) => Val.q0(avmQ0Clamp(a.i.toLong - b.i))
|
|
case (AddSatQ16, Q16_16, Q16_16) => Val.q16(avmClamp(a.i.toLong + b.i))
|
|
case (SubSatQ16, Q16_16, Q16_16) => Val.q16(avmClamp(a.i.toLong - b.i))
|
|
case (MulSatQ16, Q16_16, Q16_16) => Val.q16(avmClamp(floorDiv(a.i.toLong * b.i, Q16Scale)))
|
|
case (DivSatQ16, Q16_16, Q16_16) => Val.q16(avmClamp(floorDiv(a.i.toLong * Q16Scale, b.i)))
|
|
case (LtQ16, Q16_16, Q16_16) => Val.bool(ltQ16V6(a.i, b.i))
|
|
case (EqQ16, Q16_16, Q16_16) => Val.bool(a.i == b.i)
|
|
case (And, BoolTy, BoolTy) => Val.bool(a.b && b.b)
|
|
case (Or, BoolTy, BoolTy) => Val.bool(a.b || b.b)
|
|
case (Not, BoolTy, _) => Val.bool(!a.b)
|
|
case _ => throw new RuntimeException("type mismatch")
|
|
}
|
|
|
|
// ── Instructions ────────────────────────────────────────────
|
|
sealed trait Op
|
|
case class PushQ16(x: Int) extends Op; case class PushBool(b: Boolean) extends Op
|
|
case class PushQ0(x: Int) extends Op; case object Pop extends Op
|
|
case object Dup extends Op; case object Swap extends Op
|
|
case class Load(i: Int) extends Op; case class Store(i: Int) extends Op
|
|
case class Jump(t: Int) extends Op; case class JumpIf(t: Int) extends Op
|
|
case class Primitive(p: Prim) extends Op; case object Halt extends Op
|
|
|
|
// ── State ───────────────────────────────────────────────────
|
|
case class State(pc: Int, stack: List[Val], locals: Vector[Option[Val]], halted: Boolean)
|
|
def initState(nLocals: Int = 0): State = State(0, Nil, Vector.fill(nLocals)(None), false)
|
|
|
|
// ── Step ────────────────────────────────────────────────────
|
|
def step(s: State, prog: Vector[Op]): Option[State] = {
|
|
if (s.halted) return None
|
|
if (s.pc < 0 || s.pc >= prog.length) return Some(State(s.pc, s.stack, s.locals, true))
|
|
|
|
val instr = prog(s.pc); var stack = s.stack; val npc = s.pc + 1
|
|
|
|
val growing = instr match {
|
|
case _: PushQ16 | _: PushBool | _: PushQ0 | Dup | Load => true; case _ => false
|
|
}
|
|
if (growing && stack.length >= AVMMaxStack) return None
|
|
|
|
instr match {
|
|
case PushQ16(x) => stack = Val.q16(x) :: stack
|
|
case PushBool(b) => stack = Val.bool(b) :: stack
|
|
case PushQ0(x) => stack = Val.q0(x) :: stack
|
|
case Pop => stack match {
|
|
case Nil => return None; case _ :: xs => stack = xs}
|
|
case Dup => stack match {
|
|
case Nil => return None; case x :: xs => stack = x :: x :: xs}
|
|
case Swap => stack match {
|
|
case a :: b :: xs => stack = b :: a :: xs; case _ => return None}
|
|
case Load(i) =>
|
|
if (i >= s.locals.length || s.locals(i).isEmpty) return None
|
|
stack = s.locals(i).get :: stack
|
|
case Store(i) => stack match {
|
|
case Nil => return None
|
|
case v :: xs =>
|
|
if (i >= s.locals.length) return None
|
|
val newLocals = s.locals.updated(i, Some(v))
|
|
return Some(State(npc, xs, newLocals, false))
|
|
}
|
|
case Jump(t) => if (t < 0 || t >= prog.length) return None
|
|
else return Some(State(t, stack, s.locals, false))
|
|
case JumpIf(t) => stack match {
|
|
case Val(BoolTy, _, true) :: xs =>
|
|
if (t < 0 || t >= prog.length) return None
|
|
return Some(State(t, xs, s.locals, false))
|
|
case Val(BoolTy, _, false) :: xs => stack = xs
|
|
case _ => return None
|
|
}
|
|
case Primitive(p) =>
|
|
val arity = primArity(p)
|
|
if (stack.length < arity) return None
|
|
val b = if (arity >= 2) { val (v, rest) = (stack.head, stack.tail); stack = rest; v } else Val.bool(false)
|
|
val a = stack.head; stack = stack.tail
|
|
try { stack = execPrim(p, a, b) :: stack } catch { case _: Throwable => return None }
|
|
case Halt => return Some(State(s.pc, stack, s.locals, true))
|
|
}
|
|
Some(State(npc, stack, s.locals, false))
|
|
}
|
|
|
|
// ── Run (fuel-bounded) ─────────────────────────────────────
|
|
@annotation.tailrec
|
|
def run(s: State, prog: Vector[Op], fuel: Int = 10000): Option[State] = {
|
|
if (fuel <= 0 || s.halted) return Some(s)
|
|
step(s, prog) match {
|
|
case None => None
|
|
case Some(next) => run(next, prog, fuel - 1)
|
|
}
|
|
}
|
|
}
|