mirror of
https://github.com/allaunthefox/SilverSight.git
synced 2026-07-31 01:25:21 +00:00
Python port rewritten to match spec: - Added Q0_16, PUSH_Q0, PUSH_BOOL as separate opcodes - Added V6 comparison (lt_q16_v6) - Added floor division (Lean Int.ediv) - Added stack depth limit (AVM_MAX_STACK = 1024) - Added type checking in exec_prim - All 10 tests passing Go AVM port: added test_avm_test.go with 8 test cases Milestone: Python → ✅, Go → 🔄
144 lines
5.4 KiB
C++
144 lines
5.4 KiB
C++
// AVM ISA v1 — C++ Port (Strict Functional Execution)
|
|
#include <cstdint>
|
|
#include <vector>
|
|
#include <optional>
|
|
#include <variant>
|
|
#include <stdexcept>
|
|
#include <functional>
|
|
|
|
constexpr int32_t Q16_SCALE = 65536;
|
|
|
|
inline int32_t q16_mul(int32_t a, int32_t b) {
|
|
return static_cast<int32_t>(
|
|
(static_cast<int64_t>(a) * static_cast<int64_t>(b)) / Q16_SCALE);
|
|
}
|
|
inline int32_t q16_div(int32_t a, int32_t b) {
|
|
if (b == 0) throw std::runtime_error("div by zero");
|
|
return static_cast<int32_t>(
|
|
(static_cast<int64_t>(a) * Q16_SCALE) / b);
|
|
}
|
|
|
|
// ── Value ─────────────────────────────────────────
|
|
struct AvmVal {
|
|
enum Ty { Q0_16, Q16_16, Bool };
|
|
Ty ty;
|
|
int32_t raw;
|
|
AvmVal(Ty t, int32_t r) : ty(t), raw(r) {}
|
|
static AvmVal q16(int32_t x) { return {Q16_16, x}; }
|
|
static AvmVal q0(int32_t x) {
|
|
if (x < -32767) x = -32767;
|
|
if (x > 32767) x = 32767;
|
|
return {Q0_16, x};
|
|
}
|
|
static AvmVal boolean(bool x) { return {Bool, x ? 1 : 0}; }
|
|
};
|
|
|
|
// ── Primitives ─────────────────────────────────────
|
|
enum class Prim : int32_t {
|
|
AddSatQ0, SubSatQ0, AddSatQ16, SubSatQ16, MulSatQ16, DivSatQ16,
|
|
LtQ16, EqQ16, And, Or, Not
|
|
};
|
|
|
|
AvmVal exec_prim(Prim op, const AvmVal& a, const std::optional<AvmVal>& b) {
|
|
auto bv = [&]() -> const AvmVal& { return b.value(); };
|
|
switch (op) {
|
|
case Prim::AddSatQ16: return AvmVal::q16(a.raw + bv().raw);
|
|
case Prim::SubSatQ16: return AvmVal::q16(a.raw - bv().raw);
|
|
case Prim::MulSatQ16: return AvmVal::q16(q16_mul(a.raw, bv().raw));
|
|
case Prim::DivSatQ16: return AvmVal::q16(q16_div(a.raw, bv().raw));
|
|
case Prim::LtQ16: return AvmVal::boolean(a.raw < bv().raw);
|
|
case Prim::EqQ16: return AvmVal::boolean(a.raw == bv().raw);
|
|
case Prim::And: return AvmVal::boolean(a.raw && bv().raw);
|
|
case Prim::Or: return AvmVal::boolean(a.raw || bv().raw);
|
|
case Prim::Not: return AvmVal::boolean(!a.raw);
|
|
default: throw std::runtime_error("unknown prim");
|
|
}
|
|
}
|
|
|
|
// ── Instruction ────────────────────────────────────
|
|
enum class InstrOp : int8_t {
|
|
PushQ16, PushBool, Pop, Dup, Swap, Load, Store, Jump, JumpIf, Prim, Halt
|
|
};
|
|
|
|
struct Instr {
|
|
InstrOp op;
|
|
int32_t arg;
|
|
bool arg2{false};
|
|
};
|
|
|
|
inline Instr push_q16(int32_t x) { return {InstrOp::PushQ16, x, false}; }
|
|
inline Instr push_bool(bool x) { return {InstrOp::PushBool, static_cast<int32_t>(x), true}; }
|
|
inline Instr pop() { return {InstrOp::Pop, 0, false}; }
|
|
inline Instr dup() { return {InstrOp::Dup, 0, false}; }
|
|
inline Instr swap() { return {InstrOp::Swap, 0, false}; }
|
|
inline Instr load(int i) { return {InstrOp::Load, i, false}; }
|
|
inline Instr store(int i) { return {InstrOp::Store, i, false}; }
|
|
inline Instr jump(int t) { return {InstrOp::Jump, t, false}; }
|
|
inline Instr jump_if(int t) { return {InstrOp::JumpIf, t, false}; }
|
|
inline Instr prim(Prim p) { return {InstrOp::Prim, static_cast<int32_t>(p), false}; }
|
|
inline Instr halt() { return {InstrOp::Halt, 0, false}; }
|
|
|
|
// ── State ──────────────────────────────────────────
|
|
struct State {
|
|
int pc{0};
|
|
std::vector<AvmVal> stack;
|
|
std::vector<std::optional<AvmVal>> locals;
|
|
bool halted{false};
|
|
|
|
State() : pc(0), locals(16, std::nullopt) {}
|
|
State(int pc_, std::vector<AvmVal> stack_, std::vector<std::optional<AvmVal>> locals_, bool halted_)
|
|
: pc(pc_), stack(std::move(stack_)), locals(std::move(locals_)), halted(halted_) {}
|
|
};
|
|
|
|
// ── Step ───────────────────────────────────────────
|
|
State step(const State& s, const std::vector<Instr>& prog) {
|
|
if (s.halted) throw std::runtime_error("halted");
|
|
if (s.pc < 0 || s.pc >= (int)prog.size())
|
|
return State{};
|
|
|
|
auto instr = prog[s.pc];
|
|
State ns{s.pc + 1, s.stack, s.locals, false};
|
|
auto& st = ns.stack;
|
|
|
|
switch (instr.op) {
|
|
case InstrOp::PushQ16: st.push_back(AvmVal::q16(instr.arg)); break;
|
|
case InstrOp::PushBool: st.push_back(AvmVal::boolean(instr.arg)); break;
|
|
case InstrOp::Pop: st.pop_back(); break;
|
|
case InstrOp::Dup: st.push_back(st.back()); break;
|
|
case InstrOp::Swap: std::swap(st[st.size()-2], st[st.size()-1]); break;
|
|
case InstrOp::Load: {
|
|
auto v = ns.locals[instr.arg];
|
|
if (!v.has_value()) throw std::runtime_error("missing local");
|
|
st.push_back(v.value());
|
|
break;
|
|
}
|
|
case InstrOp::Store: {
|
|
ns.locals[instr.arg] = st.back(); st.pop_back();
|
|
break;
|
|
}
|
|
case InstrOp::Jump: ns.pc = instr.arg; break;
|
|
case InstrOp::JumpIf: {
|
|
if (st.back().raw) ns.pc = instr.arg;
|
|
st.pop_back();
|
|
break;
|
|
}
|
|
case InstrOp::Prim: {
|
|
auto p = static_cast<Prim>(instr.arg);
|
|
int arity = (p == Prim::Not) ? 1 : 2;
|
|
std::optional<AvmVal> b;
|
|
if (arity == 2) { b = st.back(); st.pop_back(); }
|
|
auto a = st.back(); st.pop_back();
|
|
st.push_back(exec_prim(p, a, b));
|
|
break;
|
|
}
|
|
case InstrOp::Halt: ns.halted = true; break;
|
|
}
|
|
return ns;
|
|
}
|
|
|
|
State run(const State& init, const std::vector<Instr>& prog, int fuel) {
|
|
State s = init;
|
|
for (int i = 0; i < fuel && !s.halted; i++)
|
|
s = step(s, prog);
|
|
return s;
|
|
}
|