IT WORKS HELL YEAH.
This commit is contained in:
Generated
+17
-9
@@ -659,6 +659,12 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorful"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb474a9c3219a8254ead020421ecf1b90427f29b55f6aae9a2471fa62c126ef"
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.7"
|
||||
@@ -903,6 +909,16 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
|
||||
|
||||
[[package]]
|
||||
name = "dsa_editor"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"colorful",
|
||||
"eframe",
|
||||
"egui",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ecolor"
|
||||
version = "0.31.1"
|
||||
@@ -1005,14 +1021,6 @@ dependencies = [
|
||||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "egui_code_editor"
|
||||
version = "0.2.13"
|
||||
source = "git+https://github.com/zxq5-dev/egui_code_editor?rev=5eb313e#5eb313e38504410ce0a6b27231cda28842f542fe"
|
||||
dependencies = [
|
||||
"egui",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "egui_glow"
|
||||
version = "0.31.1"
|
||||
@@ -1048,9 +1056,9 @@ dependencies = [
|
||||
"common",
|
||||
"dirs",
|
||||
"discord-presence",
|
||||
"dsa_editor",
|
||||
"eframe",
|
||||
"egui",
|
||||
"egui_code_editor",
|
||||
"rfd",
|
||||
"serde",
|
||||
"toml",
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
members = ["emulator", "common", "assembler"]
|
||||
members = ["emulator", "common", "assembler", "dsa_editor"]
|
||||
resolver = "3"
|
||||
|
||||
[workspace.package]
|
||||
|
||||
+187
-3
@@ -1,6 +1,190 @@
|
||||
use common::{
|
||||
instructions,
|
||||
prelude::{Instruction, RTypeArgs},
|
||||
args, instructions,
|
||||
prelude::{ITypeArgs, Instruction, Interrupt, RTypeArgs, Register},
|
||||
};
|
||||
|
||||
use crate::model::{Node, Opcode};
|
||||
use crate::{
|
||||
AssembleError, expect_token, expect_type,
|
||||
model::{Node, Opcode, Token, TokenType},
|
||||
};
|
||||
|
||||
pub fn codegen(nodes: Vec<Node>) -> Result<Vec<Instruction>, AssembleError> {
|
||||
let mut instructions = vec![];
|
||||
|
||||
for node in nodes {
|
||||
instructions.push(build_instruction(node)?);
|
||||
}
|
||||
|
||||
Ok(instructions)
|
||||
}
|
||||
|
||||
fn build_instruction(node: Node) -> Result<Instruction, AssembleError> {
|
||||
let opcode = node.opcode();
|
||||
let args = node.args();
|
||||
|
||||
// println!("{node}");
|
||||
|
||||
match opcode {
|
||||
Opcode::Nop => Ok(Instruction::Nop),
|
||||
Opcode::Mov => {
|
||||
let src = expect_token!(args.get(0).unwrap(), Register)?;
|
||||
let dest = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
Ok(Instruction::Mov(args!(R, sr1: src, dr: dest)))
|
||||
}
|
||||
Opcode::Movs => {
|
||||
let src = expect_token!(args.get(0).unwrap(), Register)?;
|
||||
let dest = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
Ok(Instruction::MovSigned(args!(R, sr1: src, dr: dest)))
|
||||
}
|
||||
Opcode::Ldb
|
||||
| Opcode::Ldw
|
||||
| Opcode::Ldh
|
||||
| Opcode::Ldbs
|
||||
| Opcode::Ldhs
|
||||
| Opcode::Stb
|
||||
| Opcode::Stw
|
||||
| Opcode::Sth => {
|
||||
let base = expect_token!(args.get(0).unwrap(), Register)?;
|
||||
let dest = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
let offset = expect_token!(args.get(2).unwrap(), Immediate)?;
|
||||
let args = args!(I, immediate: offset as u16, r1: base, r2: dest);
|
||||
|
||||
match opcode {
|
||||
Opcode::Ldb => Ok(Instruction::LoadByte(args)),
|
||||
Opcode::Ldw => Ok(Instruction::LoadWord(args)),
|
||||
Opcode::Ldh => Ok(Instruction::LoadHalfword(args)),
|
||||
Opcode::Ldbs => Ok(Instruction::LoadByteSigned(args)),
|
||||
Opcode::Ldhs => Ok(Instruction::LoadHalfwordSigned(args)),
|
||||
Opcode::Stb => Ok(Instruction::StoreByte(args)),
|
||||
Opcode::Stw => Ok(Instruction::StoreWord(args)),
|
||||
Opcode::Sth => Ok(Instruction::StoreHalfword(args)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
Opcode::Lli => {
|
||||
let value = expect_token!(args.get(0).unwrap(), Immediate)?;
|
||||
let dest = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
let args = args!(I, immediate: value as u16, r1: dest);
|
||||
|
||||
Ok(Instruction::LoadLowerImmediate(args))
|
||||
}
|
||||
Opcode::Lui => {
|
||||
let value = expect_token!(args.get(0).unwrap(), Immediate)? >> 16;
|
||||
let dest = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
let args = args!(I, immediate: value as u16, r1: dest);
|
||||
|
||||
Ok(Instruction::LoadUpperImmediate(args))
|
||||
}
|
||||
Opcode::Jmp
|
||||
| Opcode::Jeq
|
||||
| Opcode::Jne
|
||||
| Opcode::Jgt
|
||||
| Opcode::Jge
|
||||
| Opcode::Jlt
|
||||
| Opcode::Jle => {
|
||||
let address = expect_token!(args.get(0).unwrap(), Immediate)?;
|
||||
let offset = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
let args = args!(I, immediate: address as u16, r1: offset);
|
||||
|
||||
match opcode {
|
||||
Opcode::Jmp => Ok(Instruction::Jump(args)),
|
||||
Opcode::Jeq => Ok(Instruction::JumpEq(args)),
|
||||
Opcode::Jne => Ok(Instruction::JumpNeq(args)),
|
||||
Opcode::Jgt => Ok(Instruction::JumpGt(args)),
|
||||
Opcode::Jge => Ok(Instruction::JumpGe(args)),
|
||||
Opcode::Jlt => Ok(Instruction::JumpLt(args)),
|
||||
Opcode::Jle => Ok(Instruction::JumpLe(args)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
Opcode::Cmp => {
|
||||
let left = expect_token!(args.get(0).unwrap(), Register)?;
|
||||
let right = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
Ok(Instruction::Compare(args!(R, sr1: left, sr2: right)))
|
||||
}
|
||||
Opcode::Inc => {
|
||||
let reg = expect_token!(args.get(0).unwrap(), Register)?;
|
||||
Ok(Instruction::Increment(args!(R, sr1: reg)))
|
||||
}
|
||||
Opcode::Dec => {
|
||||
let reg = expect_token!(args.get(0).unwrap(), Register)?;
|
||||
Ok(Instruction::Decrement(args!(R, sr1: reg)))
|
||||
}
|
||||
Opcode::Shl => {
|
||||
let reg = expect_token!(args.get(0).unwrap(), Register)?;
|
||||
let amount = expect_token!(args.get(1).unwrap(), Immediate)? as u8;
|
||||
Ok(Instruction::ShiftLeft(args!(R, sr1: reg, shamt: amount)))
|
||||
}
|
||||
Opcode::Shr => {
|
||||
let reg = expect_token!(args.get(0).unwrap(), Register)?;
|
||||
let amount = expect_token!(args.get(1).unwrap(), Immediate)? as u8;
|
||||
Ok(Instruction::ShiftRight(args!(R, sr1: reg, shamt: amount)))
|
||||
}
|
||||
Opcode::Add
|
||||
| Opcode::Sub
|
||||
| Opcode::And
|
||||
| Opcode::Or
|
||||
| Opcode::Xor
|
||||
| Opcode::Nand
|
||||
| Opcode::Nor
|
||||
| Opcode::Xnor => {
|
||||
let left = expect_token!(args.get(0).unwrap(), Register)?;
|
||||
let right = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
let dest = expect_token!(args.get(2).unwrap(), Register)?;
|
||||
let args = args!(R, sr1: left, sr2: right, dr: dest);
|
||||
|
||||
match opcode {
|
||||
Opcode::Add => Ok(Instruction::Add(args)),
|
||||
Opcode::Sub => Ok(Instruction::Sub(args)),
|
||||
Opcode::And => Ok(Instruction::And(args)),
|
||||
Opcode::Or => Ok(Instruction::Or(args)),
|
||||
Opcode::Xor => Ok(Instruction::Xor(args)),
|
||||
Opcode::Nand => Ok(Instruction::Nand(args)),
|
||||
Opcode::Nor => Ok(Instruction::Nor(args)),
|
||||
Opcode::Xnor => Ok(Instruction::Xnor(args)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
Opcode::Iadd | Opcode::Isub => {
|
||||
let reg = expect_token!(args.get(0).unwrap(), Register)?;
|
||||
let immediate = expect_token!(args.get(1).unwrap(), Immediate)? as u16;
|
||||
let dest = expect_token!(args.get(2).unwrap(), Register)?;
|
||||
let args = args!(I, immediate: immediate, r1: reg, r2: dest);
|
||||
|
||||
match opcode {
|
||||
Opcode::Iadd => Ok(Instruction::AddImmediate(args)),
|
||||
Opcode::Isub => Ok(Instruction::SubImmediate(args)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
Opcode::Not => {
|
||||
let reg = expect_token!(args.get(0).unwrap(), Register)?;
|
||||
let dest = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
Ok(Instruction::Not(args!(R, sr1: reg, dr: dest)))
|
||||
}
|
||||
Opcode::Int => {
|
||||
let code = expect_token!(args.get(0).unwrap(), Immediate)? as u8;
|
||||
Ok(Instruction::Interrupt(Interrupt::Software(code)))
|
||||
}
|
||||
Opcode::Irt => Ok(Instruction::IntReturn),
|
||||
Opcode::Hlt => Ok(Instruction::Halt),
|
||||
Opcode::Data => {
|
||||
let immediate = expect_token!(args.get(0).unwrap(), Immediate)?;
|
||||
Ok(Instruction::Data(immediate))
|
||||
}
|
||||
Opcode::Segment => {
|
||||
let immediate = expect_token!(args.get(0).unwrap(), Immediate)?;
|
||||
Ok(Instruction::Segment(immediate))
|
||||
}
|
||||
Opcode::Db
|
||||
| Opcode::Dh
|
||||
| Opcode::Dw
|
||||
| Opcode::Resb
|
||||
| Opcode::Resh
|
||||
| Opcode::Resw
|
||||
| Opcode::Push
|
||||
| Opcode::Pop
|
||||
| Opcode::Lwi
|
||||
| Opcode::Include => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
+43
-11
@@ -29,7 +29,14 @@ fn try_expand(
|
||||
match node.opcode() {
|
||||
Opcode::Push => expand_push(node.clone(), result)?,
|
||||
Opcode::Pop => expand_pop(node.clone(), result)?,
|
||||
Opcode::Ldb | Opcode::Ldh | Opcode::Ldw => expand_ldx(node.clone(), result)?,
|
||||
Opcode::Ldb
|
||||
| Opcode::Ldbs
|
||||
| Opcode::Ldh
|
||||
| Opcode::Ldhs
|
||||
| Opcode::Ldw
|
||||
| Opcode::Stb
|
||||
| Opcode::Sth
|
||||
| Opcode::Stw => expand_ldx(node.clone(), result)?,
|
||||
Opcode::Lwi => expand_lwi(node.clone(), result)?,
|
||||
Opcode::Resb | Opcode::Resh | Opcode::Resw => expand_resx(node.clone(), result)?,
|
||||
Opcode::Db | Opcode::Dh | Opcode::Dw => expand_dx(node.clone(), result)?,
|
||||
@@ -43,8 +50,20 @@ fn expand_push(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError
|
||||
let reg = expect_type!(current.arg(0).unwrap(), Register)?;
|
||||
|
||||
nodes.extend(vec![
|
||||
node!(label, Opcode::Iadd, reg.clone(), Token::Immediate(4)),
|
||||
node!(None, Opcode::Stw, reg, Token::Register(Register::Spr)),
|
||||
node!(
|
||||
label,
|
||||
Opcode::Iadd,
|
||||
reg.clone(),
|
||||
Token::Immediate(4),
|
||||
reg.clone()
|
||||
),
|
||||
node!(
|
||||
None,
|
||||
Opcode::Stw,
|
||||
reg,
|
||||
Token::Register(Register::Spr),
|
||||
Token::Immediate(0)
|
||||
),
|
||||
]);
|
||||
|
||||
Ok(())
|
||||
@@ -55,8 +74,20 @@ fn expand_pop(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError>
|
||||
let reg = expect_type!(current.arg(0).unwrap(), Register)?;
|
||||
|
||||
nodes.extend(vec![
|
||||
node!(label, Opcode::Isub, reg.clone(), Token::Immediate(4)),
|
||||
node!(None, Opcode::Ldw, reg, Token::Register(Register::Spr)),
|
||||
node!(
|
||||
label,
|
||||
Opcode::Isub,
|
||||
reg.clone(),
|
||||
Token::Immediate(4),
|
||||
reg.clone()
|
||||
),
|
||||
node!(
|
||||
None,
|
||||
Opcode::Ldw,
|
||||
reg,
|
||||
Token::Register(Register::Spr),
|
||||
Token::Immediate(0)
|
||||
),
|
||||
]);
|
||||
|
||||
Ok(())
|
||||
@@ -65,24 +96,25 @@ fn expand_pop(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError>
|
||||
fn expand_ldx(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let opcode = current.opcode();
|
||||
let name = expect_type!(current.arg(0).unwrap(), Symbol)?;
|
||||
let reg = expect_type!(current.arg(2).unwrap(), Register)?;
|
||||
let reg = expect_type!(current.arg(1).unwrap(), Register)?;
|
||||
let offset = expect_type!(current.arg(2).unwrap(), Immediate)?;
|
||||
|
||||
nodes.extend(vec![
|
||||
node!(current.label(), Opcode::Lli, name.clone(), reg.clone()),
|
||||
node!(None, Opcode::Lui, name.clone(), reg.clone()),
|
||||
node!(None, opcode, reg.clone(), Token::Immediate(0), reg),
|
||||
node!(None, opcode, reg.clone(), reg, offset),
|
||||
]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_lwi(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let reg = expect_type!(current.arg(0).unwrap(), Register)?;
|
||||
let name = expect_type!(current.arg(1).unwrap(), Symbol)?;
|
||||
let val = expect_type!(current.arg(0).unwrap(), Symbol, Immediate)?;
|
||||
let reg = expect_type!(current.arg(1).unwrap(), Register)?;
|
||||
|
||||
nodes.extend(vec![
|
||||
node!(current.label(), Opcode::Lli, name.clone(), reg.clone()),
|
||||
node!(None, Opcode::Lui, name.clone(), reg.clone()),
|
||||
node!(current.label(), Opcode::Lli, val.clone(), reg.clone()),
|
||||
node!(None, Opcode::Lui, val.clone(), reg.clone()),
|
||||
]);
|
||||
|
||||
Ok(())
|
||||
|
||||
+16
-11
@@ -9,10 +9,11 @@ use std::{
|
||||
use common::prelude::Instruction;
|
||||
|
||||
use crate::{
|
||||
codegen::codegen,
|
||||
expand::expand_pseudo_ops,
|
||||
model::{Node, Symbol, Token, TokenType},
|
||||
parser::{Parser, Program},
|
||||
resolver::{resolve_dependencies, resolve_symbols},
|
||||
resolver::{create_sections, resolve_dependencies, resolve_symbols},
|
||||
};
|
||||
|
||||
pub mod codegen;
|
||||
@@ -22,26 +23,25 @@ pub mod model;
|
||||
pub mod parser;
|
||||
pub mod resolver;
|
||||
|
||||
pub fn assemble(src: &Path) -> Vec<Instruction> {
|
||||
pub fn assemble(src: &Path) -> Result<Vec<Instruction>, AssembleError> {
|
||||
let mut modules = HashSet::<u64>::new();
|
||||
let mut program = Program::new();
|
||||
|
||||
let hash = quick_hash(src);
|
||||
modules.insert(hash);
|
||||
|
||||
match prepare_dependency(src, &mut modules, &mut program) {
|
||||
Ok(_) => {}
|
||||
Err(err) => println!("BIG ERROR {err:?}"),
|
||||
}
|
||||
|
||||
prepare_dependency(src, &mut modules, &mut program)?;
|
||||
let mut nodes = program.nodes;
|
||||
resolve_symbols(&mut nodes).unwrap();
|
||||
|
||||
for node in nodes {
|
||||
println!("{node}");
|
||||
create_sections(&mut nodes)?;
|
||||
resolve_symbols(&mut nodes)?;
|
||||
|
||||
let instructions = codegen(nodes)?;
|
||||
for inst in instructions.iter() {
|
||||
println!("{}", inst);
|
||||
}
|
||||
|
||||
vec![]
|
||||
Ok(instructions)
|
||||
}
|
||||
|
||||
fn prepare_dependency(
|
||||
@@ -71,6 +71,7 @@ fn prepare_dependency(
|
||||
|
||||
log(&format!("{:20} {:20}", "Resolving Deps", filename));
|
||||
let nodes = resolve_dependencies(parsed)?;
|
||||
|
||||
let deps = Parser::get_dependencies(&nodes)?;
|
||||
|
||||
log(&format!(
|
||||
@@ -79,6 +80,10 @@ fn prepare_dependency(
|
||||
));
|
||||
let nodes = expand_pseudo_ops(nodes, file_hash)?;
|
||||
|
||||
for n in nodes.iter() {
|
||||
println!("{}", n);
|
||||
}
|
||||
|
||||
program.add_module(nodes);
|
||||
|
||||
for dep in deps {
|
||||
|
||||
+11
-6
@@ -13,10 +13,15 @@ fn main() {
|
||||
let src = PathBuf::from(input_path);
|
||||
let mut output_file = fs::File::create(output_path).unwrap();
|
||||
|
||||
assembler::assemble(&src)
|
||||
.iter()
|
||||
.map(|i| i.encode())
|
||||
.for_each(|i| {
|
||||
output_file.write_all(&i.to_le_bytes()).unwrap();
|
||||
});
|
||||
match assembler::assemble(&src) {
|
||||
Ok(res) => {
|
||||
res.iter().map(|i| i.encode()).for_each(|i| {
|
||||
output_file.write_all(&i.to_le_bytes()).unwrap();
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@ impl fmt::Display for Opcode {
|
||||
|
||||
// special - generated by assembler
|
||||
Opcode::Data => write!(f, "data"),
|
||||
Opcode::Segment => write!(f, "[SEGMENT]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,6 +250,7 @@ pub enum Opcode {
|
||||
|
||||
// fake instructions (these aren't present in the binary as instructions)
|
||||
Data,
|
||||
Segment,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -376,6 +378,7 @@ impl Opcode {
|
||||
Self::Hlt => Some(0x24),
|
||||
Self::Iadd => Some(0x25),
|
||||
Self::Isub => Some(0x26),
|
||||
Self::Segment => Some(0x27),
|
||||
// Pseudo-instructions don't have opcode values
|
||||
_ => None,
|
||||
}
|
||||
|
||||
+50
-34
@@ -68,6 +68,8 @@ impl Parser {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
println!("{:?}", self.peek_next()?);
|
||||
|
||||
// check if the Node starts with a label
|
||||
let label = expect_token!(self.peek_next()?, Symbol).ok();
|
||||
if label.is_some() {
|
||||
@@ -85,32 +87,25 @@ impl Parser {
|
||||
args = vec![reg1, reg2];
|
||||
}
|
||||
|
||||
Opcode::Ldb | Opcode::Ldbs | Opcode::Ldh | Opcode::Ldhs | Opcode::Ldw => {
|
||||
Opcode::Ldb
|
||||
| Opcode::Ldbs
|
||||
| Opcode::Ldh
|
||||
| Opcode::Ldhs
|
||||
| Opcode::Ldw
|
||||
| Opcode::Stb
|
||||
| Opcode::Sth
|
||||
| Opcode::Stw => {
|
||||
let base = expect_type!(self.next()?, Register, Symbol)?;
|
||||
let dest = expect_type!(self.next()?, Register, Symbol)?;
|
||||
let dest = expect_type!(self.next()?, Register)?;
|
||||
|
||||
let mut offset = Token::Immediate(0);
|
||||
if let Ok(next) = self.peek_next() {
|
||||
if let Ok(_) = expect_type!(next, Register, Immediate) {
|
||||
if let Ok(_) = expect_type!(next, Immediate) {
|
||||
offset = self.next()?;
|
||||
}
|
||||
}
|
||||
|
||||
args = vec![base, offset, dest];
|
||||
}
|
||||
|
||||
Opcode::Stb | Opcode::Sth | Opcode::Stw => {
|
||||
let base = expect_type!(self.next()?, Register, Symbol)?;
|
||||
let dest = expect_type!(self.next()?, Register, Symbol)?;
|
||||
|
||||
let mut offset = Token::Immediate(0);
|
||||
if let Ok(next) = self.peek_next() {
|
||||
if let Ok(_) = expect_type!(next, Register, Immediate) {
|
||||
offset = self.next()?;
|
||||
}
|
||||
}
|
||||
|
||||
args = vec![base, offset, dest];
|
||||
args = vec![base, dest, offset];
|
||||
}
|
||||
|
||||
Opcode::Add
|
||||
@@ -159,14 +154,31 @@ impl Parser {
|
||||
| Opcode::Jlt
|
||||
| Opcode::Jle => {
|
||||
let imm = expect_type!(self.next()?, Immediate, Symbol)?;
|
||||
args = vec![imm];
|
||||
let offset = if expect_type!(self.peek_next()?, Register).is_ok() {
|
||||
self.next()?
|
||||
} else {
|
||||
Token::Register(Register::Zero)
|
||||
};
|
||||
args = vec![imm, offset];
|
||||
}
|
||||
|
||||
// I-type instructions
|
||||
Opcode::Lui | Opcode::Lli | Opcode::Lwi | Opcode::Iadd | Opcode::Isub => {
|
||||
let reg = expect_type!(self.next()?, Register)?;
|
||||
Opcode::Lui | Opcode::Lli | Opcode::Lwi => {
|
||||
let imm = expect_type!(self.next()?, Immediate, Symbol)?;
|
||||
args = vec![reg, imm];
|
||||
let reg = expect_type!(self.next()?, Register)?;
|
||||
args = vec![imm, reg];
|
||||
}
|
||||
|
||||
// Immediate Arithmetic
|
||||
Opcode::Iadd | Opcode::Isub => {
|
||||
let reg = expect_type!(self.next()?, Register)?;
|
||||
let imm = expect_type!(self.next()?, Immediate)?;
|
||||
let reg2 = if expect_type!(self.peek_next()?, Register).is_ok() {
|
||||
self.next()?
|
||||
} else {
|
||||
reg.clone()
|
||||
};
|
||||
args = vec![reg, imm, reg2];
|
||||
}
|
||||
|
||||
// D-type pseudoinstructions (data definition)
|
||||
@@ -199,10 +211,10 @@ impl Parser {
|
||||
|
||||
// Instructions with no arguments
|
||||
Opcode::Hlt | Opcode::Nop | Opcode::Irt => {
|
||||
args = Vec::new();
|
||||
args = vec![];
|
||||
}
|
||||
|
||||
Opcode::Data => {
|
||||
Opcode::Data | Opcode::Segment => {
|
||||
return Err(AssembleError::Generic);
|
||||
}
|
||||
}
|
||||
@@ -238,14 +250,14 @@ impl Parser {
|
||||
Opcode::Dh => {
|
||||
// dh can take u16 immediates
|
||||
while !self.tokens.is_empty() {
|
||||
if let Token::Immediate(val) = self.tokens.last().unwrap() {
|
||||
if *val <= u16::MAX as u32 {
|
||||
match self.tokens.last().unwrap() {
|
||||
Token::StringLit(_) => {
|
||||
values.push(self.tokens.pop().unwrap());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
Token::Immediate(val) if *val <= u16::MAX as u32 => {
|
||||
values.push(self.tokens.pop().unwrap());
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,10 +265,14 @@ impl Parser {
|
||||
Opcode::Dw => {
|
||||
// dw can take u32 immediates
|
||||
while !self.tokens.is_empty() {
|
||||
if let Token::Immediate(_) = self.tokens.last().unwrap() {
|
||||
values.push(self.tokens.pop().unwrap());
|
||||
} else {
|
||||
break;
|
||||
match self.tokens.last().unwrap() {
|
||||
Token::StringLit(_) => {
|
||||
values.push(self.tokens.pop().unwrap());
|
||||
}
|
||||
Token::Immediate(val) if *val <= u32::MAX as u32 => {
|
||||
values.push(self.tokens.pop().unwrap());
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
use common::prelude::Register;
|
||||
|
||||
use crate::{
|
||||
AssembleError,
|
||||
model::{Module, Node, Opcode, Symbol, Token},
|
||||
quick_hash,
|
||||
node, quick_hash,
|
||||
};
|
||||
|
||||
pub fn resolve_symbols(nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
@@ -119,3 +121,36 @@ pub fn resolve_dependencies(mut nodes: Vec<Node>) -> Result<Vec<Node>, AssembleE
|
||||
|
||||
Ok(nodes)
|
||||
}
|
||||
|
||||
pub fn create_sections(nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let mut res = Vec::<Node>::with_capacity(nodes.len());
|
||||
|
||||
res.push(node!(None, Opcode::Segment, Token::Immediate(0)));
|
||||
|
||||
for n in nodes.iter() {
|
||||
if n.opcode() == Opcode::Data {
|
||||
res.push(n.clone());
|
||||
}
|
||||
}
|
||||
|
||||
res.push(node!(None, Opcode::Segment, Token::Immediate(1)));
|
||||
let start = res.len() + 1;
|
||||
res.insert(
|
||||
0,
|
||||
node!(
|
||||
None,
|
||||
Opcode::Jmp,
|
||||
Token::Immediate(start as u32 * 4),
|
||||
Token::Register(Register::Zero)
|
||||
),
|
||||
);
|
||||
for n in nodes.iter() {
|
||||
if !matches!(n.opcode(), Opcode::Data | Opcode::Include) {
|
||||
res.push(n.clone());
|
||||
}
|
||||
}
|
||||
|
||||
*nodes = res;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ impl TryFrom<u8> for Register {
|
||||
type Error = RegisterParseError;
|
||||
|
||||
fn try_from(idx: u8) -> Result<Self, Self::Error> {
|
||||
if idx > 0x18 {
|
||||
if idx > 0x1C {
|
||||
return Err(RegisterParseError::InvalidIndex(idx));
|
||||
}
|
||||
|
||||
@@ -111,6 +111,11 @@ impl TryFrom<u8> for Register {
|
||||
0x15 => Self::Mmr,
|
||||
0x16 => Self::Zero,
|
||||
0x17 => Self::NoReg,
|
||||
0x18 => Self::Mar,
|
||||
0x19 => Self::Mdr,
|
||||
0x1A => Self::Sts,
|
||||
0x1B => Self::Cir,
|
||||
0x1C => Self::Pcx,
|
||||
_ => unreachable!("This is already checked for in top `if` branch."),
|
||||
})
|
||||
}
|
||||
@@ -145,6 +150,7 @@ impl TryFrom<&str> for Register {
|
||||
"mmr" => Ok(Self::Mmr),
|
||||
"zero" => Ok(Self::Zero),
|
||||
"null" => Ok(Self::NoReg),
|
||||
"pcx" => Ok(Self::Pcx),
|
||||
_ => Err(RegisterParseError::InvalidName(value.to_string())),
|
||||
}
|
||||
}
|
||||
@@ -248,6 +254,14 @@ pub enum Instruction {
|
||||
Interrupt(Interrupt) = 0x22,
|
||||
IntReturn = 0x23,
|
||||
Halt = 0x24,
|
||||
|
||||
// Immediate Arithmetic
|
||||
AddImmediate(args::ITypeArgs) = 0x25,
|
||||
SubImmediate(args::ITypeArgs) = 0x26,
|
||||
|
||||
// Fake Instructions
|
||||
Data(u32) = 0x3E,
|
||||
Segment(u32) = 0x3F,
|
||||
}
|
||||
|
||||
impl PartialEq for Instruction {
|
||||
@@ -319,6 +333,10 @@ impl Instruction {
|
||||
Self::Nand(_) => "nand",
|
||||
Self::Nor(_) => "nor",
|
||||
Self::Xnor(_) => "xnor",
|
||||
Self::Data(_) => "data",
|
||||
Self::AddImmediate(_) => "addi",
|
||||
Self::SubImmediate(_) => "subi",
|
||||
Self::Segment(_) => "[SEGMENT]",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +373,11 @@ impl std::fmt::Display for Instruction {
|
||||
| Self::StoreByte(args)
|
||||
| Self::StoreHalfword(args)
|
||||
| Self::StoreWord(args) => {
|
||||
write!(f, " {:x}({}), {}", args.immediate, args.r1, args.r2)
|
||||
write!(
|
||||
f,
|
||||
" {}({:x}/{}), {}",
|
||||
args.r1, args.immediate, args.immediate, args.r2
|
||||
)
|
||||
}
|
||||
Self::Jump(args)
|
||||
| Self::JumpEq(args)
|
||||
@@ -364,10 +386,10 @@ impl std::fmt::Display for Instruction {
|
||||
| Self::JumpGe(args)
|
||||
| Self::JumpLt(args)
|
||||
| Self::JumpLe(args) => {
|
||||
write!(f, " ({:x}){}", args.immediate, args.r1)
|
||||
write!(f, " 0x{:x}/{}({})", args.immediate, args.immediate, args.r1)
|
||||
}
|
||||
Self::LoadLowerImmediate(args) | Self::LoadUpperImmediate(args) => {
|
||||
write!(f, " {}, {}, {}", args.immediate, args.r1, args.r2)
|
||||
write!(f, " 0x{:x}, {}, {}", args.immediate, args.r1, args.r2)
|
||||
}
|
||||
Self::Compare(args) | Self::Not(args) => {
|
||||
write!(f, " {}, {}", args.sr1, args.sr2)
|
||||
@@ -388,6 +410,8 @@ impl std::fmt::Display for Instruction {
|
||||
|
||||
Self::Increment(a) | Self::Decrement(a) => write!(f, " {}", a.dr),
|
||||
Self::Interrupt(a) => write!(f, " {}", a.as_u8()),
|
||||
Self::Data(a) => write!(f, " {}", a),
|
||||
Self::Segment(x) => write!(f, " [SEGMENT {}]", x),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -439,6 +463,9 @@ impl TryFrom<u32> for Instruction {
|
||||
0x22 => Ok(Self::Interrupt(Interrupt::from((data & 0xFF) as u8))),
|
||||
0x23 => Ok(Self::IntReturn),
|
||||
0x24 => Ok(Self::Halt),
|
||||
0x25 => Ok(Self::AddImmediate(ITypeArgs::try_from(data)?)),
|
||||
0x26 => Ok(Self::SubImmediate(ITypeArgs::try_from(data)?)),
|
||||
0x3F => Ok(Self::Segment(data as u8 as u32)),
|
||||
_ => Err(InstructionDecodeError::InvalidOpcode(opcode)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,3 +155,54 @@ impl TryFrom<u32> for RTypeArgs {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! args {
|
||||
// R-type arguments - allows omitting any field
|
||||
(R $(, $field:ident: $value:expr)* $(,)?) => {{
|
||||
let mut sr1: Option<Register> = None;
|
||||
let mut sr2: Option<Register> = None;
|
||||
let mut dr: Option<Register> = None;
|
||||
let mut shamt: Option<u8> = None;
|
||||
|
||||
$(
|
||||
args!(@assign_r_option sr1, sr2, dr, shamt, $field, $value);
|
||||
)*
|
||||
|
||||
RTypeArgs::new(sr1, sr2, dr, shamt)
|
||||
}};
|
||||
|
||||
// I-type arguments - requires immediate, allows omitting registers
|
||||
(I, immediate: $immediate:expr $(, $field:ident: $value:expr)* $(,)?) => {{
|
||||
let mut r1: Option<Register> = None;
|
||||
let mut r2: Option<Register> = None;
|
||||
|
||||
$(
|
||||
args!(@assign_i_option r1, r2, $field, $value);
|
||||
)*
|
||||
|
||||
ITypeArgs::new($immediate, r1, r2)
|
||||
}};
|
||||
|
||||
// Internal helpers (same as above for R-type)
|
||||
(@assign_r_option $sr1:ident, $sr2:ident, $dr:ident, $shamt:ident, sr1, $value:expr) => {
|
||||
$sr1 = Some($value);
|
||||
};
|
||||
(@assign_r_option $sr1:ident, $sr2:ident, $dr:ident, $shamt:ident, sr2, $value:expr) => {
|
||||
$sr2 = Some($value);
|
||||
};
|
||||
(@assign_r_option $sr1:ident, $sr2:ident, $dr:ident, $shamt:ident, dr, $value:expr) => {
|
||||
$dr = Some($value);
|
||||
};
|
||||
(@assign_r_option $sr1:ident, $sr2:ident, $dr:ident, $shamt:ident, shamt, $value:expr) => {
|
||||
$shamt = Some($value);
|
||||
};
|
||||
|
||||
// Internal helpers for I-type (without immediate handling)
|
||||
(@assign_i_option $r1:ident, $r2:ident, r1, $value:expr) => {
|
||||
$r1 = Some($value);
|
||||
};
|
||||
(@assign_i_option $r1:ident, $r2:ident, r2, $value:expr) => {
|
||||
$r2 = Some($value);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -49,11 +49,17 @@ impl Encode for Instruction {
|
||||
StoreWord, LoadLowerImmediate, LoadUpperImmediate, Jump,
|
||||
JumpEq, JumpNeq, JumpGt, JumpGe, JumpLt, JumpLe, Compare,
|
||||
Add, Sub, Increment, Decrement, ShiftLeft, ShiftRight,
|
||||
And, Or, Not, Xor, Nand, Nor, Xnor
|
||||
And, Or, Not, Xor, Nand, Nor, Xnor, AddImmediate, SubImmediate
|
||||
],
|
||||
no_args: [Nop, IntReturn, Halt],
|
||||
special: [
|
||||
Self::Interrupt(_) => todo!()
|
||||
Self::Interrupt(_) => todo!(),
|
||||
Self::Data(data) => data,
|
||||
Self::Segment(segment) => {
|
||||
let opcode = u32::from(self.opcode());
|
||||
let segment = segment as u8;
|
||||
(opcode << 26) | (segment as u32)
|
||||
}
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ impl Syntax {
|
||||
"db", "dh", "dw", "resb", "resh", "resw", "push", "pop", "lwi", "call",
|
||||
"ret",
|
||||
]),
|
||||
types: BTreeSet::from(["ptr", "byte", "word", "dword", "qword"]),
|
||||
types: BTreeSet::from([]),
|
||||
special: BTreeSet::from([
|
||||
"rg0", "rg1", "rg2", "rg3", "rg4", "rg5", "rg6", "rg7", "rg8", "rg9",
|
||||
"rga", "rgb", "rgc", "rgd", "rge", "rgf", "acc", "spr", "bpr", "ret",
|
||||
|
||||
+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
|
||||
}
|
||||
|
||||
+14
-21
@@ -1,34 +1,27 @@
|
||||
// include print "../resources/dsa/print.dsa"
|
||||
// Fibonacci sequence calculator in DSA assembly
|
||||
// Calculates the first 8 Fibonacci numbers: 0, 1, 1, 2, 3, 5, 8, 13
|
||||
dw fib_count: 6 // How many more numbers to calculate after F(0) and F(1)
|
||||
db fib_count: 10 // How many more numbers to calculate after F(0) and F(1)
|
||||
|
||||
init:
|
||||
// Initialize the first two Fibonacci numbers
|
||||
lli rg0, 0 // F(0) = 0
|
||||
lli rg1, 1 // F(1) = 1
|
||||
lli 0, rg0 // F(0) = 0
|
||||
lli 1, rg1 // F(1) = 1
|
||||
ldb fib_count, rg2
|
||||
|
||||
loop:
|
||||
add rg0, rg1, acc // rg4 = rg0 + rg1 (new Fibonacci number)
|
||||
|
||||
push rg0
|
||||
// Load loop counter
|
||||
ldw fib_count, zero, rg2 // Load number of iterations remaining
|
||||
|
||||
fibonacci_loop:
|
||||
// Calculate next Fibonacci number: F(n) = F(n-1) + F(n-2)
|
||||
add rg0, rg1, rg4 // rg4 = rg0 + rg1 (new Fibonacci number)
|
||||
|
||||
// Shift the sequence forward
|
||||
mov rg1, rg0 // rg0 = previous rg1 (F(n-2) = F(n-1))
|
||||
mov rg4, rg1 // rg1 = rg4 (F(n-1) = F(n))
|
||||
mov acc, rg1 // rg1 = rg4 (F(n-1) = F(n))
|
||||
|
||||
// Decrement loop counter
|
||||
dec rg2 // rg2 = rg2 - 1
|
||||
|
||||
// Check if we should continue looping
|
||||
cmp rg2, zero // Compare counter with 0
|
||||
jgt fibonacci_loop // Jump back if counter > 0
|
||||
jgt loop // Jump back if counter > 0
|
||||
|
||||
finish:
|
||||
mov rg1, acc // Final Fibonacci number is in acc
|
||||
push rg0,
|
||||
push rg1,
|
||||
// Final Fibonacci number is in rg1
|
||||
hlt
|
||||
|
||||
jmp print::run
|
||||
// jmp print::run
|
||||
|
||||
|
||||
+16
-19
@@ -1,28 +1,25 @@
|
||||
dw stack: 0x10000
|
||||
dw screen: 0x20000
|
||||
db string: "Dominos sucks!"
|
||||
db string2: 0, 1, 2, 3, 4, 5, 6
|
||||
db length: 14
|
||||
// PRINT LIBRARY.
|
||||
// don't run this as a program as it won't do anything useful.
|
||||
|
||||
init:
|
||||
ldw stack, bpr
|
||||
mov bpr, spr
|
||||
dw display: 0x20000
|
||||
|
||||
start:
|
||||
ldb length, rg0
|
||||
lwi rg1, string
|
||||
lwi rg2, display
|
||||
pop ret // return address
|
||||
pop rg0 // length
|
||||
pop rg1 // string
|
||||
ldw display, rg2
|
||||
|
||||
loop:
|
||||
// read from string and write to display
|
||||
ldb rg1, rg3, rg4
|
||||
stb rg3, rg1, rg4
|
||||
ldw rg1, acc
|
||||
stw acc, rg2
|
||||
|
||||
// increment the offset & decrement the loop counter
|
||||
inc rg4
|
||||
dec rg0
|
||||
|
||||
// if loop counter <= 0 return.
|
||||
cmp rg0, zero,
|
||||
jgt loop
|
||||
iadd rg1, 4
|
||||
iadd rg2, 4
|
||||
|
||||
cmp rg0, zero
|
||||
jge loop
|
||||
|
||||
end:
|
||||
jmp ret
|
||||
@@ -0,0 +1,36 @@
|
||||
dw string: "this is some random string idk"
|
||||
dw len: 8
|
||||
|
||||
start:
|
||||
// we use lwi in this case because
|
||||
// in a generic case 'string' could be a 32 bit addr
|
||||
lwi string, rg1
|
||||
push rg1
|
||||
lwi len, rg1
|
||||
push rg1
|
||||
push pcx
|
||||
jmp start
|
||||
hlt
|
||||
|
||||
dw display: 0x20000
|
||||
|
||||
print:
|
||||
pop ret // return address
|
||||
pop rg0 // length
|
||||
pop rg1 // string
|
||||
ldw display, rg2
|
||||
|
||||
loop:
|
||||
ldw rg1, acc
|
||||
stw acc, rg2
|
||||
|
||||
dec rg0
|
||||
|
||||
iadd rg1, 4
|
||||
iadd rg2, 4
|
||||
|
||||
cmp rg0, zero
|
||||
jge loop
|
||||
|
||||
end:
|
||||
jmp 4, ret
|
||||
Binary file not shown.
Reference in New Issue
Block a user