dev/vm-improvements #3
@ -8,6 +8,7 @@ pub enum OpCode {
|
||||
Jmp = 0x02,
|
||||
JmpIfFalse = 0x03,
|
||||
JmpIfTrue = 0x04,
|
||||
Trap = 0x05,
|
||||
|
||||
// 6.2 Stack
|
||||
PushConst = 0x10,
|
||||
@ -18,6 +19,7 @@ pub enum OpCode {
|
||||
PushF64 = 0x15,
|
||||
PushBool = 0x16,
|
||||
PushI32 = 0x17,
|
||||
PopN = 0x18,
|
||||
|
||||
// 6.3 Arithmetic
|
||||
Add = 0x20,
|
||||
@ -74,6 +76,7 @@ impl TryFrom<u16> for OpCode {
|
||||
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),
|
||||
@ -82,6 +85,7 @@ impl TryFrom<u16> for OpCode {
|
||||
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),
|
||||
@ -127,8 +131,10 @@ impl OpCode {
|
||||
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,
|
||||
@ -178,7 +184,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_opcode_decoding() {
|
||||
assert_eq!(OpCode::try_from(0x00).unwrap(), OpCode::Nop);
|
||||
assert_eq!(OpCode::try_from(0x05).unwrap(), OpCode::Trap);
|
||||
assert_eq!(OpCode::try_from(0x10).unwrap(), OpCode::PushConst);
|
||||
assert_eq!(OpCode::try_from(0x18).unwrap(), OpCode::PopN);
|
||||
assert_eq!(OpCode::try_from(0x20).unwrap(), OpCode::Add);
|
||||
assert_eq!(OpCode::try_from(0x70).unwrap(), OpCode::Syscall);
|
||||
assert_eq!(OpCode::try_from(0x80).unwrap(), OpCode::FrameSync);
|
||||
|
||||
@ -243,6 +243,14 @@ impl VirtualMachine {
|
||||
break;
|
||||
}
|
||||
|
||||
if opcode == OpCode::Trap {
|
||||
self.pc += 2; // Advance PC past the opcode
|
||||
self.cycles += OpCode::Trap.cycles();
|
||||
steps_executed += 1;
|
||||
ending_reason = Some(LogicalFrameEndingReason::Breakpoint);
|
||||
break;
|
||||
}
|
||||
|
||||
// Execute a single step (Fetch-Decode-Execute)
|
||||
self.step(native, hw)?;
|
||||
steps_executed += 1;
|
||||
@ -323,6 +331,10 @@ impl VirtualMachine {
|
||||
self.pc = addr;
|
||||
}
|
||||
}
|
||||
OpCode::Trap => {
|
||||
// Handled in run_budget for interruption,
|
||||
// but we need to advance PC if executed via step() directly.
|
||||
}
|
||||
OpCode::PushConst => {
|
||||
let idx = self.read_u32()? as usize;
|
||||
let val = self.program.constant_pool.get(idx).cloned().ok_or("Invalid constant index")?;
|
||||
@ -347,6 +359,12 @@ impl VirtualMachine {
|
||||
OpCode::Pop => {
|
||||
self.pop()?;
|
||||
}
|
||||
OpCode::PopN => {
|
||||
let n = self.read_u16()?;
|
||||
for _ in 0..n {
|
||||
self.pop()?;
|
||||
}
|
||||
}
|
||||
OpCode::Dup => {
|
||||
let val = self.peek()?.clone();
|
||||
self.push(val);
|
||||
@ -1180,4 +1198,46 @@ mod tests {
|
||||
vm.step(&mut native, &mut hw).unwrap(); // PushI32
|
||||
assert_eq!(vm.pop().unwrap(), Value::Int32(100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trap_opcode() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
|
||||
let mut rom = Vec::new();
|
||||
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&42i32.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Trap as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
|
||||
assert_eq!(report.reason, LogicalFrameEndingReason::Breakpoint);
|
||||
assert_eq!(vm.pc, 8); // PushI32 (6 bytes) + Trap (2 bytes)
|
||||
assert_eq!(vm.peek().unwrap(), &Value::Int32(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pop_n_opcode() {
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
|
||||
let mut rom = Vec::new();
|
||||
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&1i32.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&2i32.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&3i32.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::PopN as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&2u16.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
|
||||
assert_eq!(vm.pop().unwrap(), Value::Int32(1));
|
||||
assert!(vm.pop().is_err()); // Stack should be empty
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user