add function calls

This commit is contained in:
Steve Biedermann 2024-12-12 15:22:45 +01:00
parent 9db4c73a52
commit 17374c5fcc
3 changed files with 191 additions and 79 deletions

View File

@ -5,6 +5,10 @@ use thiserror::Error;
pub enum ExecutionError { pub enum ExecutionError {
#[error("Trying to access invalid memory location!")] #[error("Trying to access invalid memory location!")]
InvalidMemoryLocation, InvalidMemoryLocation,
#[error("Stack Overflow")]
StackOverflow,
#[error("Stack Underflow")]
StackUnderflow,
} }
#[derive(Debug)] #[derive(Debug)]

View File

@ -30,30 +30,48 @@ fn main() -> anyhow::Result<()> {
.ok() .ok()
}) })
.unwrap_or_else(|| { .unwrap_or_else(|| {
use Bytecode::*;
vec![ vec![
Bytecode::Nop as u32, /* 0 */ Jmp as u32,
Bytecode::Nop as u32, /* 1 */ 15,
Bytecode::Nop as u32, /* 2 */ Pop as u32,
Bytecode::Nop as u32, /* 3 */ 5,
Bytecode::Set as u32, /* 4 */ Pop as u32,
0x00, /* 5 */ 0,
42, /* 6 */ Pop as u32,
Bytecode::Set as u32, /* 7 */ 1,
0x01, /* 8 */ Add as u32,
10, /* 9 */ 0,
Bytecode::Add as u32, /* 10 */ 1,
0x00, /* 11 */ PushReg as u32,
0x01, /* 12 */ 5,
Bytecode::Store as u32, /* 13 */ RetReg as u32,
0x40, /* 14 */ 0,
0x00, /* 15 */ Nop as u32,
Bytecode::Inspect as u32, /* 16 */ Nop as u32,
0x40, /* 17 */ Nop as u32,
0xFF, /* 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.set_verbose(verbose);
cpu.load_memory(0, &memory); cpu.load_memory(0, &memory);

View File

@ -3,15 +3,22 @@ use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
#[derive(Debug, PartialEq, PartialOrd, Copy, Clone, Hash, Eq, Ord, FromPrimitive, ToPrimitive)] #[derive(Debug, PartialEq, PartialOrd, Copy, Clone, Hash, Eq, Ord, FromPrimitive, ToPrimitive)]
#[repr(usize)] #[repr(u32)]
pub enum Bytecode { pub enum Bytecode {
Nop = 0x00, Nop = 0x00,
Set = 0x01, LoadValue = 0x01,
Load = 0x02, LoadMemory = 0x02,
Store = 0x03, Store = 0x03,
Add = 0x12, PushValue = 0x04,
PushReg = 0x05,
Pop = 0x06,
Add = 0x10,
Call = 0xF0,
Ret = 0xF1,
RetReg = 0xF2,
Jmp = 0xF3,
Inspect = 0xFE, Inspect = 0xFE,
Halt = 0xFF, Halt = 0xFFFFFFFF,
} }
#[derive(Debug)] #[derive(Debug)]
@ -19,9 +26,8 @@ pub struct NativeCpu {
memory: Vec<u32>, memory: Vec<u32>,
memory_score_multiplier: usize, memory_score_multiplier: usize,
registers: Vec<u32>, registers: Vec<u32>,
pc: u32, // Program counter instruction_pointer: u32,
// sp: u32, // Stack pointer stack_pointer: u32, // Stack pointer
// call_stack: Vec<u32>, // Call stack for function calls
verbose: bool, verbose: bool,
cycles: usize, // Performance counter for cycles cycles: usize, // Performance counter for cycles
memory_access_score: usize, // Tracks the performance score memory_access_score: usize, // Tracks the performance score
@ -81,14 +87,13 @@ impl CpuTrait for NativeCpu {
} }
impl NativeCpu { impl NativeCpu {
pub fn new(memory: u64, registers: u8) -> Self { pub fn new(memory_size: u32, registers: u8) -> Self {
Self { Self {
memory: vec![0; memory as usize], memory: vec![0; memory_size as usize],
memory_score_multiplier: 10, memory_score_multiplier: 10,
registers: vec![0; registers as usize], registers: vec![0; registers as usize],
pc: 0, instruction_pointer: 0,
// sp: 1024 * 1024, // Stack grows downward stack_pointer: memory_size - 1, // Stack starts at the top of memory
// call_stack: Vec::new(),
verbose: false, verbose: false,
cycles: 0, cycles: 0,
memory_access_score: 0, memory_access_score: 0,
@ -107,17 +112,19 @@ impl NativeCpu {
fn run(&mut self, cycles: isize) -> Result<CpuStats, ExecutionError> { fn run(&mut self, cycles: isize) -> Result<CpuStats, ExecutionError> {
let mut executed = 0; let mut executed = 0;
while (cycles < 0 || executed < cycles) && (self.pc as usize) < self.memory.len() { while (cycles < 0 || executed < cycles)
let opcode = self.read_memory(self.pc)?; && (self.instruction_pointer as usize) < self.memory.len()
self.pc += 1; {
let opcode = self.read_memory(self.instruction_pointer)?;
self.instruction_pointer += 1;
match Bytecode::from_u32(opcode) { match Bytecode::from_u32(opcode) {
Some(Bytecode::Set) => { Some(Bytecode::LoadValue) => {
let reg = self.read_memory(self.pc)?; let reg = self.read_memory(self.instruction_pointer)?;
self.pc += 1; self.instruction_pointer += 1;
let imm = self.read_memory(self.pc)?; let imm = self.read_memory(self.instruction_pointer)?;
self.pc += 1; self.instruction_pointer += 1;
if self.verbose { if self.verbose {
println!("SET R{}, {}", reg, imm); println!("SET R{}, {}", reg, imm);
@ -126,12 +133,12 @@ impl NativeCpu {
self.registers[reg as usize] = imm; self.registers[reg as usize] = imm;
self.cycles += 2; self.cycles += 2;
} }
Some(Bytecode::Load) => { Some(Bytecode::LoadMemory) => {
let reg = self.read_memory(self.pc)?; let reg = self.read_memory(self.instruction_pointer)?;
self.pc += 1; self.instruction_pointer += 1;
let addr = self.read_memory(self.pc)?; let addr = self.read_memory(self.instruction_pointer)?;
self.pc += 1; self.instruction_pointer += 1;
if self.verbose { if self.verbose {
println!("LOAD R{}, @{:#02x}", reg, addr); println!("LOAD R{}, @{:#02x}", reg, addr);
@ -141,11 +148,11 @@ impl NativeCpu {
self.cycles += 2; self.cycles += 2;
} }
Some(Bytecode::Store) => { Some(Bytecode::Store) => {
let addr = self.read_memory(self.pc)?; let addr = self.read_memory(self.instruction_pointer)?;
self.pc += 1; self.instruction_pointer += 1;
let reg = self.read_memory(self.pc)?; let reg = self.read_memory(self.instruction_pointer)?;
self.pc += 1; self.instruction_pointer += 1;
if self.verbose { if self.verbose {
println!("STORE @{:#02x}, R{}", addr, reg); println!("STORE @{:#02x}, R{}", addr, reg);
@ -155,17 +162,17 @@ impl NativeCpu {
self.cycles += 2; self.cycles += 2;
} }
Some(Bytecode::Inspect) => { Some(Bytecode::Inspect) => {
let addr = self.read_memory(self.pc)?; let addr = self.read_memory(self.instruction_pointer)?;
self.pc += 1; self.instruction_pointer += 1;
println!("INSPECT @{:#02x} = {}", addr, self.read_memory(addr)?); println!("INSPECT @{:#02x} = {}", addr, self.read_memory(addr)?);
} }
Some(Bytecode::Add) => { Some(Bytecode::Add) => {
let reg1 = self.read_memory(self.pc)?; let reg1 = self.read_memory(self.instruction_pointer)?;
self.pc += 1; self.instruction_pointer += 1;
let reg2 = self.read_memory(self.pc)?; let reg2 = self.read_memory(self.instruction_pointer)?;
self.pc += 1; self.instruction_pointer += 1;
if self.verbose { if self.verbose {
println!( println!(
@ -181,6 +188,97 @@ impl NativeCpu {
self.registers[reg1 as usize].wrapping_add(self.registers[reg2 as usize]); self.registers[reg1 as usize].wrapping_add(self.registers[reg2 as usize]);
self.cycles += 1; 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) => { Some(Bytecode::Halt) => {
if self.verbose { if self.verbose {
println!("HALT"); println!("HALT");
@ -237,30 +335,22 @@ impl NativeCpu {
Ok(()) Ok(())
} }
// fn push_to_stack(&mut self, value: u32) -> Result<(), ExecutionError> { fn push_stack(&mut self, value: u32) -> Result<(), ExecutionError> {
// self.valid_address(self.sp)?; if self.stack_pointer == 0 {
// self.sp -= 1; return Err(ExecutionError::StackOverflow);
// self.memory[self.sp as usize] = value; }
// Ok(())
// }
// fn pop_from_stack(&mut self) -> Result<u32, ExecutionError> { self.memory[self.stack_pointer as usize] = value;
// self.valid_address(self.sp)?; self.stack_pointer -= 1;
// let value = self.memory[self.sp as usize]; Ok(())
// self.sp += 1; }
// Ok(value)
// }
// fn call_function(&mut self, function_address: u32) { fn pop_stack(&mut self) -> Result<u32, ExecutionError> {
// self.call_stack.push(self.pc); // Save current PC to call stack if self.stack_pointer as usize >= self.memory.len() - 1 {
// self.pc = function_address; // Jump to the function's address return Err(ExecutionError::StackUnderflow);
// } }
// fn return_from_function(&mut self) { self.stack_pointer += 1;
// if let Some(return_address) = self.call_stack.pop() { Ok(self.memory[self.stack_pointer as usize])
// self.pc = return_address; // Restore PC from the call stack }
// } else {
// panic!("Call stack underflow: No return address");
// }
// }
} }