diff --git a/dsa/assembler/src/assembler/mod.rs b/dsa/assembler/src/assembler/mod.rs index f1ab0ad..36c04f6 100644 --- a/dsa/assembler/src/assembler/mod.rs +++ b/dsa/assembler/src/assembler/mod.rs @@ -15,6 +15,7 @@ use std::collections::HashMap; use std::io::Cursor; use common::formats::binary_dse::{DseExecutable, SymbolEntry}; use common::prelude::Instruction; +use common::process::{ProcessAgent, TraceableProcess}; use crate::assembler::resolver2::{resolve_address, resolve_dependencies, resolve_symbols, split_sections, SectionId}; // Module declarations @@ -31,8 +32,6 @@ pub mod parser; // pub mod resolver; pub mod resolver2; - - // Re-exports pub use self::{ codegen::codegen, @@ -43,6 +42,7 @@ pub use self::{ // resolver::{create_sections, resolve_dependencies, resolve_symbols}, }; +#[derive(Debug)] pub struct Assembler { src_path: PathBuf, result_tx: mpsc::Sender, AssembleError>>, @@ -50,6 +50,15 @@ pub struct Assembler { is_running: bool, } +impl TraceableProcess for Assembler { + type Output = DseExecutable; + type Error = AssembleError; + + fn run(mut self: Box, ctx: ProcessAgent) -> Result { + assemble(&self.src_path) + } +} + impl Assembler { #[must_use] pub fn new(src_path: impl Into) -> Self { @@ -63,7 +72,7 @@ impl Assembler { } /// Start the compilation process in a separate thread - pub fn start(&mut self, args: ()) { + pub fn start(&mut self) { if self.is_running { return; } @@ -143,8 +152,6 @@ impl Assembler { } } -impl Assembler {} - // fn assemble(src: &Path) -> Result, AssembleError> { // let mut modules = HashSet::new(); // let mut program = Program::new(); @@ -332,6 +339,8 @@ pub enum AssembleError { MissingArgument(u8), } +impl std::error::Error for AssembleError {} + impl fmt::Display for AssembleError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { diff --git a/dsa/assembler/src/main.rs b/dsa/assembler/src/main.rs index 80bb7ba..f48095e 100644 --- a/dsa/assembler/src/main.rs +++ b/dsa/assembler/src/main.rs @@ -16,7 +16,7 @@ fn main() { let args = Args::parse(); let mut engine = Assembler::new(PathBuf::from(args.input_path)); - engine.start(()); + engine.start(); let result = engine.output().expect("assembler failed."); if let Err(e) = fs::write(args.output_path, result) { diff --git a/dsa/common/src/lib.rs b/dsa/common/src/lib.rs index 6e0d694..d5638b3 100644 --- a/dsa/common/src/lib.rs +++ b/dsa/common/src/lib.rs @@ -1,6 +1,7 @@ pub mod asm; pub mod isa; pub mod formats; +pub mod process; pub mod prelude { pub use crate::isa::instructions::Instruction; diff --git a/dsa/common/src/process.rs b/dsa/common/src/process.rs new file mode 100644 index 0000000..7dee177 --- /dev/null +++ b/dsa/common/src/process.rs @@ -0,0 +1,23 @@ +use std::fmt::Debug; +use std::sync::mpsc; + +#[derive(Debug, Clone)] +pub struct ProcessAgent { + tx: mpsc::Sender, +} + +impl ProcessAgent { + pub fn new(tx: mpsc::Sender) -> Self { + Self { tx } + } + + pub fn log(&self, msg: impl Into) { + let _ = self.tx.send(msg.into()); + } +} + +pub trait TraceableProcess: Debug + Send + 'static { + type Output: Send + 'static; + type Error: std::error::Error + Send + 'static; + fn run(self: Box, ctx: ProcessAgent) -> Result; +} \ No newline at end of file diff --git a/dsa/emulator/emulator_ui/src/lib.rs b/dsa/emulator/emulator_ui/src/lib.rs index 74e79a4..fbaf689 100644 --- a/dsa/emulator/emulator_ui/src/lib.rs +++ b/dsa/emulator/emulator_ui/src/lib.rs @@ -3,4 +3,4 @@ #![feature(str_as_str)] pub mod ui; -pub mod args; \ No newline at end of file +pub mod args; diff --git a/dsa/emulator/emulator_ui/src/ui/mod.rs b/dsa/emulator/emulator_ui/src/ui/mod.rs index 6dc1193..c46f44f 100644 --- a/dsa/emulator/emulator_ui/src/ui/mod.rs +++ b/dsa/emulator/emulator_ui/src/ui/mod.rs @@ -1,21 +1,5 @@ -use std::fmt::Debug; -use std::fs::File; -use std::io::BufReader; -use std::path::PathBuf; -use std::time::Duration; -use eframe::NativeOptions; -use egui::{CentralPanel, Panel, Ui}; -use egui_tiles::TileId; -use common::formats::binary_dse::DseExecutable; -use dialog::confirm_dialog::ConfirmDialog; -use panels::documentation::Documentation; -use panels::editor::Editor; -use dialog::file_dialog::DsaFileDialog; -use emulator_core::{EmulatorController}; -use panels::PanelFactory; use crate::ui::controller::Controller; use crate::ui::dialog::error_dialog::ErrorDialog; -use crate::ui::panels::{CentralTiles, TilePane}; use crate::ui::panels::about::AboutScreen; use crate::ui::panels::disassembler::BinaryViewer; use crate::ui::panels::display::Display; @@ -23,11 +7,30 @@ use crate::ui::panels::framebuffer::FrameBuffer; use crate::ui::panels::memory::MemoryInspector; use crate::ui::panels::registers::Registers; use crate::ui::panels::serial::Serial; +use crate::ui::panels::{CentralTiles, TilePane}; +use common::formats::binary_dse::DseExecutable; +use common::process::TraceableProcess; +use dialog::confirm_dialog::ConfirmDialog; +use dialog::file_dialog::DsaFileDialog; +use eframe::NativeOptions; +use egui::{CentralPanel, Panel, Ui}; +use egui_tiles::TileId; +use emulator_core::EmulatorController; +use panels::PanelFactory; +use panels::documentation::Documentation; +use panels::editor::Editor; +use process_manager::{ProcessManager, ProcessSpawner}; +use std::fmt::Debug; +use std::fs::File; +use std::io::BufReader; +use std::path::PathBuf; +use std::time::Duration; mod dialog; mod panels; mod ui_helpers; mod controller; +pub mod process_manager; pub fn run_app(mut controller: EmulatorController) -> eframe::Result<()> { // initialise emulator @@ -56,8 +59,8 @@ pub enum DataSource { Data(Vec), } -#[derive(Debug, Clone)] pub enum Action { + None, RequestCloseTab(TileId), CloseTab(TileId), Quit, @@ -67,6 +70,7 @@ pub enum Action { SaveFile(PathBuf, TileId), RequestSaveAs(TileId), ShowError(String), + SpawnProcess(ProcessSpawner) } @@ -80,6 +84,8 @@ pub struct DamnSimpleUI { error_dialog: ErrorDialog, tools: Vec, + + process_manager: ProcessManager } @@ -95,6 +101,10 @@ impl eframe::App for DamnSimpleUI { self.perform_action(action); } + while let Some(action) = self.process_manager.get_action() { + self.perform_action(action); + } + self.error_dialog.show(ui); Panel::top("menu_bar").resizable(false).show(ui, |ui| { @@ -109,8 +119,8 @@ impl eframe::App for DamnSimpleUI { // TODO: File Explorer! }); - Panel::bottom("bottom_panel").resizable(false).show(ui, |ui| { - // self.process_manager.ui(ui); + Panel::bottom("bottom_panel").resizable(true).show(ui, |ui| { + self.process_manager.ui(ui); }); CentralPanel::default().show(ui, |ui| { @@ -174,12 +184,15 @@ impl DamnSimpleUI { }) ], emulator, + process_manager: ProcessManager::new() } } pub fn perform_action(&mut self, action: Action) { match action { + Action::None => {}, + Action::SpawnProcess(spawner) => spawner(&mut self.process_manager), Action::RequestCloseTab(tile_id) => { self.confirm_dialog.open( "Close Tab", diff --git a/dsa/emulator/emulator_ui/src/ui/panels/editor/mod.rs b/dsa/emulator/emulator_ui/src/ui/panels/editor/mod.rs index 721a178..d430b97 100644 --- a/dsa/emulator/emulator_ui/src/ui/panels/editor/mod.rs +++ b/dsa/emulator/emulator_ui/src/ui/panels/editor/mod.rs @@ -1,11 +1,18 @@ use crate::ui::Action; use crate::ui::panels::TilePane; +use common::process::{ProcessAgent, TraceableProcess}; use egui::{Key, Ui}; use egui_code_editor::CodeEditor; use egui_tiles::TileId; use std::any::Any; +use std::io::Error; use std::path::{Path, PathBuf}; +use std::thread; +use std::time::Duration; +use assembler::prelude::Assembler; use theme::THEME; +use crate::ui::DataSource::Data; + mod syntax; mod theme; @@ -56,27 +63,54 @@ impl TilePane for Editor { fn ui(&mut self, ui: &mut Ui, tile_id: TileId) -> Option { - egui::Panel::top("panel").show(ui, |ui| { + let response = egui::Panel::top("panel").show(ui, |ui| { + let mut action = None; + if let Some(filename) = self.filename.as_ref() { ui.label(filename.display().to_string()); } else { ui.label("New File*"); } + + if ui.button("Build").clicked() { + + let Some(filename) = self.filename.clone() else { + println!("error!"); + return None; + }; + + action = Some(Action::SpawnProcess(Box::new(move |pm| { + pm.spawn( + Assembler::new(filename), + move |result| { + return if let Ok(bin) = result { + Action::LoadBinary(Data(bin.to_memory_image())) + } else { + Action::None + } + } + ); + }))); + }; + + action }); - let mut action = None; + if let Some(action) = response.inner { + return Some(action); + } self.editor.show(ui, &mut self.buffer, &syntax::dsa()); if ui.input(|i| i.key_pressed(Key::S) && i.modifiers.ctrl) { - if let Some(filename) = self.filename.clone() { - action = Some(Action::SaveFile(filename, tile_id)); + return if let Some(filename) = self.filename.clone() { + Some(Action::SaveFile(filename, tile_id)) } else { - action = Some(Action::RequestSaveAs(tile_id)); + Some(Action::RequestSaveAs(tile_id)) } }; - action + None } fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -86,3 +120,20 @@ impl TilePane for Editor { } } + + + + + + + + + + + + + + + + + diff --git a/dsa/emulator/emulator_ui/src/ui/process_manager.rs b/dsa/emulator/emulator_ui/src/ui/process_manager.rs new file mode 100644 index 0000000..4719062 --- /dev/null +++ b/dsa/emulator/emulator_ui/src/ui/process_manager.rs @@ -0,0 +1,91 @@ +use crate::ui::Action; +use common::process::{ProcessAgent, TraceableProcess}; +use egui::Ui; +use std::collections::VecDeque; +use std::sync::mpsc; +use std::thread::JoinHandle; + +pub type ProcessSpawner = Box; + +pub struct ProcessHandle { + id: usize, + name: String, + rx: mpsc::Receiver, + join_handle: Option>, + + // current process state + logs: Vec, +} + +impl ProcessHandle { + pub fn spawn( + id: usize, + name: impl Into, + process: P, + on_complete: impl FnOnce(Result) -> Action + Send + 'static + ) -> Self { + let (tx, rx) = mpsc::channel::(); + let agent = ProcessAgent::new( tx ); + + let join_handle = std::thread::spawn(move || { + let result = TraceableProcess::run(Box::new(process), agent); + on_complete(result) + }); + + Self { + rx, id, name: name.into(), join_handle: Some(join_handle), logs: Vec::new() + } + } +} + +#[derive(Default)] +pub struct ProcessManager { + _id_counter: usize, + + handles: Vec, + terminated_actions: VecDeque, +} + +impl ProcessManager { + pub fn new() -> Self { + Default::default() + } + + fn new_id(&mut self) -> usize { + let id = self._id_counter; + self._id_counter += 1; + id + } + + pub fn get_action(&mut self) -> Option { + self.terminated_actions.pop_front() + } + + pub fn spawn(&mut self, process: P, on_complete: impl FnOnce(Result) -> Action + Send + 'static) { + let handle = ProcessHandle::spawn(self.new_id(), "process", process, on_complete); + self.handles.push(handle); + } + + pub fn ui(&mut self, ui: &mut Ui) { + for handle in &mut self.handles { + let is_finished = handle.join_handle + .as_ref() + .is_some_and(|jh| jh.is_finished()); + + if is_finished { + if let Some(jh) = handle.join_handle.take() { + println!("finished! {}", handle.name); + if let Ok(action) = jh.join() { + self.terminated_actions.push_back(action); + } + } + } + } + + ui.horizontal(|ui| { + for h in &self.handles { + ui.label(&h.name); + }; + }); + } +} \ No newline at end of file