IT WORKS HELL YEAH.
This commit is contained in:
+1
-1
@@ -14,7 +14,7 @@ required-features = ["config"]
|
||||
[dependencies]
|
||||
common = { path = "../common" }
|
||||
assembler = { path = "../assembler" }
|
||||
dsa_editor = { git = "https://github.com/zxq5-dev/egui_code_editor", package = "egui_code_editor", rev = "5eb313e" }
|
||||
dsa_editor = { path = "../dsa_editor" }
|
||||
eframe = "0.31.1"
|
||||
egui = "0.31.1"
|
||||
rfd = "0.15.3"
|
||||
|
||||
@@ -36,8 +36,6 @@ pub fn run_emulator(
|
||||
let mut instruction_count = 0;
|
||||
|
||||
loop {
|
||||
println!("Looping");
|
||||
|
||||
let cmd = if running == Running::Running {
|
||||
match cmd_rx.try_recv() {
|
||||
Ok(cmd) => Some(cmd),
|
||||
@@ -52,8 +50,6 @@ pub fn run_emulator(
|
||||
};
|
||||
|
||||
if let Some(cmd) = cmd {
|
||||
println!("Received command: {cmd:?}");
|
||||
|
||||
match cmd {
|
||||
Command::Start => {
|
||||
running = Running::Running;
|
||||
@@ -69,18 +65,14 @@ pub fn run_emulator(
|
||||
.expect("This is a valid path, WTF."),
|
||||
));
|
||||
}
|
||||
|
||||
println!("Emulator started");
|
||||
}
|
||||
Command::Stop => {
|
||||
running = Running::Paused;
|
||||
println!("Emulator stopped");
|
||||
}
|
||||
Command::Reset => {
|
||||
running = Running::Paused;
|
||||
processor.reset();
|
||||
instruction_count = 0;
|
||||
println!("Emulator rebooted");
|
||||
}
|
||||
Command::Step => {
|
||||
running = Running::Paused;
|
||||
@@ -98,15 +90,12 @@ pub fn run_emulator(
|
||||
}
|
||||
|
||||
instruction_count += 1;
|
||||
println!("Stepped one instruction");
|
||||
}
|
||||
Command::Read(new, _size) => {
|
||||
addr = new;
|
||||
println!("Memory view for address 0x{addr:08x}");
|
||||
}
|
||||
Command::Write(offset, data) => {
|
||||
processor.memory.write_range(offset, data);
|
||||
println!("Program loaded");
|
||||
}
|
||||
Command::Interrupt(_interrupt) => {
|
||||
todo!("implement interrupts")
|
||||
|
||||
@@ -78,7 +78,7 @@ impl MemoryUnit for MainStore {
|
||||
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_le_bytes(bytes)
|
||||
u32::from_be_bytes(bytes)
|
||||
}
|
||||
|
||||
fn read_range(&mut self, addr: u32, size: u32) -> Vec<u8> {
|
||||
|
||||
@@ -220,8 +220,8 @@ impl Executable for Instruction {
|
||||
// address must be byte-aligned.
|
||||
Self::StoreByte(a) => {
|
||||
cpu.memory.write_byte(
|
||||
cpu.get(a.r1) + u32::from(a.immediate),
|
||||
cpu.get(a.r2) as u8,
|
||||
cpu.get(a.r2) + u32::from(a.immediate),
|
||||
cpu.get(a.r1) as u8,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -231,16 +231,16 @@ impl Executable for Instruction {
|
||||
// split the value into bytes and then write two bytes
|
||||
let bytes = (cpu.get(a.r1) as u16).to_le_bytes();
|
||||
cpu.memory
|
||||
.write_byte(cpu.get(a.r1) + u32::from(a.immediate), bytes[0]);
|
||||
.write_byte(cpu.get(a.r2) + u32::from(a.immediate), bytes[0]);
|
||||
cpu.memory
|
||||
.write_byte(cpu.get(a.r1) + u32::from(a.immediate) + 1, bytes[1]);
|
||||
.write_byte(cpu.get(a.r2) + u32::from(a.immediate) + 1, bytes[1]);
|
||||
}
|
||||
|
||||
// Stores a word from SrcReg in memory address (base + offset) The effective
|
||||
// address must be 4-byte-aligned.
|
||||
Self::StoreWord(a) => {
|
||||
cpu.memory
|
||||
.write_word(cpu.get(a.r1) + u32::from(a.immediate), cpu.get(a.r2));
|
||||
.write_word(cpu.get(a.r2) + u32::from(a.immediate), cpu.get(a.r1));
|
||||
}
|
||||
|
||||
// Loads a 16-bit literal value into reg, setting the bottom 16 bits of the
|
||||
@@ -339,6 +339,14 @@ impl Executable for Instruction {
|
||||
*cpu.reg(a.dr) = sub(cpu.get(a.sr1), cpu.get(a.sr2));
|
||||
}
|
||||
|
||||
Self::AddImmediate(a) => {
|
||||
*cpu.reg(a.r2) = add(cpu.get(a.r1), u32::from(a.immediate));
|
||||
}
|
||||
|
||||
Self::SubImmediate(a) => {
|
||||
*cpu.reg(a.r2) = sub(cpu.get(a.r1), u32::from(a.immediate));
|
||||
}
|
||||
|
||||
// Performs bitwise AND on Src1 and Src2 storing the result in a.dr
|
||||
Self::And(a) => *cpu.reg(a.dr) = and(cpu.get(a.sr1), cpu.get(a.sr2)),
|
||||
|
||||
@@ -384,7 +392,13 @@ impl Executable for Instruction {
|
||||
cpu.halted = true;
|
||||
}
|
||||
|
||||
_ => todo!(),
|
||||
Self::Segment(_) => {}
|
||||
Self::Data(_) => {}
|
||||
|
||||
_ => {
|
||||
println!("unimplemented instruction: {}", self);
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
use crate::emulator::{
|
||||
system::model::State,
|
||||
ui::interface::{Category, Component},
|
||||
};
|
||||
|
||||
use eframe::egui;
|
||||
use egui::{Color32, FontId, Margin, RichText, Vec2};
|
||||
|
||||
const VGA_WIDTH: usize = 80;
|
||||
const VGA_HEIGHT: usize = 25;
|
||||
|
||||
pub struct Display {
|
||||
visible: bool,
|
||||
}
|
||||
|
||||
impl Display {
|
||||
pub fn new() -> Self {
|
||||
Self { visible: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Display {
|
||||
fn name(&self) -> &'static str {
|
||||
"Display"
|
||||
}
|
||||
|
||||
fn category(&self) -> Category {
|
||||
Category::IO
|
||||
}
|
||||
|
||||
fn visible(&mut self) -> &mut bool {
|
||||
&mut self.visible
|
||||
}
|
||||
|
||||
fn render(&mut self, state: &mut State, ui: &mut egui::Ui, ctx: &egui::Context) {
|
||||
let display: Vec<u8> = state.display_view.clone();
|
||||
let font_id = FontId::monospace(12.0);
|
||||
|
||||
let char_width = ui.fonts(|f| f.glyph_width(&font_id, 'W'));
|
||||
let line_height = ui.fonts(|f| f.row_height(&font_id));
|
||||
|
||||
let display_size = Vec2::new(
|
||||
char_width * VGA_WIDTH as f32,
|
||||
line_height * VGA_HEIGHT as f32,
|
||||
);
|
||||
|
||||
let (rect, _response) = ui.allocate_exact_size(display_size, egui::Sense::all());
|
||||
|
||||
// Fill background
|
||||
// ui.painter().rect_filled(rect, 0.0, Color32::BLACK);
|
||||
|
||||
// Draw text
|
||||
for y in 0..VGA_HEIGHT {
|
||||
let mut row_text = String::with_capacity(VGA_WIDTH);
|
||||
for x in 0..VGA_WIDTH {
|
||||
let index = y * VGA_WIDTH + x;
|
||||
if index < display.len() {
|
||||
let byte = display[index];
|
||||
let ch = if byte >= 32 && byte <= 126 {
|
||||
byte as char
|
||||
} else {
|
||||
' '
|
||||
};
|
||||
row_text.push(ch);
|
||||
} else {
|
||||
row_text.push(' ');
|
||||
}
|
||||
}
|
||||
|
||||
let text_pos = rect.min + Vec2::new(0.0, y as f32 * line_height);
|
||||
ui.painter().text(
|
||||
text_pos,
|
||||
egui::Align2::LEFT_TOP,
|
||||
row_text,
|
||||
font_id.clone(),
|
||||
Color32::WHITE,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,10 @@ use crate::emulator::{
|
||||
};
|
||||
|
||||
pub struct Editor {
|
||||
path: PathBuf,
|
||||
path: Option<PathBuf>,
|
||||
text: String,
|
||||
buffer: String,
|
||||
unsaved: bool,
|
||||
output: Vec<u8>,
|
||||
sender: Sender<Command>,
|
||||
cursor_col: usize,
|
||||
@@ -38,6 +40,10 @@ impl Component for Editor {
|
||||
}
|
||||
|
||||
fn render(&mut self, state: &mut State, ui: &mut Ui, ctx: &Context) {
|
||||
if self.buffer != self.text {
|
||||
self.unsaved = true;
|
||||
}
|
||||
|
||||
ui.vertical(|ui| {
|
||||
// Top bar
|
||||
|
||||
@@ -62,7 +68,7 @@ impl Component for Editor {
|
||||
},
|
||||
);
|
||||
|
||||
ui.label(format!("Ln {}, Col {}", self.cursor_line, self.cursor_col));
|
||||
self.render_bottom_bar(state, ui, ctx);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -71,9 +77,11 @@ impl Editor {
|
||||
#[must_use]
|
||||
pub fn new(sender: Sender<Command>) -> Self {
|
||||
Self {
|
||||
path: PathBuf::new(),
|
||||
path: None,
|
||||
text: String::new(),
|
||||
buffer: String::new(),
|
||||
output: Vec::new(),
|
||||
unsaved: true,
|
||||
sender,
|
||||
cursor_col: 1,
|
||||
cursor_line: 1,
|
||||
@@ -85,25 +93,31 @@ impl Editor {
|
||||
}
|
||||
|
||||
fn filename(&self) -> &str {
|
||||
self.path
|
||||
.file_name()
|
||||
.unwrap_or_else(|| OsStr::new("Unnamed!"))
|
||||
.to_str()
|
||||
.map_or_else(
|
||||
|| unreachable!("File name should be valid UTF-8."),
|
||||
|ext| ext,
|
||||
)
|
||||
if let Some(path) = &self.path {
|
||||
return path
|
||||
.file_name()
|
||||
.unwrap_or_else(|| OsStr::new("Unnamed!"))
|
||||
.to_str()
|
||||
.map_or_else(
|
||||
|| unreachable!("File name should be valid UTF-8."),
|
||||
|ext| ext,
|
||||
);
|
||||
}
|
||||
"Unnamed!"
|
||||
}
|
||||
|
||||
fn extension(&self) -> &str {
|
||||
self.path
|
||||
.extension()
|
||||
.unwrap_or_else(|| OsStr::new("Unknown!"))
|
||||
.to_str()
|
||||
.map_or_else(
|
||||
|| unreachable!("File name should be valid UTF-8."),
|
||||
|ext| ext,
|
||||
)
|
||||
if let Some(path) = &self.path {
|
||||
return path
|
||||
.extension()
|
||||
.map_or_else(|| OsStr::new("Unknown!"), |ext| ext)
|
||||
.to_str()
|
||||
.map_or_else(
|
||||
|| unreachable!("File name should be valid UTF-8."),
|
||||
|ext| ext,
|
||||
);
|
||||
}
|
||||
"Unknown!"
|
||||
}
|
||||
|
||||
fn save(&mut self) {
|
||||
@@ -113,6 +127,17 @@ impl Editor {
|
||||
)
|
||||
});
|
||||
|
||||
if let Some(path) = &self.path {
|
||||
if let Err(why) = std::fs::write(path, &self.text) {
|
||||
self.error = Some(format!("Failed to save file: {why}"));
|
||||
return;
|
||||
}
|
||||
|
||||
self.buffer = self.text.clone();
|
||||
self.unsaved = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(path) = FileDialog::new()
|
||||
.add_filter("damn simple files", &["dsa", "dsb", "dsc", "dsd"])
|
||||
.add_filter("all", &["*"])
|
||||
@@ -122,7 +147,9 @@ impl Editor {
|
||||
if let Err(why) = std::fs::write(&path, &self.text) {
|
||||
self.error = Some(format!("Failed to save file: {why}"));
|
||||
} else {
|
||||
self.path = path;
|
||||
self.path = Some(path);
|
||||
self.buffer = self.text.clone();
|
||||
self.unsaved = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,8 +168,10 @@ impl Editor {
|
||||
.pick_file()
|
||||
{
|
||||
if let Ok(contents) = std::fs::read_to_string(&path) {
|
||||
self.path = path;
|
||||
self.text = contents;
|
||||
self.path = Some(path);
|
||||
self.text = contents.clone();
|
||||
self.buffer = contents;
|
||||
self.unsaved = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,7 +180,7 @@ impl Editor {
|
||||
// Output area with synchronized scrolling
|
||||
egui::ScrollArea::vertical()
|
||||
.id_salt("output_scroll")
|
||||
.max_width(350.0)
|
||||
.max_width(400.0)
|
||||
.show(ui, |ui| {
|
||||
if self.output.is_empty() {
|
||||
ui.label(
|
||||
@@ -178,7 +207,7 @@ impl Editor {
|
||||
bytes[i] = byte;
|
||||
}
|
||||
}
|
||||
let value = u32::from_le_bytes(bytes);
|
||||
let value = u32::from_be_bytes(bytes);
|
||||
|
||||
// Address column
|
||||
ui.with_layout(
|
||||
@@ -245,8 +274,9 @@ impl Editor {
|
||||
.with_fontsize(12.0)
|
||||
.with_rows(0)
|
||||
.with_theme(ColorTheme::default())
|
||||
.with_syntax(Syntax::dsa())
|
||||
.with_numlines(true)
|
||||
.desired_width(available_width - 450.0);
|
||||
.desired_width(available_width - 500.0);
|
||||
|
||||
let mut editor = ed.clone();
|
||||
|
||||
@@ -257,17 +287,27 @@ impl Editor {
|
||||
editor.show(ui, &mut self.text);
|
||||
}
|
||||
|
||||
fn render_toolbar(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
||||
fn render_bottom_bar(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(format!("File type: {}", self.extension()));
|
||||
ui.label(format!("Filename: {}", self.filename()));
|
||||
|
||||
// error display
|
||||
ui.label(
|
||||
egui::RichText::new(self.error.clone().unwrap_or_default())
|
||||
.color(egui::Color32::RED),
|
||||
);
|
||||
|
||||
// line and col
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
ui.label(format!("Ln {}, Col {}", self.cursor_line, self.cursor_col));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn render_toolbar(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(format!("File type: {}", self.extension()));
|
||||
ui.label(format!("Filename: {}", self.filename()));
|
||||
ui.label(format!("Unsaved: {}", self.unsaved));
|
||||
|
||||
// number of lines in the file
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
let line_count = self.text.lines().count();
|
||||
@@ -291,20 +331,22 @@ impl Editor {
|
||||
|
||||
// builds the current file
|
||||
if ui.button("Build").clicked() {
|
||||
// TODO: uncomment this once assembler works!!!
|
||||
// let instructions = assembler::assemble(&self.text);
|
||||
// self.output = instructions
|
||||
// .iter()
|
||||
// .flat_map(|i| i.encode().to_le_bytes().to_vec())
|
||||
// .collect();
|
||||
if !self.unsaved {
|
||||
if let Some(path) = &self.path {
|
||||
let instructions = match assembler::assemble(path) {
|
||||
Ok(instructions) => instructions,
|
||||
Err(error) => {
|
||||
self.error = Some(error.to_string());
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
self.output = vec![
|
||||
0x2e, 0xe0, 0x00, 0x00, 0x2e, 0xe1, 0x00, 0x01, 0x2e, 0xe2, 0x00,
|
||||
0x06, 0x2e, 0xf6, 0x00, 0x00, 0x64, 0x01, 0x18, 0x00, 0x04, 0x37,
|
||||
0x00, 0x00, 0x04, 0x77, 0x08, 0x00, 0x58, 0x57, 0x10, 0x00, 0x50,
|
||||
0x56, 0xb8, 0x00, 0x42, 0xf7, 0x00, 0x04, 0x04, 0x37, 0x80, 0x00,
|
||||
0x92, 0xf7, 0xb8, 0x00,
|
||||
];
|
||||
self.output = instructions
|
||||
.iter()
|
||||
.flat_map(|i| i.encode().to_be_bytes().to_vec())
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loads the generated binary into the assembler at the provided offset
|
||||
|
||||
@@ -131,8 +131,7 @@ impl Component for MemoryInspector {
|
||||
.fold(0u32, |acc, &byte| acc << 8 | u32::from(byte));
|
||||
|
||||
ui.monospace(format!("{combined}"));
|
||||
ui.monospace(format!("{:?}", Instruction::decode(combined)));
|
||||
ui.monospace("TODO! instruction");
|
||||
ui.monospace(format!("{}", Instruction::decode(combined).unwrap_or(Instruction::Nop)));
|
||||
|
||||
ui.end_row();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod control_unit;
|
||||
pub mod display;
|
||||
pub mod editor;
|
||||
pub mod interface;
|
||||
pub mod memory_inspector;
|
||||
|
||||
@@ -18,8 +18,9 @@ use dsa_rs::emulator::{
|
||||
processor::Processor,
|
||||
},
|
||||
ui::{
|
||||
control_unit::ControlPanel, editor::Editor, interface::EmulatorUI,
|
||||
memory_inspector::MemoryInspector, stack_inspector::StackInspector,
|
||||
control_unit::ControlPanel, display::Display, editor::Editor,
|
||||
interface::EmulatorUI, memory_inspector::MemoryInspector,
|
||||
stack_inspector::StackInspector,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -97,5 +98,8 @@ fn setup_ui(cmd_sender: Sender<Command>, state_reciever: Receiver<State>) -> Emu
|
||||
let editor = Editor::new(cmd_sender.clone());
|
||||
ui.add_component(Box::new(editor));
|
||||
|
||||
let display = Display::new();
|
||||
ui.add_component(Box::new(display));
|
||||
|
||||
ui
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user