// SPDX-License-Identifier: LGPL-3.0-or-later // Copyright (c) 2024 Patrick_Pluto use crate::assembler; pub trait CPU { fn create(debug: bool, initial_state: Vec) -> Self; fn assemble(source: String) -> Vec; fn start(&mut self); } // This is an example implementation of a CPU emulator. You can copy this for your own custom ISA, or do something else. pub struct ExampleCPU { debug: bool, execution_pointer: u8, operands: [u8; 3], ram: [u8; 256], registers: [u8; 4], } impl CPU for ExampleCPU { // Here is where you can create your own CPU Instance. fn create(debug: bool, initial_state: Vec) -> ExampleCPU { let mut instance: ExampleCPU = ExampleCPU { debug, execution_pointer: 0, operands: [0; 3], ram: [0; 256], registers: [0; 4], }; let len = initial_state.len(); let ram_len = instance.ram.len(); if len >= ram_len { instance.ram.copy_from_slice(&initial_state[..ram_len]); } else { instance.ram[..len].copy_from_slice(&initial_state); } instance } // Here is where you define the steps for assembling your custom Assembly language. fn assemble(mut source: String) -> Vec { let mut assembler: assembler::Assembler = assembler::Assembler::create(vec![ assembler::Opcode::create("nop", 0), assembler::Opcode::create("load", 1), assembler::Opcode::create("store", 2), assembler::Opcode::create("add", 3), assembler::Opcode::create("sub", 4), assembler::Opcode::create("jump", 5), assembler::Opcode::create("jpiz", 6), ]); assembler.assemble(&mut source) } // This starts the CPU emulator. fn start(&mut self) { loop { self.fetch_and_decode() } } } // These are the implementations specific to ExampleCPU impl ExampleCPU { // This fetches the operands, based on the position of the execution_pointer, then runs the selected instruction. fn fetch_and_decode(&mut self) { self.operands[0] = self.ram[self.execution_pointer as usize]; self.operands[1] = self.ram[(self.execution_pointer + 1) as usize]; self.operands[2] = self.ram[(self.execution_pointer + 2) as usize]; if self.debug { println!( "0x{:02X} 0x{:02X} 0x{:02X}", self.operands[0], self.operands[1], self.operands[2] ); } match self.operands[0] { 0 => self.nop(), 1 => self.load(), 2 => self.store(), 3 => self.add(), 4 => self.sub(), 5 => self.jump(), 6 => self.jpiz(), _ => self.nop(), }; } // Here is where all of the implementations of the instructions should go. fn nop(&mut self) { self.execution_pointer += 1; } fn load(&mut self) { self.registers[self.operands[1] as usize] = self.ram[self.operands[2] as usize]; self.execution_pointer += 3; } fn store(&mut self) { self.ram[self.operands[1] as usize] = self.registers[self.operands[2] as usize]; self.execution_pointer += 3; } fn add(&mut self) { self.registers[self.operands[1] as usize] += self.registers[self.operands[2] as usize]; self.execution_pointer += 3; } fn sub(&mut self) { self.registers[self.operands[1] as usize] -= self.registers[self.operands[2] as usize]; self.execution_pointer += 3; } fn jump(&mut self) { self.execution_pointer = self.operands[1]; } fn jpiz(&mut self) { if self.registers[self.operands[2] as usize] == 0 { self.execution_pointer = self.operands[1]; } else { self.execution_pointer += 3; } } }