dev/vm-improvements #3

Merged
bquarkz merged 7 commits from dev/vm-improvements into master 2026-01-20 10:18:45 +00:00
2 changed files with 68 additions and 0 deletions
Showing only changes of commit 481deacc65 - Show all commits

View File

@ -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);

View File

@ -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
}
}