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 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,
|
impl std::fmt::Display for RegisterParseError {
|
||||||
0x3 => Register::Rg3,
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
0x4 => Register::Rg4,
|
match self {
|
||||||
0x5 => Register::Rg5,
|
Self::InvalidIndex(idx) => write!(f, "invalid index given ({idx})"),
|
||||||
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::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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user