diff --git a/Cargo.lock b/Cargo.lock index 0a353ad..0942501 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index aaacca0..b15e2f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["emulator", "common", "assembler"] +members = ["emulator", "common", "assembler", "dsa_editor"] resolver = "3" [workspace.package] diff --git a/assembler/src/codegen.rs b/assembler/src/codegen.rs index c32381d..2d1a005 100644 --- a/assembler/src/codegen.rs +++ b/assembler/src/codegen.rs @@ -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) -> Result, AssembleError> { + let mut instructions = vec![]; + + for node in nodes { + instructions.push(build_instruction(node)?); + } + + Ok(instructions) +} + +fn build_instruction(node: Node) -> Result { + 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!(), + } +} diff --git a/assembler/src/expand.rs b/assembler/src/expand.rs index 63a52f8..deb60d2 100644 --- a/assembler/src/expand.rs +++ b/assembler/src/expand.rs @@ -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) -> 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) -> 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) -> Result<(), AssembleError> fn expand_ldx(current: Node, nodes: &mut Vec) -> 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) -> 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(()) diff --git a/assembler/src/lib.rs b/assembler/src/lib.rs index a831fc0..1be29e5 100644 --- a/assembler/src/lib.rs +++ b/assembler/src/lib.rs @@ -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 { +pub fn assemble(src: &Path) -> Result, AssembleError> { let mut modules = HashSet::::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 { diff --git a/assembler/src/main.rs b/assembler/src/main.rs index 012f777..d1cacc1 100644 --- a/assembler/src/main.rs +++ b/assembler/src/main.rs @@ -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); + } + } } diff --git a/assembler/src/model.rs b/assembler/src/model.rs index 6f2a635..04c4479 100644 --- a/assembler/src/model.rs +++ b/assembler/src/model.rs @@ -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, } diff --git a/assembler/src/parser.rs b/assembler/src/parser.rs index 10fefca..c7d4c96 100644 --- a/assembler/src/parser.rs +++ b/assembler/src/parser.rs @@ -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, } } } diff --git a/assembler/src/resolver.rs b/assembler/src/resolver.rs index eeaff22..9a82028 100644 --- a/assembler/src/resolver.rs +++ b/assembler/src/resolver.rs @@ -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) -> Result<(), AssembleError> { @@ -119,3 +121,36 @@ pub fn resolve_dependencies(mut nodes: Vec) -> Result, AssembleE Ok(nodes) } + +pub fn create_sections(nodes: &mut Vec) -> Result<(), AssembleError> { + let mut res = Vec::::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(()) +} diff --git a/common/src/instructions.rs b/common/src/instructions.rs index 801470a..de9ee7f 100644 --- a/common/src/instructions.rs +++ b/common/src/instructions.rs @@ -81,7 +81,7 @@ impl TryFrom for Register { type Error = RegisterParseError; fn try_from(idx: u8) -> Result { - if idx > 0x18 { + if idx > 0x1C { return Err(RegisterParseError::InvalidIndex(idx)); } @@ -111,6 +111,11 @@ impl TryFrom 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 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)), } } diff --git a/common/src/instructions/args.rs b/common/src/instructions/args.rs index d4c9e48..664c31a 100644 --- a/common/src/instructions/args.rs +++ b/common/src/instructions/args.rs @@ -155,3 +155,54 @@ impl TryFrom for RTypeArgs { }) } } + +#[macro_export] +macro_rules! args { + // R-type arguments - allows omitting any field + (R $(, $field:ident: $value:expr)* $(,)?) => {{ + let mut sr1: Option = None; + let mut sr2: Option = None; + let mut dr: Option = None; + let mut shamt: Option = 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 = None; + let mut r2: Option = 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); + }; +} diff --git a/common/src/instructions/encode.rs b/common/src/instructions/encode.rs index d7b6b32..0847638 100644 --- a/common/src/instructions/encode.rs +++ b/common/src/instructions/encode.rs @@ -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) + } ] ) } diff --git a/dsa_editor/src/syntax/dsa.rs b/dsa_editor/src/syntax/dsa.rs index 2a76f1b..2c04169 100644 --- a/dsa_editor/src/syntax/dsa.rs +++ b/dsa_editor/src/syntax/dsa.rs @@ -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", diff --git a/emulator/Cargo.toml b/emulator/Cargo.toml index 55b18db..b6d022f 100644 --- a/emulator/Cargo.toml +++ b/emulator/Cargo.toml @@ -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" diff --git a/emulator/src/emulator/system/emulator.rs b/emulator/src/emulator/system/emulator.rs index f0d4012..ef341ef 100644 --- a/emulator/src/emulator/system/emulator.rs +++ b/emulator/src/emulator/system/emulator.rs @@ -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") diff --git a/emulator/src/emulator/system/memory.rs b/emulator/src/emulator/system/memory.rs index 5382542..aa655e7 100644 --- a/emulator/src/emulator/system/memory.rs +++ b/emulator/src/emulator/system/memory.rs @@ -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 { diff --git a/emulator/src/emulator/system/processor/mod.rs b/emulator/src/emulator/system/processor/mod.rs index f1e93bc..8db8e0b 100644 --- a/emulator/src/emulator/system/processor/mod.rs +++ b/emulator/src/emulator/system/processor/mod.rs @@ -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!() + } } } } diff --git a/emulator/src/emulator/ui/display.rs b/emulator/src/emulator/ui/display.rs new file mode 100644 index 0000000..8f82a81 --- /dev/null +++ b/emulator/src/emulator/ui/display.rs @@ -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 = 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, + ); + } + } +} diff --git a/emulator/src/emulator/ui/editor.rs b/emulator/src/emulator/ui/editor.rs index 703aaea..555fcae 100644 --- a/emulator/src/emulator/ui/editor.rs +++ b/emulator/src/emulator/ui/editor.rs @@ -12,8 +12,10 @@ use crate::emulator::{ }; pub struct Editor { - path: PathBuf, + path: Option, text: String, + buffer: String, + unsaved: bool, output: Vec, sender: Sender, 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) -> 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 diff --git a/emulator/src/emulator/ui/memory_inspector.rs b/emulator/src/emulator/ui/memory_inspector.rs index 6aeae7f..939bc09 100644 --- a/emulator/src/emulator/ui/memory_inspector.rs +++ b/emulator/src/emulator/ui/memory_inspector.rs @@ -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(); } diff --git a/emulator/src/emulator/ui/mod.rs b/emulator/src/emulator/ui/mod.rs index 178c392..fabc0b6 100644 --- a/emulator/src/emulator/ui/mod.rs +++ b/emulator/src/emulator/ui/mod.rs @@ -1,4 +1,5 @@ pub mod control_unit; +pub mod display; pub mod editor; pub mod interface; pub mod memory_inspector; diff --git a/emulator/src/main.rs b/emulator/src/main.rs index 67d89f7..3cd99e3 100644 --- a/emulator/src/main.rs +++ b/emulator/src/main.rs @@ -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, state_reciever: Receiver) -> 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 } diff --git a/resources/dsa/fib.dsa b/resources/dsa/fib.dsa index 9ac49f9..5042ee0 100644 --- a/resources/dsa/fib.dsa +++ b/resources/dsa/fib.dsa @@ -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 diff --git a/resources/dsa/print.dsa b/resources/dsa/print.dsa index 6501d2c..900e0ee 100644 --- a/resources/dsa/print.dsa +++ b/resources/dsa/print.dsa @@ -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 \ No newline at end of file diff --git a/resources/dsa/test.dsa b/resources/dsa/test.dsa new file mode 100644 index 0000000..d12de2c --- /dev/null +++ b/resources/dsa/test.dsa @@ -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 \ No newline at end of file diff --git a/resources/dsb/test.dsb b/resources/dsb/test.dsb new file mode 100644 index 0000000..5ce7782 Binary files /dev/null and b/resources/dsb/test.dsb differ