diff --git a/emulator/dsa_gui/src/state.rs b/emulator/dsa_gui/src/state.rs index 2206533..e1f4117 100644 --- a/emulator/dsa_gui/src/state.rs +++ b/emulator/dsa_gui/src/state.rs @@ -3,13 +3,12 @@ use egui_tiles::TileId; use std::fmt::Debug; use std::path::PathBuf; - /// the action enum forms the core of the application's state. /// - Actions are used as the main way that Tiles inside the application pass state updates back to the parent UI, /// specifically when those state updates involve other files. for example, pressing "build" in the editor would emit /// a SpawnProcess action, running some task which on completion emits a LoadBinary action on the result +#[derive(Debug)] pub enum Action { - None, RequestCloseTab(TileId), CloseTab(TileId), Quit, @@ -18,7 +17,7 @@ pub enum Action { SaveFile(PathBuf, TileId), RequestSaveAs(TileId), ShowError(String), - SpawnProcess(ProcessSpawner) + SpawnProcess(ProcessSpawner), } #[derive(Debug, Clone)] @@ -26,4 +25,3 @@ pub enum DataSource { File(PathBuf), Data(Vec), } - diff --git a/emulator/dsa_gui/src/ui/mod.rs b/emulator/dsa_gui/src/ui/mod.rs index b31f5ff..797bbef 100644 --- a/emulator/dsa_gui/src/ui/mod.rs +++ b/emulator/dsa_gui/src/ui/mod.rs @@ -17,13 +17,12 @@ use egui_tiles::TileId; use process_manager::ProcessManager; use std::fs::File; use std::io::BufReader; -use std::path::Path; use std::time::Duration; use tiles::PanelFactory; use tiles::documentation::Documentation; use tiles::editor::Editor; use tiles::io_devices::framebuffer::FrameBufferUi; -use zxq5_egui::file_tree::{FileTree, FileTreeEvent}; +use zxq5_egui::context_tree::{ContextTree, NewFileTree}; pub mod appearance; mod controller; @@ -45,7 +44,7 @@ pub struct DamnSimpleUI { show_process_manager: bool, process_manager: ProcessManager, - file_tree: FileTree, + file_tree: ContextTree, } impl eframe::App for DamnSimpleUI { @@ -75,17 +74,13 @@ impl eframe::App for DamnSimpleUI { }); Panel::left("left_panel").resizable(true).show(ui, |ui| { - let inner_res = self.file_tree.ui(ui, &mut |ui: &mut Ui, path: &Path| { - ui.button("Load Binary") - .clicked() - .then_some(Action::LoadBinary(DataSource::File(path.to_path_buf()))) - }); + let inner_res = self.file_tree.ui(ui); if let Some(event) = inner_res.inner { - match event { - FileTreeEvent::Clicked(path) => self.perform_action(Action::OpenFile(path)), - FileTreeEvent::ContextAction(action) => self.perform_action(action), - } + // match event { + // ContextTreeEvent::Clicked(node) => self.perform_action(Action::OpenFile(node)), + // ContextTreeEvent::ContextAction(action, node) => self.perform_action(action), + // } } }); @@ -151,15 +146,23 @@ impl DamnSimpleUI { emulator, show_process_manager: true, process_manager: ProcessManager::new(), - file_tree: FileTree::build(&std::env::current_dir().unwrap()), + + file_tree: ContextTree::new( + NewFileTree::build(&std::env::current_dir().unwrap()), + |ui, node| match node { + NewFileTree::Leaf { filename } => ui + .button("Open Binary") + .clicked() + .then_some(Action::LoadBinary(DataSource::File(filename.clone()))), + NewFileTree::Branch { .. } => None, + }, + ), } } pub fn perform_action(&mut self, action: Action) { match action { - Action::None => {} - - Action::SpawnProcess(spawner) => spawner(&mut self.process_manager), + Action::SpawnProcess(spawner) => spawner.run(&mut self.process_manager), Action::RequestCloseTab(tile_id) => { self.confirm_dialog.open( "Close Tab", diff --git a/emulator/dsa_gui/src/ui/process_manager.rs b/emulator/dsa_gui/src/ui/process_manager.rs index 02f96cb..85bb976 100644 --- a/emulator/dsa_gui/src/ui/process_manager.rs +++ b/emulator/dsa_gui/src/ui/process_manager.rs @@ -2,11 +2,32 @@ use crate::state::Action; use crate::ui::ui_helpers::text::strip_ansi; use dsa_tasks::{ProcessAgent, TraceableProcess}; use std::collections::VecDeque; +use std::fmt::Debug; use std::sync::mpsc; use std::thread::JoinHandle; use zxq5_egui::tabs::painted_tab; -pub type ProcessSpawner = Box; +pub struct ProcessSpawner(Box); + +impl Debug for ProcessSpawner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "ProcessSpawner") + } +} + +impl ProcessSpawner { + pub(crate) fn run(self, process_manager: &mut ProcessManager) { + self.0(process_manager); + } + + pub fn new(func: impl FnOnce(&mut ProcessManager) + Send + 'static) -> Self { + Self(Box::new(func)) + } + + pub fn into_action(self) -> Action { + Action::SpawnProcess(self) + } +} pub struct ProcessHandle { id: usize, @@ -23,10 +44,10 @@ impl ProcessHandle { id: usize, name: impl Into, process: P, - on_complete: impl FnOnce(Result) -> Action + Send + 'static + on_complete: impl FnOnce(Result) -> Action + Send + 'static, ) -> Self { let (tx, rx) = mpsc::channel::(); - let agent = ProcessAgent::new( tx ); + let agent = ProcessAgent::new(tx); let join_handle = std::thread::spawn(move || { let result = TraceableProcess::run(Box::new(process), agent); @@ -34,7 +55,11 @@ impl ProcessHandle { }); Self { - rx, id, name: name.into(), join_handle: Some(join_handle), logs: Vec::new() + rx, + id, + name: name.into(), + join_handle: Some(join_handle), + logs: Vec::new(), } } } @@ -63,7 +88,12 @@ impl ProcessManager { self.terminated_actions.pop_front() } - pub fn spawn(&mut self, name: impl Into, process: P, on_complete: impl FnOnce(Result) -> Action + Send + 'static) { + pub fn spawn( + &mut self, + name: impl Into, + process: P, + on_complete: impl FnOnce(Result) -> Action + Send + 'static, + ) { let handle = ProcessHandle::spawn(self.new_id(), name, process, on_complete); self.handles.push(handle); } @@ -79,7 +109,10 @@ impl ProcessManager { /// Checks for finished processes and queues their resulting Actions. fn poll_finished(&mut self) { for handle in &mut self.handles { - let is_finished = handle.join_handle.as_ref().is_some_and(|jh| jh.is_finished()); + 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() { if let Ok(action) = jh.join() { @@ -102,11 +135,13 @@ impl ProcessManager { } // keep active_tab pointing at something valid - if !self.active_tab.map_or(false, |id| self.handles.iter().any(|h| h.id == id)) { + if !self + .active_tab + .map_or(false, |id| self.handles.iter().any(|h| h.id == id)) + { self.active_tab = self.handles.first().map(|h| h.id); } - let mut to_close = None; ui.horizontal(|ui| { @@ -147,4 +182,4 @@ impl ProcessManager { } } } -} \ No newline at end of file +} diff --git a/emulator/dsa_gui/src/ui/tiles/editor/mod.rs b/emulator/dsa_gui/src/ui/tiles/editor/mod.rs index c17ad8c..5fe5154 100644 --- a/emulator/dsa_gui/src/ui/tiles/editor/mod.rs +++ b/emulator/dsa_gui/src/ui/tiles/editor/mod.rs @@ -1,5 +1,6 @@ use crate::state::Action; use crate::state::DataSource::Data; +use crate::ui::process_manager::ProcessSpawner; use crate::ui::tiles::TilePane; use crate::ui::tiles::editor::syntax::SyntaxFrom; use assembler::prelude::Assembler; @@ -30,7 +31,10 @@ impl Default for Editor { Self { buffer: String::new(), filename: None, - editor: CodeEditor::default().with_fontsize(12.0).with_theme(THEME).with_numlines(true), + editor: CodeEditor::default() + .with_fontsize(12.0) + .with_theme(THEME) + .with_numlines(true), syntax: Syntax::default(), completer: Completer::default().with_auto_indent().with_user_words(), } @@ -70,106 +74,90 @@ impl Editor { } } - pub fn create_build_action(&self, filename: PathBuf, on_complete: impl FnOnce(DseExecutable) -> Action + Send + 'static) -> Action { - Action::SpawnProcess(Box::new(move |pm| { + pub fn create_build_action( + &self, + filename: PathBuf, + on_complete: impl FnOnce(DseExecutable) -> Action + Send + 'static, + ) -> Action { + ProcessSpawner::new(move |pm| { let builder = Assembler::new(filename); - pm.spawn( - builder.label(), - builder, - move |result| match result { - Ok(bin) => on_complete(bin), - Err(e) => Action::ShowError(e.to_string()), - } - ); - })) + pm.spawn(builder.label(), builder, move |result| match result { + Ok(bin) => on_complete(bin), + Err(e) => Action::ShowError(e.to_string()), + }); + }) + .into_action() } } impl TilePane for Editor { fn title(&self) -> egui::WidgetText { - self.filename.clone() + self.filename + .clone() .map(|f| f.file_name().unwrap().to_str().unwrap().into()) .unwrap_or("New File*".into()) } fn ui(&mut self, ui: &mut Ui, tile_id: TileId) -> Option { - let mut action = Action::None; + let mut action = None; egui::Panel::top("panel").show(ui, |ui| { ui.horizontal(|ui| { - if PaddedWidget::button("Build & Load").ui(ui).clicked() { let Some(filename) = self.filename.clone() else { - action = Action::ShowError("Error - no filename set".to_string()); + action = Some(Action::ShowError("Error - no filename set".to_string())); return; }; - action = self.create_build_action( - filename, |res| { - Action::LoadBinary(Data(res.to_memory_image())) - } - ); + action = Some(self.create_build_action(filename, |res| { + Action::LoadBinary(Data(res.to_memory_image())) + })); } if PaddedWidget::button("Build").ui(ui).clicked() { let Some(filename) = self.filename.clone() else { - action = Action::ShowError("Error - no filename set".to_string()); + action = Some(Action::ShowError("Error - no filename set".to_string())); return; }; - action = self.create_build_action( - filename.clone(), move |res| { - let filename = filename.with_extension("dsb"); - let mut writer = File::create(&filename).unwrap(); - res.write(&mut writer).unwrap(); - Action::OpenFile(filename) - } - ); + action = Some(self.create_build_action(filename.clone(), move |res| { + let filename = filename.with_extension("dsb"); + let mut writer = File::create(&filename).unwrap(); + res.write(&mut writer).unwrap(); + Action::OpenFile(filename) + })); } PaddedWidget::label( self.filename .as_ref() .map(|f| f.display().to_string()) - .unwrap_or_else(|| String::from("New File")) - ).color(Color32::DARK_GRAY).ui(ui); + .unwrap_or_else(|| String::from("New File")), + ) + .color(Color32::DARK_GRAY) + .ui(ui); }); }); - self.editor.show_with_completer(ui, &mut self.buffer, &self.syntax, &mut self.completer); + self.editor + .show_with_completer(ui, &mut self.buffer, &self.syntax, &mut self.completer); if ui.input(|i| i.key_pressed(Key::S) && i.modifiers.ctrl) { action = if let Some(filename) = self.filename.clone() { - Action::SaveFile(filename, tile_id) + Some(Action::SaveFile(filename, tile_id)) } else { - Action::RequestSaveAs(tile_id) + Some(Action::RequestSaveAs(tile_id)) } }; - Some(action) + action } - fn as_any_mut(&mut self) -> &mut dyn Any { self } + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } fn close_requires_confirmation(&mut self, _tile_id: TileId) -> bool { true } } - - - - - - - - - - - - - - - - - - diff --git a/emulator/dsa_gui/src/ui/tiles/mod.rs b/emulator/dsa_gui/src/ui/tiles/mod.rs index 3d3ef6a..c04e0f4 100644 --- a/emulator/dsa_gui/src/ui/tiles/mod.rs +++ b/emulator/dsa_gui/src/ui/tiles/mod.rs @@ -1,16 +1,18 @@ -pub mod documentation; -pub mod editor; pub mod about; pub mod disassembler; +pub mod documentation; +pub mod editor; +pub mod io_devices; pub mod memory; pub mod registers; -pub mod io_devices; use crate::state::Action; use crate::state::Action::RequestCloseTab; use eframe::epaint::{Color32, CornerRadius}; use egui::{Id, Response, Stroke, Ui}; -use egui_tiles::{Behavior, Container, SimplificationOptions, TabState, Tile, TileId, Tiles, Tree, UiResponse}; +use egui_tiles::{ + Behavior, Container, SimplificationOptions, TabState, Tile, TileId, Tiles, Tree, UiResponse, +}; use std::any::Any; use zxq5_egui::tabs::painted_tab; @@ -19,7 +21,6 @@ use zxq5_egui::tabs::painted_tab; /// type after it's been handed off to the tree (e.g. to write into the /// editor's buffer from an `Action` handler). pub trait TilePane: Any { - /// the title that's shown on the tab in the UI. fn title(&self) -> egui::WidgetText; @@ -75,15 +76,23 @@ impl Behavior for TileBehavior { } fn on_tab_close(&mut self, tiles: &mut Tiles, tile_id: TileId) -> bool { + println!("on_tab_close_1"); + let Some(Tile::Pane(pane)) = tiles.get_mut(tile_id) else { - return false + println!("on_tab_close_ret1"); + return false; }; + println!("on_tab_close_2"); + if pane.close_requires_confirmation(tile_id) { + println!("on_tab_close_ret2"); self.pending_action = Some(RequestCloseTab(tile_id)); return false; } + println!("on_tab_close_3"); + tiles.remove(tile_id); true } @@ -112,7 +121,6 @@ impl Behavior for TileBehavior { 36.0 // taller tab row = more vertical padding around the label } - fn simplification_options(&self) -> SimplificationOptions { SimplificationOptions { // Keep the tab bar around even with only one tab, and don't let @@ -122,7 +130,13 @@ impl Behavior for TileBehavior { } } - fn tab_bg_color(&self, visuals: &egui::Visuals, _tiles: &Tiles>, _tile_id: TileId, tab_state: &TabState) -> egui::Color32 { + fn tab_bg_color( + &self, + visuals: &egui::Visuals, + _tiles: &Tiles>, + _tile_id: TileId, + tab_state: &TabState, + ) -> egui::Color32 { visuals.widgets.inactive.bg_fill } } @@ -169,7 +183,8 @@ impl CentralTiles { match self.tree.root { Some(root) => { - if let Some(Tile::Container(Container::Tabs(tabs))) = self.tree.tiles.get_mut(root) { + if let Some(Tile::Container(Container::Tabs(tabs))) = self.tree.tiles.get_mut(root) + { tabs.children.push(tile_id); tabs.active = Some(tile_id); } else { @@ -205,9 +220,20 @@ impl CentralTiles { } pub fn ui(&mut self, ui: &mut egui::Ui) -> Option { - self.behavior.pending_action = None; + println!("centraltiles_ui_1"); + + // self.behavior.pending_action = None; self.tree.ui(&mut self.behavior, ui); - self.behavior.pending_action.take() + + println!("centraltiles_ui_2"); + + let action = self.behavior.pending_action.take(); + + println!("centraltiles_ui_3 {action:?}"); + + action + + // self.behavior.pending_action.take() } } @@ -219,6 +245,9 @@ impl PanelFactory { T: TilePane + 'static, F: Fn() -> T + 'static, { - Self(name.to_string(), Box::new(move || Box::new(constructor()) as Box)) + Self( + name.to_string(), + Box::new(move || Box::new(constructor()) as Box), + ) } -} \ No newline at end of file +} diff --git a/libraries/zxq5_egui/src/context_tree.rs b/libraries/zxq5_egui/src/context_tree.rs new file mode 100644 index 0000000..5241fae --- /dev/null +++ b/libraries/zxq5_egui/src/context_tree.rs @@ -0,0 +1,150 @@ +use egui::{InnerResponse, TextBuffer, Widget}; +use std::borrow::Cow; +use std::path::{Path, PathBuf}; + +#[derive(Clone, PartialOrd, Ord, Eq, PartialEq)] +pub enum NewFileTree { + Branch { + path: PathBuf, + children: Vec, + }, + Leaf { + filename: PathBuf, + }, +} + +impl NewFileTree { + pub fn build(path: &Path) -> Self { + match path.is_dir() { + true => Self::Branch { + path: path.to_path_buf(), + children: { + let mut children: Vec = std::fs::read_dir(path) + .map(|ok| { + ok.filter_map(|file| file.ok()) + .map(|file| Self::build(&path.join(file.file_name()))) + .collect() + }) + .unwrap_or_default(); + children.sort_unstable(); + children + }, + }, + false => Self::Leaf { + filename: path.to_path_buf(), + }, + } + } +} + +impl TreeView for NewFileTree { + fn node(&self) -> Node<'_, Self> { + match self { + Self::Branch { path, children } => Node::Branch { + name: path.file_name().unwrap().to_string_lossy(), + children, + }, + Self::Leaf { filename } => Node::Leaf { + name: filename.file_name().unwrap().to_string_lossy(), + }, + } + } +} + +pub enum ContextTreeEvent<'t, A, T: TreeView> { + Clicked(Node<'t, T>), + ContextAction(A, Node<'t, T>), +} + +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub enum Node<'t, T: TreeView> { + Leaf { + name: Cow<'t, str>, + }, + Branch { + name: Cow<'t, str>, + children: &'t Vec, + }, +} + +pub trait TreeView: Sized + Clone { + fn node(&self) -> Node<'_, Self>; +} + +pub struct ContextTree { + root: T, + context: Box Option>, +} + +impl ContextTree +where + Tree: TreeView, +{ + type Ctx = Box Option>; + + pub fn new Option + 'static>( + root: Tree, + context: T, + ) -> Self { + Self { + root, + context: Box::new(context), + } + } + + fn ctx_menu<'t>( + ui: &mut egui::Ui, + ctx: &mut Self::Ctx, + tree: &'t Tree, + ) -> Option> { + ctx(ui, tree).and_then(|a| Some(ContextTreeEvent::ContextAction(a, tree.node()))) + } + + pub fn ui( + &mut self, + ui: &mut egui::Ui, + ) -> InnerResponse>> { + let mut action = None; + let frame_response = egui::Frame::new().show(ui, |ui| { + egui::ScrollArea::both() + .auto_shrink([false; 2]) + .show(ui, |ui| { + Self::ui_inner(ui, &mut action, &mut self.context, &self.root); + }) + }); + InnerResponse::new(action, frame_response.response) + } + + fn ui_inner<'ctx, 't>( + ui: &mut egui::Ui, + action: &mut Option>, + ctx: &mut Self::Ctx, + tree: &'t Tree, + ) { + match tree.node() { + Node::Leaf { ref name } => { + let response = egui::Button::new(name.as_str()).frame(false).ui(ui); + + response.context_menu(|ui| *action = Self::ctx_menu(ui, ctx, tree)); + + if response.clicked() { + *action = Some(ContextTreeEvent::Clicked(tree.node())); + } + } + Node::Branch { + ref name, + ref children, + } => { + let show_response = egui::CollapsingHeader::new(name.as_str()).show(ui, |ui| { + for child in children.iter() { + Self::ui_inner(ui, action, ctx, child); + } + }); + + show_response + .header_response + .context_menu(|ui| *action = Self::ctx_menu(ui, ctx, tree)); + } + } + } +} diff --git a/libraries/zxq5_egui/src/file_tree.rs b/libraries/zxq5_egui/src/file_tree.rs index 446e6e0..791d3f4 100644 --- a/libraries/zxq5_egui/src/file_tree.rs +++ b/libraries/zxq5_egui/src/file_tree.rs @@ -1,4 +1,4 @@ -use egui::{CollapsingHeader, InnerResponse}; +use egui::{CollapsingHeader, InnerResponse, Widget}; use std::cmp::Ordering; use std::path::{Path, PathBuf}; @@ -70,15 +70,19 @@ impl FileTree { ) { match self { Self::Leaf { filename } => { - let response = ui.button(filename.file_name().unwrap().to_string_lossy()); - if response.clicked() { - *action = Some(FileTreeEvent::Clicked(PathBuf::from(filename))); - } + let response = egui::Button::new(filename.file_name().unwrap().to_string_lossy()) + .frame(false) + .ui(ui); + response.context_menu(|ui| { if let Some(a) = ctx(ui, filename) { *action = Some(FileTreeEvent::ContextAction(a)); } }); + + if response.clicked() { + *action = Some(FileTreeEvent::Clicked(PathBuf::from(filename))); + } } Self::Branch { path, children } => { CollapsingHeader::new(path.file_name().unwrap().to_string_lossy()) diff --git a/libraries/zxq5_egui/src/lib.rs b/libraries/zxq5_egui/src/lib.rs index c75dd1f..8736b9a 100644 --- a/libraries/zxq5_egui/src/lib.rs +++ b/libraries/zxq5_egui/src/lib.rs @@ -1,3 +1,8 @@ +#![feature(trait_alias)] +#![feature(str_as_str)] +#![feature(inherent_associated_types)] + pub mod buttons; -pub mod tabs; +pub mod context_tree; pub mod file_tree; +pub mod tabs; diff --git a/tooling/assembler/src/assembler/codegen.rs b/tooling/assembler/src/assembler/codegen.rs index 789dfbd..9d9dc6d 100644 --- a/tooling/assembler/src/assembler/codegen.rs +++ b/tooling/assembler/src/assembler/codegen.rs @@ -4,17 +4,11 @@ use crate::{assembler::AssembleError, expect_token}; use common::prelude::*; use dsa_tasks::ProcessAgent; -pub fn codegen(nodes: Vec, ctx: &ProcessAgent) -> Result, AssembleError> { - let mut instructions = vec![]; - - for node in nodes { - ctx.log(format!("{}", node)); - instructions.push(build_instruction(&node)?); - } - - ctx.log("Assembly Successful ✅"); - - Ok(instructions) +pub fn codegen(nodes: &[Node], ctx: &ProcessAgent) -> Result, AssembleError> { + nodes + .iter() + .map(build_instruction) + .collect::, _>>() } fn build_instruction(node: &Node) -> Result { @@ -41,6 +35,7 @@ fn build_instruction(node: &Node) -> Result { Opcode::Jmp | Opcode::Call | Opcode::Jic | Opcode::Jnc => { ins_jump_unconditional(opcode, &args) } + Opcode::Jez | Opcode::Jnz => ins_jump_conditional(opcode, &args), Opcode::Shl | Opcode::Shr => ins_bitshift(opcode, &args), Opcode::Add @@ -72,10 +67,7 @@ fn build_instruction(node: &Node) -> Result { } } -fn ins_mov( - opcode: Opcode, - args: &[Token], -) -> Result { +fn ins_mov(opcode: Opcode, args: &[Token]) -> Result { let Some(src_token) = args.first() else { return Err(AssembleError::MissingArgument(0)); }; @@ -92,10 +84,7 @@ fn ins_mov( } } -fn ins_cmov( - opcode: Opcode, - args: &[Token], -) -> Result { +fn ins_cmov(opcode: Opcode, args: &[Token]) -> Result { let Some(src_token) = args.first() else { return Err(AssembleError::MissingArgument(0)); }; @@ -116,10 +105,7 @@ fn ins_cmov( } } -fn ins_ldx_stx( - opcode: Opcode, - args: &[Token], -) -> Result { +fn ins_ldx_stx(opcode: Opcode, args: &[Token]) -> Result { let Some(src_token) = args.first() else { return Err(AssembleError::MissingArgument(0)); }; @@ -147,10 +133,7 @@ fn ins_ldx_stx( } } -fn ins_stack( - opcode: Opcode, - args: &[Token], -) -> Result { +fn ins_stack(opcode: Opcode, args: &[Token]) -> Result { let Some(reg_token) = args.first() else { return Err(AssembleError::MissingArgument(0)); }; @@ -163,10 +146,7 @@ fn ins_stack( } } -fn ins_load_imm( - opcode: Opcode, - args: &[Token], -) -> Result { +fn ins_load_imm(opcode: Opcode, args: &[Token]) -> Result { let Some(value_token) = args.first() else { return Err(AssembleError::MissingArgument(0)); }; @@ -184,10 +164,7 @@ fn ins_load_imm( } } -fn ins_jump_unconditional( - opcode: Opcode, - args: &[Token], -) -> Result { +fn ins_jump_unconditional(opcode: Opcode, args: &[Token]) -> Result { let Some(addr_imm_token) = args.first() else { return Err(AssembleError::MissingArgument(0)); }; @@ -206,21 +183,16 @@ fn ins_jump_unconditional( } } -fn ins_jump_conditional( - opcode: Opcode, - args: &[Token], -) -> Result { +fn ins_jump_conditional(opcode: Opcode, args: &[Token]) -> Result { let Some(condition_token) = args.first() else { return Err(AssembleError::MissingArgument(0)); }; let Some(addr_imm_token) = args.get(1) else { return Err(AssembleError::MissingArgument(1)); }; - let addr_reg_token = if let Some(token) = args.get(2) { - token - } else { - &Token::Register(Register::Zero) - }; + let addr_reg_token = args + .get(2) + .map_or(&Token::Register(Register::Zero), |token| token); let condition = expect_token!(condition_token, Register)?; let addr_imm = expect_token!(addr_imm_token, Immediate)?; @@ -233,10 +205,7 @@ fn ins_jump_conditional( }) } -fn ins_comparison( - opcode: Opcode, - args: &[Token], -) -> Result { +fn ins_comparison(opcode: Opcode, args: &[Token]) -> Result { let Some(left_token) = args.first() else { return Err(AssembleError::MissingArgument(0)); }; @@ -261,10 +230,7 @@ fn ins_comparison( }) } -fn ins_bitshift( - opcode: Opcode, - args: &[Token], -) -> Result { +fn ins_bitshift(opcode: Opcode, args: &[Token]) -> Result { let Some(src_reg) = args.first() else { return Err(AssembleError::MissingArgument(0)); }; @@ -290,10 +256,7 @@ fn ins_bitshift( }) } -fn ins_arithmetic( - opcode: Opcode, - args: &[Token], -) -> Result { +fn ins_arithmetic(opcode: Opcode, args: &[Token]) -> Result { let Some(left_token) = args.first() else { return Err(AssembleError::MissingArgument(0)); }; @@ -321,10 +284,7 @@ fn ins_arithmetic( }) } -fn ins_imm_arithmetic( - opcode: Opcode, - args: &[Token], -) -> Result { +fn ins_imm_arithmetic(opcode: Opcode, args: &[Token]) -> Result { let Some(reg_token) = args.first() else { return Err(AssembleError::MissingArgument(0)); }; @@ -368,9 +328,7 @@ fn ins_interrupt(args: &[Token]) -> Result { Ok(Instruction::int(code)) } -fn build_data_instruction( - args: &[Token], -) -> Result { +fn build_data_instruction(args: &[Token]) -> Result { let Some(immediate_token) = args.first() else { return Err(AssembleError::MissingArgument(0)); }; @@ -379,9 +337,7 @@ fn build_data_instruction( Ok(Instruction::data(immediate)) } -fn build_segment_instruction( - args: &[Token], -) -> Result { +fn build_segment_instruction(args: &[Token]) -> Result { let Some(immediate_token) = args.first() else { return Err(AssembleError::MissingArgument(0)); }; @@ -390,6 +346,6 @@ fn build_segment_instruction( Ok(Instruction::data( // mask the first 6 bits for the opcode // this is really deprecated tbh. may remove in future. - Opcode::Segment as u32 | (immediate & 0x05FFFFFF), + Opcode::Segment as u32 | (immediate & 0x05FF_FFFF), )) } diff --git a/tooling/assembler/src/assembler/mod.rs b/tooling/assembler/src/assembler/mod.rs index 9f764c1..c9e0876 100644 --- a/tooling/assembler/src/assembler/mod.rs +++ b/tooling/assembler/src/assembler/mod.rs @@ -1,15 +1,16 @@ #![allow(dead_code, unused)] -use dsa_tasks::{ProcessAgent, TraceableProcess}; -use crate::assembler::resolver2::{SectionId, resolve_address, resolve_dependencies, resolve_symbols, split_sections}; +use crate::assembler::resolver2::{ + SectionId, resolve_address, resolve_dependencies, resolve_symbols, split_sections, +}; use dsa_formats::binary_dse::{DseExecutable, SymbolEntry}; +use dsa_tasks::{ProcessAgent, TraceableProcess}; use std::{ collections::HashSet, fmt, fs, hash::{DefaultHasher, Hash, Hasher}, path::{Path, PathBuf}, - sync:: - mpsc::{self}, + sync::mpsc::{self}, }; // Module declarations @@ -64,6 +65,16 @@ impl TraceableProcess for Assembler { fn run(mut self: Box, ctx: ProcessAgent) -> Result { assemble(&self.src_path, &ctx) } + + fn label(&self) -> String { + format!( + "Assembling {}", + self.src_path + .file_name() + .expect("a valid path must have a file name") + .to_string_lossy() + ) + } } fn assemble(src: &Path, ctx: &ProcessAgent) -> Result { @@ -74,21 +85,14 @@ fn assemble(src: &Path, ctx: &ProcessAgent) -> Result = instructions - .iter() - .map(|i| i.0).collect(); + let instructions = codegen(&text_nodes, ctx)?; + let text: Vec = instructions.iter().map(|i| i.0).collect(); let data = build_data_bytes(&data_nodes)?; @@ -117,20 +121,16 @@ fn assemble(src: &Path, ctx: &ProcessAgent) -> Result Result, AssembleError> { @@ -144,7 +144,6 @@ fn build_data_bytes(data_nodes: &[Node]) -> Result, AssembleError> { Ok(bytes) } - fn prepare_dependency( path: &Path, modules: &mut HashSet, @@ -157,21 +156,26 @@ fn prepare_dependency( .expect("Failed to get file name from path"); if let Ok(path) = path.canonicalize() { - ctx.log(format!("{:20} {:20} [{}]", "Building", filename, path.display())); + ctx.log(format!( + "{:30} {:20} [{}]", + "Building", + filename, + path.display() + )); } let src = fs::read_to_string(path).map_err(|_| AssembleError::InvalidFile(path.to_path_buf()))?; let file_hash = quick_hash(path); - ctx.log(format!("{:20} {:20}", "Tokenising", filename)); + ctx.log(format!("{:30} {:20}", "Lexing", filename)); let tokens = lexer::lexer(src, file_hash)?; // let tokens = Lexer::new(src, file_hash).run()?; - ctx.log(format!("{:20} {:20}", "Parsing", filename)); + ctx.log(format!("{:30} {:20}", "Parsing", filename)); let parsed = Parser::parse_nodes(tokens)?; - ctx.log(format!("{:20} {:20}", "Resolving Deps", filename)); + ctx.log(format!("{:30} {:20}", "Resolving Dependencies", filename)); // Get the parent directory of the source file to use as the base directory let base_dir = path .parent() @@ -181,20 +185,20 @@ fn prepare_dependency( let deps = Parser::get_dependencies(&nodes, path)?; - ctx.log(format!("{:20} {:20}", "Expanding Pseudo-ops", filename)); + ctx.log(format!("{:30} {:20}", "Expanding Macros", filename)); // add a section instruction nodes.insert( 0, node!(None, Opcode::Segment, Token::Immediate(file_hash as u32)), ); - + program.add_module(nodes); for dep in deps { ctx.log(format!( "{:20} {:20}", - "Including", + "Including Dependency", dep.file_name() .and_then(|f| f.to_str()) .expect("Dependency path has no file name or is not valid UTF-8") @@ -202,7 +206,7 @@ fn prepare_dependency( let dep_hash = quick_hash(&dep); if modules.insert(dep_hash) { - prepare_dependency(dep.as_path(), modules, program, &ctx)?; + prepare_dependency(dep.as_path(), modules, program, ctx)?; } }