""" 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