more optimisations test program ~54MIPS -> ~110MIPS
This commit is contained in:
@@ -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<u32, Instruction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<u32> {
|
||||||
|
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<Instruction> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,9 +25,11 @@ pub fn run_emulator(
|
|||||||
let mut running = Running::Paused;
|
let mut running = Running::Paused;
|
||||||
let mut step = 0;
|
let mut step = 0;
|
||||||
let mut addr;
|
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 size = 256;
|
||||||
|
|
||||||
|
let record_history = true;
|
||||||
|
|
||||||
state_tx
|
state_tx
|
||||||
.send(StateUpdate::Running(Running::Paused))
|
.send(StateUpdate::Running(Running::Paused))
|
||||||
.expect("Failed to send initial state!");
|
.expect("Failed to send initial state!");
|
||||||
@@ -164,33 +166,11 @@ pub fn run_emulator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if running == Running::Paused && step > 0 {
|
if running == Running::Running {
|
||||||
step -= 1;
|
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 {
|
if step > 0 {
|
||||||
step -= 1;
|
step -= 1;
|
||||||
update = true;
|
update = true;
|
||||||
|
|
||||||
@@ -212,8 +192,16 @@ pub fn run_emulator(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
history.push(instruction);
|
if record_history {
|
||||||
if matches!(instruction.1, Instruction::Halt) {
|
history.push((
|
||||||
|
instruction.0,
|
||||||
|
processor
|
||||||
|
.get(Register::Cir)
|
||||||
|
.expect("CIR should never be invalid"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(instruction, (_, Instruction::Halt)) {
|
||||||
running = Running::Halted;
|
running = Running::Halted;
|
||||||
step = 0;
|
step = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,15 +36,7 @@ pub struct MainStore {
|
|||||||
pub data: FxHashMap<u32, Block>,
|
pub data: FxHashMap<u32, Block>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Block {
|
pub type Block = [u8; 256];
|
||||||
data: [u8; 256],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Block {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { data: [0; 256] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for MainStore {
|
impl Default for MainStore {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@@ -67,12 +59,12 @@ impl MainStore {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mut_block(&mut self, addr: u32) -> &mut Block {
|
fn mut_block(&mut self, addr: u32) -> &mut Block {
|
||||||
self.data.entry(addr).or_default()
|
self.data.entry(addr).or_insert([0; 256])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn block(&mut self, addr: u32) -> &Block {
|
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 {
|
fn read_byte(&mut self, addr: u32) -> u8 {
|
||||||
let (block_addr, offset) = Self::segment_addr(addr);
|
let (block_addr, offset) = Self::segment_addr(addr);
|
||||||
let block = self.block(block_addr);
|
let block = self.block(block_addr);
|
||||||
block.data[offset as usize]
|
block[offset as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -99,7 +91,7 @@ impl MemoryUnit for MainStore {
|
|||||||
let offset = offset as usize;
|
let offset = offset as usize;
|
||||||
let block = self.block(block_addr);
|
let block = self.block(block_addr);
|
||||||
Ok(u32::from_be_bytes(
|
Ok(u32::from_be_bytes(
|
||||||
block.data[offset..=offset + 3]
|
block[offset..=offset + 3]
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("Failed to read word!"),
|
.expect("Failed to read word!"),
|
||||||
))
|
))
|
||||||
@@ -119,7 +111,7 @@ impl MemoryUnit for MainStore {
|
|||||||
fn write_byte(&mut self, addr: u32, value: u8) {
|
fn write_byte(&mut self, addr: u32, value: u8) {
|
||||||
let (block_addr, offset) = Self::segment_addr(addr);
|
let (block_addr, offset) = Self::segment_addr(addr);
|
||||||
let block = self.mut_block(block_addr);
|
let block = self.mut_block(block_addr);
|
||||||
block.data[offset as usize] = value;
|
block[offset as usize] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -130,7 +122,7 @@ impl MemoryUnit for MainStore {
|
|||||||
|
|
||||||
let (block_addr, offset) = Self::segment_addr(addr);
|
let (block_addr, offset) = Self::segment_addr(addr);
|
||||||
let block = self.mut_block(block_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());
|
.copy_from_slice(&value.to_be_bytes());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -141,7 +133,7 @@ impl MemoryUnit for MainStore {
|
|||||||
let mut current_block = self.mut_block(current_block_addr);
|
let mut current_block = self.mut_block(current_block_addr);
|
||||||
let mut offset = addr % 256;
|
let mut offset = addr % 256;
|
||||||
for byte in value {
|
for byte in value {
|
||||||
current_block.data[offset as usize] = byte;
|
current_block[offset as usize] = byte;
|
||||||
offset += 1;
|
offset += 1;
|
||||||
if offset >= 256 {
|
if offset >= 256 {
|
||||||
offset = 0;
|
offset = 0;
|
||||||
@@ -154,12 +146,12 @@ impl MemoryUnit for MainStore {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn read_block(&mut self, addr: u32) -> &[u8; 256] {
|
fn read_block(&mut self, addr: u32) -> &[u8; 256] {
|
||||||
let (block_addr, _) = Self::segment_addr(addr);
|
let (block_addr, _) = Self::segment_addr(addr);
|
||||||
&self.block(block_addr).data
|
self.block(block_addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_block(&mut self, addr: u32, data: &[u8; 256]) {
|
fn write_block(&mut self, addr: u32, data: &[u8; 256]) {
|
||||||
let (block_addr, _) = Self::segment_addr(addr);
|
let (block_addr, _) = Self::segment_addr(addr);
|
||||||
let _ = self.data.insert(block_addr, Block { data: *data });
|
let _ = self.data.insert(block_addr, *data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pub mod cache;
|
||||||
pub mod emulator;
|
pub mod emulator;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
pub mod model;
|
pub mod model;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ pub struct State {
|
|||||||
|
|
||||||
pub error_log: Vec<String>,
|
pub error_log: Vec<String>,
|
||||||
|
|
||||||
pub instruction_history: Vec<(u32, Instruction)>,
|
pub instruction_history: Vec<(u32, u32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
@@ -154,7 +154,7 @@ pub enum StateUpdate {
|
|||||||
MemoryView(Vec<u8>),
|
MemoryView(Vec<u8>),
|
||||||
DisplayView(Vec<u8>),
|
DisplayView(Vec<u8>),
|
||||||
Error(String),
|
Error(String),
|
||||||
InstructionHistory(Vec<(u32, Instruction)>),
|
InstructionHistory(Vec<(u32, u32)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::emulator::system::{
|
use crate::emulator::system::{
|
||||||
|
cache::Cache,
|
||||||
memory::MemoryUnit,
|
memory::MemoryUnit,
|
||||||
model::{IODevice, ProcessorError, RegFile},
|
model::{IODevice, ProcessorError, RegFile},
|
||||||
};
|
};
|
||||||
@@ -17,6 +18,7 @@ pub struct Processor {
|
|||||||
pub io_devices: Vec<Arc<dyn IODevice>>,
|
pub io_devices: Vec<Arc<dyn IODevice>>,
|
||||||
|
|
||||||
pub void: u32,
|
pub void: u32,
|
||||||
|
pub cache: Cache,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log(message: &str) {
|
fn log(message: &str) {
|
||||||
@@ -32,6 +34,7 @@ impl Processor {
|
|||||||
halted: false,
|
halted: false,
|
||||||
io_devices,
|
io_devices,
|
||||||
void: 0,
|
void: 0,
|
||||||
|
cache: Cache::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,17 +58,31 @@ impl Processor {
|
|||||||
|
|
||||||
// Set MAR to the previous value of PCX.
|
// Set MAR to the previous value of PCX.
|
||||||
*self.reg(Register::Mar)? = addr;
|
*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].
|
// Set CIR to the value of RAM[MAR].
|
||||||
*self.reg(Register::Mar)? = val;
|
*self.reg(Register::Cir)? = encoded;
|
||||||
|
|
||||||
// Decode and execute the instruction.
|
let decoded = if let Some(val) = self.cache.lookup_instruction(addr) {
|
||||||
let instruction = Instruction::decode(val)
|
val
|
||||||
.map_err(|_| ProcessorError::InvalidInstruction(val))?;
|
} else {
|
||||||
|
let decoded = Instruction::decode(encoded)
|
||||||
|
.map_err(|_| ProcessorError::InvalidInstruction(encoded))?;
|
||||||
|
self.cache.insert(addr, decoded);
|
||||||
|
decoded
|
||||||
|
};
|
||||||
|
|
||||||
instruction.execute(self)?;
|
decoded.execute(self)?;
|
||||||
Ok((addr, instruction))
|
Ok((addr, decoded))
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn fetch(&self) -> Result<u32, ProcessorError> {
|
const fn fetch(&self) -> Result<u32, ProcessorError> {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use common::prelude::Instruction;
|
||||||
use egui::{Context, Ui};
|
use egui::{Context, Ui};
|
||||||
|
|
||||||
use crate::emulator::{
|
use crate::emulator::{
|
||||||
@@ -57,8 +58,11 @@ impl Component for History {
|
|||||||
.color(egui::Color32::from_rgb(255, 200, 200)),
|
.color(egui::Color32::from_rgb(255, 200, 200)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let decoded = Instruction::decode(instruction.1)
|
||||||
|
.unwrap_or(Instruction::Nop);
|
||||||
|
|
||||||
ui.label(
|
ui.label(
|
||||||
egui::RichText::new(instruction.1.to_string())
|
egui::RichText::new(decoded.to_string())
|
||||||
.font(egui::FontId::monospace(12.0))
|
.font(egui::FontId::monospace(12.0))
|
||||||
.color(egui::Color32::from_rgb(200, 255, 200)),
|
.color(egui::Color32::from_rgb(200, 255, 200)),
|
||||||
);
|
);
|
||||||
|
|||||||
Binary file not shown.
+8
-76
@@ -1,80 +1,12 @@
|
|||||||
include fib: "./lib/maths/fib.dsa"
|
// program to just test compute power
|
||||||
include maths: "./lib/maths/core.dsa"
|
|
||||||
include print: "./lib/io/print.dsa"
|
|
||||||
|
|
||||||
dw idt: 0xFFFF0000
|
dw large_num: 0x333333 // 333,333 instructions
|
||||||
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"
|
|
||||||
start:
|
start:
|
||||||
|
ldw large_num, rg0
|
||||||
|
|
||||||
lwi 37, rg0
|
// run approx 1m instructions
|
||||||
lwi 12, rg1
|
loop:
|
||||||
push rg0
|
dec rg0
|
||||||
push rg1
|
cmp rg0, zero
|
||||||
call maths::divmod
|
jgt loop
|
||||||
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
|
|
||||||
|
|
||||||
hlt
|
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
|
|
||||||
Reference in New Issue
Block a user