325 lines
12 KiB
Rust
325 lines
12 KiB
Rust
/// Represents a single instruction in the Prometeu Virtual Machine.
|
|
///
|
|
/// Each OpCode is encoded as a 16-bit unsigned integer (u16) in the bytecode.
|
|
/// The PVM is a stack-based machine, meaning most instructions take their
|
|
/// operands from the top of the stack and push their results back onto it.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
#[repr(u16)]
|
|
pub enum OpCode {
|
|
// --- 6.1 Execution Control ---
|
|
/// No operation. Does nothing for 1 cycle.
|
|
Nop = 0x00,
|
|
/// Stops the Virtual Machine execution immediately.
|
|
Halt = 0x01,
|
|
/// Unconditional jump to a specific PC (Program Counter) address.
|
|
/// Operand: addr (u32)
|
|
Jmp = 0x02,
|
|
/// Jumps to `addr` if the value at the top of the stack is `false`.
|
|
/// Operand: addr (u32)
|
|
/// Stack: [bool] -> []
|
|
JmpIfFalse = 0x03,
|
|
/// Jumps to `addr` if the value at the top of the stack is `true`.
|
|
/// Operand: addr (u32)
|
|
/// Stack: [bool] -> []
|
|
JmpIfTrue = 0x04,
|
|
/// Triggers a software breakpoint. Used for debugging.
|
|
Trap = 0x05,
|
|
|
|
// --- 6.2 Stack Manipulation ---
|
|
/// Loads a constant from the Constant Pool into the stack.
|
|
/// Operand: index (u32)
|
|
/// Stack: [] -> [value]
|
|
PushConst = 0x10,
|
|
/// Removes the top value from the stack.
|
|
/// Stack: [val] -> []
|
|
Pop = 0x11,
|
|
/// Duplicates the top value of the stack.
|
|
/// Stack: [val] -> [val, val]
|
|
Dup = 0x12,
|
|
/// Swaps the two top values of the stack.
|
|
/// Stack: [a, b] -> [b, a]
|
|
Swap = 0x13,
|
|
/// Pushes a 64-bit integer literal onto the stack.
|
|
/// Operand: value (i64)
|
|
PushI64 = 0x14,
|
|
/// Pushes a 64-bit float literal onto the stack.
|
|
/// Operand: value (f64)
|
|
PushF64 = 0x15,
|
|
/// Pushes a boolean literal onto the stack (0=false, 1=true).
|
|
/// Operand: value (u8)
|
|
PushBool = 0x16,
|
|
/// Pushes a 32-bit integer literal onto the stack.
|
|
/// Operand: value (i32)
|
|
PushI32 = 0x17,
|
|
/// Removes `n` values from the stack.
|
|
/// Operand: n (u32)
|
|
PopN = 0x18,
|
|
// --- 6.3 Arithmetic ---
|
|
/// Adds the two top values (a + b).
|
|
/// Stack: [a, b] -> [result]
|
|
Add = 0x20,
|
|
/// Subtracts the top value from the second one (a - b).
|
|
/// Stack: [a, b] -> [result]
|
|
Sub = 0x21,
|
|
/// Multiplies the two top values (a * b).
|
|
/// Stack: [a, b] -> [result]
|
|
Mul = 0x22,
|
|
/// Divides the second top value by the top one (a / b).
|
|
/// Stack: [a, b] -> [result]
|
|
Div = 0x23,
|
|
/// Remainder of the division of the second top value by the top one (a % b).
|
|
/// Stack: [a, b] -> [result]
|
|
Mod = 0x24,
|
|
// --- 6.4 Comparison and Logic ---
|
|
/// Checks if a equals b.
|
|
/// Stack: [a, b] -> [bool]
|
|
Eq = 0x30,
|
|
/// Checks if a is not equal to b.
|
|
/// Stack: [a, b] -> [bool]
|
|
Neq = 0x31,
|
|
/// Checks if a is less than b.
|
|
/// Stack: [a, b] -> [bool]
|
|
Lt = 0x32,
|
|
/// Checks if a is greater than b.
|
|
/// Stack: [a, b] -> [bool]
|
|
Gt = 0x33,
|
|
/// Logical AND.
|
|
/// Stack: [bool, bool] -> [bool]
|
|
And = 0x34,
|
|
/// Logical OR.
|
|
/// Stack: [bool, bool] -> [bool]
|
|
Or = 0x35,
|
|
/// Logical NOT.
|
|
/// Stack: [bool] -> [bool]
|
|
Not = 0x36,
|
|
/// Bitwise AND.
|
|
/// Stack: [int, int] -> [int]
|
|
BitAnd = 0x37,
|
|
/// Bitwise OR.
|
|
/// Stack: [int, int] -> [int]
|
|
BitOr = 0x38,
|
|
/// Bitwise XOR.
|
|
/// Stack: [int, int] -> [int]
|
|
BitXor = 0x39,
|
|
/// Bitwise Shift Left.
|
|
/// Stack: [int, count] -> [int]
|
|
Shl = 0x3A,
|
|
/// Bitwise Shift Right.
|
|
/// Stack: [int, count] -> [int]
|
|
Shr = 0x3B,
|
|
/// Checks if a is less than or equal to b.
|
|
/// Stack: [a, b] -> [bool]
|
|
Lte = 0x3C,
|
|
/// Checks if a is greater than or equal to b.
|
|
/// Stack: [a, b] -> [bool]
|
|
Gte = 0x3D,
|
|
/// Negates a number (-a).
|
|
/// Stack: [num] -> [num]
|
|
Neg = 0x3E,
|
|
|
|
// --- 6.5 Variables ---
|
|
/// Loads a value from a global variable slot.
|
|
/// Operand: slot_index (u32)
|
|
/// Stack: [] -> [value]
|
|
GetGlobal = 0x40,
|
|
/// Stores the top value into a global variable slot.
|
|
/// Operand: slot_index (u32)
|
|
/// Stack: [value] -> []
|
|
SetGlobal = 0x41,
|
|
/// Loads a value from a local variable slot in the current frame.
|
|
/// Operand: slot_index (u32)
|
|
/// Stack: [] -> [value]
|
|
GetLocal = 0x42,
|
|
/// Stores the top value into a local variable slot in the current frame.
|
|
/// Operand: slot_index (u32)
|
|
/// Stack: [value] -> []
|
|
SetLocal = 0x43,
|
|
|
|
// --- 6.6 Functions ---
|
|
/// Calls a function by its index in the function table.
|
|
/// Operand: func_id (u32)
|
|
/// Stack: [arg0, arg1, ...] -> [return_slots...]
|
|
Call = 0x50,
|
|
/// Returns from the current function.
|
|
/// Stack: [return_val] -> [return_val]
|
|
Ret = 0x51,
|
|
/// Creates a closure capturing values from the operand stack (Model B).
|
|
/// Operands: fn_id (u32), capture_count (u32)
|
|
/// Stack before: [..., captured_N, ..., captured_1]
|
|
/// Pops capture_count values (top-first), preserves order as [captured_1..captured_N]
|
|
/// and stores them inside the closure environment. Pushes a HeapRef to the closure.
|
|
MakeClosure = 0x52,
|
|
/// Calls a closure value with hidden arg0 semantics (Model B).
|
|
/// Operand: arg_count (u32) — number of user-supplied args (excludes hidden arg0)
|
|
/// Stack before: [..., argN, ..., arg1, closure_ref]
|
|
/// Behavior:
|
|
/// - Pops `closure_ref` and validates it is a Closure.
|
|
/// - Pops `arg_count` user args.
|
|
/// - Fetches `fn_id` from the closure and creates a new call frame.
|
|
/// - Injects hidden arg0 = closure_ref, followed by user args as arg1..argN.
|
|
CallClosure = 0x53,
|
|
|
|
// --- 7.x Concurrency / Coroutines ---
|
|
/// Spawns a new coroutine to run a function with arguments.
|
|
/// Operands: fn_id (u32), arg_count (u32)
|
|
/// Semantics:
|
|
/// - Pops `arg_count` arguments from the current operand stack (top-first),
|
|
/// preserving user order as arg1..argN for the callee.
|
|
/// - Allocates a new Coroutine object with its own stack and a single entry frame
|
|
/// pointing at `fn_id`.
|
|
/// - Enqueues the coroutine into the scheduler ready queue.
|
|
/// - Does NOT switch execution immediately; current coroutine continues.
|
|
Spawn = 0x54,
|
|
|
|
/// Cooperatively yields the current coroutine. Execution continues
|
|
/// until the next VM safepoint (FRAME_SYNC), where the scheduler
|
|
/// may switch to another ready coroutine.
|
|
Yield = 0x55,
|
|
|
|
/// Suspends the current coroutine for a number of logical ticks.
|
|
/// Operand: duration_ticks (u32)
|
|
/// Semantics:
|
|
/// - Set the coroutine wake tick to `current_tick + duration_ticks`.
|
|
/// - End the current logical frame (as if reaching FRAME_SYNC).
|
|
/// - The coroutine will resume execution on or after the wake tick.
|
|
Sleep = 0x56,
|
|
|
|
// --- 6.8 Peripherals and System ---
|
|
/// Pre-load host binding call by `SYSC` table index.
|
|
/// Operand: sysc_index (u32)
|
|
/// This opcode is valid only in PBX artifact form and must be patched by the loader
|
|
/// into a final numeric `SYSCALL <id>` before verification or execution.
|
|
Hostcall = 0x71,
|
|
/// Invokes a final numeric system function (Firmware/OS).
|
|
/// Raw `SYSCALL` is valid only after loader patching and is rejected in PBX pre-load artifacts.
|
|
/// Operand: syscall_id (u32)
|
|
/// Stack: [args...] -> [results...] (depends on syscall)
|
|
Syscall = 0x70,
|
|
/// Invokes a VM-owned intrinsic by final numeric id.
|
|
/// Operand: intrinsic_id (u32)
|
|
/// Stack: [args...] -> [results...] (depends on intrinsic metadata)
|
|
Intrinsic = 0x72,
|
|
/// Synchronizes the VM with the hardware frame (usually 60Hz).
|
|
/// Execution pauses until the next VSync.
|
|
FrameSync = 0x80,
|
|
}
|
|
|
|
impl TryFrom<u16> for OpCode {
|
|
type Error = String;
|
|
|
|
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
|
match value {
|
|
0x00 => Ok(OpCode::Nop),
|
|
0x01 => Ok(OpCode::Halt),
|
|
0x02 => Ok(OpCode::Jmp),
|
|
0x03 => Ok(OpCode::JmpIfFalse),
|
|
0x04 => Ok(OpCode::JmpIfTrue),
|
|
0x05 => Ok(OpCode::Trap),
|
|
0x10 => Ok(OpCode::PushConst),
|
|
0x11 => Ok(OpCode::Pop),
|
|
0x12 => Ok(OpCode::Dup),
|
|
0x13 => Ok(OpCode::Swap),
|
|
0x14 => Ok(OpCode::PushI64),
|
|
0x15 => Ok(OpCode::PushF64),
|
|
0x16 => Ok(OpCode::PushBool),
|
|
0x17 => Ok(OpCode::PushI32),
|
|
0x18 => Ok(OpCode::PopN),
|
|
0x20 => Ok(OpCode::Add),
|
|
0x21 => Ok(OpCode::Sub),
|
|
0x22 => Ok(OpCode::Mul),
|
|
0x23 => Ok(OpCode::Div),
|
|
0x24 => Ok(OpCode::Mod),
|
|
0x30 => Ok(OpCode::Eq),
|
|
0x31 => Ok(OpCode::Neq),
|
|
0x32 => Ok(OpCode::Lt),
|
|
0x33 => Ok(OpCode::Gt),
|
|
0x34 => Ok(OpCode::And),
|
|
0x35 => Ok(OpCode::Or),
|
|
0x36 => Ok(OpCode::Not),
|
|
0x37 => Ok(OpCode::BitAnd),
|
|
0x38 => Ok(OpCode::BitOr),
|
|
0x39 => Ok(OpCode::BitXor),
|
|
0x3A => Ok(OpCode::Shl),
|
|
0x3B => Ok(OpCode::Shr),
|
|
0x3C => Ok(OpCode::Lte),
|
|
0x3D => Ok(OpCode::Gte),
|
|
0x3E => Ok(OpCode::Neg),
|
|
0x40 => Ok(OpCode::GetGlobal),
|
|
0x41 => Ok(OpCode::SetGlobal),
|
|
0x42 => Ok(OpCode::GetLocal),
|
|
0x43 => Ok(OpCode::SetLocal),
|
|
0x50 => Ok(OpCode::Call),
|
|
0x51 => Ok(OpCode::Ret),
|
|
0x52 => Ok(OpCode::MakeClosure),
|
|
0x53 => Ok(OpCode::CallClosure),
|
|
0x54 => Ok(OpCode::Spawn),
|
|
0x55 => Ok(OpCode::Yield),
|
|
0x56 => Ok(OpCode::Sleep),
|
|
0x70 => Ok(OpCode::Syscall),
|
|
0x71 => Ok(OpCode::Hostcall),
|
|
0x72 => Ok(OpCode::Intrinsic),
|
|
0x80 => Ok(OpCode::FrameSync),
|
|
_ => Err(format!("Invalid OpCode: 0x{:04X}", value)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl OpCode {
|
|
/// Returns the cost of the instruction in VM cycles.
|
|
/// This is used for performance monitoring and resource limiting (Certification).
|
|
pub fn cycles(&self) -> u64 {
|
|
match self {
|
|
OpCode::Nop => 1,
|
|
OpCode::Halt => 1,
|
|
OpCode::Jmp => 2,
|
|
OpCode::JmpIfFalse => 3,
|
|
OpCode::JmpIfTrue => 3,
|
|
OpCode::Trap => 1,
|
|
OpCode::PushConst => 2,
|
|
OpCode::Pop => 1,
|
|
OpCode::PopN => 2,
|
|
OpCode::Dup => 1,
|
|
OpCode::Swap => 1,
|
|
OpCode::PushI64 => 2,
|
|
OpCode::PushF64 => 2,
|
|
OpCode::PushBool => 2,
|
|
OpCode::PushI32 => 2,
|
|
OpCode::Add => 2,
|
|
OpCode::Sub => 2,
|
|
OpCode::Mul => 4,
|
|
OpCode::Div => 6,
|
|
OpCode::Mod => 6,
|
|
OpCode::Eq => 2,
|
|
OpCode::Neq => 2,
|
|
OpCode::Lt => 2,
|
|
OpCode::Gt => 2,
|
|
OpCode::And => 2,
|
|
OpCode::Or => 2,
|
|
OpCode::Not => 1,
|
|
OpCode::BitAnd => 2,
|
|
OpCode::BitOr => 2,
|
|
OpCode::BitXor => 2,
|
|
OpCode::Shl => 2,
|
|
OpCode::Shr => 2,
|
|
OpCode::Lte => 2,
|
|
OpCode::Gte => 2,
|
|
OpCode::Neg => 1,
|
|
OpCode::GetGlobal => 3,
|
|
OpCode::SetGlobal => 3,
|
|
OpCode::GetLocal => 2,
|
|
OpCode::SetLocal => 2,
|
|
OpCode::Call => 5,
|
|
OpCode::Ret => 4,
|
|
OpCode::MakeClosure => 8,
|
|
OpCode::CallClosure => 6,
|
|
OpCode::Spawn => 6,
|
|
OpCode::Yield => 1,
|
|
OpCode::Sleep => 1,
|
|
OpCode::Syscall => 1,
|
|
OpCode::Hostcall => 1,
|
|
OpCode::Intrinsic => 1,
|
|
OpCode::FrameSync => 1,
|
|
}
|
|
}
|
|
}
|