working emulator UI - just need to implement the instruction set
This commit is contained in:
@@ -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;
|
||||
Reference in New Issue
Block a user