working emulator UI - just need to implement the instruction set
This commit is contained in:
Generated
+3973
File diff suppressed because it is too large
Load Diff
@@ -8,3 +8,5 @@ name = "dsa_rs"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
eframe = "0.31.1"
|
||||
egui = "0.31.1"
|
||||
|
||||
+187
-52
@@ -1,7 +1,138 @@
|
||||
type Offset = u16;
|
||||
type Immediate = u16;
|
||||
|
||||
pub enum Interrupt {
|
||||
Software(u8),
|
||||
}
|
||||
|
||||
pub type Address = u32;
|
||||
|
||||
impl Interrupt {
|
||||
fn as_u8(&self) -> u8 {
|
||||
match self {
|
||||
Interrupt::Software(code) => *code,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Interrupt {
|
||||
fn from(_code: u8) -> Self {
|
||||
todo!("implement this once a hardware interrupt convention is established");
|
||||
#[allow(unreachable_code)]
|
||||
Interrupt::Software(_code)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Register {
|
||||
// general purpose registers
|
||||
Rg0,
|
||||
Rg1,
|
||||
Rg2,
|
||||
Rg3,
|
||||
Rg4,
|
||||
Rg5,
|
||||
Rg6,
|
||||
Rg7,
|
||||
Rg8,
|
||||
Rg9,
|
||||
Rga,
|
||||
Rgb,
|
||||
Rgc,
|
||||
Rgd,
|
||||
Rge,
|
||||
Rgf,
|
||||
|
||||
// special purpose registers
|
||||
Acc,
|
||||
Spr,
|
||||
Bpr,
|
||||
Ret,
|
||||
Idr,
|
||||
Mmr,
|
||||
Zero,
|
||||
None,
|
||||
|
||||
// system registers - can't be written to by instructions.
|
||||
Mar,
|
||||
Mdr,
|
||||
Sts,
|
||||
Cir,
|
||||
Pcx,
|
||||
}
|
||||
|
||||
impl From<u8> for Register {
|
||||
fn from(idx: u8) -> Register {
|
||||
match idx {
|
||||
// system registers are not indexable in the reg file so they cannot be modified by instructions.
|
||||
0x0 => Register::Rg0,
|
||||
0x1 => Register::Rg1,
|
||||
0x2 => Register::Rg2,
|
||||
0x3 => Register::Rg3,
|
||||
0x4 => Register::Rg4,
|
||||
0x5 => Register::Rg5,
|
||||
0x6 => Register::Rg6,
|
||||
0x7 => Register::Rg7,
|
||||
0x8 => Register::Rg8,
|
||||
0x9 => Register::Rg9,
|
||||
0xA => Register::Rga,
|
||||
0xB => Register::Rgb,
|
||||
0xC => Register::Rgc,
|
||||
0xD => Register::Rgd,
|
||||
0xE => Register::Rge,
|
||||
0xF => Register::Rgf,
|
||||
0x10 => Register::Acc,
|
||||
0x11 => Register::Spr,
|
||||
0x12 => Register::Bpr,
|
||||
0x13 => Register::Ret,
|
||||
0x14 => Register::Idr,
|
||||
0x15 => Register::Mmr,
|
||||
0x16 => Register::Zero,
|
||||
0x17 => Register::None,
|
||||
_ => panic!("invalid register"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Register {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Register::Rg0 => write!(f, "Rg0"),
|
||||
Register::Rg1 => write!(f, "Rg1"),
|
||||
Register::Rg2 => write!(f, "Rg2"),
|
||||
Register::Rg3 => write!(f, "Rg3"),
|
||||
Register::Rg4 => write!(f, "Rg4"),
|
||||
Register::Rg5 => write!(f, "Rg5"),
|
||||
Register::Rg6 => write!(f, "Rg6"),
|
||||
Register::Rg7 => write!(f, "Rg7"),
|
||||
Register::Rg8 => write!(f, "Rg8"),
|
||||
Register::Rg9 => write!(f, "Rg9"),
|
||||
Register::Rga => write!(f, "Rga"),
|
||||
Register::Rgb => write!(f, "Rgb"),
|
||||
Register::Rgc => write!(f, "Rgc"),
|
||||
Register::Rgd => write!(f, "Rgd"),
|
||||
Register::Rge => write!(f, "Rge"),
|
||||
Register::Rgf => write!(f, "Rgf"),
|
||||
Register::Acc => write!(f, "Acc"),
|
||||
Register::Spr => write!(f, "Spr"),
|
||||
Register::Bpr => write!(f, "Bpr"),
|
||||
Register::Ret => write!(f, "Ret"),
|
||||
Register::Idr => write!(f, "Idr"),
|
||||
Register::Mmr => write!(f, "Mmr"),
|
||||
Register::Zero => write!(f, "Zero"),
|
||||
Register::None => write!(f, "None"),
|
||||
Register::Mar => write!(f, "Mar"),
|
||||
Register::Mdr => write!(f, "Mdr"),
|
||||
Register::Sts => write!(f, "Sts"),
|
||||
Register::Cir => write!(f, "Cir"),
|
||||
Register::Pcx => write!(f, "Pcx"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Instruction {
|
||||
// No-op
|
||||
Nop,
|
||||
|
||||
|
||||
// Data transfer instructions
|
||||
Mov(Register, Register),
|
||||
MovSigned(Register, Register),
|
||||
@@ -55,61 +186,65 @@ pub enum Instruction {
|
||||
Halt,
|
||||
}
|
||||
|
||||
type Offset = u16;
|
||||
type Immediate = u16;
|
||||
impl Instruction {
|
||||
pub fn encode(&self) -> u32 {
|
||||
todo!("imlement instruction encoding")
|
||||
}
|
||||
|
||||
pub enum Interrupt {
|
||||
Software(u8)
|
||||
pub fn decode(data: u32) -> Self {
|
||||
// TODO: this needs to actually decode something
|
||||
Instruction::Nop
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Interrupt {
|
||||
fn into(self) -> u8 {
|
||||
impl std::fmt::Display for Instruction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Interrupt::Software(code) => code,
|
||||
Instruction::Nop => write!(f, "No Operation"),
|
||||
Instruction::Mov(a, b) => write!(f, "MOV {}, {}", a, b),
|
||||
Instruction::MovSigned(a, b) => write!(f, "MOVS {}, {}", a, b),
|
||||
Instruction::LoadByte(a, b, c) => write!(f, "LDB {}, {}, {}", a, b, c),
|
||||
Instruction::LoadByteSigned(a, b, c) => write!(f, "LDBS {}, {}, {}", a, b, c),
|
||||
Instruction::LoadHalfword(a, b, c) => write!(f, "LDH {}, {}, {}", a, b, c),
|
||||
Instruction::LoadHalfwordSigned(a, b, c) => write!(f, "LDHS {}, {}, {}", a, b, c),
|
||||
Instruction::LoadWord(a, b, c) => write!(f, "LDW {}, {}, {}", a, b, c),
|
||||
Instruction::LoadWordSigned(a, b, c) => write!(f, "LDWS {}, {}, {}", a, b, c),
|
||||
|
||||
Instruction::StoreByte(a, b, c) => write!(f, "STB {}, {}, {}", a, b, c),
|
||||
Instruction::StoreHalfword(a, b, c) => write!(f, "STH {}, {}, {}", a, b, c),
|
||||
Instruction::StoreWord(a, b, c) => write!(f, "STW {}, {}, {}", a, b, c),
|
||||
|
||||
Instruction::LoadLowerImmediate(a, b) => write!(f, "LLI {}, {}", a, b),
|
||||
Instruction::LoadUpperImmediate(a, b) => write!(f, "LUI {}, {}", a, b),
|
||||
|
||||
Instruction::Jump(a, b) => write!(f, "JMP {}, {}", a, b),
|
||||
Instruction::JumpEq(a, b) => write!(f, "JEQ {}, {}", a, b),
|
||||
Instruction::JumpNeq(a, b) => write!(f, "JNEQ {}, {}", a, b),
|
||||
Instruction::JumpGt(a, b) => write!(f, "JGT {}, {}", a, b),
|
||||
Instruction::JumpGe(a, b) => write!(f, "JGE {}, {}", a, b),
|
||||
Instruction::JumpLt(a, b) => write!(f, "JLT {}, {}", a, b),
|
||||
Instruction::JumpLe(a, b) => write!(f, "JLE {}, {}", a, b),
|
||||
|
||||
Instruction::Compare(a, b) => write!(f, "CMP {}, {}", a, b),
|
||||
|
||||
Instruction::Add(a, b, c) => write!(f, "ADD {}, {}, {}", a, b, c),
|
||||
Instruction::Sub(a, b, c) => write!(f, "SUB {}, {}, {}", a, b, c),
|
||||
Instruction::Increment(a) => write!(f, "INC {}", a),
|
||||
Instruction::Decrement(a) => write!(f, "DEC {}", a),
|
||||
Instruction::ShiftLeft(a, b, c) => write!(f, "SHL {}, {}, {}", a, b, c),
|
||||
Instruction::ShiftRight(a, b, c) => write!(f, "SHR {}, {}, {}", a, b, c),
|
||||
|
||||
Instruction::And(a, b, c) => write!(f, "AND {}, {}, {}", a, b, c),
|
||||
Instruction::Or(a, b, c) => write!(f, "OR {}, {}, {}", a, b, c),
|
||||
Instruction::Not(a, b) => write!(f, "NOT {}, {}", a, b),
|
||||
Instruction::Xor(a, b, c) => write!(f, "XOR {}, {}, {}", a, b, c),
|
||||
Instruction::Nand(a, b, c) => write!(f, "NAND {}, {}, {}", a, b, c),
|
||||
Instruction::Nor(a, b, c) => write!(f, "NOR {}, {}, {}", a, b, c),
|
||||
Instruction::Xnor(a, b, c) => write!(f, "XNOR {}, {}, {}", a, b, c),
|
||||
|
||||
Instruction::Interrupt(a) => write!(f, "INT {}", a.as_u8()),
|
||||
Instruction::IntReturn => write!(f, "INTR"),
|
||||
Instruction::Halt => write!(f, "HALT"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Interrupt {
|
||||
fn from(code: u8) -> Self {
|
||||
todo!("implement this once a hardware interrupt convention is established");
|
||||
Interrupt::Software(code)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Register {
|
||||
// general purpose registers
|
||||
Rg0,
|
||||
Rg1,
|
||||
Rg2,
|
||||
Rg3,
|
||||
Rg4,
|
||||
Rg5,
|
||||
Rg6,
|
||||
Rg7,
|
||||
Rg8,
|
||||
Rg9,
|
||||
Rg10,
|
||||
Rg11,
|
||||
Rg12,
|
||||
Rg13,
|
||||
Rg14,
|
||||
Rg15,
|
||||
|
||||
// special purpose registers
|
||||
Acc,
|
||||
Spr,
|
||||
Bpr,
|
||||
Ret,
|
||||
Idr,
|
||||
Mmr,
|
||||
Zero,
|
||||
None,
|
||||
|
||||
// system registers - can't be written to by instructions.
|
||||
Mar,
|
||||
Mdr,
|
||||
Sts,
|
||||
Cir,
|
||||
Pcx,
|
||||
}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
pub mod instructions;
|
||||
pub mod instructions;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod ui;
|
||||
pub mod system;
|
||||
@@ -0,0 +1,131 @@
|
||||
use std::{sync::{mpsc::{self, Receiver, Sender}, Arc, Mutex}, thread, time::Duration};
|
||||
|
||||
use crate::{common::instructions::{Instruction, Register}, emulator::system::{model::{Command, Running, State}, processor::Processor}};
|
||||
|
||||
pub fn run_emulator(
|
||||
cmd_rx: Receiver<Command>,
|
||||
state_tx: Sender<State>,
|
||||
cpu: Arc<Mutex<Processor>>,
|
||||
) {
|
||||
let mut running = Running::Paused;
|
||||
let mut addr = 0u32;
|
||||
|
||||
// Send initial state
|
||||
let memory_view = cpu.lock().unwrap().memory.read_range(addr, 256);
|
||||
let initial_state = state(&cpu, &running, 0, memory_view);
|
||||
let _ = state_tx.send(initial_state);
|
||||
|
||||
let mut instruction_count = 0;
|
||||
|
||||
loop {
|
||||
let cmd = if running == Running::Running {
|
||||
match cmd_rx.try_recv() {
|
||||
Ok(cmd) => Some(cmd),
|
||||
Err(mpsc::TryRecvError::Empty) => None,
|
||||
Err(mpsc::TryRecvError::Disconnected) => break,
|
||||
}
|
||||
} else {
|
||||
match cmd_rx.recv() {
|
||||
Ok(cmd) => Some(cmd),
|
||||
Err(_) => break,
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(cmd) = cmd {
|
||||
match cmd {
|
||||
Command::Start => {
|
||||
running = Running::Running;
|
||||
println!("Emulator started");
|
||||
}
|
||||
Command::Stop => {
|
||||
running = Running::Paused;
|
||||
println!("Emulator stopped");
|
||||
}
|
||||
Command::Reset => {
|
||||
running = Running::Paused;
|
||||
if let Ok(mut cpu_lock) = cpu.lock() {
|
||||
cpu_lock.reset();
|
||||
}
|
||||
instruction_count = 0;
|
||||
println!("Emulator rebooted");
|
||||
}
|
||||
Command::Step => {
|
||||
running = Running::Paused;
|
||||
// Execute one cycle
|
||||
if let Ok(mut cpu_lock) = cpu.lock() {
|
||||
cpu_lock.cycle(); // or advance() - whatever your method is called
|
||||
}
|
||||
instruction_count += 1;
|
||||
println!("Stepped one instruction");
|
||||
}
|
||||
Command::Read(new, size) => {
|
||||
addr = new as u32;
|
||||
println!("Memory view for address 0x{:08x}", addr);
|
||||
}
|
||||
Command::Write(offset, data) => {
|
||||
if let Ok(mut cpu_lock) = cpu.lock() {
|
||||
cpu_lock.memory.write_range(offset, data);
|
||||
}
|
||||
println!("Program loaded");
|
||||
}
|
||||
Command::Interrupt(interrupt) => {
|
||||
todo!("implement interrupts")
|
||||
}
|
||||
}
|
||||
|
||||
let memory_view = cpu.lock().unwrap().memory.read_range(addr, 256);
|
||||
let state = state(&cpu, &running, instruction_count, memory_view);
|
||||
|
||||
let _ = state_tx.send(state);
|
||||
}
|
||||
|
||||
if running == Running::Running {
|
||||
let mut update = false;
|
||||
// Execute one cycle
|
||||
if let Ok(mut cpu_lock) = cpu.lock() {
|
||||
cpu_lock.cycle();
|
||||
if let Instruction::Halt = Instruction::decode(cpu_lock.get(Register::Cir)) {
|
||||
running = Running::Halted;
|
||||
update = true;
|
||||
}
|
||||
}
|
||||
|
||||
instruction_count += 1;
|
||||
|
||||
// Send state updates every 100 instructions
|
||||
if instruction_count % 100 == 0 {
|
||||
update = true;
|
||||
}
|
||||
|
||||
if update {
|
||||
let memory_view = cpu.lock().unwrap().memory.read_range(addr, 256);
|
||||
let state = state(&cpu, &running, instruction_count, memory_view);
|
||||
let _ = state_tx.send(state);
|
||||
}
|
||||
} else {
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn state(
|
||||
cpu: &Arc<Mutex<Processor>>,
|
||||
running: &Running,
|
||||
instruction_count: usize,
|
||||
memory_view: Vec<u8>,
|
||||
) -> State {
|
||||
if let Ok(mut cpu_lock) = cpu.lock() {
|
||||
State {
|
||||
// TODO: Replace with actual register access from your CPU
|
||||
reg_file: cpu_lock.registers.clone(),
|
||||
running: running.clone(),
|
||||
instructions: instruction_count,
|
||||
stack_view: cpu_lock.get_stack(32),
|
||||
memory_view,
|
||||
display_view: cpu_lock.display(),
|
||||
error: None,
|
||||
}
|
||||
} else {
|
||||
panic!("Failed to lock CPU");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub trait MemoryUnit: Send + Sync {
|
||||
fn reset(&mut self);
|
||||
fn read_byte(&mut self, addr: u32) -> u8;
|
||||
fn write_byte(&mut self, addr: u32, value: u8);
|
||||
fn read_word(&mut self, addr: u32) -> u32;
|
||||
fn write_word(&mut self, addr: u32, value: u32);
|
||||
fn read_range(&mut self, addr: u32, size: u32) -> Vec<u8>;
|
||||
fn write_range(&mut self, addr: u32, value: Vec<u8>);
|
||||
}
|
||||
|
||||
pub struct MainStore {
|
||||
pub data: HashMap<u32, Block>,
|
||||
}
|
||||
|
||||
struct Block {
|
||||
data: [u8; 256],
|
||||
}
|
||||
|
||||
impl MainStore {
|
||||
pub fn new() -> MainStore {
|
||||
MainStore {
|
||||
data: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn segment_addr(addr: u32) -> (u32, u8) {
|
||||
(addr / 256, (addr % 256) as u8)
|
||||
}
|
||||
|
||||
fn mut_block<'a>(&'a mut self, addr: u32) -> &'a mut Block {
|
||||
if !self.data.contains_key(&addr) {
|
||||
self.data.insert(addr, Block { data: [0; 256] });
|
||||
}
|
||||
|
||||
self.data.get_mut(&addr).unwrap()
|
||||
}
|
||||
|
||||
fn block(&mut self, addr: u32) -> &Block {
|
||||
if !self.data.contains_key(&addr) {
|
||||
self.data.insert(addr, Block { data: [0; 256] });
|
||||
}
|
||||
|
||||
self.data.get(&addr).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl MemoryUnit for MainStore {
|
||||
fn reset(&mut self) {
|
||||
self.data.clear();
|
||||
}
|
||||
|
||||
fn read_byte(&mut self, addr: u32) -> u8 {
|
||||
let (block_addr, offset) = MainStore::segment_addr(addr);
|
||||
let block = self.block(block_addr);
|
||||
block.data[offset as usize]
|
||||
}
|
||||
|
||||
fn read_word(&mut self, addr: u32) -> u32 {
|
||||
let (block_addr, offset) = MainStore::segment_addr(addr);
|
||||
let block = self.mut_block(block_addr);
|
||||
let mut bytes = [0; 4];
|
||||
bytes[0] = block.data[(offset + 0) as usize];
|
||||
bytes[1] = block.data[(offset + 1) as usize];
|
||||
bytes[2] = block.data[(offset + 2) as usize];
|
||||
bytes[3] = block.data[(offset + 3) as usize];
|
||||
u32::from_be_bytes(bytes)
|
||||
}
|
||||
|
||||
fn read_range(&mut self, addr: u32, size: u32) -> Vec<u8> {
|
||||
let mut data = Vec::with_capacity(size as usize);
|
||||
for i in 0..size {
|
||||
data.push(self.read_byte(addr + i));
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
fn write_byte(&mut self, addr: u32, value: u8) {
|
||||
let (block_addr, offset) = MainStore::segment_addr(addr);
|
||||
let block = self.mut_block(block_addr);
|
||||
block.data[offset as usize] = value;
|
||||
}
|
||||
|
||||
fn write_word(&mut self, addr: u32, value: u32) {
|
||||
let (block_addr, offset) = MainStore::segment_addr(addr);
|
||||
let block = self.mut_block(block_addr);
|
||||
block.data[(offset + 0) as usize] = (value >> 24) as u8;
|
||||
block.data[(offset + 1) as usize] = (value >> 16) as u8;
|
||||
block.data[(offset + 2) as usize] = (value >> 8) as u8;
|
||||
block.data[(offset + 3) as usize] = value as u8;
|
||||
}
|
||||
|
||||
fn write_range(&mut self, addr: u32, value: Vec<u8>) {
|
||||
todo!("we might need this for DMA");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod model;
|
||||
pub mod emulator;
|
||||
pub mod processor;
|
||||
pub mod memory;
|
||||
@@ -0,0 +1,216 @@
|
||||
use crate::common::instructions::{Address, Interrupt, Register};
|
||||
|
||||
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
pub enum Running {
|
||||
Running,
|
||||
Paused,
|
||||
Halted,
|
||||
}
|
||||
|
||||
pub enum Command {
|
||||
Start,
|
||||
Stop,
|
||||
Step,
|
||||
Reset,
|
||||
Interrupt(Interrupt),
|
||||
|
||||
// Performs direct read/write operations on the emulator's memory.
|
||||
Read(Address, u32),
|
||||
Write(Address, Vec<u8>),
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
pub struct RegFile {
|
||||
// General Purpose Registers
|
||||
rg0: u32,
|
||||
rg1: u32,
|
||||
rg2: u32,
|
||||
rg3: u32,
|
||||
rg4: u32,
|
||||
rg5: u32,
|
||||
rg6: u32,
|
||||
rg7: u32,
|
||||
rg8: u32,
|
||||
rg9: u32,
|
||||
rga: u32,
|
||||
rgb: u32,
|
||||
rgc: u32,
|
||||
rgd: u32,
|
||||
rge: u32,
|
||||
rgf: u32,
|
||||
|
||||
// Special Purpose Registers
|
||||
acc: u32,
|
||||
spr: u32,
|
||||
bpr: u32,
|
||||
ret: u32,
|
||||
idr: u32,
|
||||
mmr: u32,
|
||||
|
||||
// System Registers
|
||||
mar: u32,
|
||||
mdr: u32,
|
||||
sts: u32,
|
||||
cir: u32,
|
||||
pcx: u32,
|
||||
}
|
||||
|
||||
impl RegFile {
|
||||
pub fn all(&self) -> Vec<(&str, u32)> {
|
||||
vec![
|
||||
("rg0", self.rg0),
|
||||
("rg1", self.rg1),
|
||||
("rg2", self.rg2),
|
||||
("rg3", self.rg3),
|
||||
("rg4", self.rg4),
|
||||
("rg5", self.rg5),
|
||||
("rg6", self.rg6),
|
||||
("rg7", self.rg7),
|
||||
("rg8", self.rg8),
|
||||
("rg9", self.rg9),
|
||||
("rga", self.rga),
|
||||
("rgb", self.rgb),
|
||||
("rgc", self.rgc),
|
||||
("rgd", self.rgd),
|
||||
("rge", self.rge),
|
||||
("rgf", self.rgf),
|
||||
("acc", self.acc),
|
||||
("spr", self.spr),
|
||||
("bpr", self.bpr),
|
||||
("ret", self.ret),
|
||||
("idr", self.idr),
|
||||
("mmr", self.mmr),
|
||||
("mar", self.mar),
|
||||
("mdr", self.mdr),
|
||||
("sts", self.sts),
|
||||
("cir", self.cir),
|
||||
("pcx", self.pcx),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.rg0 = 0;
|
||||
self.rg1 = 0;
|
||||
self.rg2 = 0;
|
||||
self.rg3 = 0;
|
||||
self.rg4 = 0;
|
||||
self.rg5 = 0;
|
||||
self.rg6 = 0;
|
||||
self.rg7 = 0;
|
||||
self.rg8 = 0;
|
||||
self.rg9 = 0;
|
||||
self.rga = 0;
|
||||
self.rgb = 0;
|
||||
self.rgc = 0;
|
||||
self.rgd = 0;
|
||||
self.rge = 0;
|
||||
self.rgf = 0;
|
||||
self.acc = 0;
|
||||
self.spr = 0;
|
||||
self.bpr = 0;
|
||||
self.ret = 0;
|
||||
self.idr = 0;
|
||||
self.mmr = 0;
|
||||
self.mar = 0;
|
||||
self.mdr = 0;
|
||||
self.sts = 0;
|
||||
self.cir = 0;
|
||||
self.pcx = 0;
|
||||
}
|
||||
|
||||
|
||||
pub fn reg(&mut self, reg: Register) -> &mut u32 {
|
||||
match reg {
|
||||
Register::Rg0 => &mut self.rg0,
|
||||
Register::Rg1 => &mut self.rg1,
|
||||
Register::Rg2 => &mut self.rg2,
|
||||
Register::Rg3 => &mut self.rg3,
|
||||
Register::Rg4 => &mut self.rg4,
|
||||
Register::Rg5 => &mut self.rg5,
|
||||
Register::Rg6 => &mut self.rg6,
|
||||
Register::Rg7 => &mut self.rg7,
|
||||
Register::Rg8 => &mut self.rg8,
|
||||
Register::Rg9 => &mut self.rg9,
|
||||
Register::Rga => &mut self.rga,
|
||||
Register::Rgb => &mut self.rgb,
|
||||
Register::Rgc => &mut self.rgc,
|
||||
Register::Rgd => &mut self.rgd,
|
||||
Register::Rge => &mut self.rge,
|
||||
Register::Rgf => &mut self.rgf,
|
||||
Register::Acc => &mut self.acc,
|
||||
Register::Spr => &mut self.spr,
|
||||
Register::Bpr => &mut self.bpr,
|
||||
Register::Ret => &mut self.ret,
|
||||
Register::Idr => &mut self.idr,
|
||||
Register::Mmr => &mut self.mmr,
|
||||
Register::Mar => &mut self.mar,
|
||||
Register::Mdr => &mut self.mdr,
|
||||
Register::Sts => &mut self.sts,
|
||||
Register::Cir => &mut self.cir,
|
||||
Register::Pcx => &mut self.pcx,
|
||||
_ => panic!("invalid register"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, reg: Register) -> u32 {
|
||||
match reg {
|
||||
Register::Rg0 => self.rg0,
|
||||
Register::Rg1 => self.rg1,
|
||||
Register::Rg2 => self.rg2,
|
||||
Register::Rg3 => self.rg3,
|
||||
Register::Rg4 => self.rg4,
|
||||
Register::Rg5 => self.rg5,
|
||||
Register::Rg6 => self.rg6,
|
||||
Register::Rg7 => self.rg7,
|
||||
Register::Rg8 => self.rg8,
|
||||
Register::Rg9 => self.rg9,
|
||||
Register::Rga => self.rga,
|
||||
Register::Rgb => self.rgb,
|
||||
Register::Rgc => self.rgc,
|
||||
Register::Rgd => self.rgd,
|
||||
Register::Rge => self.rge,
|
||||
Register::Rgf => self.rgf,
|
||||
Register::Acc => self.acc,
|
||||
Register::Spr => self.spr,
|
||||
Register::Bpr => self.bpr,
|
||||
Register::Ret => self.ret,
|
||||
Register::Idr => self.idr,
|
||||
Register::Mmr => self.mmr,
|
||||
Register::Mar => self.mar,
|
||||
Register::Mdr => self.mdr,
|
||||
Register::Sts => self.sts,
|
||||
Register::Cir => self.cir,
|
||||
Register::Pcx => self.pcx,
|
||||
Register::Zero => 0,
|
||||
_ => panic!("invalid register"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct State {
|
||||
pub reg_file: RegFile,
|
||||
pub running: Running,
|
||||
pub instructions: usize,
|
||||
|
||||
// Memory access views
|
||||
pub stack_view: Vec<u8>,
|
||||
pub memory_view: Vec<u8>,
|
||||
pub display_view: Vec<u8>,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
reg_file: RegFile::default(),
|
||||
running: Running::Paused,
|
||||
instructions: 0,
|
||||
stack_view: vec![],
|
||||
memory_view: vec![],
|
||||
display_view: vec![],
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
use std::{cmp::{max, min}, sync::Arc};
|
||||
|
||||
use crate::{common::instructions::{Address, Instruction, Register}, emulator::system::{memory::MemoryUnit, model::RegFile}};
|
||||
|
||||
pub struct Processor {
|
||||
pub memory: Box<dyn MemoryUnit>,
|
||||
pub registers: RegFile,
|
||||
// pub io_devices: Vec<Arc<dyn IODevice>>,
|
||||
}
|
||||
|
||||
impl Processor {
|
||||
// io_devices: Vec<Arc<dyn IODevice>>
|
||||
pub fn new(memory: Box<dyn MemoryUnit>) -> Self {
|
||||
Processor {
|
||||
// io_devices,
|
||||
memory,
|
||||
registers: RegFile::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
// set all registers to zero
|
||||
// run memory.reset()
|
||||
self.registers.reset();
|
||||
self.memory.reset();
|
||||
}
|
||||
|
||||
pub fn cycle(&mut self) -> Instruction {
|
||||
// get value from PCX
|
||||
let addr = self.fetch();
|
||||
// increment PCX
|
||||
self.advance();
|
||||
|
||||
// set MAR to the previous value of PCX
|
||||
*self.reg(Register::Mar) = addr;
|
||||
let val = self.memory.read_word(addr);
|
||||
|
||||
// set CIR to the value of RAM[MAR]
|
||||
*self.reg(Register::Mar) = val as u32;
|
||||
// decode and execute the instruction
|
||||
let instruction = Instruction::decode(val);
|
||||
instruction.execute(self);
|
||||
instruction
|
||||
}
|
||||
|
||||
fn fetch(&self) -> u32 {
|
||||
self.get(Register::Pcx)
|
||||
}
|
||||
|
||||
pub fn get(&self, reg: Register) -> u32 {
|
||||
self.registers.get(reg)
|
||||
}
|
||||
|
||||
pub fn reg(&mut self, reg: Register) -> &mut u32 {
|
||||
self.registers.reg(reg)
|
||||
}
|
||||
|
||||
pub fn display(&mut self) -> Vec<u8> {
|
||||
self.memory.read_range(0x20000, 2000)
|
||||
}
|
||||
|
||||
pub fn cmp(&mut self, a: u32, b: u32) {
|
||||
self.set_flag(Flag::Equal, a == b);
|
||||
self.set_flag(Flag::GreaterThan, a > b);
|
||||
self.set_flag(Flag::LessThan, a < b);
|
||||
}
|
||||
|
||||
// stack operations
|
||||
|
||||
pub fn push(&mut self, value: u32) {
|
||||
let stack_ptr = self.get(Register::Spr);
|
||||
*self.reg(Register::Spr) += 4;
|
||||
self.memory.write_word(stack_ptr, value);
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> u32 {
|
||||
*self.reg(Register::Spr) -= 4;
|
||||
self.memory.read_word(self.get(Register::Spr))
|
||||
}
|
||||
|
||||
// functions to set new state
|
||||
|
||||
fn set_flag(&mut self, flag: Flag, value: bool) {
|
||||
if value {
|
||||
*self.reg(Register::Sts) |= flag as u32;
|
||||
} else {
|
||||
*self.reg(Register::Sts) &= !(flag as u32);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_flag(&self, flag: Flag) -> bool {
|
||||
self.get(Register::Sts) & (flag as u32) != 0
|
||||
}
|
||||
|
||||
fn advance(&mut self) {
|
||||
// increment PCX
|
||||
*self.reg(Register::Pcx) += 4;
|
||||
}
|
||||
|
||||
fn jump(&mut self, addr: &Address) {
|
||||
*self.reg(Register::Pcx) = self.get(Register::from(*addr as u8));
|
||||
}
|
||||
|
||||
fn begin_interrupt(&mut self, _code: u8) {
|
||||
// first we get the address of the interrupt descriptor table.
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn end_interrupt(&mut self) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub fn get_stack(&mut self, n: u32) -> Vec<u8> {
|
||||
let addr = self.get(Register::Spr);
|
||||
let size = n*4;
|
||||
// returns the stack
|
||||
self.memory.read_range(
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Flag {
|
||||
Equal = 0,
|
||||
GreaterThan = 1,
|
||||
LessThan = 2,
|
||||
Zero = 3,
|
||||
Positive = 4,
|
||||
Negative = 5,
|
||||
Carry = 6,
|
||||
UserMode = 7,
|
||||
InterruptsEnabled = 8,
|
||||
}
|
||||
|
||||
trait Executable {
|
||||
fn execute(&self, cpu: &mut Processor);
|
||||
}
|
||||
|
||||
impl Executable for Instruction {
|
||||
fn execute(&self, cpu: &mut Processor) {
|
||||
match self {
|
||||
// Instruction::Nop => (),
|
||||
// Instruction::Mov(reg0, reg1) => *cpu.reg(reg1) = cpu.get(reg0),
|
||||
|
||||
// Instruction::Ldb(addr) => {
|
||||
// cpu.mdr = cpu.memory.read_word(match addr {
|
||||
// Address::Literal(addr) => *addr,
|
||||
// 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) => {
|
||||
// *cpu.reg(reg2) = add(cpu.get(reg0), cpu.get(reg1))
|
||||
// }
|
||||
// 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),
|
||||
// Instruction::Jeq(addr) if cpu.get_flag(Flag::Equal) => cpu.jump(addr),
|
||||
// Instruction::Jne(addr) if !cpu.get_flag(Flag::Equal) => cpu.jump(addr),
|
||||
// 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) => {
|
||||
// cpu.jump(addr)
|
||||
// }
|
||||
// Instruction::Jge(addr)
|
||||
// if cpu.get_flag(Flag::GreaterThan) || cpu.get_flag(Flag::Equal) =>
|
||||
// {
|
||||
// cpu.jump(addr)
|
||||
// }
|
||||
// Instruction::Hlt => (),
|
||||
// Instruction::Int(interrupt_code) => {
|
||||
// cpu.begin_interrupt(*interrupt_code);
|
||||
// }
|
||||
// Instruction::Cll(addr) => {
|
||||
// cpu.ret = cpu.pcx;
|
||||
// cpu.pcx = addr.force_resolve();
|
||||
// }
|
||||
// Instruction::Ret => {
|
||||
// cpu.pcx = cpu.ret;
|
||||
// }
|
||||
// Instruction::Psh(reg) => {
|
||||
// cpu.push(cpu.get(reg));
|
||||
// }
|
||||
// Instruction::Pop(reg) => {
|
||||
// let val = cpu.pop();
|
||||
// *cpu.reg(reg) = val;
|
||||
// }
|
||||
// Instruction::Lui(addr) => {
|
||||
// *cpu.reg(&Reg::MDR) = match addr {
|
||||
// Address::Literal(addr) => *addr,
|
||||
// Address::Register(reg) => cpu.get(reg),
|
||||
// _ => panic!("invalid address"),
|
||||
// };
|
||||
// }
|
||||
// Instruction::Irt => {
|
||||
// cpu.end_interrupt();
|
||||
// }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mathematical and logical functions & other operations
|
||||
fn add(a: u32, b: u32) -> u32 {
|
||||
a + b
|
||||
}
|
||||
|
||||
fn sub(a: u32, b: u32) -> u32 {
|
||||
a - b
|
||||
}
|
||||
|
||||
fn and(a: u32, b: u32) -> u32 {
|
||||
a & b
|
||||
}
|
||||
|
||||
fn inc(a: u32) -> u32 {
|
||||
a + 1
|
||||
}
|
||||
|
||||
fn dec(a: u32) -> u32 {
|
||||
a - 1
|
||||
}
|
||||
|
||||
fn shl(a: u32) -> u32 {
|
||||
a << 1
|
||||
}
|
||||
|
||||
fn shr(a: u32) -> u32 {
|
||||
a >> 1
|
||||
}
|
||||
|
||||
fn or(a: u32, b: u32) -> u32 {
|
||||
a | b
|
||||
}
|
||||
|
||||
fn not(a: u32) -> u32 {
|
||||
!a
|
||||
}
|
||||
|
||||
fn xor(a: u32, b: u32) -> u32 {
|
||||
a ^ b
|
||||
}
|
||||
|
||||
fn nand(a: u32, b: u32) -> u32 {
|
||||
!(a & b)
|
||||
}
|
||||
|
||||
fn nor(a: u32, b: u32) -> u32 {
|
||||
!(a | b)
|
||||
}
|
||||
|
||||
fn xnor(a: u32, b: u32) -> u32 {
|
||||
!(a ^ b)
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use crate::{common::instructions::Register, emulator::{system::model::{Command, Running, State}, ui::interface::Component}};
|
||||
|
||||
pub struct ControlPanel {
|
||||
visible: bool,
|
||||
sender: Sender<Command>,
|
||||
}
|
||||
|
||||
impl ControlPanel {
|
||||
pub fn new(sender: Sender<Command>) -> Self {
|
||||
Self {
|
||||
visible: false,
|
||||
sender,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for ControlPanel {
|
||||
fn category(&self) -> super::interface::Category {
|
||||
super::interface::Category::Control
|
||||
}
|
||||
|
||||
fn visible(&mut self) -> &mut bool {
|
||||
&mut self.visible
|
||||
}
|
||||
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"Control Panel"
|
||||
}
|
||||
|
||||
|
||||
fn render(&mut self, state: &mut State, ui: &mut egui::Ui, ctx: &egui::Context) {
|
||||
ui.horizontal(|ui| {
|
||||
// Pause / Run
|
||||
if ui
|
||||
.button(if state.running == Running::Running {
|
||||
"Pause"
|
||||
} else {
|
||||
"Run"
|
||||
})
|
||||
.clicked()
|
||||
{
|
||||
if state.running == Running::Running {
|
||||
self.sender.send(Command::Stop).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
|
||||
if ui.button("Step").clicked() {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn render_register_table(state: &mut State, ui: &mut egui::Ui, _ctx: &egui::Context) {
|
||||
// Left column - Registers
|
||||
ui.vertical(|ui| {
|
||||
ui.heading("Registers");
|
||||
|
||||
egui::ScrollArea::vertical()
|
||||
.id_salt("register_inspector_scroll")
|
||||
.show(ui, |ui| {
|
||||
egui::Grid::new("registers_grid")
|
||||
.num_columns(8)
|
||||
.spacing([40.0, 4.0])
|
||||
.striped(true)
|
||||
.show(ui, |ui| {
|
||||
ui.label("Register");
|
||||
ui.label("Value");
|
||||
ui.label("Register");
|
||||
ui.label("Value");
|
||||
ui.label("Register");
|
||||
ui.label("Value");
|
||||
ui.label("Register");
|
||||
ui.label("Value");
|
||||
ui.end_row();
|
||||
|
||||
// iterate over state.reg_file.iter() in chunks of 4 registers
|
||||
for chunk in state.reg_file.all().chunks(4) {
|
||||
for reg in chunk {
|
||||
ui.label(format!("{:?}", reg.0));
|
||||
ui.label(format!(
|
||||
"0x{:08X} ({})",
|
||||
reg.1,
|
||||
reg.1,
|
||||
));
|
||||
}
|
||||
ui.end_row();
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
use crate::{common::instructions::{Address, Interrupt}, emulator::system::model::{Command, Running, State}};
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
|
||||
pub trait Component {
|
||||
fn render(&mut self, state: &mut State, ui: &mut egui::Ui, ctx: &egui::Context);
|
||||
fn visible(&mut self) -> &mut bool;
|
||||
fn name(&self) -> &'static str;
|
||||
fn category(&self) -> Category;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Category {
|
||||
Control,
|
||||
Memory,
|
||||
Io,
|
||||
Programming,
|
||||
}
|
||||
|
||||
impl Category {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Category::Control => "Control Systems",
|
||||
Category::Memory => "Memory Systems",
|
||||
Category::Io => "I/O Systems",
|
||||
Category::Programming => "Programming",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list() -> Vec<Category> {
|
||||
vec![
|
||||
Category::Control,
|
||||
Category::Memory,
|
||||
Category::Io,
|
||||
Category::Programming,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EmulatorUI {
|
||||
pub sender: Sender<Command>,
|
||||
pub receiver: Receiver<State>,
|
||||
pub state: State,
|
||||
pub components: Vec<Box<dyn Component>>,
|
||||
}
|
||||
|
||||
impl EmulatorUI {
|
||||
pub fn new(sender: Sender<Command>, receiver: Receiver<State>) -> Self {
|
||||
Self {
|
||||
sender,
|
||||
receiver,
|
||||
state: State::default(),
|
||||
components: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_component(&mut self, component: Box<dyn Component>) {
|
||||
self.components.push(component);
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for EmulatorUI {
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||
if let Running::Running = self.state.running {
|
||||
ctx.request_repaint();
|
||||
}
|
||||
|
||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||
ui.with_layout(
|
||||
egui::Layout::top_down_justified(egui::Align::Center)
|
||||
.with_main_align(egui::Align::Min),
|
||||
|ui| {
|
||||
ui.allocate_space(egui::vec2(0.0, 15.0));
|
||||
ui.heading("DSA Simulator (Damn Simple Architecture 🔥)");
|
||||
ui.allocate_space(egui::vec2(0.0, 15.0));
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
|
||||
egui::Window::new("Main Menu")
|
||||
.resizable(false)
|
||||
.default_width(300.0)
|
||||
.show(ctx, |ui| {
|
||||
super::menu::render_menu(self, ui, ctx);
|
||||
});
|
||||
|
||||
for c in self.components.iter_mut() {
|
||||
let mut visible = *c.visible();
|
||||
if visible == true {
|
||||
egui::Window::new(c.name())
|
||||
.open(&mut visible)
|
||||
.show(ctx, |ui| {
|
||||
c.render(&mut self.state, ui, ctx);
|
||||
});
|
||||
}
|
||||
*c.visible() = visible;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
egui::TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| {
|
||||
// interface::bottompanel::render_bottom_panel(self, ui, ctx);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use crate::emulator::ui::interface::{Category, EmulatorUI};
|
||||
|
||||
pub fn render_menu(state: &mut EmulatorUI, ui: &mut egui::Ui, _ctx: &egui::Context) {
|
||||
ui.with_layout(
|
||||
egui::Layout::top_down_justified(egui::Align::Center),
|
||||
|ui| {
|
||||
ui.set_max_width(300.0);
|
||||
ui.set_min_width(300.0);
|
||||
ui.spacing_mut().button_padding = egui::vec2(10.0, 5.0);
|
||||
|
||||
for cat in Category::list() {
|
||||
ui.add_space(10.0);
|
||||
ui.heading(cat.as_str());
|
||||
ui.add_space(10.0);
|
||||
|
||||
for comp in state.components.iter_mut() {
|
||||
let name = comp.name();
|
||||
if comp.category() == cat {
|
||||
ui.toggle_value(comp.visible(), name);
|
||||
}
|
||||
}
|
||||
|
||||
ui.add_space(10.0);
|
||||
ui.separator();
|
||||
}
|
||||
|
||||
ui.add_space(10.0);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod interface;
|
||||
pub mod menu;
|
||||
pub mod control_unit;
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
pub mod common;
|
||||
pub mod common;
|
||||
pub mod emulator;
|
||||
|
||||
+50
-2
@@ -1,5 +1,53 @@
|
||||
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 egui::Memory;
|
||||
|
||||
fn main() -> Result<(), eframe::Error> {
|
||||
|
||||
// Initialize Channels
|
||||
let (cmd_sender, cmd_receiver) = std::sync::mpsc::channel();
|
||||
let (state_sender, state_receiver) = std::sync::mpsc::channel();
|
||||
|
||||
let mainstore = MainStore::new();
|
||||
let processor = Processor::new(Box::new(mainstore));
|
||||
|
||||
|
||||
thread::spawn(move || {
|
||||
run_emulator(
|
||||
cmd_receiver,
|
||||
state_sender,
|
||||
Arc::new(Mutex::new(processor)),
|
||||
);
|
||||
});
|
||||
|
||||
// Create UI
|
||||
let mut ui = EmulatorUI::new(cmd_sender.clone(), state_receiver);
|
||||
|
||||
// Create UI modules
|
||||
let control_unit = ControlPanel::new(cmd_sender.clone());
|
||||
ui.add_component(Box::new(control_unit));
|
||||
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Run UI
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
eframe::run_native(
|
||||
"DSA Simulator (Damn Simple Architecture 🔥)",
|
||||
options,
|
||||
Box::new(move |cc| {
|
||||
cc.egui_ctx.set_visuals(egui::Visuals::default());
|
||||
Ok(Box::new(ui))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user