diff --git a/emulator/src/emulator/system/cache.rs b/emulator/src/emulator/system/cache.rs new file mode 100644 index 0000000..1213d78 --- /dev/null +++ b/emulator/src/emulator/system/cache.rs @@ -0,0 +1,53 @@ +use common::prelude::Instruction; +use rustc_hash::FxHashMap; + +#[derive(Debug)] +pub struct Cache { + addr: u32, + instruction_block: Option<[u8; 256]>, + instruction_lookup: FxHashMap, +} + +impl Cache { + #[must_use] + pub fn new() -> Self { + Self { + addr: 0, + instruction_block: None, + instruction_lookup: FxHashMap::default(), + } + } + + pub fn lookup_value(&mut self, addr: u32) -> Option { + if addr < self.addr || addr >= self.addr + 256 || self.instruction_block.is_none() + { + return None; + } + + Some(u32::from_be_bytes( + self.instruction_block.expect("this should not be none!") + [(addr - self.addr) as usize..(addr - self.addr + 4) as usize] + .try_into() + .expect("Failed to convert bytes to u32"), + )) + } + + pub const fn set(&mut self, addr: u32, block: &[u8; 256]) { + self.addr = addr - addr % 256; + self.instruction_block = Some(*block); + } + + pub fn lookup_instruction(&mut self, instruction: u32) -> Option { + self.instruction_lookup.get(&instruction).copied() + } + + pub fn insert(&mut self, value: u32, instruction: Instruction) { + self.instruction_lookup.insert(value, instruction); + } +} + +impl Default for Cache { + fn default() -> Self { + Self::new() + } +} diff --git a/emulator/src/emulator/system/emulator.rs b/emulator/src/emulator/system/emulator.rs index 9830dd6..cb87d72 100644 --- a/emulator/src/emulator/system/emulator.rs +++ b/emulator/src/emulator/system/emulator.rs @@ -25,9 +25,11 @@ pub fn run_emulator( let mut running = Running::Paused; let mut step = 0; let mut addr; - let mut history = Vec::<(u32, Instruction)>::with_capacity(32768); + let mut history = Vec::<(u32, u32)>::with_capacity(32768); let size = 256; + let record_history = true; + state_tx .send(StateUpdate::Running(Running::Paused)) .expect("Failed to send initial state!"); @@ -164,33 +166,11 @@ pub fn run_emulator( } } - if running == Running::Paused && step > 0 { - step -= 1; - update = true; - - // Execute one cycle. - match processor.cycle() { - Ok((addr, instruction)) => { - history.push((addr, instruction)); - } - Err(why) => { - let pcx = processor - .get(Register::Pcx) - .expect("SPR should never be invalid"); - report_err( - state_tx, - &format!( - "Could not decode instruction at {pcx:x}. Reason: {why}" - ), - &mut processor, - ); - } - } - instruction_count += 1; - continue; + if running == Running::Running { + step += 1; } - if running == Running::Running { + if step > 0 { step -= 1; update = true; @@ -212,8 +192,16 @@ pub fn run_emulator( } }; - history.push(instruction); - if matches!(instruction.1, Instruction::Halt) { + if record_history { + history.push(( + instruction.0, + processor + .get(Register::Cir) + .expect("CIR should never be invalid"), + )); + } + + if matches!(instruction, (_, Instruction::Halt)) { running = Running::Halted; step = 0; } diff --git a/emulator/src/emulator/system/memory.rs b/emulator/src/emulator/system/memory.rs index 6b83e15..e195b6b 100644 --- a/emulator/src/emulator/system/memory.rs +++ b/emulator/src/emulator/system/memory.rs @@ -36,15 +36,7 @@ pub struct MainStore { pub data: FxHashMap, } -pub struct Block { - data: [u8; 256], -} - -impl Default for Block { - fn default() -> Self { - Self { data: [0; 256] } - } -} +pub type Block = [u8; 256]; impl Default for MainStore { fn default() -> Self { @@ -67,12 +59,12 @@ impl MainStore { #[inline] fn mut_block(&mut self, addr: u32) -> &mut Block { - self.data.entry(addr).or_default() + self.data.entry(addr).or_insert([0; 256]) } #[inline] fn block(&mut self, addr: u32) -> &Block { - self.data.entry(addr).or_default() + self.data.entry(addr).or_insert([0; 256]) } } @@ -86,7 +78,7 @@ impl MemoryUnit for MainStore { fn read_byte(&mut self, addr: u32) -> u8 { let (block_addr, offset) = Self::segment_addr(addr); let block = self.block(block_addr); - block.data[offset as usize] + block[offset as usize] } #[inline] @@ -99,7 +91,7 @@ impl MemoryUnit for MainStore { let offset = offset as usize; let block = self.block(block_addr); Ok(u32::from_be_bytes( - block.data[offset..=offset + 3] + block[offset..=offset + 3] .try_into() .expect("Failed to read word!"), )) @@ -119,7 +111,7 @@ impl MemoryUnit for MainStore { fn write_byte(&mut self, addr: u32, value: u8) { let (block_addr, offset) = Self::segment_addr(addr); let block = self.mut_block(block_addr); - block.data[offset as usize] = value; + block[offset as usize] = value; } #[inline] @@ -130,7 +122,7 @@ impl MemoryUnit for MainStore { let (block_addr, offset) = Self::segment_addr(addr); let block = self.mut_block(block_addr); - block.data[offset as usize..=(offset + 3) as usize] + block[offset as usize..=(offset + 3) as usize] .copy_from_slice(&value.to_be_bytes()); Ok(()) } @@ -141,7 +133,7 @@ impl MemoryUnit for MainStore { let mut current_block = self.mut_block(current_block_addr); let mut offset = addr % 256; for byte in value { - current_block.data[offset as usize] = byte; + current_block[offset as usize] = byte; offset += 1; if offset >= 256 { offset = 0; @@ -154,12 +146,12 @@ impl MemoryUnit for MainStore { #[inline] fn read_block(&mut self, addr: u32) -> &[u8; 256] { let (block_addr, _) = Self::segment_addr(addr); - &self.block(block_addr).data + self.block(block_addr) } #[inline] fn write_block(&mut self, addr: u32, data: &[u8; 256]) { let (block_addr, _) = Self::segment_addr(addr); - let _ = self.data.insert(block_addr, Block { data: *data }); + let _ = self.data.insert(block_addr, *data); } } diff --git a/emulator/src/emulator/system/mod.rs b/emulator/src/emulator/system/mod.rs index e483168..6356927 100644 --- a/emulator/src/emulator/system/mod.rs +++ b/emulator/src/emulator/system/mod.rs @@ -1,3 +1,4 @@ +pub mod cache; pub mod emulator; pub mod memory; pub mod model; diff --git a/emulator/src/emulator/system/model.rs b/emulator/src/emulator/system/model.rs index 2d49a94..6477d47 100644 --- a/emulator/src/emulator/system/model.rs +++ b/emulator/src/emulator/system/model.rs @@ -78,7 +78,7 @@ pub struct State { pub error_log: Vec, - pub instruction_history: Vec<(u32, Instruction)>, + pub instruction_history: Vec<(u32, u32)>, } impl State { @@ -154,7 +154,7 @@ pub enum StateUpdate { MemoryView(Vec), DisplayView(Vec), Error(String), - InstructionHistory(Vec<(u32, Instruction)>), + InstructionHistory(Vec<(u32, u32)>), } #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] diff --git a/emulator/src/emulator/system/processor/mod.rs b/emulator/src/emulator/system/processor/mod.rs index 29139da..96730b6 100644 --- a/emulator/src/emulator/system/processor/mod.rs +++ b/emulator/src/emulator/system/processor/mod.rs @@ -4,6 +4,7 @@ use std::{ }; use crate::emulator::system::{ + cache::Cache, memory::MemoryUnit, model::{IODevice, ProcessorError, RegFile}, }; @@ -17,6 +18,7 @@ pub struct Processor { pub io_devices: Vec>, pub void: u32, + pub cache: Cache, } fn log(message: &str) { @@ -32,6 +34,7 @@ impl Processor { halted: false, io_devices, void: 0, + cache: Cache::new(), } } @@ -55,17 +58,31 @@ impl Processor { // Set MAR to the previous value of PCX. *self.reg(Register::Mar)? = addr; - let val = self.memory.read_word(addr)?; + + let encoded = if let Some(val) = self.cache.lookup_value(addr) { + val + } else { + let block = self.memory.read_block(addr); + self.cache.set(addr, block); + self.cache + .lookup_value(addr) + .expect("Failed to lookup value!") + }; // Set CIR to the value of RAM[MAR]. - *self.reg(Register::Mar)? = val; + *self.reg(Register::Cir)? = encoded; - // Decode and execute the instruction. - let instruction = Instruction::decode(val) - .map_err(|_| ProcessorError::InvalidInstruction(val))?; + let decoded = if let Some(val) = self.cache.lookup_instruction(addr) { + val + } else { + let decoded = Instruction::decode(encoded) + .map_err(|_| ProcessorError::InvalidInstruction(encoded))?; + self.cache.insert(addr, decoded); + decoded + }; - instruction.execute(self)?; - Ok((addr, instruction)) + decoded.execute(self)?; + Ok((addr, decoded)) } const fn fetch(&self) -> Result { diff --git a/emulator/src/emulator/ui/history.rs b/emulator/src/emulator/ui/history.rs index 64be793..c24a1e4 100644 --- a/emulator/src/emulator/ui/history.rs +++ b/emulator/src/emulator/ui/history.rs @@ -1,3 +1,4 @@ +use common::prelude::Instruction; use egui::{Context, Ui}; use crate::emulator::{ @@ -57,8 +58,11 @@ impl Component for History { .color(egui::Color32::from_rgb(255, 200, 200)), ); + let decoded = Instruction::decode(instruction.1) + .unwrap_or(Instruction::Nop); + ui.label( - egui::RichText::new(instruction.1.to_string()) + egui::RichText::new(decoded.to_string()) .font(egui::FontId::monospace(12.0)) .color(egui::Color32::from_rgb(200, 255, 200)), ); diff --git a/profile.json.gz b/profile.json.gz new file mode 100644 index 0000000..551b20e Binary files /dev/null and b/profile.json.gz differ diff --git a/resources/dsa/main.dsa b/resources/dsa/main.dsa index 552c80f..2499bf3 100644 --- a/resources/dsa/main.dsa +++ b/resources/dsa/main.dsa @@ -1,80 +1,12 @@ -include fib: "./lib/maths/fib.dsa" -include maths: "./lib/maths/core.dsa" -include print: "./lib/io/print.dsa" +// program to just test compute power -dw idt: 0xFFFF0000 -dw stack: 0x10000 -init: - // setup interrupt handlers - ldw idt, idr - lwi handle_hard_fault, rg0 - stw rg0, idr, 4 - // set up a stack. - ldw stack, bpr - mov bpr, spr - -dw string: "hello world" +dw large_num: 0x333333 // 333,333 instructions start: + ldw large_num, rg0 - lwi 37, rg0 - lwi 12, rg1 - push rg0 - push rg1 - call maths::divmod - pop rg0 // result - pop rg1 // remainder - - push rg1 - push rg0 - call print::print_hex_byte - call print::print_whitespace - pop zero - call print::print_hex_byte - call print::print_newline - - lwi string, rg0 - //lwi 10, rg0 - pusha 4 - push rg0 - call print::print - //call fib::fib_n - pop zero - call print::print_newline - popa 4 - - pusha 4 - push rg0 - call print::print - //call fib::fib_n - pop zero - call print::print_newline - popa 4 - - pusha 4 - push rg0 - call print::print - //call fib::fib_n - pop zero - call print::print_newline - popa 4 - - pusha 4 - push rg0 - call print::print - //call fib::fib_n - pop zero - call print::print_newline - popa 4 - +// run approx 1m instructions +loop: + dec rg0 + cmp rg0, zero + jgt loop hlt - -// fault handler in case we fail DSA. -dw hard_fault_err: "FATAL: Illegal Instruction or Memory Access!" -handle_hard_fault: - call print::clear - call print::reset - lwi hard_fault_err, rg0 - push rg0 - call print::print - pop zero - hlt \ No newline at end of file