% AVM ISA v1 — Octave Port (Strict Functional Execution)
% All functions are pure (no global mutation).

Q16_SCALE = 65536;

function r = q16_mul(a, b)
  r = int32((int64(a) * int64(b)) / Q16_SCALE);
end

function r = q16_div(a, b)
  if b == 0, error("div by zero"); end
  r = int32((int64(a) * Q16_SCALE) / int64(b));
end

% ── Constructors ──────────────────────────────────
function v = make_q16(x)
  v.ty = 'q16'; v.val = int32(x);
end
function v = make_bool(x)
  v.ty = 'bool'; v.val = int32(x);
end
function i = make_instr(op, arg)
  i.op = int8(op); i.arg = int32(arg);
end

% ── Instructions ──────────────────────────────────
function i = push_q16(x);   i = make_instr(0, x); end
function i = push_bool(x);  i = make_instr(1, x); end
function i = pop();         i = make_instr(2, 0); end
function i = dup();         i = make_instr(3, 0); end
function i = swap();        i = make_instr(4, 0); end
function i = load(x);       i = make_instr(5, x); end
function i = store(x);      i = make_instr(6, x); end
function i = jump(x);       i = make_instr(7, x); end
function i = jump_if(x);    i = make_instr(8, x); end
function i = prim(x);       i = make_instr(9, x); end
function i = halt();        i = make_instr(10, 0); end

% Primitives
PRIM_ADD=0; PRIM_SUB=1; PRIM_MUL=2; PRIM_DIV=3;
PRIM_LT=4; PRIM_EQ=5; PRIM_AND=6; PRIM_OR=7; PRIM_NOT=8;

% ── State ─────────────────────────────────────────
function s = new_state(n)
  s.pc = 1;
  s.stack = cell(1024, 1); s.sp = 0;
  s.locals = cell(n, 1);
  s.halted = false;
end

function s = state_push(s, v)
  s.sp = s.sp + 1;
  s.stack{s.sp} = v;
end

function [v, s] = state_pop(s)
  v = s.stack{s.sp};
  s.sp = s.sp - 1;
end

% ── Step ──────────────────────────────────────────
function ns = step(state, prog)
  ns = state;
  if ns.halted, return; end
  if ns.pc < 1 || ns.pc > length(prog)
    ns.halted = true; return;
  end

  instr = prog{ns.pc};

  switch instr.op
    case 0  % push_q16
      ns = state_push(ns, make_q16(instr.arg));
    case 1  % push_bool
      ns = state_push(ns, make_bool(instr.arg));
    case 2  % pop
      [~, ns] = state_pop(ns);
    case 3  % dup
      ns = state_push(ns, ns.stack{ns.sp});
    case 4  % swap
      [a, ns] = state_pop(ns); [b, ns] = state_pop(ns);
      ns = state_push(ns, a); ns = state_push(ns, b);
    case 5  % load
      ns = state_push(ns, ns.locals{instr.arg + 1});
    case 6  % store
      [v, ns] = state_pop(ns);
      ns.locals{instr.arg + 1} = v;
    case 7  % jump
      ns.pc = instr.arg; return;
    case 8  % jump_if
      [v, ns] = state_pop(ns);
      if v.val != 0, ns.pc = instr.arg; end
    case 9  % prim
      arity = 2;
      if instr.arg == PRIM_NOT, arity = 1; end
      if arity >= 2, [b, ns] = state_pop(ns); end
      [a, ns] = state_pop(ns);
      switch instr.arg
        case PRIM_ADD
          r = make_q16(a.val + b.val);
        case PRIM_SUB
          r = make_q16(a.val - b.val);
        case PRIM_MUL
          r = make_q16(q16_mul(a.val, b.val));
        case PRIM_DIV
          r = make_q16(q16_div(a.val, b.val));
        case PRIM_LT
          r = make_bool(a.val < b.val);
        case PRIM_EQ
          r = make_bool(a.val == b.val);
        case PRIM_AND
          r = make_bool(a.val && b.val);
        case PRIM_OR
          r = make_bool(a.val || b.val);
        case PRIM_NOT
          r = make_bool(a.val == 0);
      end
      ns = state_push(ns, r);
    case 10 % halt
      ns.halted = true;
  end
  ns.pc = ns.pc + 1;
end

function s = run(init, prog, fuel)
  s = init;
  for i = 1:fuel
    if s.halted, break; end
    s = step(s, prog);
  end
end
