diff --git a/crates/prometeu-core/src/virtual_machine/opcode.rs b/crates/prometeu-core/src/virtual_machine/opcode.rs index 72071448..7e509732 100644 --- a/crates/prometeu-core/src/virtual_machine/opcode.rs +++ b/crates/prometeu-core/src/virtual_machine/opcode.rs @@ -13,6 +13,9 @@ pub enum OpCode { Pop = 0x11, Dup = 0x12, Swap = 0x13, + PushI64 = 0x14, + PushF64 = 0x15, + PushBool = 0x16, // 6.3 Arithmetic Add = 0x20, @@ -64,6 +67,9 @@ impl TryFrom for OpCode { 0x11 => Ok(OpCode::Pop), 0x12 => Ok(OpCode::Dup), 0x13 => Ok(OpCode::Swap), + 0x14 => Ok(OpCode::PushI64), + 0x15 => Ok(OpCode::PushF64), + 0x16 => Ok(OpCode::PushBool), 0x20 => Ok(OpCode::Add), 0x21 => Ok(OpCode::Sub), 0x22 => Ok(OpCode::Mul), @@ -104,6 +110,9 @@ impl OpCode { OpCode::Pop => 1, OpCode::Dup => 1, OpCode::Swap => 1, + OpCode::PushI64 => 2, + OpCode::PushF64 => 2, + OpCode::PushBool => 2, OpCode::Add => 2, OpCode::Sub => 2, OpCode::Mul => 4, diff --git a/crates/prometeu-core/src/virtual_machine/virtual_machine.rs b/crates/prometeu-core/src/virtual_machine/virtual_machine.rs index c97f371f..54ca5a89 100644 --- a/crates/prometeu-core/src/virtual_machine/virtual_machine.rs +++ b/crates/prometeu-core/src/virtual_machine/virtual_machine.rs @@ -312,6 +312,18 @@ impl VirtualMachine { let val = self.program.constant_pool.get(idx).cloned().ok_or("Invalid constant index")?; self.push(val); } + OpCode::PushI64 => { + let val = self.read_i64()?; + self.push(Value::Integer(val)); + } + OpCode::PushF64 => { + let val = self.read_f64()?; + self.push(Value::Float(val)); + } + OpCode::PushBool => { + let val = self.read_u8()?; + self.push(Value::Boolean(val != 0)); + } OpCode::Pop => { self.pop()?; } @@ -534,6 +546,26 @@ impl VirtualMachine { Ok(u32::from_le_bytes(bytes)) } + fn read_i64(&mut self) -> Result { + if self.pc + 8 > self.program.rom.len() { + return Err("Unexpected end of ROM".into()); + } + let mut bytes = [0u8; 8]; + bytes.copy_from_slice(&self.program.rom[self.pc..self.pc + 8]); + self.pc += 8; + Ok(i64::from_le_bytes(bytes)) + } + + fn read_f64(&mut self) -> Result { + if self.pc + 8 > self.program.rom.len() { + return Err("Unexpected end of ROM".into()); + } + let mut bytes = [0u8; 8]; + bytes.copy_from_slice(&self.program.rom[self.pc..self.pc + 8]); + self.pc += 8; + Ok(f64::from_le_bytes(bytes)) + } + fn read_u16(&mut self) -> Result { if self.pc + 2 > self.program.rom.len() { return Err("Unexpected end of ROM".into()); @@ -546,6 +578,15 @@ impl VirtualMachine { Ok(u16::from_le_bytes(bytes)) } + fn read_u8(&mut self) -> Result { + if self.pc + 1 > self.program.rom.len() { + return Err("Unexpected end of ROM".into()); + } + let val = self.program.rom[self.pc]; + self.pc += 1; + Ok(val) + } + pub fn push(&mut self, val: Value) { self.operand_stack.push(val); } @@ -579,3 +620,97 @@ impl VirtualMachine { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::virtual_machine::Value; + use crate::prometeu_os::NativeInterface; + use crate::hardware::HardwareBridge; + + struct MockNative; + impl NativeInterface for MockNative { + fn syscall(&mut self, _id: u32, _vm: &mut VirtualMachine, _hw: &mut dyn HardwareBridge) -> Result { + Ok(0) + } + } + + struct MockHardware; + impl HardwareBridge for MockHardware { + fn gfx(&self) -> &crate::hardware::Gfx { todo!() } + fn gfx_mut(&mut self) -> &mut crate::hardware::Gfx { todo!() } + fn audio(&self) -> &crate::hardware::Audio { todo!() } + fn audio_mut(&mut self) -> &mut crate::hardware::Audio { todo!() } + fn pad(&self) -> &crate::hardware::Pad { todo!() } + fn pad_mut(&mut self) -> &mut crate::hardware::Pad { todo!() } + fn touch(&self) -> &crate::hardware::Touch { todo!() } + fn touch_mut(&mut self) -> &mut crate::hardware::Touch { todo!() } + } + + #[test] + fn test_push_i64_immediate() { + let mut rom = Vec::new(); + rom.extend_from_slice(&(OpCode::PushI64 as u16).to_le_bytes()); + rom.extend_from_slice(&42i64.to_le_bytes()); + rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + + let mut vm = VirtualMachine::new(rom, vec![]); + let mut native = MockNative; + let mut hw = MockHardware; + + vm.step(&mut native, &mut hw).unwrap(); + assert_eq!(vm.peek().unwrap(), &Value::Integer(42)); + } + + #[test] + fn test_push_f64_immediate() { + let mut rom = Vec::new(); + rom.extend_from_slice(&(OpCode::PushF64 as u16).to_le_bytes()); + rom.extend_from_slice(&3.14f64.to_le_bytes()); + rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + + let mut vm = VirtualMachine::new(rom, vec![]); + let mut native = MockNative; + let mut hw = MockHardware; + + vm.step(&mut native, &mut hw).unwrap(); + assert_eq!(vm.peek().unwrap(), &Value::Float(3.14)); + } + + #[test] + fn test_push_bool_immediate() { + let mut rom = Vec::new(); + // True + rom.extend_from_slice(&(OpCode::PushBool as u16).to_le_bytes()); + rom.push(1); + // False + rom.extend_from_slice(&(OpCode::PushBool as u16).to_le_bytes()); + rom.push(0); + rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + + let mut vm = VirtualMachine::new(rom, vec![]); + let mut native = MockNative; + let mut hw = MockHardware; + + vm.step(&mut native, &mut hw).unwrap(); // Push true + assert_eq!(vm.peek().unwrap(), &Value::Boolean(true)); + vm.step(&mut native, &mut hw).unwrap(); // Push false + assert_eq!(vm.peek().unwrap(), &Value::Boolean(false)); + } + + #[test] + fn test_push_const_string() { + let mut rom = Vec::new(); + rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes()); + rom.extend_from_slice(&0u32.to_le_bytes()); + rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + + let cp = vec![Value::String("hello".into())]; + let mut vm = VirtualMachine::new(rom, cp); + let mut native = MockNative; + let mut hw = MockHardware; + + vm.step(&mut native, &mut hw).unwrap(); + assert_eq!(vm.peek().unwrap(), &Value::String("hello".into())); + } +} diff --git a/test-cartridges/color-square/program.pbc b/test-cartridges/color-square/program.pbc index 972e08a0..074d1147 100644 Binary files a/test-cartridges/color-square/program.pbc and b/test-cartridges/color-square/program.pbc differ