/// 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, // --- 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 at a specific address. /// Operands: addr (u32), args_count (u32) /// Stack: [arg0, arg1, ...] -> [return_value] Call = 0x50, /// Returns from the current function. /// Stack: [return_val] -> [return_val] Ret = 0x51, /// Starts a new local scope (for blocks/loops). PushScope = 0x52, /// Ends the current local scope, discarding its local variables. PopScope = 0x53, // --- 6.7 HIP (Heap Interface Protocol) --- /// Allocates `slots` slots on the heap with the given `type_id`. /// Operands: type_id (u32), slots (u32) /// Stack: [] -> [gate] Alloc = 0x60, /// Reads a value from the heap at `gate + offset`. /// Operand: offset (u32) /// Stack: [gate] -> [value] GateLoad = 0x61, /// Writes a value to the heap at `gate + offset`. /// Operand: offset (u32) /// Stack: [gate, value] -> [] GateStore = 0x62, /// Marks the beginning of a Peek scope for a gate. /// Stack: [gate] -> [gate] GateBeginPeek = 0x63, /// Marks the end of a Peek scope for a gate. /// Stack: [gate] -> [gate] GateEndPeek = 0x64, /// Marks the beginning of a Borrow scope for a gate. /// Stack: [gate] -> [gate] GateBeginBorrow = 0x65, /// Marks the end of a Borrow scope for a gate. /// Stack: [gate] -> [gate] GateEndBorrow = 0x66, /// Marks the beginning of a Mutate scope for a gate. /// Stack: [gate] -> [gate] GateBeginMutate = 0x67, /// Marks the end of a Mutate scope for a gate. /// Stack: [gate] -> [gate] GateEndMutate = 0x68, /// Increments the reference count of a gate. /// Stack: [gate] -> [gate] GateRetain = 0x69, /// Decrements the reference count of a gate. /// Stack: [gate] -> [] GateRelease = 0x6A, // --- 6.8 Peripherals and System --- /// Invokes a system function (Firmware/OS). /// Operand: syscall_id (u32) /// Stack: [args...] -> [results...] (depends on syscall) Syscall = 0x70, /// Synchronizes the VM with the hardware frame (usually 60Hz). /// Execution pauses until the next VSync. FrameSync = 0x80, } impl TryFrom for OpCode { type Error = String; fn try_from(value: u16) -> Result { 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), 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::PushScope), 0x53 => Ok(OpCode::PopScope), 0x60 => Ok(OpCode::Alloc), 0x61 => Ok(OpCode::GateLoad), 0x62 => Ok(OpCode::GateStore), 0x63 => Ok(OpCode::GateBeginPeek), 0x64 => Ok(OpCode::GateEndPeek), 0x65 => Ok(OpCode::GateBeginBorrow), 0x66 => Ok(OpCode::GateEndBorrow), 0x67 => Ok(OpCode::GateBeginMutate), 0x68 => Ok(OpCode::GateEndMutate), 0x69 => Ok(OpCode::GateRetain), 0x6A => Ok(OpCode::GateRelease), 0x70 => Ok(OpCode::Syscall), 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::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::PushScope => 3, OpCode::PopScope => 3, OpCode::Alloc => 10, OpCode::GateLoad => 3, OpCode::GateStore => 3, OpCode::GateBeginPeek => 1, OpCode::GateEndPeek => 1, OpCode::GateBeginBorrow => 1, OpCode::GateEndBorrow => 1, OpCode::GateBeginMutate => 1, OpCode::GateEndMutate => 1, OpCode::GateRetain => 1, OpCode::GateRelease => 1, OpCode::Syscall => 1, OpCode::FrameSync => 1, } } } #[cfg(test)] mod tests { use super::*; use crate::asm::{assemble, Asm, Operand}; #[test] fn test_opcode_stability() { // Normative test: ensures opcode numeric values are frozen. assert_eq!(OpCode::Nop as u16, 0x00); assert_eq!(OpCode::PushConst as u16, 0x10); assert_eq!(OpCode::Alloc as u16, 0x60); assert_eq!(OpCode::GateLoad as u16, 0x61); assert_eq!(OpCode::GateStore as u16, 0x62); assert_eq!(OpCode::GateBeginPeek as u16, 0x63); assert_eq!(OpCode::GateEndPeek as u16, 0x64); assert_eq!(OpCode::GateBeginBorrow as u16, 0x65); assert_eq!(OpCode::GateEndBorrow as u16, 0x66); assert_eq!(OpCode::GateBeginMutate as u16, 0x67); assert_eq!(OpCode::GateEndMutate as u16, 0x68); assert_eq!(OpCode::GateRetain as u16, 0x69); assert_eq!(OpCode::GateRelease as u16, 0x6A); assert_eq!(OpCode::FrameSync as u16, 0x80); } #[test] fn test_hip_bytecode_golden() { // Golden test for HIP opcodes and their encodings. // Rule: All multi-byte operands are little-endian. let instructions = vec![ Asm::Op(OpCode::Alloc, vec![Operand::U32(0x11223344), Operand::U32(0x55667788)]), Asm::Op(OpCode::GateLoad, vec![Operand::U32(0xAABBCCDD)]), Asm::Op(OpCode::GateStore, vec![Operand::U32(0x11223344)]), Asm::Op(OpCode::GateBeginPeek, vec![]), Asm::Op(OpCode::GateRetain, vec![]), Asm::Op(OpCode::GateRelease, vec![]), ]; let bytes = assemble(&instructions).unwrap(); let mut expected = Vec::new(); // Alloc (0x60, 0x00) + type_id (44 33 22 11) + slots (88 77 66 55) expected.extend_from_slice(&[0x60, 0x00, 0x44, 0x33, 0x22, 0x11, 0x88, 0x77, 0x66, 0x55]); // GateLoad (0x61, 0x00) + offset (DD CC BB AA) expected.extend_from_slice(&[0x61, 0x00, 0xDD, 0xCC, 0xBB, 0xAA]); // GateStore (0x62, 0x00) + offset (44 33 22 11) expected.extend_from_slice(&[0x62, 0x00, 0x44, 0x33, 0x22, 0x11]); // GateBeginPeek (0x63, 0x00) expected.extend_from_slice(&[0x63, 0x00]); // GateRetain (0x69, 0x00) expected.extend_from_slice(&[0x69, 0x00]); // GateRelease (0x6A, 0x00) expected.extend_from_slice(&[0x6A, 0x00]); assert_eq!(bytes, expected); } }