add function calls
This commit is contained in:
parent
9db4c73a52
commit
17374c5fcc
|
|
@ -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)]
|
||||||
|
|
|
||||||
58
src/main.rs
58
src/main.rs
|
|
@ -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);
|
||||||
|
|
|
||||||
214
src/native.rs
214
src/native.rs
|
|
@ -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(())
|
|
||||||
// }
|
self.memory[self.stack_pointer as usize] = value;
|
||||||
|
self.stack_pointer -= 1;
|
||||||
// fn pop_from_stack(&mut self) -> Result<u32, ExecutionError> {
|
Ok(())
|
||||||
// self.valid_address(self.sp)?;
|
}
|
||||||
// let value = self.memory[self.sp as usize];
|
|
||||||
// self.sp += 1;
|
fn pop_stack(&mut self) -> Result<u32, ExecutionError> {
|
||||||
// Ok(value)
|
if self.stack_pointer as usize >= self.memory.len() - 1 {
|
||||||
// }
|
return Err(ExecutionError::StackUnderflow);
|
||||||
|
}
|
||||||
// fn call_function(&mut self, function_address: u32) {
|
|
||||||
// self.call_stack.push(self.pc); // Save current PC to call stack
|
self.stack_pointer += 1;
|
||||||
// self.pc = function_address; // Jump to the function's address
|
Ok(self.memory[self.stack_pointer as usize])
|
||||||
// }
|
}
|
||||||
|
|
||||||
// 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");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue