added a simple process manager for the emulator UI. responsible for running and providing log information on build tasks etc.
first implementation so a bit jank still.
This commit is contained in:
@@ -15,6 +15,7 @@ use std::collections::HashMap;
|
|||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use common::formats::binary_dse::{DseExecutable, SymbolEntry};
|
use common::formats::binary_dse::{DseExecutable, SymbolEntry};
|
||||||
use common::prelude::Instruction;
|
use common::prelude::Instruction;
|
||||||
|
use common::process::{ProcessAgent, TraceableProcess};
|
||||||
use crate::assembler::resolver2::{resolve_address, resolve_dependencies, resolve_symbols, split_sections, SectionId};
|
use crate::assembler::resolver2::{resolve_address, resolve_dependencies, resolve_symbols, split_sections, SectionId};
|
||||||
|
|
||||||
// Module declarations
|
// Module declarations
|
||||||
@@ -31,8 +32,6 @@ pub mod parser;
|
|||||||
// pub mod resolver;
|
// pub mod resolver;
|
||||||
pub mod resolver2;
|
pub mod resolver2;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Re-exports
|
// Re-exports
|
||||||
pub use self::{
|
pub use self::{
|
||||||
codegen::codegen,
|
codegen::codegen,
|
||||||
@@ -43,6 +42,7 @@ pub use self::{
|
|||||||
// resolver::{create_sections, resolve_dependencies, resolve_symbols},
|
// resolver::{create_sections, resolve_dependencies, resolve_symbols},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Assembler {
|
pub struct Assembler {
|
||||||
src_path: PathBuf,
|
src_path: PathBuf,
|
||||||
result_tx: mpsc::Sender<Result<Vec<u8>, AssembleError>>,
|
result_tx: mpsc::Sender<Result<Vec<u8>, AssembleError>>,
|
||||||
@@ -50,6 +50,15 @@ pub struct Assembler {
|
|||||||
is_running: bool,
|
is_running: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TraceableProcess for Assembler {
|
||||||
|
type Output = DseExecutable;
|
||||||
|
type Error = AssembleError;
|
||||||
|
|
||||||
|
fn run(mut self: Box<Self>, ctx: ProcessAgent) -> Result<Self::Output, Self::Error> {
|
||||||
|
assemble(&self.src_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Assembler {
|
impl Assembler {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(src_path: impl Into<PathBuf>) -> Self {
|
pub fn new(src_path: impl Into<PathBuf>) -> Self {
|
||||||
@@ -63,7 +72,7 @@ impl Assembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Start the compilation process in a separate thread
|
/// Start the compilation process in a separate thread
|
||||||
pub fn start(&mut self, args: ()) {
|
pub fn start(&mut self) {
|
||||||
if self.is_running {
|
if self.is_running {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -143,8 +152,6 @@ impl Assembler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Assembler {}
|
|
||||||
|
|
||||||
// fn assemble(src: &Path) -> Result<Vec<Instruction>, AssembleError> {
|
// fn assemble(src: &Path) -> Result<Vec<Instruction>, AssembleError> {
|
||||||
// let mut modules = HashSet::new();
|
// let mut modules = HashSet::new();
|
||||||
// let mut program = Program::new();
|
// let mut program = Program::new();
|
||||||
@@ -332,6 +339,8 @@ pub enum AssembleError {
|
|||||||
MissingArgument(u8),
|
MissingArgument(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for AssembleError {}
|
||||||
|
|
||||||
impl fmt::Display for AssembleError {
|
impl fmt::Display for AssembleError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ fn main() {
|
|||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
let mut engine = Assembler::new(PathBuf::from(args.input_path));
|
let mut engine = Assembler::new(PathBuf::from(args.input_path));
|
||||||
engine.start(());
|
engine.start();
|
||||||
let result = engine.output().expect("assembler failed.");
|
let result = engine.output().expect("assembler failed.");
|
||||||
|
|
||||||
if let Err(e) = fs::write(args.output_path, result) {
|
if let Err(e) = fs::write(args.output_path, result) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
pub mod asm;
|
pub mod asm;
|
||||||
pub mod isa;
|
pub mod isa;
|
||||||
pub mod formats;
|
pub mod formats;
|
||||||
|
pub mod process;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::isa::instructions::Instruction;
|
pub use crate::isa::instructions::Instruction;
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ProcessAgent {
|
||||||
|
tx: mpsc::Sender<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcessAgent {
|
||||||
|
pub fn new(tx: mpsc::Sender<String>) -> Self {
|
||||||
|
Self { tx }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log(&self, msg: impl Into<String>) {
|
||||||
|
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<Self>, ctx: ProcessAgent) -> Result<Self::Output, Self::Error>;
|
||||||
|
}
|
||||||
@@ -3,4 +3,4 @@
|
|||||||
#![feature(str_as_str)]
|
#![feature(str_as_str)]
|
||||||
|
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
pub mod args;
|
pub mod args;
|
||||||
|
|||||||
@@ -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::controller::Controller;
|
||||||
use crate::ui::dialog::error_dialog::ErrorDialog;
|
use crate::ui::dialog::error_dialog::ErrorDialog;
|
||||||
use crate::ui::panels::{CentralTiles, TilePane};
|
|
||||||
use crate::ui::panels::about::AboutScreen;
|
use crate::ui::panels::about::AboutScreen;
|
||||||
use crate::ui::panels::disassembler::BinaryViewer;
|
use crate::ui::panels::disassembler::BinaryViewer;
|
||||||
use crate::ui::panels::display::Display;
|
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::memory::MemoryInspector;
|
||||||
use crate::ui::panels::registers::Registers;
|
use crate::ui::panels::registers::Registers;
|
||||||
use crate::ui::panels::serial::Serial;
|
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 dialog;
|
||||||
mod panels;
|
mod panels;
|
||||||
mod ui_helpers;
|
mod ui_helpers;
|
||||||
mod controller;
|
mod controller;
|
||||||
|
pub mod process_manager;
|
||||||
|
|
||||||
pub fn run_app(mut controller: EmulatorController) -> eframe::Result<()> {
|
pub fn run_app(mut controller: EmulatorController) -> eframe::Result<()> {
|
||||||
// initialise emulator
|
// initialise emulator
|
||||||
@@ -56,8 +59,8 @@ pub enum DataSource {
|
|||||||
Data(Vec<u8>),
|
Data(Vec<u8>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
|
None,
|
||||||
RequestCloseTab(TileId),
|
RequestCloseTab(TileId),
|
||||||
CloseTab(TileId),
|
CloseTab(TileId),
|
||||||
Quit,
|
Quit,
|
||||||
@@ -67,6 +70,7 @@ pub enum Action {
|
|||||||
SaveFile(PathBuf, TileId),
|
SaveFile(PathBuf, TileId),
|
||||||
RequestSaveAs(TileId),
|
RequestSaveAs(TileId),
|
||||||
ShowError(String),
|
ShowError(String),
|
||||||
|
SpawnProcess(ProcessSpawner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -80,6 +84,8 @@ pub struct DamnSimpleUI {
|
|||||||
error_dialog: ErrorDialog,
|
error_dialog: ErrorDialog,
|
||||||
|
|
||||||
tools: Vec<PanelFactory>,
|
tools: Vec<PanelFactory>,
|
||||||
|
|
||||||
|
process_manager: ProcessManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -95,6 +101,10 @@ impl eframe::App for DamnSimpleUI {
|
|||||||
self.perform_action(action);
|
self.perform_action(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while let Some(action) = self.process_manager.get_action() {
|
||||||
|
self.perform_action(action);
|
||||||
|
}
|
||||||
|
|
||||||
self.error_dialog.show(ui);
|
self.error_dialog.show(ui);
|
||||||
|
|
||||||
Panel::top("menu_bar").resizable(false).show(ui, |ui| {
|
Panel::top("menu_bar").resizable(false).show(ui, |ui| {
|
||||||
@@ -109,8 +119,8 @@ impl eframe::App for DamnSimpleUI {
|
|||||||
// TODO: File Explorer!
|
// TODO: File Explorer!
|
||||||
});
|
});
|
||||||
|
|
||||||
Panel::bottom("bottom_panel").resizable(false).show(ui, |ui| {
|
Panel::bottom("bottom_panel").resizable(true).show(ui, |ui| {
|
||||||
// self.process_manager.ui(ui);
|
self.process_manager.ui(ui);
|
||||||
});
|
});
|
||||||
|
|
||||||
CentralPanel::default().show(ui, |ui| {
|
CentralPanel::default().show(ui, |ui| {
|
||||||
@@ -174,12 +184,15 @@ impl DamnSimpleUI {
|
|||||||
})
|
})
|
||||||
],
|
],
|
||||||
emulator,
|
emulator,
|
||||||
|
process_manager: ProcessManager::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn perform_action(&mut self, action: Action) {
|
pub fn perform_action(&mut self, action: Action) {
|
||||||
match action {
|
match action {
|
||||||
|
Action::None => {},
|
||||||
|
|
||||||
|
Action::SpawnProcess(spawner) => spawner(&mut self.process_manager),
|
||||||
Action::RequestCloseTab(tile_id) => {
|
Action::RequestCloseTab(tile_id) => {
|
||||||
self.confirm_dialog.open(
|
self.confirm_dialog.open(
|
||||||
"Close Tab",
|
"Close Tab",
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
use crate::ui::Action;
|
use crate::ui::Action;
|
||||||
use crate::ui::panels::TilePane;
|
use crate::ui::panels::TilePane;
|
||||||
|
use common::process::{ProcessAgent, TraceableProcess};
|
||||||
use egui::{Key, Ui};
|
use egui::{Key, Ui};
|
||||||
use egui_code_editor::CodeEditor;
|
use egui_code_editor::CodeEditor;
|
||||||
use egui_tiles::TileId;
|
use egui_tiles::TileId;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::io::Error;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
use assembler::prelude::Assembler;
|
||||||
use theme::THEME;
|
use theme::THEME;
|
||||||
|
use crate::ui::DataSource::Data;
|
||||||
|
|
||||||
mod syntax;
|
mod syntax;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
||||||
@@ -56,27 +63,54 @@ impl TilePane for Editor {
|
|||||||
|
|
||||||
fn ui(&mut self, ui: &mut Ui, tile_id: TileId) -> Option<Action> {
|
fn ui(&mut self, ui: &mut Ui, tile_id: TileId) -> Option<Action> {
|
||||||
|
|
||||||
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() {
|
if let Some(filename) = self.filename.as_ref() {
|
||||||
ui.label(filename.display().to_string());
|
ui.label(filename.display().to_string());
|
||||||
} else {
|
} else {
|
||||||
ui.label("New File*");
|
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());
|
self.editor.show(ui, &mut self.buffer, &syntax::dsa());
|
||||||
|
|
||||||
if ui.input(|i| i.key_pressed(Key::S) && i.modifiers.ctrl) {
|
if ui.input(|i| i.key_pressed(Key::S) && i.modifiers.ctrl) {
|
||||||
if let Some(filename) = self.filename.clone() {
|
return if let Some(filename) = self.filename.clone() {
|
||||||
action = Some(Action::SaveFile(filename, tile_id));
|
Some(Action::SaveFile(filename, tile_id))
|
||||||
} else {
|
} else {
|
||||||
action = Some(Action::RequestSaveAs(tile_id));
|
Some(Action::RequestSaveAs(tile_id))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
action
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||||
@@ -86,3 +120,20 @@ impl TilePane for Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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<dyn FnOnce(&mut ProcessManager) + Send>;
|
||||||
|
|
||||||
|
pub struct ProcessHandle {
|
||||||
|
id: usize,
|
||||||
|
name: String,
|
||||||
|
rx: mpsc::Receiver<String>,
|
||||||
|
join_handle: Option<JoinHandle<Action>>,
|
||||||
|
|
||||||
|
// current process state
|
||||||
|
logs: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcessHandle {
|
||||||
|
pub fn spawn<P: TraceableProcess>(
|
||||||
|
id: usize,
|
||||||
|
name: impl Into<String>,
|
||||||
|
process: P,
|
||||||
|
on_complete: impl FnOnce(Result<P::Output, P::Error>) -> Action + Send + 'static
|
||||||
|
) -> Self {
|
||||||
|
let (tx, rx) = mpsc::channel::<String>();
|
||||||
|
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<ProcessHandle>,
|
||||||
|
terminated_actions: VecDeque<Action>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Action> {
|
||||||
|
self.terminated_actions.pop_front()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn<P: TraceableProcess>(&mut self, process: P, on_complete: impl FnOnce(Result<P::Output, P::Error>) -> 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);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user