SilverSight/julia/AVMIsa/avm.jl

231 lines
8.1 KiB
Julia

"""
AVM ISA v1 — Julia Port (Strict Functional Execution)
"""
module AVM
using ..Q16_16
export Prim, Instr, State, step, run, prim_add, q16_val
# ── Primitives ──────────────────────────────────────────────────────
@enum Prim begin
ADD_SAT_Q0
SUB_SAT_Q0
ADD_SAT_Q16
SUB_SAT_Q16
MUL_SAT_Q16
DIV_SAT_Q16
LT_Q16
EQ_Q16
AND_OP
OR_OP
NOT_OP
end
const prim_add = ADD_SAT_Q16
const prim_sub = SUB_SAT_Q16
const prim_mul = MUL_SAT_Q16
const prim_div = DIV_SAT_Q16
const prim_lt = LT_Q16
const prim_eq = EQ_Q16
# ── Instructions ────────────────────────────────────────────────────
struct Instr
op::Int8 # 0=push_val, 1=push_q16, 2=pop, 3=dup, 4=swap,
# 5=load, 6=store, 7=jump, 8=jump_if, 9=prim, 10=halt
arg::Int32 # payload: Q16_16 raw, local index, jump target, or Prim
arg2::Bool # for push_val: Bool value
end
# Constructors
push_q16(x::Integer) = Instr(1, Int32(x), false)
push_val(x::Bool) = Instr(0, 0, x)
pop() = Instr(2, 0, false)
dup() = Instr(3, 0, false)
swap() = Instr(4, 0, false)
load(i::Integer) = Instr(5, i, false)
store(i::Integer) = Instr(6, i, false)
jump(t::Integer) = Instr(7, t, false)
jump_if(t::Integer) = Instr(8, t, false)
prim(p::Prim) = Instr(9, Int32(p), false)
halt() = Instr(10, 0, false)
# ── Step Error ─────────────────────────────────────────────────────
@enum StepError begin
EMPTY_STACK
STACK_UNDERFLOW
TYPE_MISMATCH
MISSING_LOCAL
DIVISION_BY_ZERO
JUMP_OUT_OF_BOUNDS
STACK_OVERFLOW
HALTED
UNKNOWN_INSTR
end
# ── AVM constants ───────────────────────────────────────────────────
const AVM_CLAMP_MIN = -2147483647
const AVM_CLAMP_MAX = 2147483647
const AVM_Q0_MIN = -32767
const AVM_Q0_MAX = 32767
const AVM_MAX_STACK = 1024
# Type tags: 0=Bool, 1=Q16_16, 2=Q0_16
const TYPE_BOOL = Int8(0)
const TYPE_Q16 = Int8(1)
const TYPE_Q0 = Int8(2)
# ── State ──────────────────────────────────────────────────────────
struct State
pc::Int
stack::Vector{Union{Int32, Bool}}
types::Vector{Int8} # 0=Bool, 1=Q16_16, 2=Q0_16
locals::Vector{Union{Nothing, Union{Int32, Bool}}}
local_types::Vector{Int8}
halted::Bool
end
function State(n_locals::Int=0)
State(1, Union{Int32, Bool}[], Int8[],
[nothing for _ in 1:n_locals], zeros(Int8, n_locals),
false)
end
q16_val(s::State, i::Int) = s.stack[i]::Int32
bool_val(s::State, i::Int) = s.stack[i]::Bool
q16_val(v::Int32) = v
# ── Primitive execution ────────────────────────────────────────────
avm_clamp(x::Int64) = Int32(min(AVM_CLAMP_MAX, max(AVM_CLAMP_MIN, x)))
avm_q0_clamp(x::Int64) = Int32(min(AVM_Q0_MAX, max(AVM_Q0_MIN, x)))
lt_q16_v6(a::Int32, b::Int32) = (a < 0) != (b < 0) ? (a < 0) : (a < b)
function exec_prim(op::Prim, a::Union{Int32, Bool}, b::Union{Int32, Bool, Nothing})
if op == ADD_SAT_Q0
a::Int32; b::Int32
return (avm_q0_clamp(Int64(a) + Int64(b)), TYPE_Q0)
elseif op == SUB_SAT_Q0
a::Int32; b::Int32
return (avm_q0_clamp(Int64(a) - Int64(b)), TYPE_Q0)
elseif op == ADD_SAT_Q16
return (avm_clamp(Int64(a::Int32) + Int64(b::Int32)), TYPE_Q16)
elseif op == SUB_SAT_Q16
return (avm_clamp(Int64(a::Int32) - Int64(b::Int32)), TYPE_Q16)
elseif op == MUL_SAT_Q16
prod = Int64(a::Int32) * Int64(b::Int32)
result = div(prod, Q16_16.Q16_SCALE)
return (avm_clamp(result), TYPE_Q16)
elseif op == DIV_SAT_Q16
b::Int32 == 0 && error(DIVISION_BY_ZERO)
num = Int64(a::Int32) * Q16_16.Q16_SCALE
result = div(num, Int64(b::Int32))
return (avm_clamp(result), TYPE_Q16)
elseif op == LT_Q16
return (lt_q16_v6(a::Int32, b::Int32), TYPE_BOOL)
elseif op == EQ_Q16
return (a::Int32 == b::Int32, TYPE_BOOL)
elseif op == AND_OP
return (a::Bool && b::Bool, TYPE_BOOL)
elseif op == OR_OP
return (a::Bool || b::Bool, TYPE_BOOL)
elseif op == NOT_OP
return (!a::Bool, TYPE_BOOL)
else
error(TYPE_MISMATCH)
end
end
# ── Step ───────────────────────────────────────────────────────────
function step(s::State, program::Vector{Instr})::State
s.halted && error(HALTED)
s.pc < 1 && return State(s.pc, copy(s.stack), copy(s.types),
copy(s.locals), copy(s.local_types), true)
s.pc > length(program) && return State(s.pc, copy(s.stack), copy(s.types),
copy(s.locals), copy(s.local_types), true)
instr = program[s.pc]
stack = copy(s.stack)
types = copy(s.types)
locals = copy(s.locals)
local_types = copy(s.local_types)
pc = s.pc + 1
halted = false
length(stack) >= AVM_MAX_STACK && (instr.op in (0, 1, 3, 5)) && error(STACK_OVERFLOW)
if instr.op == 0 # push_val (Bool)
push!(stack, instr.arg2)
push!(types, TYPE_BOOL)
elseif instr.op == 1 # push_q16
push!(stack, instr.arg)
push!(types, TYPE_Q16)
elseif instr.op == 2 # pop
isempty(stack) && error(EMPTY_STACK)
pop!(stack); pop!(types)
elseif instr.op == 3 # dup
isempty(stack) && error(EMPTY_STACK)
push!(stack, stack[end]); push!(types, types[end])
elseif instr.op == 4 # swap
length(stack) < 2 && error(STACK_UNDERFLOW)
a = pop!(stack); ta = pop!(types)
b = pop!(stack); tb = pop!(types)
push!(stack, a); push!(types, ta)
push!(stack, b); push!(types, tb)
elseif instr.op == 5 # load
i = Int(instr.arg) + 1
i > length(locals) && error(MISSING_LOCAL)
locals[i] === nothing && error(MISSING_LOCAL)
push!(stack, locals[i]); push!(types, local_types[i])
elseif instr.op == 6 # store
i = Int(instr.arg) + 1
isempty(stack) && error(EMPTY_STACK)
i > length(locals) && error(MISSING_LOCAL)
locals[i] = pop!(stack); local_types[i] = pop!(types)
elseif instr.op == 7 # jump
target = Int(instr.arg) + 1
(target < 1 || target > length(program)) && error(JUMP_OUT_OF_BOUNDS)
pc = target
elseif instr.op == 8 # jump_if
isempty(stack) && error(EMPTY_STACK)
cond = pop!(stack)::Bool; pop!(types)
if cond
target = Int(instr.arg) + 1
(target < 1 || target > length(program)) && error(JUMP_OUT_OF_BOUNDS)
pc = target
end
elseif instr.op == 9 # prim
p = Prim(instr.arg)
arity = (p == NOT_OP) ? 1 : 2
length(stack) < arity && error(STACK_UNDERFLOW)
b = arity >= 2 ? pop!(stack) : nothing; if arity >= 2; pop!(types); end
a = pop!(stack); pop!(types)
result, type_tag = exec_prim(p, a, b)
push!(stack, result); push!(types, type_tag)
elseif instr.op == 10 # halt
halted = true
else
error(UNKNOWN_INSTR)
end
State(pc, stack, types, locals, local_types, halted)
end
# ── Run (fuel-bounded) ────────────────────────────────────────────
function run(initial::State, program::Vector{Instr}, fuel::Int)::State
state = initial
for _ in 1:fuel
state.halted && return state
state = step(state, program)
end
state
end
end # module AVM