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 {
#[error("Trying to access invalid memory location!")]
InvalidMemoryLocation,
#[error("Stack Overflow")]
StackOverflow,
#[error("Stack Underflow")]
StackUnderflow,
}
#[derive(Debug)]

View File

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

View File

@ -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<u32>,
memory_score_multiplier: usize,
registers: Vec<u32>,
pc: u32, // Program counter
// sp: u32, // Stack pointer
// call_stack: Vec<u32>, // 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<CpuStats, ExecutionError> {
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<u32, ExecutionError> {
// 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<u32, ExecutionError> {
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])
}
}