CPU can now decode instructions, just waiting on the assembler
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
type Offset = u16;
|
type Offset = u16;
|
||||||
type Immediate = u16;
|
type Immediate = u16;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum Interrupt {
|
pub enum Interrupt {
|
||||||
Software(u8),
|
Software(u8),
|
||||||
}
|
}
|
||||||
@@ -23,6 +24,7 @@ impl From<u8> for Interrupt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum Register {
|
pub enum Register {
|
||||||
// general purpose registers
|
// general purpose registers
|
||||||
Rg0,
|
Rg0,
|
||||||
@@ -129,6 +131,7 @@ impl std::fmt::Display for Register {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub enum Instruction {
|
pub enum Instruction {
|
||||||
// No-op
|
// No-op
|
||||||
Nop,
|
Nop,
|
||||||
@@ -142,7 +145,6 @@ pub enum Instruction {
|
|||||||
LoadHalfword(Register, Offset, Register),
|
LoadHalfword(Register, Offset, Register),
|
||||||
LoadHalfwordSigned(Register, Offset, Register),
|
LoadHalfwordSigned(Register, Offset, Register),
|
||||||
LoadWord(Register, Offset, Register),
|
LoadWord(Register, Offset, Register),
|
||||||
LoadWordSigned(Register, Offset, Register),
|
|
||||||
|
|
||||||
StoreByte(Register, Offset, Register),
|
StoreByte(Register, Offset, Register),
|
||||||
StoreHalfword(Register, Offset, Register),
|
StoreHalfword(Register, Offset, Register),
|
||||||
@@ -168,8 +170,8 @@ pub enum Instruction {
|
|||||||
Sub(Register, Register, Register),
|
Sub(Register, Register, Register),
|
||||||
Increment(Register),
|
Increment(Register),
|
||||||
Decrement(Register),
|
Decrement(Register),
|
||||||
ShiftLeft(Register, Register, Register),
|
ShiftLeft(Register, Register, Immediate),
|
||||||
ShiftRight(Register, Register, Register),
|
ShiftRight(Register, Register, Immediate),
|
||||||
|
|
||||||
// Logical
|
// Logical
|
||||||
And(Register, Register, Register),
|
And(Register, Register, Register),
|
||||||
@@ -208,7 +210,6 @@ impl std::fmt::Display for Instruction {
|
|||||||
Instruction::LoadHalfword(a, b, c) => write!(f, "LDH {}, {}, {}", 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::LoadHalfwordSigned(a, b, c) => write!(f, "LDHS {}, {}, {}", a, b, c),
|
||||||
Instruction::LoadWord(a, b, c) => write!(f, "LDW {}, {}, {}", a, b, c),
|
Instruction::LoadWord(a, b, c) => write!(f, "LDW {}, {}, {}", a, b, c),
|
||||||
Instruction::LoadWordSigned(a, b, c) => write!(f, "LDWS {}, {}, {}", a, b, c),
|
|
||||||
|
|
||||||
Instruction::StoreByte(a, b, c) => write!(f, "STB {}, {}, {}", 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::StoreHalfword(a, b, c) => write!(f, "STH {}, {}, {}", a, b, c),
|
||||||
|
|||||||
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
pub mod ui;
|
|
||||||
pub mod system;
|
pub mod system;
|
||||||
|
pub mod ui;
|
||||||
@@ -1,6 +1,19 @@
|
|||||||
use std::{sync::{mpsc::{self, Receiver, Sender}, Arc, Mutex}, thread, time::Duration};
|
use std::{
|
||||||
|
sync::{
|
||||||
|
Arc, Mutex,
|
||||||
|
mpsc::{self, Receiver, Sender},
|
||||||
|
},
|
||||||
|
thread,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{common::instructions::{Instruction, Register}, emulator::system::{model::{Command, Running, State}, processor::Processor}};
|
use crate::{
|
||||||
|
common::instructions::{Instruction, Register},
|
||||||
|
emulator::system::{
|
||||||
|
model::{Command, Running, State},
|
||||||
|
processor::Processor,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn run_emulator(
|
pub fn run_emulator(
|
||||||
cmd_rx: Receiver<Command>,
|
cmd_rx: Receiver<Command>,
|
||||||
@@ -9,9 +22,10 @@ pub fn run_emulator(
|
|||||||
) {
|
) {
|
||||||
let mut running = Running::Paused;
|
let mut running = Running::Paused;
|
||||||
let mut addr = 0u32;
|
let mut addr = 0u32;
|
||||||
|
let mut size = 256;
|
||||||
|
|
||||||
// Send initial state
|
// Send initial state
|
||||||
let memory_view = cpu.lock().unwrap().memory.read_range(addr, 256);
|
let memory_view = cpu.lock().unwrap().memory.read_range(addr, size);
|
||||||
let initial_state = state(&cpu, &running, 0, memory_view);
|
let initial_state = state(&cpu, &running, 0, memory_view);
|
||||||
let _ = state_tx.send(initial_state);
|
let _ = state_tx.send(initial_state);
|
||||||
|
|
||||||
@@ -73,7 +87,7 @@ pub fn run_emulator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let memory_view = cpu.lock().unwrap().memory.read_range(addr, 256);
|
let memory_view = cpu.lock().unwrap().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);
|
||||||
@@ -98,7 +112,7 @@ pub fn run_emulator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if update {
|
if update {
|
||||||
let memory_view = cpu.lock().unwrap().memory.read_range(addr, 256);
|
let memory_view = cpu.lock().unwrap().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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
pub mod model;
|
|
||||||
pub mod emulator;
|
pub mod emulator;
|
||||||
pub mod processor;
|
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
|
pub mod model;
|
||||||
|
pub mod processor;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use crate::common::instructions::{Address, Interrupt, Register};
|
use crate::common::instructions::{Address, Interrupt, Register};
|
||||||
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
pub enum Running {
|
pub enum Running {
|
||||||
Running,
|
Running,
|
||||||
@@ -59,33 +58,33 @@ pub struct RegFile {
|
|||||||
impl RegFile {
|
impl RegFile {
|
||||||
pub fn all(&self) -> Vec<(&str, u32)> {
|
pub fn all(&self) -> Vec<(&str, u32)> {
|
||||||
vec![
|
vec![
|
||||||
("rg0", self.rg0),
|
("Rg0", self.rg0),
|
||||||
("rg1", self.rg1),
|
("Rg1", self.rg1),
|
||||||
("rg2", self.rg2),
|
("Rg2", self.rg2),
|
||||||
("rg3", self.rg3),
|
("Rg3", self.rg3),
|
||||||
("rg4", self.rg4),
|
("Rg4", self.rg4),
|
||||||
("rg5", self.rg5),
|
("Rg5", self.rg5),
|
||||||
("rg6", self.rg6),
|
("Rg6", self.rg6),
|
||||||
("rg7", self.rg7),
|
("Rg7", self.rg7),
|
||||||
("rg8", self.rg8),
|
("Rg8", self.rg8),
|
||||||
("rg9", self.rg9),
|
("Rg9", self.rg9),
|
||||||
("rga", self.rga),
|
("Rga", self.rga),
|
||||||
("rgb", self.rgb),
|
("Rgb", self.rgb),
|
||||||
("rgc", self.rgc),
|
("Rgc", self.rgc),
|
||||||
("rgd", self.rgd),
|
("Rgd", self.rgd),
|
||||||
("rge", self.rge),
|
("Rge", self.rge),
|
||||||
("rgf", self.rgf),
|
("Rgf", self.rgf),
|
||||||
("acc", self.acc),
|
("Acc", self.acc),
|
||||||
("spr", self.spr),
|
("Spr", self.spr),
|
||||||
("bpr", self.bpr),
|
("Bpr", self.bpr),
|
||||||
("ret", self.ret),
|
("Ret", self.ret),
|
||||||
("idr", self.idr),
|
("Idr", self.idr),
|
||||||
("mmr", self.mmr),
|
("Mmr", self.mmr),
|
||||||
("mar", self.mar),
|
("Mar", self.mar),
|
||||||
("mdr", self.mdr),
|
("Mdr", self.mdr),
|
||||||
("sts", self.sts),
|
("Sts", self.sts),
|
||||||
("cir", self.cir),
|
("Cir", self.cir),
|
||||||
("pcx", self.pcx),
|
("Pcx", self.pcx),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +118,6 @@ impl RegFile {
|
|||||||
self.pcx = 0;
|
self.pcx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn reg(&mut self, reg: Register) -> &mut u32 {
|
pub fn reg(&mut self, reg: Register) -> &mut u32 {
|
||||||
match reg {
|
match reg {
|
||||||
Register::Rg0 => &mut self.rg0,
|
Register::Rg0 => &mut self.rg0,
|
||||||
@@ -188,7 +186,6 @@ impl RegFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub reg_file: RegFile,
|
pub reg_file: RegFile,
|
||||||
pub running: Running,
|
pub running: Running,
|
||||||
|
|||||||
+233
-102
@@ -1,10 +1,17 @@
|
|||||||
use std::{cmp::{max, min}, sync::Arc};
|
use std::{
|
||||||
|
cmp::{max, min},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{common::instructions::{Address, Instruction, Register}, emulator::system::{memory::MemoryUnit, model::RegFile}};
|
use crate::{
|
||||||
|
common::instructions::{Address, Instruction, Interrupt, Register},
|
||||||
|
emulator::system::{memory::MemoryUnit, model::RegFile},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Processor {
|
pub struct Processor {
|
||||||
pub memory: Box<dyn MemoryUnit>,
|
pub memory: Box<dyn MemoryUnit>,
|
||||||
pub registers: RegFile,
|
pub registers: RegFile,
|
||||||
|
pub halted: bool,
|
||||||
// pub io_devices: Vec<Arc<dyn IODevice>>,
|
// pub io_devices: Vec<Arc<dyn IODevice>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,6 +22,7 @@ impl Processor {
|
|||||||
// io_devices,
|
// io_devices,
|
||||||
memory,
|
memory,
|
||||||
registers: RegFile::default(),
|
registers: RegFile::default(),
|
||||||
|
halted: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,6 +34,7 @@ impl Processor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn cycle(&mut self) -> Instruction {
|
pub fn cycle(&mut self) -> Instruction {
|
||||||
|
self.halted = false;
|
||||||
// get value from PCX
|
// get value from PCX
|
||||||
let addr = self.fetch();
|
let addr = self.fetch();
|
||||||
// increment PCX
|
// increment PCX
|
||||||
@@ -39,7 +48,8 @@ impl Processor {
|
|||||||
*self.reg(Register::Mar) = val as u32;
|
*self.reg(Register::Mar) = val as u32;
|
||||||
// decode and execute the instruction
|
// decode and execute the instruction
|
||||||
let instruction = Instruction::decode(val);
|
let instruction = Instruction::decode(val);
|
||||||
instruction.execute(self);
|
|
||||||
|
instruction.clone().execute(self);
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,11 +107,11 @@ impl Processor {
|
|||||||
*self.reg(Register::Pcx) += 4;
|
*self.reg(Register::Pcx) += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn jump(&mut self, addr: &Address) {
|
fn jump(&mut self, reg: Register, offset: u16) {
|
||||||
*self.reg(Register::Pcx) = self.get(Register::from(*addr as u8));
|
*self.reg(Register::Pcx) = self.get(reg) + offset as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin_interrupt(&mut self, _code: u8) {
|
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!();
|
||||||
}
|
}
|
||||||
@@ -112,11 +122,11 @@ impl Processor {
|
|||||||
|
|
||||||
pub fn get_stack(&mut self, n: u32) -> Vec<u8> {
|
pub fn get_stack(&mut self, n: u32) -> Vec<u8> {
|
||||||
let addr = self.get(Register::Spr);
|
let addr = self.get(Register::Spr);
|
||||||
let size = n*4;
|
let size = n * 4;
|
||||||
// returns the stack
|
// returns the stack
|
||||||
self.memory.read_range(
|
self.memory.read_range(
|
||||||
max(addr, 0), // ensures that we cannot read from a negative address
|
max(addr, 0), // ensures that we cannot read from a negative address
|
||||||
min(size, addr), // ensures we don't read above the top of the stack
|
min(size, addr), // ensures we don't read above the top of the stack
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,102 +145,209 @@ enum Flag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trait Executable {
|
trait Executable {
|
||||||
fn execute(&self, cpu: &mut Processor);
|
fn execute(self, cpu: &mut Processor);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Executable for Instruction {
|
impl Executable for Instruction {
|
||||||
fn execute(&self, cpu: &mut Processor) {
|
fn execute(self, cpu: &mut Processor) {
|
||||||
match self {
|
match self {
|
||||||
// Instruction::Nop => (),
|
// No operation - a blank line.
|
||||||
// Instruction::Mov(reg0, reg1) => *cpu.reg(reg1) = cpu.get(reg0),
|
Self::Nop => {}
|
||||||
|
|
||||||
// Instruction::Ldb(addr) => {
|
// Copies from SrcReg to DestReg.
|
||||||
// cpu.mdr = cpu.memory.read_word(match addr {
|
Self::Mov(src, dest) => {
|
||||||
// Address::Literal(addr) => *addr,
|
*cpu.reg(dest) = cpu.get(src);
|
||||||
// Address::Register(reg) => cpu.get(reg),
|
}
|
||||||
// _ => panic!("invalid address"),
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// Instruction::Stb(addr) => cpu.memory.write_word(
|
|
||||||
// match addr {
|
|
||||||
// Address::Literal(addr) => *addr,
|
|
||||||
// Address::Register(reg) => cpu.get(reg),
|
|
||||||
// _ => panic!("invalid address"),
|
|
||||||
// },
|
|
||||||
// cpu.get(&Reg::MDR),
|
|
||||||
// ),
|
|
||||||
|
|
||||||
// Instruction::Add(reg0, reg1, reg2) => {
|
// Copies from SrcReg to DestReg, sign extending the value to take up a full word.
|
||||||
// *cpu.reg(reg2) = add(cpu.get(reg0), cpu.get(reg1))
|
Self::MovSigned(src, dest) => {
|
||||||
// }
|
*cpu.reg(dest) = sign_extend(cpu.get(src));
|
||||||
// Instruction::Sub(reg0, reg1, reg2) => {
|
}
|
||||||
// *cpu.reg(reg2) = sub(cpu.get(reg0), cpu.get(reg1))
|
|
||||||
// }
|
|
||||||
// Instruction::Inc(reg0) => *cpu.reg(reg0) = inc(cpu.get(reg0)),
|
|
||||||
// Instruction::Dec(reg0) => *cpu.reg(reg0) = dec(cpu.get(reg0)),
|
|
||||||
// Instruction::Shl(reg0) => *cpu.reg(reg0) = shl(cpu.get(reg0)),
|
|
||||||
// Instruction::Shr(reg0) => *cpu.reg(reg0) = shr(cpu.get(reg0)),
|
|
||||||
// Instruction::And(reg0, reg1, reg2) => {
|
|
||||||
// *cpu.reg(reg2) = and(cpu.get(reg0), cpu.get(reg1))
|
|
||||||
// }
|
|
||||||
// Instruction::Orr(reg0, reg1, reg2) => *cpu.reg(reg2) = or(cpu.get(reg0), cpu.get(reg1)),
|
|
||||||
// Instruction::Not(reg0, reg1) => *cpu.reg(reg1) = not(cpu.get(reg0)),
|
|
||||||
// Instruction::Xor(reg0, reg1, reg2) => {
|
|
||||||
// *cpu.reg(reg2) = xor(cpu.get(reg0), cpu.get(reg1))
|
|
||||||
// }
|
|
||||||
// Instruction::Nnd(reg0, reg1, reg2) => {
|
|
||||||
// *cpu.reg(reg2) = nand(cpu.get(reg0), cpu.get(reg1))
|
|
||||||
// }
|
|
||||||
// Instruction::Nor(reg0, reg1, reg2) => {
|
|
||||||
// *cpu.reg(reg2) = nor(cpu.get(reg0), cpu.get(reg1))
|
|
||||||
// }
|
|
||||||
// Instruction::Xnr(reg0, reg1, reg2) => {
|
|
||||||
// *cpu.reg(reg2) = xnor(cpu.get(reg0), cpu.get(reg1))
|
|
||||||
// }
|
|
||||||
// Instruction::Cmp(reg0, reg1) => cpu.cmp(cpu.get(reg0), cpu.get(reg1)),
|
|
||||||
|
|
||||||
// Instruction::Jmp(addr) => cpu.jump(addr),
|
// Loads a byte from memory address (base + offset) into DestReg. The effective address must be byte-aligned.
|
||||||
// Instruction::Jeq(addr) if cpu.get_flag(Flag::Equal) => cpu.jump(addr),
|
Self::LoadByte(base, offset, dest) => {
|
||||||
// Instruction::Jne(addr) if !cpu.get_flag(Flag::Equal) => cpu.jump(addr),
|
*cpu.reg(dest) = cpu.memory.read_byte(cpu.get(base) + offset as u32) as u32;
|
||||||
// Instruction::Jgt(addr) if cpu.get_flag(Flag::GreaterThan) => cpu.jump(addr),
|
}
|
||||||
// Instruction::Jlt(addr) if cpu.get_flag(Flag::LessThan) => cpu.jump(addr),
|
|
||||||
// Instruction::Jle(addr) if cpu.get_flag(Flag::LessThan) || cpu.get_flag(Flag::Equal) => {
|
// Loads a sign-extended byte from memory address (base + offset) into DestReg. The effective address must be byte-aligned.
|
||||||
// cpu.jump(addr)
|
Self::LoadByteSigned(base, offset, dest) => {
|
||||||
// }
|
*cpu.reg(dest) =
|
||||||
// Instruction::Jge(addr)
|
sign_extend(cpu.memory.read_byte(cpu.get(base) + offset as u32) as u32);
|
||||||
// if cpu.get_flag(Flag::GreaterThan) || cpu.get_flag(Flag::Equal) =>
|
}
|
||||||
// {
|
|
||||||
// cpu.jump(addr)
|
// Loads a half-word from memory address (base + offset) into DestReg. The effective address must be 2-byte-aligned.
|
||||||
// }
|
Self::LoadHalfword(base, offset, dest) => {
|
||||||
// Instruction::Hlt => (),
|
// we read an entire word, then right shift so we only get the first half of the word
|
||||||
// Instruction::Int(interrupt_code) => {
|
*cpu.reg(dest) = cpu.memory.read_word(cpu.get(base) + offset as u32) >> 16;
|
||||||
// cpu.begin_interrupt(*interrupt_code);
|
}
|
||||||
// }
|
|
||||||
// Instruction::Cll(addr) => {
|
// Loads a sign-extended half-word from memory address (base + offset) into DestReg. The effective address must be 2-byte-aligned.
|
||||||
// cpu.ret = cpu.pcx;
|
Self::LoadHalfwordSigned(base, offset, dest) => {
|
||||||
// cpu.pcx = addr.force_resolve();
|
*cpu.reg(dest) =
|
||||||
// }
|
sign_extend(cpu.memory.read_word(cpu.get(base) + offset as u32) >> 16);
|
||||||
// Instruction::Ret => {
|
}
|
||||||
// cpu.pcx = cpu.ret;
|
|
||||||
// }
|
// Loads a word from memory address (base + offset) into DestReg. The effective address must be 4-byte-aligned.
|
||||||
// Instruction::Psh(reg) => {
|
Self::LoadWord(base, offset, dest) => {
|
||||||
// cpu.push(cpu.get(reg));
|
*cpu.reg(dest) = cpu.memory.read_word(cpu.get(base) + offset as u32);
|
||||||
// }
|
}
|
||||||
// Instruction::Pop(reg) => {
|
|
||||||
// let val = cpu.pop();
|
// Stores a byte from SrcReg in memory address (base + offset) The effective address must be byte-aligned.
|
||||||
// *cpu.reg(reg) = val;
|
Self::StoreByte(src, offset, base) => {
|
||||||
// }
|
cpu.memory
|
||||||
// Instruction::Lui(addr) => {
|
.write_byte(cpu.get(base) + offset as u32, cpu.get(src) as u8);
|
||||||
// *cpu.reg(&Reg::MDR) = match addr {
|
}
|
||||||
// Address::Literal(addr) => *addr,
|
|
||||||
// Address::Register(reg) => cpu.get(reg),
|
// Stores a half-word from SrcReg in memory address (base + offset) The effective address must be 2-byte-aligned.
|
||||||
// _ => panic!("invalid address"),
|
Self::StoreHalfword(src, offset, base) => {
|
||||||
// };
|
// split the value into bytes and then write two bytes
|
||||||
// }
|
let bytes = (cpu.get(src) as u16).to_be_bytes();
|
||||||
// Instruction::Irt => {
|
cpu.memory
|
||||||
// cpu.end_interrupt();
|
.write_byte(cpu.get(base) + offset as u32, bytes[0]);
|
||||||
// }
|
cpu.memory
|
||||||
_ => {}
|
.write_byte(cpu.get(base) + offset as u32 + 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) = (cpu.get(reg) & 0xFFFF_0000) | imm as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unconditionally jumps to the calculated address or direct address
|
||||||
|
Self::Jump(reg, offset) => cpu.jump(reg, offset),
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increments the value in the given register
|
||||||
|
Self::Increment(reg) => *cpu.reg(reg) = inc(cpu.get(reg)),
|
||||||
|
|
||||||
|
// Decrements the value in the given register
|
||||||
|
Self::Decrement(reg) => *cpu.reg(reg) = dec(cpu.get(reg)),
|
||||||
|
|
||||||
|
// Left shifts the value in Reg by the given amount (either a register, or a literal value)
|
||||||
|
Self::ShiftLeft(src, reg, imm) => {
|
||||||
|
let regval = cpu.get(reg);
|
||||||
|
*cpu.reg(reg) = shl(
|
||||||
|
cpu.get(src),
|
||||||
|
if regval != 0 { regval as u8 } else { imm as u8 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right shifts the value in Reg by the given amount (either a register, or a literal value).
|
||||||
|
Self::ShiftRight(src, reg, imm) => {
|
||||||
|
let regval = cpu.get(reg);
|
||||||
|
*cpu.reg(reg) = shr(
|
||||||
|
cpu.get(src),
|
||||||
|
if regval != 0 { regval as u8 } else { imm as u8 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)),
|
||||||
|
|
||||||
|
// 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)),
|
||||||
|
|
||||||
|
// 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)),
|
||||||
|
|
||||||
|
// 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)),
|
||||||
|
|
||||||
|
// 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)),
|
||||||
|
|
||||||
|
// 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)),
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Initiates an interrupt with the given 8 bit interrupt code.
|
||||||
|
Triggering an interrupt invokes the following behaviour
|
||||||
|
- The return address is saved to the RET register
|
||||||
|
- The stack base ptr is set to the kernel stack.
|
||||||
|
*/
|
||||||
|
Self::Interrupt(interrupt_code) => {
|
||||||
|
cpu.begin_interrupt(interrupt_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns from an interrupt,
|
||||||
|
Self::IntReturn => {
|
||||||
|
cpu.end_interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Halts the processor.
|
||||||
|
Self::Halt => {
|
||||||
|
cpu.halted = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,12 +373,12 @@ fn dec(a: u32) -> u32 {
|
|||||||
a - 1
|
a - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shl(a: u32) -> u32 {
|
fn shl(a: u32, amount: u8) -> u32 {
|
||||||
a << 1
|
a << amount
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shr(a: u32) -> u32 {
|
fn shr(a: u32, amount: u8) -> u32 {
|
||||||
a >> 1
|
a >> amount
|
||||||
}
|
}
|
||||||
|
|
||||||
fn or(a: u32, b: u32) -> u32 {
|
fn or(a: u32, b: u32) -> u32 {
|
||||||
@@ -287,3 +404,17 @@ fn nor(a: u32, b: u32) -> u32 {
|
|||||||
fn xnor(a: u32, b: u32) -> u32 {
|
fn xnor(a: u32, b: u32) -> u32 {
|
||||||
!(a ^ b)
|
!(a ^ b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sign_extend(val: u32) -> u32 {
|
||||||
|
let (mask, sign_bit): (u32, u8) = match val {
|
||||||
|
0..=0xFF => (0xFFFFFF00, 7),
|
||||||
|
0..=0xFFFF => (0xFFFF0000, 15),
|
||||||
|
_ => (0x00000000, 31),
|
||||||
|
};
|
||||||
|
|
||||||
|
if val & (1 << sign_bit) != 0 {
|
||||||
|
val | mask
|
||||||
|
} else {
|
||||||
|
val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
use crate::{common::instructions::Register, emulator::{system::model::{Command, Running, State}, ui::interface::Component}};
|
use crate::{
|
||||||
|
common::instructions::Register,
|
||||||
|
emulator::{
|
||||||
|
system::model::{Command, Running, State},
|
||||||
|
ui::interface::Component,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct ControlPanel {
|
pub struct ControlPanel {
|
||||||
visible: bool,
|
visible: bool,
|
||||||
@@ -25,66 +31,63 @@ impl Component for ControlPanel {
|
|||||||
&mut self.visible
|
&mut self.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"Control Panel"
|
"Control Panel"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
// Pause / Run
|
// Pause / Run
|
||||||
if ui
|
if ui
|
||||||
.button(if state.running == Running::Running {
|
.button(if state.running == Running::Running {
|
||||||
"Pause"
|
"Pause"
|
||||||
} else {
|
} else {
|
||||||
"Run"
|
"Run"
|
||||||
})
|
})
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
if state.running == Running::Running {
|
if state.running == Running::Running {
|
||||||
self.sender.send(Command::Stop).unwrap_or_else(|_| state.error = Some("Failed to send command".to_string()));
|
self.sender.send(Command::Stop).unwrap_or_else(|_| {
|
||||||
} else {
|
state.error = Some("Failed to send command".to_string())
|
||||||
self.sender.send(Command::Start).unwrap_or_else(|_| 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())
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Step
|
// Step
|
||||||
if ui.button("Step").clicked() {
|
if ui.button("Step").clicked() {
|
||||||
self.sender.send(Command::Step).unwrap_or_else(|_| state.error = Some("Failed to send command".to_string()));
|
self.sender
|
||||||
}
|
.send(Command::Step)
|
||||||
|
.unwrap_or_else(|_| state.error = Some("Failed to send command".to_string()));
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
// Status info
|
|
||||||
ui.label(format!(
|
|
||||||
"Status: {}",
|
|
||||||
match state.running {
|
|
||||||
Running::Running => "Running",
|
|
||||||
Running::Paused => "Paused",
|
|
||||||
Running::Halted => "Halted",
|
|
||||||
}
|
}
|
||||||
));
|
|
||||||
ui.label(format!(
|
|
||||||
"Instructions: {}",
|
|
||||||
state.instructions
|
|
||||||
));
|
|
||||||
ui.label(format!("PC: 0x{:08X}", state.reg_file.get(Register::Pcx)));
|
|
||||||
ui.label(format!(
|
|
||||||
"Last Instruction: {:?}",
|
|
||||||
"TODO: DECODE INSTRUCTION" // TODO: decode instruction
|
|
||||||
// Instruction::decode(state.current_state.cir)
|
|
||||||
));
|
|
||||||
});
|
|
||||||
|
|
||||||
render_register_table(state, ui, ctx);
|
ui.separator();
|
||||||
|
|
||||||
|
// Status info
|
||||||
|
ui.label(format!(
|
||||||
|
"Status: {}",
|
||||||
|
match state.running {
|
||||||
|
Running::Running => "Running",
|
||||||
|
Running::Paused => "Paused",
|
||||||
|
Running::Halted => "Halted",
|
||||||
|
}
|
||||||
|
));
|
||||||
|
ui.label(format!("Instructions: {}", state.instructions));
|
||||||
|
ui.label(format!("PC: 0x{:08X}", state.reg_file.get(Register::Pcx)));
|
||||||
|
ui.label(format!(
|
||||||
|
"Last Instruction: {:?}",
|
||||||
|
"TODO: DECODE INSTRUCTION" // TODO: decode instruction
|
||||||
|
// Instruction::decode(state.current_state.cir)
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
render_register_table(state, ui, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn render_register_table(state: &mut State, ui: &mut egui::Ui, _ctx: &egui::Context) {
|
fn render_register_table(state: &mut State, ui: &mut egui::Ui, _ctx: &egui::Context) {
|
||||||
// Left column - Registers
|
// Left column - Registers
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
@@ -111,12 +114,8 @@ 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(format!("{}", reg.0));
|
||||||
ui.label(format!(
|
ui.label(format!("0x{:08X} ({})", reg.1, reg.1,));
|
||||||
"0x{:08X} ({})",
|
|
||||||
reg.1,
|
|
||||||
reg.1,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
use crate::{common::instructions::{Address, Interrupt}, emulator::system::model::{Command, Running, State}};
|
use crate::{
|
||||||
|
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 {
|
||||||
@@ -56,10 +59,18 @@ impl EmulatorUI {
|
|||||||
pub fn add_component(&mut self, component: Box<dyn Component>) {
|
pub fn add_component(&mut self, component: Box<dyn Component>) {
|
||||||
self.components.push(component);
|
self.components.push(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_state(&mut self) {
|
||||||
|
while let Ok(state) = self.receiver.try_recv() {
|
||||||
|
self.state = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
if let Running::Running = self.state.running {
|
if let Running::Running = self.state.running {
|
||||||
ctx.request_repaint();
|
ctx.request_repaint();
|
||||||
}
|
}
|
||||||
@@ -89,10 +100,10 @@ impl eframe::App for EmulatorUI {
|
|||||||
let mut visible = *c.visible();
|
let mut visible = *c.visible();
|
||||||
if visible == true {
|
if visible == true {
|
||||||
egui::Window::new(c.name())
|
egui::Window::new(c.name())
|
||||||
.open(&mut visible)
|
.open(&mut visible)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
c.render(&mut self.state, ui, ctx);
|
c.render(&mut self.state, ui, ctx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
*c.visible() = visible;
|
*c.visible() = visible;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
use std::{num::ParseIntError, sync::mpsc::Sender};
|
||||||
|
|
||||||
|
use crate::emulator::{system::model::{Command, State}, ui::interface::Component};
|
||||||
|
|
||||||
|
pub struct MemoryInspector {
|
||||||
|
view_size: u32,
|
||||||
|
view_addr: u32,
|
||||||
|
visible: bool,
|
||||||
|
addr_input: String,
|
||||||
|
sender: Sender<Command>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryInspector {
|
||||||
|
pub fn new(sender: Sender<Command>) -> Self {
|
||||||
|
Self {
|
||||||
|
view_size: 256,
|
||||||
|
view_addr: 0,
|
||||||
|
visible: false,
|
||||||
|
addr_input: String::new(),
|
||||||
|
sender,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for MemoryInspector {
|
||||||
|
fn category(&self) -> super::interface::Category {
|
||||||
|
super::interface::Category::Memory
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"Memory Inspector"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visible(&mut self) -> &mut bool {
|
||||||
|
&mut self.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, state: &mut State, ui: &mut egui::Ui, ctx: &egui::Context) {
|
||||||
|
// Right column - Memory
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.heading("Memory Inspector");
|
||||||
|
ui.add_space(10.0);
|
||||||
|
|
||||||
|
// Address input section
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Address:");
|
||||||
|
|
||||||
|
let address_response = ui.add(
|
||||||
|
egui::TextEdit::singleline(&mut self.addr_input)
|
||||||
|
.hint_text("0x1000 or 4096")
|
||||||
|
.desired_width(150.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
ui.add_space(10.0);
|
||||||
|
|
||||||
|
// Search button
|
||||||
|
let search_clicked = ui.button("🔍 Search").clicked();
|
||||||
|
|
||||||
|
// Handle Enter key in text field
|
||||||
|
let enter_pressed =
|
||||||
|
address_response.lost_focus() && ctx.input(|i| i.key_pressed(egui::Key::Enter));
|
||||||
|
|
||||||
|
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();
|
||||||
|
} else {
|
||||||
|
state.error = Some("Invalid address".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.label("(hex or decimal)");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show input error if any
|
||||||
|
if let Some(error) = &state.error {
|
||||||
|
ui.colored_label(egui::Color32::RED, format!("Error: {}", error));
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.add_space(10.0);
|
||||||
|
|
||||||
|
// Memory table
|
||||||
|
egui::ScrollArea::vertical()
|
||||||
|
.auto_shrink(true)
|
||||||
|
.id_salt("memory_inspector_scroll")
|
||||||
|
.show(ui, |ui| {
|
||||||
|
egui::Grid::new("memory_grid")
|
||||||
|
.spacing([12.0, 2.0])
|
||||||
|
.min_col_width(5.0)
|
||||||
|
.striped(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
// Header
|
||||||
|
ui.strong("Address");
|
||||||
|
|
||||||
|
for i in 0..4 {
|
||||||
|
ui.strong(format!("{:X}", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.strong("Decimal");
|
||||||
|
ui.strong("Instruction");
|
||||||
|
|
||||||
|
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 &byte in chunk {
|
||||||
|
ui.monospace(format!("{:02X}", byte));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill remaining columns if last row is incomplete
|
||||||
|
for _ in chunk.len()..4 {
|
||||||
|
ui.label("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// combine all 4 bytes in the chunk into a u32
|
||||||
|
let combined =
|
||||||
|
chunk.iter().fold(0u32, |acc, &byte| acc << 8 | byte as u32);
|
||||||
|
|
||||||
|
ui.monospace(format!("{}", combined));
|
||||||
|
// ui.monospace(format!("{:?}", Instruction::decode(combined)));
|
||||||
|
ui.monospace("TODO! instruction");
|
||||||
|
|
||||||
|
ui.end_row();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_address(address: &str) -> Result<u32, ParseIntError> {
|
||||||
|
if address.starts_with("0x") {
|
||||||
|
return u32::from_str_radix(&address[2..], 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
if address.starts_with("0b") {
|
||||||
|
return u32::from_str_radix(&address[2..], 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if address.starts_with("0o") {
|
||||||
|
return u32::from_str_radix(&address[1..], 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32::from_str_radix(address, 10)
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
pub mod control_unit;
|
||||||
pub mod interface;
|
pub mod interface;
|
||||||
pub mod menu;
|
pub mod menu;
|
||||||
pub mod control_unit;
|
pub mod memory_inspector;
|
||||||
|
pub mod stack_inspector;
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
use crate::{common::instructions::Register, emulator::{system::model::State, ui::interface::{Component, EmulatorUI}}};
|
||||||
|
|
||||||
|
pub struct StackInspector {
|
||||||
|
visible: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StackInspector {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for StackInspector {
|
||||||
|
fn visible(&mut self) -> &mut bool {
|
||||||
|
&mut self.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"Stack Inspector"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn category(&self) -> super::interface::Category {
|
||||||
|
super::interface::Category::Memory
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, state: &mut State, ui: &mut egui::Ui, ctx: &egui::Context) {
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.heading("Stack Inspector");
|
||||||
|
egui::ScrollArea::vertical()
|
||||||
|
.id_salt("stack_inspector_scroll")
|
||||||
|
.show(ui, |ui| {
|
||||||
|
egui::Grid::new("stack_grid")
|
||||||
|
.num_columns(2)
|
||||||
|
.spacing([40.0, 4.0])
|
||||||
|
.striped(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label("Address");
|
||||||
|
ui.label("Value");
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
|
for (i, value) in state.stack_view.iter().take(32).enumerate() {
|
||||||
|
ui.label(format!(
|
||||||
|
"{} [{}]",
|
||||||
|
i,
|
||||||
|
state.reg_file.get(Register::Spr) - i as u32 * 4
|
||||||
|
));
|
||||||
|
ui.label(format!("0x{:08X} ({})", value, value));
|
||||||
|
ui.end_row();
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.stack_view.is_empty() {
|
||||||
|
ui.label("(empty)");
|
||||||
|
ui.label("-");
|
||||||
|
ui.end_row();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+13
-16
@@ -1,10 +1,14 @@
|
|||||||
use std::{sync::{Arc, Mutex}, thread};
|
use std::{
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread,
|
||||||
|
};
|
||||||
|
|
||||||
use dsa_rs::emulator::{system::{emulator::run_emulator, memory::MainStore, processor::Processor}, ui::{control_unit::ControlPanel, interface::EmulatorUI}};
|
use dsa_rs::emulator::{
|
||||||
use egui::Memory;
|
system::{emulator::run_emulator, memory::MainStore, processor::Processor},
|
||||||
|
ui::{control_unit::ControlPanel, interface::EmulatorUI, memory_inspector::MemoryInspector, stack_inspector::StackInspector},
|
||||||
|
};
|
||||||
|
|
||||||
fn main() -> Result<(), eframe::Error> {
|
fn main() -> Result<(), eframe::Error> {
|
||||||
|
|
||||||
// Initialize Channels
|
// Initialize Channels
|
||||||
let (cmd_sender, cmd_receiver) = std::sync::mpsc::channel();
|
let (cmd_sender, cmd_receiver) = std::sync::mpsc::channel();
|
||||||
let (state_sender, state_receiver) = std::sync::mpsc::channel();
|
let (state_sender, state_receiver) = std::sync::mpsc::channel();
|
||||||
@@ -12,13 +16,8 @@ fn main() -> Result<(), eframe::Error> {
|
|||||||
let mainstore = MainStore::new();
|
let mainstore = MainStore::new();
|
||||||
let processor = Processor::new(Box::new(mainstore));
|
let processor = Processor::new(Box::new(mainstore));
|
||||||
|
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
run_emulator(
|
run_emulator(cmd_receiver, state_sender, Arc::new(Mutex::new(processor)));
|
||||||
cmd_receiver,
|
|
||||||
state_sender,
|
|
||||||
Arc::new(Mutex::new(processor)),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create UI
|
// Create UI
|
||||||
@@ -28,13 +27,11 @@ fn main() -> Result<(), eframe::Error> {
|
|||||||
let control_unit = ControlPanel::new(cmd_sender.clone());
|
let control_unit = ControlPanel::new(cmd_sender.clone());
|
||||||
ui.add_component(Box::new(control_unit));
|
ui.add_component(Box::new(control_unit));
|
||||||
|
|
||||||
|
let mem_inspector = MemoryInspector::new(cmd_sender.clone());
|
||||||
|
ui.add_component(Box::new(mem_inspector));
|
||||||
|
|
||||||
|
let stack_inspector = StackInspector::new();
|
||||||
|
ui.add_component(Box::new(stack_inspector));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Run UI
|
// Run UI
|
||||||
let options = eframe::NativeOptions {
|
let options = eframe::NativeOptions {
|
||||||
|
|||||||
Reference in New Issue
Block a user