emulator: applied some clippy lints

This commit is contained in:
2025-06-15 04:03:48 +01:00
parent a16f57c737
commit a240346a84
13 changed files with 357 additions and 288 deletions
+3
View File
@@ -0,0 +1,3 @@
{
"rust-analyzer.check.command": "clippy"
}
+129 -104
View File
@@ -1,7 +1,7 @@
type Offset = u16; type Offset = u16;
type Immediate = u16; type Immediate = u16;
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Interrupt { pub enum Interrupt {
Software(u8), Software(u8),
} }
@@ -9,22 +9,23 @@ pub enum Interrupt {
pub type Address = u32; pub type Address = u32;
impl Interrupt { impl Interrupt {
fn as_u8(&self) -> u8 { const fn as_u8(self) -> u8 {
match self { match self {
Interrupt::Software(code) => *code, Self::Software(code) => code,
} }
} }
} }
impl From<u8> for Interrupt { impl From<u8> for Interrupt {
fn from(_code: u8) -> Self {
todo!("implement this once a hardware interrupt convention is established");
#[allow(unreachable_code)] #[allow(unreachable_code)]
Interrupt::Software(_code) fn from(_code: u8) -> Self {
todo!("Implement this once a hardware interrupt convention is established.");
// Self::Software(_code)
} }
} }
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Register { pub enum Register {
// general purpose registers // general purpose registers
Rg0, Rg0,
@@ -62,71 +63,93 @@ pub enum Register {
Pcx, Pcx,
} }
impl From<u8> for Register { #[derive(Debug)]
fn from(idx: u8) -> Register { /// Error type for parsing register numbers.
match idx { pub enum RegisterParseError {
// system registers are not indexable in the reg file so they cannot be modified by instructions. InvalidIndex(u8),
0x0 => Register::Rg0,
0x1 => Register::Rg1,
0x2 => Register::Rg2,
0x3 => Register::Rg3,
0x4 => Register::Rg4,
0x5 => Register::Rg5,
0x6 => Register::Rg6,
0x7 => Register::Rg7,
0x8 => Register::Rg8,
0x9 => Register::Rg9,
0xA => Register::Rga,
0xB => Register::Rgb,
0xC => Register::Rgc,
0xD => Register::Rgd,
0xE => Register::Rge,
0xF => Register::Rgf,
0x10 => Register::Acc,
0x11 => Register::Spr,
0x12 => Register::Bpr,
0x13 => Register::Ret,
0x14 => Register::Idr,
0x15 => Register::Mmr,
0x16 => Register::Zero,
0x17 => Register::None,
_ => panic!("invalid register"),
} }
impl std::fmt::Display for RegisterParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidIndex(idx) => write!(f, "invalid index given ({idx})"),
}
}
}
impl std::error::Error for RegisterParseError {}
impl TryFrom<u8> for Register {
type Error = RegisterParseError;
fn try_from(idx: u8) -> Result<Self, Self::Error> {
if idx > 0x18 {
return Err(RegisterParseError::InvalidIndex(idx));
}
Ok(match idx {
// System registers are not indexable in the reg file so they cannot be modified by instructions.
0x0 => Self::Rg0,
0x1 => Self::Rg1,
0x2 => Self::Rg2,
0x3 => Self::Rg3,
0x4 => Self::Rg4,
0x5 => Self::Rg5,
0x6 => Self::Rg6,
0x7 => Self::Rg7,
0x8 => Self::Rg8,
0x9 => Self::Rg9,
0xA => Self::Rga,
0xB => Self::Rgb,
0xC => Self::Rgc,
0xD => Self::Rgd,
0xE => Self::Rge,
0xF => Self::Rgf,
0x10 => Self::Acc,
0x11 => Self::Spr,
0x12 => Self::Bpr,
0x13 => Self::Ret,
0x14 => Self::Idr,
0x15 => Self::Mmr,
0x16 => Self::Zero,
0x17 => Self::None,
_ => unreachable!("This is already checked for in top `if` branch."),
})
} }
} }
impl std::fmt::Display for Register { impl std::fmt::Display for Register {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Register::Rg0 => write!(f, "Rg0"), Self::Rg0 => write!(f, "Rg0"),
Register::Rg1 => write!(f, "Rg1"), Self::Rg1 => write!(f, "Rg1"),
Register::Rg2 => write!(f, "Rg2"), Self::Rg2 => write!(f, "Rg2"),
Register::Rg3 => write!(f, "Rg3"), Self::Rg3 => write!(f, "Rg3"),
Register::Rg4 => write!(f, "Rg4"), Self::Rg4 => write!(f, "Rg4"),
Register::Rg5 => write!(f, "Rg5"), Self::Rg5 => write!(f, "Rg5"),
Register::Rg6 => write!(f, "Rg6"), Self::Rg6 => write!(f, "Rg6"),
Register::Rg7 => write!(f, "Rg7"), Self::Rg7 => write!(f, "Rg7"),
Register::Rg8 => write!(f, "Rg8"), Self::Rg8 => write!(f, "Rg8"),
Register::Rg9 => write!(f, "Rg9"), Self::Rg9 => write!(f, "Rg9"),
Register::Rga => write!(f, "Rga"), Self::Rga => write!(f, "Rga"),
Register::Rgb => write!(f, "Rgb"), Self::Rgb => write!(f, "Rgb"),
Register::Rgc => write!(f, "Rgc"), Self::Rgc => write!(f, "Rgc"),
Register::Rgd => write!(f, "Rgd"), Self::Rgd => write!(f, "Rgd"),
Register::Rge => write!(f, "Rge"), Self::Rge => write!(f, "Rge"),
Register::Rgf => write!(f, "Rgf"), Self::Rgf => write!(f, "Rgf"),
Register::Acc => write!(f, "Acc"), Self::Acc => write!(f, "Acc"),
Register::Spr => write!(f, "Spr"), Self::Spr => write!(f, "Spr"),
Register::Bpr => write!(f, "Bpr"), Self::Bpr => write!(f, "Bpr"),
Register::Ret => write!(f, "Ret"), Self::Ret => write!(f, "Ret"),
Register::Idr => write!(f, "Idr"), Self::Idr => write!(f, "Idr"),
Register::Mmr => write!(f, "Mmr"), Self::Mmr => write!(f, "Mmr"),
Register::Zero => write!(f, "Zero"), Self::Zero => write!(f, "Zero"),
Register::None => write!(f, "None"), Self::None => write!(f, "None"),
Register::Mar => write!(f, "Mar"), Self::Mar => write!(f, "Mar"),
Register::Mdr => write!(f, "Mdr"), Self::Mdr => write!(f, "Mdr"),
Register::Sts => write!(f, "Sts"), Self::Sts => write!(f, "Sts"),
Register::Cir => write!(f, "Cir"), Self::Cir => write!(f, "Cir"),
Register::Pcx => write!(f, "Pcx"), Self::Pcx => write!(f, "Pcx"),
} }
} }
} }
@@ -189,63 +212,65 @@ pub enum Instruction {
} }
impl Instruction { impl Instruction {
#[must_use]
pub fn encode(&self) -> u32 { pub fn encode(&self) -> u32 {
todo!("imlement instruction encoding") todo!("imlement instruction encoding")
} }
pub fn decode(data: u32) -> Self { #[must_use]
pub const fn decode(_data: u32) -> Self {
// TODO: this needs to actually decode something // TODO: this needs to actually decode something
Instruction::Nop Self::Nop
} }
} }
impl std::fmt::Display for Instruction { impl std::fmt::Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Instruction::Nop => write!(f, "No Operation"), Self::Nop => write!(f, "No Operation"),
Instruction::Mov(a, b) => write!(f, "MOV {}, {}", a, b), Self::Mov(a, b) => write!(f, "MOV {a}, {b}"),
Instruction::MovSigned(a, b) => write!(f, "MOVS {}, {}", a, b), Self::MovSigned(a, b) => write!(f, "MOVS {a}, {b}"),
Instruction::LoadByte(a, b, c) => write!(f, "LDB {}, {}, {}", a, b, c), Self::LoadByte(a, b, c) => write!(f, "LDB {a}, {b}, {c}"),
Instruction::LoadByteSigned(a, b, c) => write!(f, "LDBS {}, {}, {}", a, b, c), Self::LoadByteSigned(a, b, c) => write!(f, "LDBS {a}, {b}, {c}"),
Instruction::LoadHalfword(a, b, c) => write!(f, "LDH {}, {}, {}", a, b, c), Self::LoadHalfword(a, b, c) => write!(f, "LDH {a}, {b}, {c}"),
Instruction::LoadHalfwordSigned(a, b, c) => write!(f, "LDHS {}, {}, {}", a, b, c), Self::LoadHalfwordSigned(a, b, c) => write!(f, "LDHS {a}, {b}, {c}"),
Instruction::LoadWord(a, b, c) => write!(f, "LDW {}, {}, {}", a, b, c), Self::LoadWord(a, b, c) => write!(f, "LDW {a}, {b}, {c}"),
Instruction::StoreByte(a, b, c) => write!(f, "STB {}, {}, {}", a, b, c), Self::StoreByte(a, b, c) => write!(f, "STB {a}, {b}, {c}"),
Instruction::StoreHalfword(a, b, c) => write!(f, "STH {}, {}, {}", a, b, c), Self::StoreHalfword(a, b, c) => write!(f, "STH {a}, {b}, {c}"),
Instruction::StoreWord(a, b, c) => write!(f, "STW {}, {}, {}", a, b, c), Self::StoreWord(a, b, c) => write!(f, "STW {a}, {b}, {c}"),
Instruction::LoadLowerImmediate(a, b) => write!(f, "LLI {}, {}", a, b), Self::LoadLowerImmediate(a, b) => write!(f, "LLI {a}, {b}"),
Instruction::LoadUpperImmediate(a, b) => write!(f, "LUI {}, {}", a, b), Self::LoadUpperImmediate(a, b) => write!(f, "LUI {a}, {b}"),
Instruction::Jump(a, b) => write!(f, "JMP {}, {}", a, b), Self::Jump(a, b) => write!(f, "JMP {a}, {b}"),
Instruction::JumpEq(a, b) => write!(f, "JEQ {}, {}", a, b), Self::JumpEq(a, b) => write!(f, "JEQ {a}, {b}"),
Instruction::JumpNeq(a, b) => write!(f, "JNEQ {}, {}", a, b), Self::JumpNeq(a, b) => write!(f, "JNEQ {a}, {b}"),
Instruction::JumpGt(a, b) => write!(f, "JGT {}, {}", a, b), Self::JumpGt(a, b) => write!(f, "JGT {a}, {b}"),
Instruction::JumpGe(a, b) => write!(f, "JGE {}, {}", a, b), Self::JumpGe(a, b) => write!(f, "JGE {a}, {b}"),
Instruction::JumpLt(a, b) => write!(f, "JLT {}, {}", a, b), Self::JumpLt(a, b) => write!(f, "JLT {a}, {b}"),
Instruction::JumpLe(a, b) => write!(f, "JLE {}, {}", a, b), Self::JumpLe(a, b) => write!(f, "JLE {a}, {b}"),
Instruction::Compare(a, b) => write!(f, "CMP {}, {}", a, b), Self::Compare(a, b) => write!(f, "CMP {a}, {b}"),
Instruction::Add(a, b, c) => write!(f, "ADD {}, {}, {}", a, b, c), Self::Add(a, b, c) => write!(f, "ADD {a}, {b}, {c}"),
Instruction::Sub(a, b, c) => write!(f, "SUB {}, {}, {}", a, b, c), Self::Sub(a, b, c) => write!(f, "SUB {a}, {b}, {c}"),
Instruction::Increment(a) => write!(f, "INC {}", a), Self::Increment(a) => write!(f, "INC {a}"),
Instruction::Decrement(a) => write!(f, "DEC {}", a), Self::Decrement(a) => write!(f, "DEC {a}"),
Instruction::ShiftLeft(a, b, c) => write!(f, "SHL {}, {}, {}", a, b, c), Self::ShiftLeft(a, b, c) => write!(f, "SHL {a}, {b}, {c}"),
Instruction::ShiftRight(a, b, c) => write!(f, "SHR {}, {}, {}", a, b, c), Self::ShiftRight(a, b, c) => write!(f, "SHR {a}, {b}, {c}"),
Instruction::And(a, b, c) => write!(f, "AND {}, {}, {}", a, b, c), Self::And(a, b, c) => write!(f, "AND {a}, {b}, {c}"),
Instruction::Or(a, b, c) => write!(f, "OR {}, {}, {}", a, b, c), Self::Or(a, b, c) => write!(f, "OR {a}, {b}, {c}"),
Instruction::Not(a, b) => write!(f, "NOT {}, {}", a, b), Self::Not(a, b) => write!(f, "NOT {a}, {b}"),
Instruction::Xor(a, b, c) => write!(f, "XOR {}, {}, {}", a, b, c), Self::Xor(a, b, c) => write!(f, "XOR {a}, {b}, {c}"),
Instruction::Nand(a, b, c) => write!(f, "NAND {}, {}, {}", a, b, c), Self::Nand(a, b, c) => write!(f, "NAND {a}, {b}, {c}"),
Instruction::Nor(a, b, c) => write!(f, "NOR {}, {}, {}", a, b, c), Self::Nor(a, b, c) => write!(f, "NOR {a}, {b}, {c}"),
Instruction::Xnor(a, b, c) => write!(f, "XNOR {}, {}, {}", a, b, c), Self::Xnor(a, b, c) => write!(f, "XNOR {a}, {b}, {c}"),
Instruction::Interrupt(a) => write!(f, "INT {}", a.as_u8()), Self::Interrupt(a) => write!(f, "INT {}", a.as_u8()),
Instruction::IntReturn => write!(f, "INTR"), Self::IntReturn => write!(f, "INTR"),
Instruction::Halt => write!(f, "HALT"), Self::Halt => write!(f, "HALT"),
} }
} }
} }
+31 -32
View File
@@ -16,17 +16,21 @@ use crate::{
}; };
pub fn run_emulator( pub fn run_emulator(
cmd_rx: Receiver<Command>, cmd_rx: &Receiver<Command>,
state_tx: Sender<State>, state_tx: &Sender<State>,
cpu: Arc<Mutex<Processor>>, cpu: &Arc<Mutex<Processor>>,
) { ) {
let mut running = Running::Paused; let mut running = Running::Paused;
let mut addr = 0u32; let mut addr = 0u32;
let mut size = 256; let size = 256;
// Send initial state // Send initial state.
let memory_view = cpu.lock().unwrap().memory.read_range(addr, size); let Ok(mut cpu_lock) = cpu.lock() else {
let initial_state = state(&cpu, &running, 0, memory_view); panic!("Failed to lock CPU.")
};
let memory_view = cpu_lock.memory.read_range(addr, size);
let initial_state = state(cpu, running, 0, memory_view);
let _ = state_tx.send(initial_state); let _ = state_tx.send(initial_state);
let mut instruction_count = 0; let mut instruction_count = 0;
@@ -57,38 +61,32 @@ pub fn run_emulator(
} }
Command::Reset => { Command::Reset => {
running = Running::Paused; running = Running::Paused;
if let Ok(mut cpu_lock) = cpu.lock() {
cpu_lock.reset(); cpu_lock.reset();
}
instruction_count = 0; instruction_count = 0;
println!("Emulator rebooted"); println!("Emulator rebooted");
} }
Command::Step => { Command::Step => {
running = Running::Paused; running = Running::Paused;
// Execute one cycle // Execute one cycle
if let Ok(mut cpu_lock) = cpu.lock() {
cpu_lock.cycle(); // or advance() - whatever your method is called cpu_lock.cycle(); // or advance() - whatever your method is called
}
instruction_count += 1; instruction_count += 1;
println!("Stepped one instruction"); println!("Stepped one instruction");
} }
Command::Read(new, size) => { Command::Read(new, _size) => {
addr = new as u32; addr = new;
println!("Memory view for address 0x{:08x}", addr); println!("Memory view for address 0x{addr:08x}");
} }
Command::Write(offset, data) => { Command::Write(offset, data) => {
if let Ok(mut cpu_lock) = cpu.lock() {
cpu_lock.memory.write_range(offset, data); cpu_lock.memory.write_range(offset, data);
}
println!("Program loaded"); println!("Program loaded");
} }
Command::Interrupt(interrupt) => { Command::Interrupt(_interrupt) => {
todo!("implement interrupts") todo!("implement interrupts")
} }
} }
let memory_view = cpu.lock().unwrap().memory.read_range(addr, size); let memory_view = cpu_lock.memory.read_range(addr, size);
let state = state(&cpu, &running, instruction_count, memory_view); let state = state(cpu, running, instruction_count, memory_view);
let _ = state_tx.send(state); let _ = state_tx.send(state);
} }
@@ -96,13 +94,14 @@ pub fn run_emulator(
if running == Running::Running { if running == Running::Running {
let mut update = false; let mut update = false;
// Execute one cycle // Execute one cycle
if let Ok(mut cpu_lock) = cpu.lock() {
cpu_lock.cycle(); cpu_lock.cycle();
if let Instruction::Halt = Instruction::decode(cpu_lock.get(Register::Cir)) { if matches!(
Instruction::decode(cpu_lock.get(Register::Cir)),
Instruction::Halt
) {
running = Running::Halted; running = Running::Halted;
update = true; update = true;
} }
}
instruction_count += 1; instruction_count += 1;
@@ -112,8 +111,8 @@ pub fn run_emulator(
} }
if update { if update {
let memory_view = cpu.lock().unwrap().memory.read_range(addr, size); let memory_view = cpu_lock.memory.read_range(addr, size);
let state = state(&cpu, &running, instruction_count, memory_view); let state = state(cpu, running, instruction_count, memory_view);
let _ = state_tx.send(state); let _ = state_tx.send(state);
} }
} else { } else {
@@ -124,22 +123,22 @@ pub fn run_emulator(
fn state( fn state(
cpu: &Arc<Mutex<Processor>>, cpu: &Arc<Mutex<Processor>>,
running: &Running, running: Running,
instruction_count: usize, instruction_count: usize,
memory_view: Vec<u8>, memory_view: Vec<u8>,
) -> State { ) -> State {
if let Ok(mut cpu_lock) = cpu.lock() { let Ok(mut cpu_lock) = cpu.lock() else {
panic!("Could not lock CPU.")
};
State { State {
// TODO: Replace with actual register access from your CPU // TODO: Replace with actual register access from your CPU.
reg_file: cpu_lock.registers.clone(), reg_file: cpu_lock.registers,
running: running.clone(), running,
instructions: instruction_count, instructions: instruction_count,
stack_view: cpu_lock.get_stack(32), stack_view: cpu_lock.get_stack(32),
memory_view, memory_view,
display_view: cpu_lock.display(), display_view: cpu_lock.display(),
error: None, error: None,
} }
} else {
panic!("Failed to lock CPU");
}
} }
+34 -21
View File
@@ -14,35 +14,48 @@ pub struct MainStore {
pub data: HashMap<u32, Block>, pub data: HashMap<u32, Block>,
} }
struct Block { pub struct Block {
data: [u8; 256], data: [u8; 256],
} }
impl Default for MainStore {
fn default() -> Self {
Self::new()
}
}
impl MainStore { impl MainStore {
pub fn new() -> MainStore { #[must_use]
MainStore { pub fn new() -> Self {
Self {
data: HashMap::new(), data: HashMap::new(),
} }
} }
fn segment_addr(addr: u32) -> (u32, u8) { const fn segment_addr(addr: u32) -> (u32, u8) {
(addr / 256, (addr % 256) as u8) (addr / 256, (addr % 256) as u8)
} }
fn mut_block<'a>(&'a mut self, addr: u32) -> &'a mut Block { fn mut_block(&mut self, addr: u32) -> &mut Block {
if !self.data.contains_key(&addr) { self.data
self.data.insert(addr, Block { data: [0; 256] }); .entry(addr)
} .or_insert_with(|| Block { data: [0; 256] });
self.data.get_mut(&addr).unwrap() self.data.get_mut(&addr).map_or_else(
|| panic!("Could not fetch block with address {addr:x?}"),
|block| block,
)
} }
fn block(&mut self, addr: u32) -> &Block { fn block(&mut self, addr: u32) -> &Block {
if !self.data.contains_key(&addr) { self.data
self.data.insert(addr, Block { data: [0; 256] }); .entry(addr)
} .or_insert_with(|| Block { data: [0; 256] });
self.data.get(&addr).unwrap() self.data.get(&addr).map_or_else(
|| panic!("Could not fetch block with address {addr:x?}"),
|block| block,
)
} }
} }
@@ -52,16 +65,16 @@ impl MemoryUnit for MainStore {
} }
fn read_byte(&mut self, addr: u32) -> u8 { fn read_byte(&mut self, addr: u32) -> u8 {
let (block_addr, offset) = MainStore::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.data[offset as usize]
} }
fn read_word(&mut self, addr: u32) -> u32 { fn read_word(&mut self, addr: u32) -> u32 {
let (block_addr, offset) = MainStore::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);
let mut bytes = [0; 4]; let mut bytes = [0; 4];
bytes[0] = block.data[(offset + 0) as usize]; bytes[0] = block.data[offset as usize];
bytes[1] = block.data[(offset + 1) as usize]; bytes[1] = block.data[(offset + 1) as usize];
bytes[2] = block.data[(offset + 2) as usize]; bytes[2] = block.data[(offset + 2) as usize];
bytes[3] = block.data[(offset + 3) as usize]; bytes[3] = block.data[(offset + 3) as usize];
@@ -77,21 +90,21 @@ 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) = MainStore::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.data[offset as usize] = value;
} }
fn write_word(&mut self, addr: u32, value: u32) { fn write_word(&mut self, addr: u32, value: u32) {
let (block_addr, offset) = MainStore::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 + 0) as usize] = (value >> 24) as u8; block.data[offset as usize] = (value >> 24) as u8;
block.data[(offset + 1) as usize] = (value >> 16) as u8; block.data[(offset + 1) as usize] = (value >> 16) as u8;
block.data[(offset + 2) as usize] = (value >> 8) as u8; block.data[(offset + 2) as usize] = (value >> 8) as u8;
block.data[(offset + 3) as usize] = value as u8; block.data[(offset + 3) as usize] = value as u8;
} }
fn write_range(&mut self, addr: u32, value: Vec<u8>) { fn write_range(&mut self, _addr: u32, _value: Vec<u8>) {
todo!("we might need this for DMA"); todo!("We might need this for DMA.");
} }
} }
+6 -4
View File
@@ -1,6 +1,6 @@
use crate::common::instructions::{Address, Interrupt, Register}; use crate::common::instructions::{Address, Interrupt, Register};
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum Running { pub enum Running {
Running, Running,
Paused, Paused,
@@ -56,6 +56,7 @@ pub struct RegFile {
} }
impl RegFile { impl RegFile {
#[must_use]
pub fn all(&self) -> Vec<(&str, u32)> { pub fn all(&self) -> Vec<(&str, u32)> {
vec![ vec![
("Rg0", self.rg0), ("Rg0", self.rg0),
@@ -88,7 +89,7 @@ impl RegFile {
] ]
} }
pub fn reset(&mut self) { pub const fn reset(&mut self) {
self.rg0 = 0; self.rg0 = 0;
self.rg1 = 0; self.rg1 = 0;
self.rg2 = 0; self.rg2 = 0;
@@ -147,10 +148,11 @@ impl RegFile {
Register::Sts => &mut self.sts, Register::Sts => &mut self.sts,
Register::Cir => &mut self.cir, Register::Cir => &mut self.cir,
Register::Pcx => &mut self.pcx, Register::Pcx => &mut self.pcx,
_ => panic!("invalid register"), _ => panic!("Invalid register."),
} }
} }
#[must_use]
pub fn get(&self, reg: Register) -> u32 { pub fn get(&self, reg: Register) -> u32 {
match reg { match reg {
Register::Rg0 => self.rg0, Register::Rg0 => self.rg0,
@@ -181,7 +183,7 @@ impl RegFile {
Register::Cir => self.cir, Register::Cir => self.cir,
Register::Pcx => self.pcx, Register::Pcx => self.pcx,
Register::Zero => 0, Register::Zero => 0,
_ => panic!("invalid register"), _ => panic!("Invalid register."),
} }
} }
} }
+60 -56
View File
@@ -1,10 +1,7 @@
use std::{ use std::cmp::{max, min};
cmp::{max, min},
sync::Arc,
};
use crate::{ use crate::{
common::instructions::{Address, Instruction, Interrupt, Register}, common::instructions::{Instruction, Interrupt, Register},
emulator::system::{memory::MemoryUnit, model::RegFile}, emulator::system::{memory::MemoryUnit, model::RegFile},
}; };
@@ -15,10 +12,12 @@ pub struct Processor {
// pub io_devices: Vec<Arc<dyn IODevice>>, // pub io_devices: Vec<Arc<dyn IODevice>>,
} }
#[allow(clippy::needless_pass_by_ref_mut)]
impl Processor { impl Processor {
// io_devices: Vec<Arc<dyn IODevice>> // io_devices: Vec<Arc<dyn IODevice>>
#[must_use]
pub fn new(memory: Box<dyn MemoryUnit>) -> Self { pub fn new(memory: Box<dyn MemoryUnit>) -> Self {
Processor { Self {
// io_devices, // io_devices,
memory, memory,
registers: RegFile::default(), registers: RegFile::default(),
@@ -45,7 +44,7 @@ impl Processor {
let val = self.memory.read_word(addr); let val = self.memory.read_word(addr);
// set CIR to the value of RAM[MAR] // set CIR to the value of RAM[MAR]
*self.reg(Register::Mar) = val as u32; *self.reg(Register::Mar) = val;
// decode and execute the instruction // decode and execute the instruction
let instruction = Instruction::decode(val); let instruction = Instruction::decode(val);
@@ -57,6 +56,7 @@ impl Processor {
self.get(Register::Pcx) self.get(Register::Pcx)
} }
#[must_use]
pub fn get(&self, reg: Register) -> u32 { pub fn get(&self, reg: Register) -> u32 {
self.registers.get(reg) self.registers.get(reg)
} }
@@ -108,10 +108,10 @@ impl Processor {
} }
fn jump(&mut self, reg: Register, offset: u16) { fn jump(&mut self, reg: Register, offset: u16) {
*self.reg(Register::Pcx) = self.get(reg) + offset as u32; *self.reg(Register::Pcx) = self.get(reg) + u32::from(offset);
} }
fn begin_interrupt(&mut self, int: Interrupt) { fn begin_interrupt(&mut self, _int: Interrupt) {
// first we get the address of the interrupt descriptor table. // first we get the address of the interrupt descriptor table.
todo!(); todo!();
} }
@@ -132,6 +132,7 @@ impl Processor {
} }
#[derive(Debug)] #[derive(Debug)]
#[expect(dead_code)]
enum Flag { enum Flag {
Equal = 0, Equal = 0,
GreaterThan = 1, GreaterThan = 1,
@@ -149,6 +150,7 @@ trait Executable {
} }
impl Executable for Instruction { impl Executable for Instruction {
#[allow(clippy::too_many_lines)]
fn execute(self, cpu: &mut Processor) { fn execute(self, cpu: &mut Processor) {
match self { match self {
// No operation - a blank line. // No operation - a blank line.
@@ -166,36 +168,37 @@ impl Executable for Instruction {
// Loads a byte from memory address (base + offset) into DestReg. The effective address must be byte-aligned. // Loads a byte from memory address (base + offset) into DestReg. The effective address must be byte-aligned.
Self::LoadByte(base, offset, dest) => { Self::LoadByte(base, offset, dest) => {
*cpu.reg(dest) = cpu.memory.read_byte(cpu.get(base) + offset as u32) as u32; *cpu.reg(dest) = u32::from(cpu.memory.read_byte(cpu.get(base) + u32::from(offset)));
} }
// Loads a sign-extended byte from memory address (base + offset) into DestReg. The effective address must be byte-aligned. // Loads a sign-extended byte from memory address (base + offset) into DestReg. The effective address must be byte-aligned.
Self::LoadByteSigned(base, offset, dest) => { Self::LoadByteSigned(base, offset, dest) => {
*cpu.reg(dest) = *cpu.reg(dest) = sign_extend(u32::from(
sign_extend(cpu.memory.read_byte(cpu.get(base) + offset as u32) as u32); cpu.memory.read_byte(cpu.get(base) + u32::from(offset)),
));
} }
// Loads a half-word from memory address (base + offset) into DestReg. The effective address must be 2-byte-aligned. // Loads a half-word from memory address (base + offset) into DestReg. The effective address must be 2-byte-aligned.
Self::LoadHalfword(base, offset, dest) => { Self::LoadHalfword(base, offset, dest) => {
// we read an entire word, then right shift so we only get the first half of the word // we read an entire word, then right shift so we only get the first half of the word
*cpu.reg(dest) = cpu.memory.read_word(cpu.get(base) + offset as u32) >> 16; *cpu.reg(dest) = cpu.memory.read_word(cpu.get(base) + u32::from(offset)) >> 16;
} }
// Loads a sign-extended half-word from memory address (base + offset) into DestReg. The effective address must be 2-byte-aligned. // Loads a sign-extended half-word from memory address (base + offset) into DestReg. The effective address must be 2-byte-aligned.
Self::LoadHalfwordSigned(base, offset, dest) => { Self::LoadHalfwordSigned(base, offset, dest) => {
*cpu.reg(dest) = *cpu.reg(dest) =
sign_extend(cpu.memory.read_word(cpu.get(base) + offset as u32) >> 16); sign_extend(cpu.memory.read_word(cpu.get(base) + u32::from(offset)) >> 16);
} }
// Loads a word from memory address (base + offset) into DestReg. The effective address must be 4-byte-aligned. // Loads a word from memory address (base + offset) into DestReg. The effective address must be 4-byte-aligned.
Self::LoadWord(base, offset, dest) => { Self::LoadWord(base, offset, dest) => {
*cpu.reg(dest) = cpu.memory.read_word(cpu.get(base) + offset as u32); *cpu.reg(dest) = cpu.memory.read_word(cpu.get(base) + u32::from(offset));
} }
// Stores a byte from SrcReg in memory address (base + offset) The effective address must be byte-aligned. // Stores a byte from SrcReg in memory address (base + offset) The effective address must be byte-aligned.
Self::StoreByte(src, offset, base) => { Self::StoreByte(src, offset, base) => {
cpu.memory cpu.memory
.write_byte(cpu.get(base) + offset as u32, cpu.get(src) as u8); .write_byte(cpu.get(base) + u32::from(offset), cpu.get(src) as u8);
} }
// Stores a half-word from SrcReg in memory address (base + offset) The effective address must be 2-byte-aligned. // Stores a half-word from SrcReg in memory address (base + offset) The effective address must be 2-byte-aligned.
@@ -203,25 +206,25 @@ impl Executable for Instruction {
// split the value into bytes and then write two bytes // split the value into bytes and then write two bytes
let bytes = (cpu.get(src) as u16).to_be_bytes(); let bytes = (cpu.get(src) as u16).to_be_bytes();
cpu.memory cpu.memory
.write_byte(cpu.get(base) + offset as u32, bytes[0]); .write_byte(cpu.get(base) + u32::from(offset), bytes[0]);
cpu.memory cpu.memory
.write_byte(cpu.get(base) + offset as u32 + 1, bytes[1]); .write_byte(cpu.get(base) + u32::from(offset) + 1, bytes[1]);
} }
// Stores a word from SrcReg in memory address (base + offset) The effective address must be 4-byte-aligned. // Stores a word from SrcReg in memory address (base + offset) The effective address must be 4-byte-aligned.
Self::StoreWord(src, offset, base) => { Self::StoreWord(src, offset, base) => {
cpu.memory cpu.memory
.write_word(cpu.get(base) + offset as u32, cpu.get(src)); .write_word(cpu.get(base) + u32::from(offset), cpu.get(src));
} }
// Loads a 16-bit literal value into reg, setting the bottom 16 bits of the word. To populate the upper 16 bits, see LUI. // Loads a 16-bit literal value into reg, setting the bottom 16 bits of the word. To populate the upper 16 bits, see LUI.
Self::LoadLowerImmediate(reg, imm) => { Self::LoadLowerImmediate(reg, imm) => {
*cpu.reg(reg) = imm as u32; *cpu.reg(reg) = u32::from(imm);
} }
// Loads a 16-bit literal value into reg, setting the top 16 bits of the word. To populate the lower 16 bits, see LLI. // Loads a 16-bit literal value into reg, setting the top 16 bits of the word. To populate the lower 16 bits, see LLI.
Self::LoadUpperImmediate(reg, imm) => { Self::LoadUpperImmediate(reg, imm) => {
*cpu.reg(reg) = (cpu.get(reg) & 0x0000_FFFF) | (imm as u32) << 16; *cpu.reg(reg) = (cpu.get(reg) & 0x0000_FFFF) | u32::from(imm) << 16;
} }
// Unconditionally jumps to the calculated address or direct address // Unconditionally jumps to the calculated address or direct address
@@ -230,42 +233,42 @@ impl Executable for Instruction {
// Jumps to the calculated address or direct address if equal flag set. // Jumps to the calculated address or direct address if equal flag set.
Self::JumpEq(reg, offset) => { Self::JumpEq(reg, offset) => {
if cpu.get_flag(Flag::Equal) { if cpu.get_flag(Flag::Equal) {
cpu.jump(reg, offset) cpu.jump(reg, offset);
} }
} }
// Jumps to the calculated address or direct address if equal flag not set. // Jumps to the calculated address or direct address if equal flag not set.
Self::JumpNeq(reg, offset) => { Self::JumpNeq(reg, offset) => {
if !cpu.get_flag(Flag::Equal) { if !cpu.get_flag(Flag::Equal) {
cpu.jump(reg, offset) cpu.jump(reg, offset);
} }
} }
// Jumps to the calculated address or direct address if greater than flag set. // Jumps to the calculated address or direct address if greater than flag set.
Self::JumpGt(reg, offset) => { Self::JumpGt(reg, offset) => {
if cpu.get_flag(Flag::GreaterThan) { if cpu.get_flag(Flag::GreaterThan) {
cpu.jump(reg, offset) cpu.jump(reg, offset);
} }
} }
// Jumps to the calculated address or direct address if greater than flag or equal flag set. // Jumps to the calculated address or direct address if greater than flag or equal flag set.
Self::JumpGe(reg, offset) => { Self::JumpGe(reg, offset) => {
if cpu.get_flag(Flag::GreaterThan) || cpu.get_flag(Flag::Equal) { if cpu.get_flag(Flag::GreaterThan) || cpu.get_flag(Flag::Equal) {
cpu.jump(reg, offset) cpu.jump(reg, offset);
} }
} }
// Jumps to the calculated address or direct address if less than flag set. // Jumps to the calculated address or direct address if less than flag set.
Self::JumpLt(reg, offset) => { Self::JumpLt(reg, offset) => {
if cpu.get_flag(Flag::LessThan) { if cpu.get_flag(Flag::LessThan) {
cpu.jump(reg, offset) cpu.jump(reg, offset);
} }
} }
// Jumps to the calculated address or direct address if less than flag or equal flag set. // Jumps to the calculated address or direct address if less than flag or equal flag set.
Self::JumpLe(reg, offset) => { Self::JumpLe(reg, offset) => {
if cpu.get_flag(Flag::LessThan) || cpu.get_flag(Flag::Equal) { if cpu.get_flag(Flag::LessThan) || cpu.get_flag(Flag::Equal) {
cpu.jump(reg, offset) cpu.jump(reg, offset);
} }
} }
@@ -294,39 +297,39 @@ impl Executable for Instruction {
} }
// Adds the value of Src2 to Src1 and writes the result to Dest // Adds the value of Src2 to Src1 and writes the result to Dest
Self::Add(srcx, srcy, dest) => { Self::Add(src1, src2, dest) => {
*cpu.reg(dest) = add(cpu.get(srcx), cpu.get(srcy)); *cpu.reg(dest) = add(cpu.get(src1), cpu.get(src2));
} }
// Subtracts the value of Src2 from Src1 and writes the result to Dest // Subtracts the value of Src2 from Src1 and writes the result to Dest
Self::Sub(srcx, srcy, dest) => { Self::Sub(src1, src2, dest) => {
*cpu.reg(dest) = sub(cpu.get(srcx), cpu.get(srcy)); *cpu.reg(dest) = sub(cpu.get(src1), cpu.get(src2));
} }
// Performs bitwise AND on Src1 and Src2 storing the result in Dest // Performs bitwise AND on Src1 and Src2 storing the result in Dest
Self::And(srcx, srcy, dest) => *cpu.reg(dest) = and(cpu.get(srcx), cpu.get(srcy)), Self::And(src1, src2, dest) => *cpu.reg(dest) = and(cpu.get(src1), cpu.get(src2)),
// Performs bitwise OR on Src1 and Src2 storing the result in Dest // Performs bitwise OR on Src1 and Src2 storing the result in Dest
Self::Or(srcx, srcy, dest) => *cpu.reg(dest) = or(cpu.get(srcx), cpu.get(srcy)), Self::Or(src1, src2, dest) => *cpu.reg(dest) = or(cpu.get(src1), cpu.get(src2)),
// Performs bitwise NOT on Src storing the result in Dest // Performs bitwise NOT on Src storing the result in Dest
Self::Not(src, dest) => *cpu.reg(dest) = not(cpu.get(src)), Self::Not(src, dest) => *cpu.reg(dest) = not(cpu.get(src)),
// Performs bitwise XOR on Src1 and Src2 storing the result in Dest // Performs bitwise XOR on Src1 and Src2 storing the result in Dest
Self::Xor(srcx, srcy, dest) => *cpu.reg(dest) = xor(cpu.get(srcx), cpu.get(srcy)), Self::Xor(src1, src2, dest) => *cpu.reg(dest) = xor(cpu.get(src1), cpu.get(src2)),
// Performs bitwise NAND on Src1 and Src2 storing the result in Dest // Performs bitwise NAND on Src1 and Src2 storing the result in Dest
Self::Nand(srcx, srcy, dest) => *cpu.reg(dest) = nand(cpu.get(srcx), cpu.get(srcy)), Self::Nand(src1, src2, dest) => *cpu.reg(dest) = nand(cpu.get(src1), cpu.get(src2)),
// Performs bitwise NOR on Src1 and Src2 storing the result in Dest // Performs bitwise NOR on Src1 and Src2 storing the result in Dest
Self::Nor(srcx, srcy, dest) => *cpu.reg(dest) = nor(cpu.get(srcx), cpu.get(srcy)), Self::Nor(src1, src2, dest) => *cpu.reg(dest) = nor(cpu.get(src1), cpu.get(src2)),
// Performs bitwise XNOR on Src1 and Src2 storing the result in Dest // Performs bitwise XNOR on Src1 and Src2 storing the result in Dest
Self::Xnor(srcx, srcy, dest) => *cpu.reg(dest) = xnor(cpu.get(srcx), cpu.get(srcy)), Self::Xnor(src1, src2, dest) => *cpu.reg(dest) = xnor(cpu.get(src1), cpu.get(src2)),
// Compares the value of Reg1 to the value in Reg2. The results of the comparisons are set in the Status register. // Compares the value of Reg1 to the value in Reg2. The results of the comparisons are set in the Status register.
Self::Compare(srcx, srcy) => { Self::Compare(src1, src2) => {
cpu.cmp(cpu.get(srcx), cpu.get(srcy)); cpu.cmp(cpu.get(src1), cpu.get(src2));
} }
/* /*
@@ -353,63 +356,64 @@ impl Executable for Instruction {
} }
// mathematical and logical functions & other operations // mathematical and logical functions & other operations
fn add(a: u32, b: u32) -> u32 { const fn add(a: u32, b: u32) -> u32 {
a + b a + b
} }
fn sub(a: u32, b: u32) -> u32 { const fn sub(a: u32, b: u32) -> u32 {
a - b a - b
} }
fn and(a: u32, b: u32) -> u32 { const fn and(a: u32, b: u32) -> u32 {
a & b a & b
} }
fn inc(a: u32) -> u32 { const fn inc(a: u32) -> u32 {
a + 1 a + 1
} }
fn dec(a: u32) -> u32 { const fn dec(a: u32) -> u32 {
a - 1 a - 1
} }
fn shl(a: u32, amount: u8) -> u32 { const fn shl(a: u32, amount: u8) -> u32 {
a << amount a << amount
} }
fn shr(a: u32, amount: u8) -> u32 { const fn shr(a: u32, amount: u8) -> u32 {
a >> amount a >> amount
} }
fn or(a: u32, b: u32) -> u32 { const fn or(a: u32, b: u32) -> u32 {
a | b a | b
} }
fn not(a: u32) -> u32 { const fn not(a: u32) -> u32 {
!a !a
} }
fn xor(a: u32, b: u32) -> u32 { const fn xor(a: u32, b: u32) -> u32 {
a ^ b a ^ b
} }
fn nand(a: u32, b: u32) -> u32 { const fn nand(a: u32, b: u32) -> u32 {
!(a & b) !(a & b)
} }
fn nor(a: u32, b: u32) -> u32 { const fn nor(a: u32, b: u32) -> u32 {
!(a | b) !(a | b)
} }
fn xnor(a: u32, b: u32) -> u32 { const fn xnor(a: u32, b: u32) -> u32 {
!(a ^ b) !(a ^ b)
} }
fn sign_extend(val: u32) -> u32 { const fn sign_extend(val: u32) -> u32 {
let (mask, sign_bit): (u32, u8) = match val { let (mask, sign_bit): (u32, u8) = match val {
0..=0xFF => (0xFFFFFF00, 7), 0..=0xFF => (0xFFFF_FF00, 7),
0..=0xFFFF => (0xFFFF0000, 15), // I presume this was the intended behaviour?
_ => (0x00000000, 31), 0x100..=0xFFFF => (0xFFFF_0000, 15),
_ => (0x0000_0000, 31),
}; };
if val & (1 << sign_bit) != 0 { if val & (1 << sign_bit) != 0 {
+6 -5
View File
@@ -14,7 +14,8 @@ pub struct ControlPanel {
} }
impl ControlPanel { impl ControlPanel {
pub fn new(sender: Sender<Command>) -> Self { #[must_use]
pub const fn new(sender: Sender<Command>) -> Self {
Self { Self {
visible: false, visible: false,
sender, sender,
@@ -48,11 +49,11 @@ impl Component for ControlPanel {
{ {
if state.running == Running::Running { if state.running == Running::Running {
self.sender.send(Command::Stop).unwrap_or_else(|_| { self.sender.send(Command::Stop).unwrap_or_else(|_| {
state.error = Some("Failed to send command".to_string()) state.error = Some("Failed to send command".to_string());
}); });
} else { } else {
self.sender.send(Command::Start).unwrap_or_else(|_| { self.sender.send(Command::Start).unwrap_or_else(|_| {
state.error = Some("Failed to send command".to_string()) state.error = Some("Failed to send command".to_string());
}); });
} }
} }
@@ -88,7 +89,7 @@ impl Component for ControlPanel {
} }
} }
fn render_register_table(state: &mut State, ui: &mut egui::Ui, _ctx: &egui::Context) { fn render_register_table(state: &State, ui: &mut egui::Ui, _ctx: &egui::Context) {
// Left column - Registers // Left column - Registers
ui.vertical(|ui| { ui.vertical(|ui| {
ui.heading("Registers"); ui.heading("Registers");
@@ -114,7 +115,7 @@ fn render_register_table(state: &mut State, ui: &mut egui::Ui, _ctx: &egui::Cont
// iterate over state.reg_file.iter() in chunks of 4 registers // iterate over state.reg_file.iter() in chunks of 4 registers
for chunk in state.reg_file.all().chunks(4) { for chunk in state.reg_file.all().chunks(4) {
for reg in chunk { for reg in chunk {
ui.label(format!("{}", reg.0)); ui.label(reg.0.to_string());
ui.label(format!("0x{:08X} ({})", reg.1, reg.1,)); ui.label(format!("0x{:08X} ({})", reg.1, reg.1,));
} }
ui.end_row(); ui.end_row();
+18 -23
View File
@@ -1,7 +1,4 @@
use crate::{ use crate::emulator::system::model::{Command, Running, State};
common::instructions::{Address, Interrupt},
emulator::system::model::{Command, Running, State},
};
use std::sync::mpsc::{Receiver, Sender}; use std::sync::mpsc::{Receiver, Sender};
pub trait Component { pub trait Component {
@@ -11,7 +8,7 @@ pub trait Component {
fn category(&self) -> Category; fn category(&self) -> Category;
} }
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Category { pub enum Category {
Control, Control,
Memory, Memory,
@@ -20,22 +17,19 @@ pub enum Category {
} }
impl Category { impl Category {
pub fn as_str(&self) -> &'static str { #[must_use]
pub const fn as_str(&self) -> &'static str {
match self { match self {
Category::Control => "Control Systems", Self::Control => "Control Systems",
Category::Memory => "Memory Systems", Self::Memory => "Memory Systems",
Category::Io => "I/O Systems", Self::Io => "I/O Systems",
Category::Programming => "Programming", Self::Programming => "Programming",
} }
} }
pub fn list() -> Vec<Category> { #[must_use]
vec![ pub fn list() -> Vec<Self> {
Category::Control, vec![Self::Control, Self::Memory, Self::Io, Self::Programming]
Category::Memory,
Category::Io,
Category::Programming,
]
} }
} }
@@ -47,6 +41,7 @@ pub struct EmulatorUI {
} }
impl EmulatorUI { impl EmulatorUI {
#[must_use]
pub fn new(sender: Sender<Command>, receiver: Receiver<State>) -> Self { pub fn new(sender: Sender<Command>, receiver: Receiver<State>) -> Self {
Self { Self {
sender, sender,
@@ -68,10 +63,10 @@ impl EmulatorUI {
} }
impl eframe::App for EmulatorUI { impl eframe::App for EmulatorUI {
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
self.update_state(); self.update_state();
if let Running::Running = self.state.running { if self.state.running == Running::Running {
ctx.request_repaint(); ctx.request_repaint();
} }
@@ -88,7 +83,7 @@ impl eframe::App for EmulatorUI {
}); });
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| { ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |_ui| {
egui::Window::new("Main Menu") egui::Window::new("Main Menu")
.resizable(false) .resizable(false)
.default_width(300.0) .default_width(300.0)
@@ -96,9 +91,9 @@ impl eframe::App for EmulatorUI {
super::menu::render_menu(self, ui, ctx); super::menu::render_menu(self, ui, ctx);
}); });
for c in self.components.iter_mut() { for c in &mut self.components {
let mut visible = *c.visible(); let mut visible = *c.visible();
if visible == true { if visible {
egui::Window::new(c.name()) egui::Window::new(c.name())
.open(&mut visible) .open(&mut visible)
.show(ctx, |ui| { .show(ctx, |ui| {
@@ -110,7 +105,7 @@ impl eframe::App for EmulatorUI {
}); });
}); });
egui::TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| { egui::TopBottomPanel::bottom("bottom_panel").show(ctx, |_ui| {
// interface::bottompanel::render_bottom_panel(self, ui, ctx); // interface::bottompanel::render_bottom_panel(self, ui, ctx);
}); });
} }
+25 -20
View File
@@ -14,7 +14,8 @@ pub struct MemoryInspector {
} }
impl MemoryInspector { impl MemoryInspector {
pub fn new(sender: Sender<Command>) -> Self { #[must_use]
pub const fn new(sender: Sender<Command>) -> Self {
Self { Self {
view_size: 256, view_size: 256,
view_addr: 0, view_addr: 0,
@@ -66,9 +67,12 @@ impl Component for MemoryInspector {
if search_clicked || enter_pressed { if search_clicked || enter_pressed {
if let Ok(new) = parse_address(&self.addr_input) { if let Ok(new) = parse_address(&self.addr_input) {
self.view_addr = new; self.view_addr = new;
self.sender
.send(Command::Read(new, self.view_size)) if let Err(why) = self.sender.send(Command::Read(new, self.view_size)) {
.unwrap(); panic!(
"Error sending message across threads -- cannot be recovered: {why}"
)
}
} else { } else {
state.error = Some("Invalid address".to_string()); state.error = Some("Invalid address".to_string());
} }
@@ -79,7 +83,7 @@ impl Component for MemoryInspector {
// Show input error if any // Show input error if any
if let Some(error) = &state.error { if let Some(error) = &state.error {
ui.colored_label(egui::Color32::RED, format!("Error: {}", error)); ui.colored_label(egui::Color32::RED, format!("Error: {error}"));
} }
ui.add_space(10.0); ui.add_space(10.0);
@@ -98,7 +102,7 @@ impl Component for MemoryInspector {
ui.strong("Address"); ui.strong("Address");
for i in 0..4 { for i in 0..4 {
ui.strong(format!("{:X}", i)); ui.strong(format!("{i:X}"));
} }
ui.strong("Decimal"); ui.strong("Decimal");
@@ -107,11 +111,11 @@ impl Component for MemoryInspector {
ui.end_row(); ui.end_row();
// Memory data (8 bytes per row) // Memory data (8 bytes per row)
for (row, chunk) in state.memory_view.chunks(4).enumerate() { for (row, chunk) in (0u32..).zip(state.memory_view.chunks(4)) {
let row_address = self.view_addr + (row * 4) as u32; let row_address = self.view_addr + (row * 4);
ui.monospace(format!("0x{:08X} ({})", row_address, row_address)); ui.monospace(format!("0x{row_address:08X} ({row_address})"));
for &byte in chunk { for &byte in chunk {
ui.monospace(format!("{:02X}", byte)); ui.monospace(format!("{byte:02X}"));
} }
// Fill remaining columns if last row is incomplete // Fill remaining columns if last row is incomplete
@@ -120,10 +124,11 @@ impl Component for MemoryInspector {
} }
// combine all 4 bytes in the chunk into a u32 // combine all 4 bytes in the chunk into a u32
let combined = let combined = chunk
chunk.iter().fold(0u32, |acc, &byte| acc << 8 | byte as u32); .iter()
.fold(0u32, |acc, &byte| acc << 8 | u32::from(byte));
ui.monospace(format!("{}", combined)); ui.monospace(format!("{combined}"));
// ui.monospace(format!("{:?}", Instruction::decode(combined))); // ui.monospace(format!("{:?}", Instruction::decode(combined)));
ui.monospace("TODO! instruction"); ui.monospace("TODO! instruction");
@@ -136,17 +141,17 @@ impl Component for MemoryInspector {
} }
fn parse_address(address: &str) -> Result<u32, ParseIntError> { fn parse_address(address: &str) -> Result<u32, ParseIntError> {
if address.starts_with("0x") { if let Some(hex_part) = address.strip_prefix("0x") {
return u32::from_str_radix(&address[2..], 16); return u32::from_str_radix(hex_part, 16);
} }
if address.starts_with("0b") { if let Some(bin_part) = address.strip_prefix("0b") {
return u32::from_str_radix(&address[2..], 2); return u32::from_str_radix(bin_part, 2);
} }
if address.starts_with("0o") { if let Some(oct_part) = address.strip_prefix("0o") {
return u32::from_str_radix(&address[1..], 8); return u32::from_str_radix(oct_part, 8);
} }
u32::from_str_radix(address, 10) address.parse::<u32>()
} }
+1 -1
View File
@@ -13,7 +13,7 @@ pub fn render_menu(state: &mut EmulatorUI, ui: &mut egui::Ui, _ctx: &egui::Conte
ui.heading(cat.as_str()); ui.heading(cat.as_str());
ui.add_space(10.0); ui.add_space(10.0);
for comp in state.components.iter_mut() { for comp in &mut state.components {
let name = comp.name(); let name = comp.name();
if comp.category() == cat { if comp.category() == cat {
ui.toggle_value(comp.visible(), name); ui.toggle_value(comp.visible(), name);
+13 -9
View File
@@ -1,17 +1,21 @@
use crate::{ use crate::{
common::instructions::Register, common::instructions::Register,
emulator::{ emulator::{system::model::State, ui::interface::Component},
system::model::State,
ui::interface::{Component, EmulatorUI},
},
}; };
pub struct StackInspector { pub struct StackInspector {
visible: bool, visible: bool,
} }
impl Default for StackInspector {
fn default() -> Self {
Self::new()
}
}
impl StackInspector { impl StackInspector {
pub fn new() -> Self { #[must_use]
pub const fn new() -> Self {
Self { visible: false } Self { visible: false }
} }
} }
@@ -29,7 +33,7 @@ impl Component for StackInspector {
super::interface::Category::Memory super::interface::Category::Memory
} }
fn render(&mut self, state: &mut State, ui: &mut egui::Ui, ctx: &egui::Context) { fn render(&mut self, state: &mut State, ui: &mut egui::Ui, _ctx: &egui::Context) {
ui.vertical(|ui| { ui.vertical(|ui| {
ui.heading("Stack Inspector"); ui.heading("Stack Inspector");
egui::ScrollArea::vertical() egui::ScrollArea::vertical()
@@ -44,13 +48,13 @@ impl Component for StackInspector {
ui.label("Value"); ui.label("Value");
ui.end_row(); ui.end_row();
for (i, value) in state.stack_view.iter().take(32).enumerate() { for (i, value) in (0u32..).zip(state.stack_view.iter().take(32)) {
ui.label(format!( ui.label(format!(
"{} [{}]", "{} [{}]",
i, i,
state.reg_file.get(Register::Spr) - i as u32 * 4 state.reg_file.get(Register::Spr) - i * 4
)); ));
ui.label(format!("0x{:08X} ({})", value, value)); ui.label(format!("0x{value:08X} ({value})"));
ui.end_row(); ui.end_row();
} }
+14
View File
@@ -1,2 +1,16 @@
#![deny(
clippy::unwrap_used,
clippy::nursery,
clippy::perf,
clippy::pedantic,
clippy::complexity
)]
#![allow(
clippy::cast_possible_truncation,
clippy::missing_panics_doc,
clippy::missing_errors_doc,
clippy::match_wildcard_for_single_variants
)]
pub mod common; pub mod common;
pub mod emulator; pub mod emulator;
+5 -1
View File
@@ -20,7 +20,11 @@ fn main() -> Result<(), eframe::Error> {
let processor = Processor::new(Box::new(mainstore)); let processor = Processor::new(Box::new(mainstore));
thread::spawn(move || { thread::spawn(move || {
run_emulator(cmd_receiver, state_sender, Arc::new(Mutex::new(processor))); run_emulator(
&cmd_receiver,
&state_sender,
&Arc::new(Mutex::new(processor)),
);
}); });
// Create UI // Create UI