emulator: applied some clippy lints
This commit is contained in:
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"rust-analyzer.check.command": "clippy"
|
||||
}
|
||||
+129
-104
@@ -1,7 +1,7 @@
|
||||
type Offset = u16;
|
||||
type Immediate = u16;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Interrupt {
|
||||
Software(u8),
|
||||
}
|
||||
@@ -9,22 +9,23 @@ pub enum Interrupt {
|
||||
pub type Address = u32;
|
||||
|
||||
impl Interrupt {
|
||||
fn as_u8(&self) -> u8 {
|
||||
const fn as_u8(self) -> u8 {
|
||||
match self {
|
||||
Interrupt::Software(code) => *code,
|
||||
Self::Software(code) => code,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Interrupt {
|
||||
#[allow(unreachable_code)]
|
||||
fn from(_code: u8) -> Self {
|
||||
todo!("implement this once a hardware interrupt convention is established");
|
||||
#[allow(unreachable_code)]
|
||||
Interrupt::Software(_code)
|
||||
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 {
|
||||
// general purpose registers
|
||||
Rg0,
|
||||
@@ -62,71 +63,93 @@ pub enum Register {
|
||||
Pcx,
|
||||
}
|
||||
|
||||
impl From<u8> for Register {
|
||||
fn from(idx: u8) -> Register {
|
||||
match idx {
|
||||
// system registers are not indexable in the reg file so they cannot be modified by instructions.
|
||||
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"),
|
||||
#[derive(Debug)]
|
||||
/// Error type for parsing register numbers.
|
||||
pub enum RegisterParseError {
|
||||
InvalidIndex(u8),
|
||||
}
|
||||
|
||||
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 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Register::Rg0 => write!(f, "Rg0"),
|
||||
Register::Rg1 => write!(f, "Rg1"),
|
||||
Register::Rg2 => write!(f, "Rg2"),
|
||||
Register::Rg3 => write!(f, "Rg3"),
|
||||
Register::Rg4 => write!(f, "Rg4"),
|
||||
Register::Rg5 => write!(f, "Rg5"),
|
||||
Register::Rg6 => write!(f, "Rg6"),
|
||||
Register::Rg7 => write!(f, "Rg7"),
|
||||
Register::Rg8 => write!(f, "Rg8"),
|
||||
Register::Rg9 => write!(f, "Rg9"),
|
||||
Register::Rga => write!(f, "Rga"),
|
||||
Register::Rgb => write!(f, "Rgb"),
|
||||
Register::Rgc => write!(f, "Rgc"),
|
||||
Register::Rgd => write!(f, "Rgd"),
|
||||
Register::Rge => write!(f, "Rge"),
|
||||
Register::Rgf => write!(f, "Rgf"),
|
||||
Register::Acc => write!(f, "Acc"),
|
||||
Register::Spr => write!(f, "Spr"),
|
||||
Register::Bpr => write!(f, "Bpr"),
|
||||
Register::Ret => write!(f, "Ret"),
|
||||
Register::Idr => write!(f, "Idr"),
|
||||
Register::Mmr => write!(f, "Mmr"),
|
||||
Register::Zero => write!(f, "Zero"),
|
||||
Register::None => write!(f, "None"),
|
||||
Register::Mar => write!(f, "Mar"),
|
||||
Register::Mdr => write!(f, "Mdr"),
|
||||
Register::Sts => write!(f, "Sts"),
|
||||
Register::Cir => write!(f, "Cir"),
|
||||
Register::Pcx => write!(f, "Pcx"),
|
||||
Self::Rg0 => write!(f, "Rg0"),
|
||||
Self::Rg1 => write!(f, "Rg1"),
|
||||
Self::Rg2 => write!(f, "Rg2"),
|
||||
Self::Rg3 => write!(f, "Rg3"),
|
||||
Self::Rg4 => write!(f, "Rg4"),
|
||||
Self::Rg5 => write!(f, "Rg5"),
|
||||
Self::Rg6 => write!(f, "Rg6"),
|
||||
Self::Rg7 => write!(f, "Rg7"),
|
||||
Self::Rg8 => write!(f, "Rg8"),
|
||||
Self::Rg9 => write!(f, "Rg9"),
|
||||
Self::Rga => write!(f, "Rga"),
|
||||
Self::Rgb => write!(f, "Rgb"),
|
||||
Self::Rgc => write!(f, "Rgc"),
|
||||
Self::Rgd => write!(f, "Rgd"),
|
||||
Self::Rge => write!(f, "Rge"),
|
||||
Self::Rgf => write!(f, "Rgf"),
|
||||
Self::Acc => write!(f, "Acc"),
|
||||
Self::Spr => write!(f, "Spr"),
|
||||
Self::Bpr => write!(f, "Bpr"),
|
||||
Self::Ret => write!(f, "Ret"),
|
||||
Self::Idr => write!(f, "Idr"),
|
||||
Self::Mmr => write!(f, "Mmr"),
|
||||
Self::Zero => write!(f, "Zero"),
|
||||
Self::None => write!(f, "None"),
|
||||
Self::Mar => write!(f, "Mar"),
|
||||
Self::Mdr => write!(f, "Mdr"),
|
||||
Self::Sts => write!(f, "Sts"),
|
||||
Self::Cir => write!(f, "Cir"),
|
||||
Self::Pcx => write!(f, "Pcx"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,63 +212,65 @@ pub enum Instruction {
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
#[must_use]
|
||||
pub fn encode(&self) -> u32 {
|
||||
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
|
||||
Instruction::Nop
|
||||
Self::Nop
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Instruction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Instruction::Nop => write!(f, "No Operation"),
|
||||
Instruction::Mov(a, b) => write!(f, "MOV {}, {}", a, b),
|
||||
Instruction::MovSigned(a, b) => write!(f, "MOVS {}, {}", a, b),
|
||||
Instruction::LoadByte(a, b, c) => write!(f, "LDB {}, {}, {}", a, b, c),
|
||||
Instruction::LoadByteSigned(a, b, c) => write!(f, "LDBS {}, {}, {}", a, b, c),
|
||||
Instruction::LoadHalfword(a, b, c) => write!(f, "LDH {}, {}, {}", a, b, c),
|
||||
Instruction::LoadHalfwordSigned(a, b, c) => write!(f, "LDHS {}, {}, {}", a, b, c),
|
||||
Instruction::LoadWord(a, b, c) => write!(f, "LDW {}, {}, {}", a, b, c),
|
||||
Self::Nop => write!(f, "No Operation"),
|
||||
Self::Mov(a, b) => write!(f, "MOV {a}, {b}"),
|
||||
Self::MovSigned(a, b) => write!(f, "MOVS {a}, {b}"),
|
||||
Self::LoadByte(a, b, c) => write!(f, "LDB {a}, {b}, {c}"),
|
||||
Self::LoadByteSigned(a, b, c) => write!(f, "LDBS {a}, {b}, {c}"),
|
||||
Self::LoadHalfword(a, b, c) => write!(f, "LDH {a}, {b}, {c}"),
|
||||
Self::LoadHalfwordSigned(a, b, c) => write!(f, "LDHS {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),
|
||||
Instruction::StoreHalfword(a, b, c) => write!(f, "STH {}, {}, {}", a, b, c),
|
||||
Instruction::StoreWord(a, b, c) => write!(f, "STW {}, {}, {}", a, b, c),
|
||||
Self::StoreByte(a, b, c) => write!(f, "STB {a}, {b}, {c}"),
|
||||
Self::StoreHalfword(a, b, c) => write!(f, "STH {a}, {b}, {c}"),
|
||||
Self::StoreWord(a, b, c) => write!(f, "STW {a}, {b}, {c}"),
|
||||
|
||||
Instruction::LoadLowerImmediate(a, b) => write!(f, "LLI {}, {}", a, b),
|
||||
Instruction::LoadUpperImmediate(a, b) => write!(f, "LUI {}, {}", a, b),
|
||||
Self::LoadLowerImmediate(a, b) => write!(f, "LLI {a}, {b}"),
|
||||
Self::LoadUpperImmediate(a, b) => write!(f, "LUI {a}, {b}"),
|
||||
|
||||
Instruction::Jump(a, b) => write!(f, "JMP {}, {}", a, b),
|
||||
Instruction::JumpEq(a, b) => write!(f, "JEQ {}, {}", a, b),
|
||||
Instruction::JumpNeq(a, b) => write!(f, "JNEQ {}, {}", a, b),
|
||||
Instruction::JumpGt(a, b) => write!(f, "JGT {}, {}", a, b),
|
||||
Instruction::JumpGe(a, b) => write!(f, "JGE {}, {}", a, b),
|
||||
Instruction::JumpLt(a, b) => write!(f, "JLT {}, {}", a, b),
|
||||
Instruction::JumpLe(a, b) => write!(f, "JLE {}, {}", a, b),
|
||||
Self::Jump(a, b) => write!(f, "JMP {a}, {b}"),
|
||||
Self::JumpEq(a, b) => write!(f, "JEQ {a}, {b}"),
|
||||
Self::JumpNeq(a, b) => write!(f, "JNEQ {a}, {b}"),
|
||||
Self::JumpGt(a, b) => write!(f, "JGT {a}, {b}"),
|
||||
Self::JumpGe(a, b) => write!(f, "JGE {a}, {b}"),
|
||||
Self::JumpLt(a, b) => write!(f, "JLT {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),
|
||||
Instruction::Sub(a, b, c) => write!(f, "SUB {}, {}, {}", a, b, c),
|
||||
Instruction::Increment(a) => write!(f, "INC {}", a),
|
||||
Instruction::Decrement(a) => write!(f, "DEC {}", a),
|
||||
Instruction::ShiftLeft(a, b, c) => write!(f, "SHL {}, {}, {}", a, b, c),
|
||||
Instruction::ShiftRight(a, b, c) => write!(f, "SHR {}, {}, {}", a, b, c),
|
||||
Self::Add(a, b, c) => write!(f, "ADD {a}, {b}, {c}"),
|
||||
Self::Sub(a, b, c) => write!(f, "SUB {a}, {b}, {c}"),
|
||||
Self::Increment(a) => write!(f, "INC {a}"),
|
||||
Self::Decrement(a) => write!(f, "DEC {a}"),
|
||||
Self::ShiftLeft(a, b, c) => write!(f, "SHL {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),
|
||||
Instruction::Or(a, b, c) => write!(f, "OR {}, {}, {}", a, b, c),
|
||||
Instruction::Not(a, b) => write!(f, "NOT {}, {}", a, b),
|
||||
Instruction::Xor(a, b, c) => write!(f, "XOR {}, {}, {}", a, b, c),
|
||||
Instruction::Nand(a, b, c) => write!(f, "NAND {}, {}, {}", a, b, c),
|
||||
Instruction::Nor(a, b, c) => write!(f, "NOR {}, {}, {}", a, b, c),
|
||||
Instruction::Xnor(a, b, c) => write!(f, "XNOR {}, {}, {}", a, b, c),
|
||||
Self::And(a, b, c) => write!(f, "AND {a}, {b}, {c}"),
|
||||
Self::Or(a, b, c) => write!(f, "OR {a}, {b}, {c}"),
|
||||
Self::Not(a, b) => write!(f, "NOT {a}, {b}"),
|
||||
Self::Xor(a, b, c) => write!(f, "XOR {a}, {b}, {c}"),
|
||||
Self::Nand(a, b, c) => write!(f, "NAND {a}, {b}, {c}"),
|
||||
Self::Nor(a, b, c) => write!(f, "NOR {a}, {b}, {c}"),
|
||||
Self::Xnor(a, b, c) => write!(f, "XNOR {a}, {b}, {c}"),
|
||||
|
||||
Instruction::Interrupt(a) => write!(f, "INT {}", a.as_u8()),
|
||||
Instruction::IntReturn => write!(f, "INTR"),
|
||||
Instruction::Halt => write!(f, "HALT"),
|
||||
Self::Interrupt(a) => write!(f, "INT {}", a.as_u8()),
|
||||
Self::IntReturn => write!(f, "INTR"),
|
||||
Self::Halt => write!(f, "HALT"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,17 +16,21 @@ use crate::{
|
||||
};
|
||||
|
||||
pub fn run_emulator(
|
||||
cmd_rx: Receiver<Command>,
|
||||
state_tx: Sender<State>,
|
||||
cpu: Arc<Mutex<Processor>>,
|
||||
cmd_rx: &Receiver<Command>,
|
||||
state_tx: &Sender<State>,
|
||||
cpu: &Arc<Mutex<Processor>>,
|
||||
) {
|
||||
let mut running = Running::Paused;
|
||||
let mut addr = 0u32;
|
||||
let mut size = 256;
|
||||
let size = 256;
|
||||
|
||||
// Send initial state
|
||||
let memory_view = cpu.lock().unwrap().memory.read_range(addr, size);
|
||||
let initial_state = state(&cpu, &running, 0, memory_view);
|
||||
// Send initial state.
|
||||
let Ok(mut cpu_lock) = cpu.lock() else {
|
||||
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 mut instruction_count = 0;
|
||||
@@ -57,38 +61,32 @@ pub fn run_emulator(
|
||||
}
|
||||
Command::Reset => {
|
||||
running = Running::Paused;
|
||||
if let Ok(mut cpu_lock) = cpu.lock() {
|
||||
cpu_lock.reset();
|
||||
}
|
||||
cpu_lock.reset();
|
||||
instruction_count = 0;
|
||||
println!("Emulator rebooted");
|
||||
}
|
||||
Command::Step => {
|
||||
running = Running::Paused;
|
||||
// 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;
|
||||
println!("Stepped one instruction");
|
||||
}
|
||||
Command::Read(new, size) => {
|
||||
addr = new as u32;
|
||||
println!("Memory view for address 0x{:08x}", addr);
|
||||
Command::Read(new, _size) => {
|
||||
addr = new;
|
||||
println!("Memory view for address 0x{addr:08x}");
|
||||
}
|
||||
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");
|
||||
}
|
||||
Command::Interrupt(interrupt) => {
|
||||
Command::Interrupt(_interrupt) => {
|
||||
todo!("implement interrupts")
|
||||
}
|
||||
}
|
||||
|
||||
let memory_view = cpu.lock().unwrap().memory.read_range(addr, size);
|
||||
let state = state(&cpu, &running, instruction_count, memory_view);
|
||||
let memory_view = cpu_lock.memory.read_range(addr, size);
|
||||
let state = state(cpu, running, instruction_count, memory_view);
|
||||
|
||||
let _ = state_tx.send(state);
|
||||
}
|
||||
@@ -96,12 +94,13 @@ pub fn run_emulator(
|
||||
if running == Running::Running {
|
||||
let mut update = false;
|
||||
// Execute one cycle
|
||||
if let Ok(mut cpu_lock) = cpu.lock() {
|
||||
cpu_lock.cycle();
|
||||
if let Instruction::Halt = Instruction::decode(cpu_lock.get(Register::Cir)) {
|
||||
running = Running::Halted;
|
||||
update = true;
|
||||
}
|
||||
cpu_lock.cycle();
|
||||
if matches!(
|
||||
Instruction::decode(cpu_lock.get(Register::Cir)),
|
||||
Instruction::Halt
|
||||
) {
|
||||
running = Running::Halted;
|
||||
update = true;
|
||||
}
|
||||
|
||||
instruction_count += 1;
|
||||
@@ -112,8 +111,8 @@ pub fn run_emulator(
|
||||
}
|
||||
|
||||
if update {
|
||||
let memory_view = cpu.lock().unwrap().memory.read_range(addr, size);
|
||||
let state = state(&cpu, &running, instruction_count, memory_view);
|
||||
let memory_view = cpu_lock.memory.read_range(addr, size);
|
||||
let state = state(cpu, running, instruction_count, memory_view);
|
||||
let _ = state_tx.send(state);
|
||||
}
|
||||
} else {
|
||||
@@ -124,22 +123,22 @@ pub fn run_emulator(
|
||||
|
||||
fn state(
|
||||
cpu: &Arc<Mutex<Processor>>,
|
||||
running: &Running,
|
||||
running: Running,
|
||||
instruction_count: usize,
|
||||
memory_view: Vec<u8>,
|
||||
) -> State {
|
||||
if let Ok(mut cpu_lock) = cpu.lock() {
|
||||
State {
|
||||
// TODO: Replace with actual register access from your CPU
|
||||
reg_file: cpu_lock.registers.clone(),
|
||||
running: running.clone(),
|
||||
instructions: instruction_count,
|
||||
stack_view: cpu_lock.get_stack(32),
|
||||
memory_view,
|
||||
display_view: cpu_lock.display(),
|
||||
error: None,
|
||||
}
|
||||
} else {
|
||||
panic!("Failed to lock CPU");
|
||||
let Ok(mut cpu_lock) = cpu.lock() else {
|
||||
panic!("Could not lock CPU.")
|
||||
};
|
||||
|
||||
State {
|
||||
// TODO: Replace with actual register access from your CPU.
|
||||
reg_file: cpu_lock.registers,
|
||||
running,
|
||||
instructions: instruction_count,
|
||||
stack_view: cpu_lock.get_stack(32),
|
||||
memory_view,
|
||||
display_view: cpu_lock.display(),
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,35 +14,48 @@ pub struct MainStore {
|
||||
pub data: HashMap<u32, Block>,
|
||||
}
|
||||
|
||||
struct Block {
|
||||
pub struct Block {
|
||||
data: [u8; 256],
|
||||
}
|
||||
|
||||
impl Default for MainStore {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl MainStore {
|
||||
pub fn new() -> MainStore {
|
||||
MainStore {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn segment_addr(addr: u32) -> (u32, u8) {
|
||||
const fn segment_addr(addr: u32) -> (u32, u8) {
|
||||
(addr / 256, (addr % 256) as u8)
|
||||
}
|
||||
|
||||
fn mut_block<'a>(&'a mut self, addr: u32) -> &'a mut Block {
|
||||
if !self.data.contains_key(&addr) {
|
||||
self.data.insert(addr, Block { data: [0; 256] });
|
||||
}
|
||||
fn mut_block(&mut self, addr: u32) -> &mut Block {
|
||||
self.data
|
||||
.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 {
|
||||
if !self.data.contains_key(&addr) {
|
||||
self.data.insert(addr, Block { data: [0; 256] });
|
||||
}
|
||||
self.data
|
||||
.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 {
|
||||
let (block_addr, offset) = MainStore::segment_addr(addr);
|
||||
let (block_addr, offset) = Self::segment_addr(addr);
|
||||
let block = self.block(block_addr);
|
||||
block.data[offset as usize]
|
||||
}
|
||||
|
||||
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 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[2] = block.data[(offset + 2) 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) {
|
||||
let (block_addr, offset) = MainStore::segment_addr(addr);
|
||||
let (block_addr, offset) = Self::segment_addr(addr);
|
||||
let block = self.mut_block(block_addr);
|
||||
block.data[offset as usize] = value;
|
||||
}
|
||||
|
||||
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);
|
||||
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 + 2) as usize] = (value >> 8) as u8;
|
||||
block.data[(offset + 3) as usize] = value as u8;
|
||||
}
|
||||
|
||||
fn write_range(&mut self, addr: u32, value: Vec<u8>) {
|
||||
todo!("we might need this for DMA");
|
||||
fn write_range(&mut self, _addr: u32, _value: Vec<u8>) {
|
||||
todo!("We might need this for DMA.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::common::instructions::{Address, Interrupt, Register};
|
||||
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub enum Running {
|
||||
Running,
|
||||
Paused,
|
||||
@@ -56,6 +56,7 @@ pub struct RegFile {
|
||||
}
|
||||
|
||||
impl RegFile {
|
||||
#[must_use]
|
||||
pub fn all(&self) -> Vec<(&str, u32)> {
|
||||
vec![
|
||||
("Rg0", self.rg0),
|
||||
@@ -88,7 +89,7 @@ impl RegFile {
|
||||
]
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
pub const fn reset(&mut self) {
|
||||
self.rg0 = 0;
|
||||
self.rg1 = 0;
|
||||
self.rg2 = 0;
|
||||
@@ -147,10 +148,11 @@ impl RegFile {
|
||||
Register::Sts => &mut self.sts,
|
||||
Register::Cir => &mut self.cir,
|
||||
Register::Pcx => &mut self.pcx,
|
||||
_ => panic!("invalid register"),
|
||||
_ => panic!("Invalid register."),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get(&self, reg: Register) -> u32 {
|
||||
match reg {
|
||||
Register::Rg0 => self.rg0,
|
||||
@@ -181,7 +183,7 @@ impl RegFile {
|
||||
Register::Cir => self.cir,
|
||||
Register::Pcx => self.pcx,
|
||||
Register::Zero => 0,
|
||||
_ => panic!("invalid register"),
|
||||
_ => panic!("Invalid register."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use std::{
|
||||
cmp::{max, min},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::cmp::{max, min};
|
||||
|
||||
use crate::{
|
||||
common::instructions::{Address, Instruction, Interrupt, Register},
|
||||
common::instructions::{Instruction, Interrupt, Register},
|
||||
emulator::system::{memory::MemoryUnit, model::RegFile},
|
||||
};
|
||||
|
||||
@@ -15,10 +12,12 @@ pub struct Processor {
|
||||
// pub io_devices: Vec<Arc<dyn IODevice>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||
impl Processor {
|
||||
// io_devices: Vec<Arc<dyn IODevice>>
|
||||
#[must_use]
|
||||
pub fn new(memory: Box<dyn MemoryUnit>) -> Self {
|
||||
Processor {
|
||||
Self {
|
||||
// io_devices,
|
||||
memory,
|
||||
registers: RegFile::default(),
|
||||
@@ -45,7 +44,7 @@ impl Processor {
|
||||
let val = self.memory.read_word(addr);
|
||||
|
||||
// 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
|
||||
let instruction = Instruction::decode(val);
|
||||
|
||||
@@ -57,6 +56,7 @@ impl Processor {
|
||||
self.get(Register::Pcx)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get(&self, reg: Register) -> u32 {
|
||||
self.registers.get(reg)
|
||||
}
|
||||
@@ -108,10 +108,10 @@ impl Processor {
|
||||
}
|
||||
|
||||
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.
|
||||
todo!();
|
||||
}
|
||||
@@ -132,6 +132,7 @@ impl Processor {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[expect(dead_code)]
|
||||
enum Flag {
|
||||
Equal = 0,
|
||||
GreaterThan = 1,
|
||||
@@ -149,6 +150,7 @@ trait Executable {
|
||||
}
|
||||
|
||||
impl Executable for Instruction {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn execute(self, cpu: &mut Processor) {
|
||||
match self {
|
||||
// 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.
|
||||
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.
|
||||
Self::LoadByteSigned(base, offset, dest) => {
|
||||
*cpu.reg(dest) =
|
||||
sign_extend(cpu.memory.read_byte(cpu.get(base) + offset as u32) as u32);
|
||||
*cpu.reg(dest) = sign_extend(u32::from(
|
||||
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.
|
||||
Self::LoadHalfword(base, offset, dest) => {
|
||||
// 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.
|
||||
Self::LoadHalfwordSigned(base, offset, 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.
|
||||
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.
|
||||
Self::StoreByte(src, offset, base) => {
|
||||
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.
|
||||
@@ -203,25 +206,25 @@ impl Executable for Instruction {
|
||||
// split the value into bytes and then write two bytes
|
||||
let bytes = (cpu.get(src) as u16).to_be_bytes();
|
||||
cpu.memory
|
||||
.write_byte(cpu.get(base) + offset as u32, bytes[0]);
|
||||
.write_byte(cpu.get(base) + u32::from(offset), bytes[0]);
|
||||
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.
|
||||
Self::StoreWord(src, offset, base) => {
|
||||
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.
|
||||
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.
|
||||
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
|
||||
@@ -230,42 +233,42 @@ impl Executable for Instruction {
|
||||
// Jumps to the calculated address or direct address if equal flag set.
|
||||
Self::JumpEq(reg, offset) => {
|
||||
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.
|
||||
Self::JumpNeq(reg, offset) => {
|
||||
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.
|
||||
Self::JumpGt(reg, offset) => {
|
||||
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.
|
||||
Self::JumpGe(reg, offset) => {
|
||||
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.
|
||||
Self::JumpLt(reg, offset) => {
|
||||
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.
|
||||
Self::JumpLe(reg, offset) => {
|
||||
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
|
||||
Self::Add(srcx, srcy, dest) => {
|
||||
*cpu.reg(dest) = add(cpu.get(srcx), cpu.get(srcy));
|
||||
Self::Add(src1, src2, dest) => {
|
||||
*cpu.reg(dest) = add(cpu.get(src1), cpu.get(src2));
|
||||
}
|
||||
|
||||
// Subtracts the value of Src2 from Src1 and writes the result to Dest
|
||||
Self::Sub(srcx, srcy, dest) => {
|
||||
*cpu.reg(dest) = sub(cpu.get(srcx), cpu.get(srcy));
|
||||
Self::Sub(src1, src2, dest) => {
|
||||
*cpu.reg(dest) = sub(cpu.get(src1), cpu.get(src2));
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
Self::Not(src, dest) => *cpu.reg(dest) = not(cpu.get(src)),
|
||||
|
||||
// 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
|
||||
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
|
||||
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
|
||||
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.
|
||||
Self::Compare(srcx, srcy) => {
|
||||
cpu.cmp(cpu.get(srcx), cpu.get(srcy));
|
||||
Self::Compare(src1, src2) => {
|
||||
cpu.cmp(cpu.get(src1), cpu.get(src2));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -353,63 +356,64 @@ impl Executable for Instruction {
|
||||
}
|
||||
|
||||
// mathematical and logical functions & other operations
|
||||
fn add(a: u32, b: u32) -> u32 {
|
||||
const fn add(a: u32, b: u32) -> u32 {
|
||||
a + b
|
||||
}
|
||||
|
||||
fn sub(a: u32, b: u32) -> u32 {
|
||||
const fn sub(a: u32, b: u32) -> u32 {
|
||||
a - b
|
||||
}
|
||||
|
||||
fn and(a: u32, b: u32) -> u32 {
|
||||
const fn and(a: u32, b: u32) -> u32 {
|
||||
a & b
|
||||
}
|
||||
|
||||
fn inc(a: u32) -> u32 {
|
||||
const fn inc(a: u32) -> u32 {
|
||||
a + 1
|
||||
}
|
||||
|
||||
fn dec(a: u32) -> u32 {
|
||||
const fn dec(a: u32) -> u32 {
|
||||
a - 1
|
||||
}
|
||||
|
||||
fn shl(a: u32, amount: u8) -> u32 {
|
||||
const fn shl(a: u32, amount: u8) -> u32 {
|
||||
a << amount
|
||||
}
|
||||
|
||||
fn shr(a: u32, amount: u8) -> u32 {
|
||||
const fn shr(a: u32, amount: u8) -> u32 {
|
||||
a >> amount
|
||||
}
|
||||
|
||||
fn or(a: u32, b: u32) -> u32 {
|
||||
const fn or(a: u32, b: u32) -> u32 {
|
||||
a | b
|
||||
}
|
||||
|
||||
fn not(a: u32) -> u32 {
|
||||
const fn not(a: u32) -> u32 {
|
||||
!a
|
||||
}
|
||||
|
||||
fn xor(a: u32, b: u32) -> u32 {
|
||||
const fn xor(a: u32, b: u32) -> u32 {
|
||||
a ^ b
|
||||
}
|
||||
|
||||
fn nand(a: u32, b: u32) -> u32 {
|
||||
const fn nand(a: u32, b: u32) -> u32 {
|
||||
!(a & b)
|
||||
}
|
||||
|
||||
fn nor(a: u32, b: u32) -> u32 {
|
||||
const fn nor(a: u32, b: u32) -> u32 {
|
||||
!(a | b)
|
||||
}
|
||||
|
||||
fn xnor(a: u32, b: u32) -> u32 {
|
||||
const fn xnor(a: u32, b: u32) -> u32 {
|
||||
!(a ^ b)
|
||||
}
|
||||
|
||||
fn sign_extend(val: u32) -> u32 {
|
||||
const fn sign_extend(val: u32) -> u32 {
|
||||
let (mask, sign_bit): (u32, u8) = match val {
|
||||
0..=0xFF => (0xFFFFFF00, 7),
|
||||
0..=0xFFFF => (0xFFFF0000, 15),
|
||||
_ => (0x00000000, 31),
|
||||
0..=0xFF => (0xFFFF_FF00, 7),
|
||||
// I presume this was the intended behaviour?
|
||||
0x100..=0xFFFF => (0xFFFF_0000, 15),
|
||||
_ => (0x0000_0000, 31),
|
||||
};
|
||||
|
||||
if val & (1 << sign_bit) != 0 {
|
||||
|
||||
@@ -14,7 +14,8 @@ pub struct ControlPanel {
|
||||
}
|
||||
|
||||
impl ControlPanel {
|
||||
pub fn new(sender: Sender<Command>) -> Self {
|
||||
#[must_use]
|
||||
pub const fn new(sender: Sender<Command>) -> Self {
|
||||
Self {
|
||||
visible: false,
|
||||
sender,
|
||||
@@ -48,11 +49,11 @@ impl Component for ControlPanel {
|
||||
{
|
||||
if state.running == Running::Running {
|
||||
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 {
|
||||
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
|
||||
ui.vertical(|ui| {
|
||||
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
|
||||
for chunk in state.reg_file.all().chunks(4) {
|
||||
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.end_row();
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
common::instructions::{Address, Interrupt},
|
||||
emulator::system::model::{Command, Running, State},
|
||||
};
|
||||
use crate::emulator::system::model::{Command, Running, State};
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
|
||||
pub trait Component {
|
||||
@@ -11,7 +8,7 @@ pub trait Component {
|
||||
fn category(&self) -> Category;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Category {
|
||||
Control,
|
||||
Memory,
|
||||
@@ -20,22 +17,19 @@ pub enum Category {
|
||||
}
|
||||
|
||||
impl Category {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
#[must_use]
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Category::Control => "Control Systems",
|
||||
Category::Memory => "Memory Systems",
|
||||
Category::Io => "I/O Systems",
|
||||
Category::Programming => "Programming",
|
||||
Self::Control => "Control Systems",
|
||||
Self::Memory => "Memory Systems",
|
||||
Self::Io => "I/O Systems",
|
||||
Self::Programming => "Programming",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list() -> Vec<Category> {
|
||||
vec![
|
||||
Category::Control,
|
||||
Category::Memory,
|
||||
Category::Io,
|
||||
Category::Programming,
|
||||
]
|
||||
#[must_use]
|
||||
pub fn list() -> Vec<Self> {
|
||||
vec![Self::Control, Self::Memory, Self::Io, Self::Programming]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +41,7 @@ pub struct EmulatorUI {
|
||||
}
|
||||
|
||||
impl EmulatorUI {
|
||||
#[must_use]
|
||||
pub fn new(sender: Sender<Command>, receiver: Receiver<State>) -> Self {
|
||||
Self {
|
||||
sender,
|
||||
@@ -68,10 +63,10 @@ impl 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();
|
||||
|
||||
if let Running::Running = self.state.running {
|
||||
if self.state.running == Running::Running {
|
||||
ctx.request_repaint();
|
||||
}
|
||||
|
||||
@@ -88,7 +83,7 @@ impl eframe::App for EmulatorUI {
|
||||
});
|
||||
|
||||
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")
|
||||
.resizable(false)
|
||||
.default_width(300.0)
|
||||
@@ -96,9 +91,9 @@ impl eframe::App for EmulatorUI {
|
||||
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();
|
||||
if visible == true {
|
||||
if visible {
|
||||
egui::Window::new(c.name())
|
||||
.open(&mut visible)
|
||||
.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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ pub struct MemoryInspector {
|
||||
}
|
||||
|
||||
impl MemoryInspector {
|
||||
pub fn new(sender: Sender<Command>) -> Self {
|
||||
#[must_use]
|
||||
pub const fn new(sender: Sender<Command>) -> Self {
|
||||
Self {
|
||||
view_size: 256,
|
||||
view_addr: 0,
|
||||
@@ -66,9 +67,12 @@ impl Component for MemoryInspector {
|
||||
if search_clicked || enter_pressed {
|
||||
if let Ok(new) = parse_address(&self.addr_input) {
|
||||
self.view_addr = new;
|
||||
self.sender
|
||||
.send(Command::Read(new, self.view_size))
|
||||
.unwrap();
|
||||
|
||||
if let Err(why) = self.sender.send(Command::Read(new, self.view_size)) {
|
||||
panic!(
|
||||
"Error sending message across threads -- cannot be recovered: {why}"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
state.error = Some("Invalid address".to_string());
|
||||
}
|
||||
@@ -79,7 +83,7 @@ impl Component for MemoryInspector {
|
||||
|
||||
// Show input error if any
|
||||
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);
|
||||
@@ -98,7 +102,7 @@ impl Component for MemoryInspector {
|
||||
ui.strong("Address");
|
||||
|
||||
for i in 0..4 {
|
||||
ui.strong(format!("{:X}", i));
|
||||
ui.strong(format!("{i:X}"));
|
||||
}
|
||||
|
||||
ui.strong("Decimal");
|
||||
@@ -107,11 +111,11 @@ impl Component for MemoryInspector {
|
||||
ui.end_row();
|
||||
|
||||
// Memory data (8 bytes per row)
|
||||
for (row, chunk) in state.memory_view.chunks(4).enumerate() {
|
||||
let row_address = self.view_addr + (row * 4) as u32;
|
||||
ui.monospace(format!("0x{:08X} ({})", row_address, row_address));
|
||||
for (row, chunk) in (0u32..).zip(state.memory_view.chunks(4)) {
|
||||
let row_address = self.view_addr + (row * 4);
|
||||
ui.monospace(format!("0x{row_address:08X} ({row_address})"));
|
||||
for &byte in chunk {
|
||||
ui.monospace(format!("{:02X}", byte));
|
||||
ui.monospace(format!("{byte:02X}"));
|
||||
}
|
||||
|
||||
// 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
|
||||
let combined =
|
||||
chunk.iter().fold(0u32, |acc, &byte| acc << 8 | byte as u32);
|
||||
let combined = chunk
|
||||
.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("TODO! instruction");
|
||||
|
||||
@@ -136,17 +141,17 @@ impl Component for MemoryInspector {
|
||||
}
|
||||
|
||||
fn parse_address(address: &str) -> Result<u32, ParseIntError> {
|
||||
if address.starts_with("0x") {
|
||||
return u32::from_str_radix(&address[2..], 16);
|
||||
if let Some(hex_part) = address.strip_prefix("0x") {
|
||||
return u32::from_str_radix(hex_part, 16);
|
||||
}
|
||||
|
||||
if address.starts_with("0b") {
|
||||
return u32::from_str_radix(&address[2..], 2);
|
||||
if let Some(bin_part) = address.strip_prefix("0b") {
|
||||
return u32::from_str_radix(bin_part, 2);
|
||||
}
|
||||
|
||||
if address.starts_with("0o") {
|
||||
return u32::from_str_radix(&address[1..], 8);
|
||||
if let Some(oct_part) = address.strip_prefix("0o") {
|
||||
return u32::from_str_radix(oct_part, 8);
|
||||
}
|
||||
|
||||
u32::from_str_radix(address, 10)
|
||||
address.parse::<u32>()
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub fn render_menu(state: &mut EmulatorUI, ui: &mut egui::Ui, _ctx: &egui::Conte
|
||||
ui.heading(cat.as_str());
|
||||
ui.add_space(10.0);
|
||||
|
||||
for comp in state.components.iter_mut() {
|
||||
for comp in &mut state.components {
|
||||
let name = comp.name();
|
||||
if comp.category() == cat {
|
||||
ui.toggle_value(comp.visible(), name);
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
use crate::{
|
||||
common::instructions::Register,
|
||||
emulator::{
|
||||
system::model::State,
|
||||
ui::interface::{Component, EmulatorUI},
|
||||
},
|
||||
emulator::{system::model::State, ui::interface::Component},
|
||||
};
|
||||
|
||||
pub struct StackInspector {
|
||||
visible: bool,
|
||||
}
|
||||
|
||||
impl Default for StackInspector {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl StackInspector {
|
||||
pub fn new() -> Self {
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
Self { visible: false }
|
||||
}
|
||||
}
|
||||
@@ -29,7 +33,7 @@ impl Component for StackInspector {
|
||||
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.heading("Stack Inspector");
|
||||
egui::ScrollArea::vertical()
|
||||
@@ -44,13 +48,13 @@ impl Component for StackInspector {
|
||||
ui.label("Value");
|
||||
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!(
|
||||
"{} [{}]",
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
+14
@@ -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 emulator;
|
||||
|
||||
+5
-1
@@ -20,7 +20,11 @@ fn main() -> Result<(), eframe::Error> {
|
||||
let processor = Processor::new(Box::new(mainstore));
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user