diff --git a/common/src/lib.rs b/common/src/lib.rs index 743cf9b..a070c64 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -5,6 +5,10 @@ use thiserror::Error; pub enum ExecutionError { #[error("Trying to access invalid memory location!")] InvalidMemoryLocation, + #[error("Stack Overflow")] + StackOverflow, + #[error("Stack Underflow")] + StackUnderflow, } #[derive(Debug)] diff --git a/src/main.rs b/src/main.rs index 3af3333..7eb2934 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,30 +30,48 @@ fn main() -> anyhow::Result<()> { .ok() }) .unwrap_or_else(|| { + use Bytecode::*; vec![ - Bytecode::Nop as u32, - Bytecode::Nop as u32, - Bytecode::Nop as u32, - Bytecode::Nop as u32, - Bytecode::Set as u32, - 0x00, - 42, - Bytecode::Set as u32, - 0x01, - 10, - Bytecode::Add as u32, - 0x00, - 0x01, - Bytecode::Store as u32, - 0x40, - 0x00, - Bytecode::Inspect as u32, - 0x40, - 0xFF, + /* 0 */ Jmp as u32, + /* 1 */ 15, + /* 2 */ Pop as u32, + /* 3 */ 5, + /* 4 */ Pop as u32, + /* 5 */ 0, + /* 6 */ Pop as u32, + /* 7 */ 1, + /* 8 */ Add as u32, + /* 9 */ 0, + /* 10 */ 1, + /* 11 */ PushReg as u32, + /* 12 */ 5, + /* 13 */ RetReg as u32, + /* 14 */ 0, + /* 15 */ Nop as u32, + /* 16 */ Nop as u32, + /* 17 */ Nop as u32, + /* 18 */ Nop as u32, + /* 19 */ PushValue as u32, + /* 20 */ 42, + /* 21 */ LoadValue as u32, + /* 22 */ 0, + /* 23 */ 10, + /* 24 */ PushReg as u32, + /* 25 */ 0, + /* 26 */ Call as u32, + /* 27 */ 2, + /* 28 */ Pop as u32, + /* 29 */ 3, + /* 30 */ Store as u32, + /* 31 */ 40, + /* 32 */ 3, + /* 33 */ Inspect as u32, + /* 34 */ 40, + /* 35 */ Halt as u32, ] }); - let mut cpu = NativeCpu::new(1024 * 1024, 4); + let mut cpu = NativeCpu::new(1024 * 1024, 6); cpu.set_verbose(verbose); cpu.load_memory(0, &memory); diff --git a/src/native.rs b/src/native.rs index 707579e..086f20d 100644 --- a/src/native.rs +++ b/src/native.rs @@ -3,15 +3,22 @@ use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::FromPrimitive; #[derive(Debug, PartialEq, PartialOrd, Copy, Clone, Hash, Eq, Ord, FromPrimitive, ToPrimitive)] -#[repr(usize)] +#[repr(u32)] pub enum Bytecode { Nop = 0x00, - Set = 0x01, - Load = 0x02, + LoadValue = 0x01, + LoadMemory = 0x02, Store = 0x03, - Add = 0x12, + PushValue = 0x04, + PushReg = 0x05, + Pop = 0x06, + Add = 0x10, + Call = 0xF0, + Ret = 0xF1, + RetReg = 0xF2, + Jmp = 0xF3, Inspect = 0xFE, - Halt = 0xFF, + Halt = 0xFFFFFFFF, } #[derive(Debug)] @@ -19,9 +26,8 @@ pub struct NativeCpu { memory: Vec, memory_score_multiplier: usize, registers: Vec, - pc: u32, // Program counter - // sp: u32, // Stack pointer - // call_stack: Vec, // Call stack for function calls + instruction_pointer: u32, + stack_pointer: u32, // Stack pointer verbose: bool, cycles: usize, // Performance counter for cycles memory_access_score: usize, // Tracks the performance score @@ -81,14 +87,13 @@ impl CpuTrait for NativeCpu { } impl NativeCpu { - pub fn new(memory: u64, registers: u8) -> Self { + pub fn new(memory_size: u32, registers: u8) -> Self { Self { - memory: vec![0; memory as usize], + memory: vec![0; memory_size as usize], memory_score_multiplier: 10, registers: vec![0; registers as usize], - pc: 0, - // sp: 1024 * 1024, // Stack grows downward - // call_stack: Vec::new(), + instruction_pointer: 0, + stack_pointer: memory_size - 1, // Stack starts at the top of memory verbose: false, cycles: 0, memory_access_score: 0, @@ -107,17 +112,19 @@ impl NativeCpu { fn run(&mut self, cycles: isize) -> Result { let mut executed = 0; - while (cycles < 0 || executed < cycles) && (self.pc as usize) < self.memory.len() { - let opcode = self.read_memory(self.pc)?; - self.pc += 1; + while (cycles < 0 || executed < cycles) + && (self.instruction_pointer as usize) < self.memory.len() + { + let opcode = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; match Bytecode::from_u32(opcode) { - Some(Bytecode::Set) => { - let reg = self.read_memory(self.pc)?; - self.pc += 1; + Some(Bytecode::LoadValue) => { + let reg = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; - let imm = self.read_memory(self.pc)?; - self.pc += 1; + let imm = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; if self.verbose { println!("SET R{}, {}", reg, imm); @@ -126,12 +133,12 @@ impl NativeCpu { self.registers[reg as usize] = imm; self.cycles += 2; } - Some(Bytecode::Load) => { - let reg = self.read_memory(self.pc)?; - self.pc += 1; + Some(Bytecode::LoadMemory) => { + let reg = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; - let addr = self.read_memory(self.pc)?; - self.pc += 1; + let addr = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; if self.verbose { println!("LOAD R{}, @{:#02x}", reg, addr); @@ -141,11 +148,11 @@ impl NativeCpu { self.cycles += 2; } Some(Bytecode::Store) => { - let addr = self.read_memory(self.pc)?; - self.pc += 1; + let addr = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; - let reg = self.read_memory(self.pc)?; - self.pc += 1; + let reg = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; if self.verbose { println!("STORE @{:#02x}, R{}", addr, reg); @@ -155,17 +162,17 @@ impl NativeCpu { self.cycles += 2; } Some(Bytecode::Inspect) => { - let addr = self.read_memory(self.pc)?; - self.pc += 1; + let addr = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; println!("INSPECT @{:#02x} = {}", addr, self.read_memory(addr)?); } Some(Bytecode::Add) => { - let reg1 = self.read_memory(self.pc)?; - self.pc += 1; + let reg1 = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; - let reg2 = self.read_memory(self.pc)?; - self.pc += 1; + let reg2 = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; if self.verbose { println!( @@ -181,6 +188,97 @@ impl NativeCpu { self.registers[reg1 as usize].wrapping_add(self.registers[reg2 as usize]); self.cycles += 1; } + Some(Bytecode::Jmp) => { + let imm = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer = imm; + + if self.verbose { + println!("JMP {}", imm); + } + + self.cycles += 2; + } + Some(Bytecode::PushValue) => { + let imm = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; + + if self.verbose { + println!("PUSH {}", imm); + } + + self.push_stack(imm)?; + + self.cycles += 2; + } + Some(Bytecode::PushReg) => { + let reg = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; + + let val = self.registers[reg as usize]; + + if self.verbose { + println!("PUSH R{}", reg); + } + + self.push_stack(val)?; + + self.cycles += 2; + } + Some(Bytecode::Pop) => { + let reg = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; + + if self.verbose { + println!("POP R{}", reg); + } + + self.registers[reg as usize] = self.pop_stack()?; + + self.cycles += 2; + } + Some(Bytecode::Call) => { + let target = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; + + if self.verbose { + println!("CALL @{:#02x}", target); + } + + // Push the current PC onto the stack as the return address + self.push_stack(self.instruction_pointer)?; + + // Jump to the target address + self.instruction_pointer = target; + + self.cycles += 3; // CALL takes 3 cycles + } + Some(Bytecode::RetReg) => { + let reg = self.read_memory(self.instruction_pointer)?; + self.instruction_pointer += 1; + + // Pop the return address from the stack + let return_address = self.pop_stack()?; + + if self.verbose { + println!("RET R{} to @{:#02x}", reg, return_address); + } + + self.push_stack(self.registers[reg as usize])?; + + self.instruction_pointer = return_address; + self.cycles += 2; // RET takes 2 cycles + } + Some(Bytecode::Ret) => { + // Pop the return address from the stack + let return_address = self.pop_stack()?; + + if self.verbose { + println!("RET to @{:#02x}", return_address); + } + + self.instruction_pointer = return_address; + self.cycles += 2; // RET takes 2 cycles + } Some(Bytecode::Halt) => { if self.verbose { println!("HALT"); @@ -237,30 +335,22 @@ impl NativeCpu { Ok(()) } - // fn push_to_stack(&mut self, value: u32) -> Result<(), ExecutionError> { - // self.valid_address(self.sp)?; - // self.sp -= 1; - // self.memory[self.sp as usize] = value; - // Ok(()) - // } + fn push_stack(&mut self, value: u32) -> Result<(), ExecutionError> { + if self.stack_pointer == 0 { + return Err(ExecutionError::StackOverflow); + } - // fn pop_from_stack(&mut self) -> Result { - // self.valid_address(self.sp)?; - // let value = self.memory[self.sp as usize]; - // self.sp += 1; - // Ok(value) - // } + self.memory[self.stack_pointer as usize] = value; + self.stack_pointer -= 1; + Ok(()) + } - // fn call_function(&mut self, function_address: u32) { - // self.call_stack.push(self.pc); // Save current PC to call stack - // self.pc = function_address; // Jump to the function's address - // } + fn pop_stack(&mut self) -> Result { + if self.stack_pointer as usize >= self.memory.len() - 1 { + return Err(ExecutionError::StackUnderflow); + } - // fn return_from_function(&mut self) { - // if let Some(return_address) = self.call_stack.pop() { - // self.pc = return_address; // Restore PC from the call stack - // } else { - // panic!("Call stack underflow: No return address"); - // } - // } + self.stack_pointer += 1; + Ok(self.memory[self.stack_pointer as usize]) + } }