diff --git a/emulator/src/emulator/cli.rs b/emulator/src/emulator/cli.rs new file mode 100644 index 0000000..16de7cb --- /dev/null +++ b/emulator/src/emulator/cli.rs @@ -0,0 +1,55 @@ +use std::path::Path; +use std::thread::sleep; +use std::time::Duration; +use std::{env, fs}; + +use crate::emulator::system::model::{Command, Running}; +use crate::emulator::{config::Config, system::model::State}; + +pub fn run_cli() -> Result<(), Box> { + // Initialize channels and read in configuration. + let (cmd_sender, cmd_receiver) = std::sync::mpsc::channel(); + let (state_sender, state_reciever) = std::sync::mpsc::channel(); + + // not needed for now. + // let config = Config::load(Path::new(".dsa.emulator.toml"))?; + + crate::setup_emulator(cmd_receiver, state_sender, None); + + // run CLI. + let mut state = State::new(cmd_sender, state_reciever); + + let mut bin_path: Option = None; + for (i, arg) in env::args().enumerate().skip(1) { + // check for args --bin and + if arg == "--bin" { + bin_path = Some(env::args().nth(i + 1).expect("Binary path not provided")); + } + } + + let binary = + fs::read(bin_path.expect("unreachable")).expect("unable to read binary file"); + state.send(Command::Write(0, binary)); + println!("{:?}", state.running); + + sleep(Duration::from_secs(1)); + + state.update().unwrap(); + state.cmd_sender.send(Command::Start).unwrap(); + loop { + sleep(Duration::from_millis(20)); + state.send(Command::DisplayRequest); + state.send(Command::RunningRequest); + state.update().expect("update failed"); + + for ch in state.serial_buff.drain(..) { + print!("{}", ch as char); + } + + if state.running == Running::Halted { + break; + } + } + + Ok(()) +} diff --git a/emulator/src/emulator/mod.rs b/emulator/src/emulator/mod.rs index 9d6cc6f..1c81a62 100644 --- a/emulator/src/emulator/mod.rs +++ b/emulator/src/emulator/mod.rs @@ -1,3 +1,4 @@ +pub mod cli; #[cfg(feature = "config")] pub mod config; pub mod misc; diff --git a/emulator/src/emulator/system/constants.rs b/emulator/src/emulator/system/constants.rs new file mode 100644 index 0000000..798b6be --- /dev/null +++ b/emulator/src/emulator/system/constants.rs @@ -0,0 +1,2 @@ +pub const DISPLAY_ADDRESS: u32 = 0x20000; +pub const SERIAL_ADDRESS: u32 = DISPLAY_ADDRESS + 2000; diff --git a/emulator/src/emulator/system/emulator.rs b/emulator/src/emulator/system/emulator.rs index cb87d72..eefed5f 100644 --- a/emulator/src/emulator/system/emulator.rs +++ b/emulator/src/emulator/system/emulator.rs @@ -135,6 +135,9 @@ pub fn run_emulator( Vec::new() }), )); + let _ = state_tx.send(StateUpdate::Serial( + processor.serial_buff.drain(..).collect(), + )); } Command::StackRequest if update => { let _ = state_tx.send(StateUpdate::StackView( diff --git a/emulator/src/emulator/system/mod.rs b/emulator/src/emulator/system/mod.rs index 6356927..ed5d54d 100644 --- a/emulator/src/emulator/system/mod.rs +++ b/emulator/src/emulator/system/mod.rs @@ -1,4 +1,5 @@ pub mod cache; +pub mod constants; pub mod emulator; pub mod memory; pub mod model; diff --git a/emulator/src/emulator/system/model.rs b/emulator/src/emulator/system/model.rs index cfd63f0..4fd0877 100644 --- a/emulator/src/emulator/system/model.rs +++ b/emulator/src/emulator/system/model.rs @@ -28,6 +28,7 @@ pub enum Command { WriteBlock(Address, Box<[u8; 256]>), // request emulator state. + SerialRequest, MemRequest(Address, u32), DisplayRequest, StackRequest, @@ -79,6 +80,7 @@ pub struct State { pub error_log: Vec, pub instruction_history: Vec<(u32, u32)>, + pub serial_buff: Vec, } impl State { @@ -95,6 +97,7 @@ impl State { display_view: vec![], error_log: vec![], instruction_history: vec![], + serial_buff: vec![], } } @@ -121,6 +124,9 @@ impl State { StateUpdate::InstructionHistory(history) => { self.instruction_history.extend(history); } + StateUpdate::Serial(buffer) => { + self.serial_buff.extend(buffer); + } } if self.error_log.len() > 256 { @@ -148,6 +154,7 @@ impl State { pub enum StateUpdate { Registers(RegFile), + Serial(Vec), Running(Running), Instructions(usize), StackView(Vec), diff --git a/emulator/src/emulator/system/processor/mod.rs b/emulator/src/emulator/system/processor/mod.rs index 710a4b4..2da44b0 100644 --- a/emulator/src/emulator/system/processor/mod.rs +++ b/emulator/src/emulator/system/processor/mod.rs @@ -5,6 +5,7 @@ use std::{ use crate::emulator::system::{ cache::Cache, + constants::{DISPLAY_ADDRESS, SERIAL_ADDRESS}, memory::MemoryUnit, model::{IODevice, ProcessorError, RegFile}, }; @@ -19,6 +20,8 @@ pub struct Processor { pub void: u32, pub cache: Cache, + + pub serial_buff: Vec, } impl Processor { @@ -31,6 +34,8 @@ impl Processor { io_devices, void: 0, cache: Cache::new(), + + serial_buff: Vec::with_capacity(32768), } } @@ -97,7 +102,7 @@ impl Processor { } pub fn display(&mut self) -> Result, ProcessorError> { - Ok(self.memory.read_range(0x20000, 2000)) + Ok(self.memory.read_range(DISPLAY_ADDRESS, 2000)) } pub fn cmp(&mut self, a: u32, b: u32) { @@ -267,30 +272,41 @@ impl Executable for Instruction { // Stores a byte from SrcReg in memory address (base + offset) The effective // address must be byte-aligned. Self::StoreByte(a) => { - cpu.memory.write_byte( - cpu.get(a.r2)? + u32::from(a.immediate), - cpu.get(a.r1)? as u8, - ); + let addr = cpu.get(a.r2)? + u32::from(a.immediate); + let val = cpu.get(a.r1)? as u8; + if addr == SERIAL_ADDRESS { + cpu.serial_buff.push(val); + } + cpu.memory.write_byte(addr, val); } // Stores a half-word from SrcReg in memory address (base + offset) The // effective address must be 2-byte-aligned. Self::StoreHalfword(a) => { - // split the value into bytes and then write two bytes + let addr = cpu.get(a.r2)? + u32::from(a.immediate); let bytes = (cpu.get(a.r1)? as u16).to_le_bytes(); - cpu.memory - .write_byte(cpu.get(a.r2)? + u32::from(a.immediate), bytes[0]); - cpu.memory - .write_byte(cpu.get(a.r2)? + u32::from(a.immediate) + 1, bytes[1]); + + if addr == SERIAL_ADDRESS { + cpu.serial_buff.extend(bytes); + } + + // split the value into bytes and then write two bytes + cpu.memory.write_byte(addr, bytes[0]); + cpu.memory.write_byte(addr + 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.r2)? + u32::from(a.immediate), - cpu.get(a.r1)?, - )?; + let addr = cpu.get(a.r2)? + u32::from(a.immediate); + let val = cpu.get(a.r1)?; + let bytes = val.to_le_bytes(); + + if addr == SERIAL_ADDRESS { + cpu.serial_buff.extend(bytes); + } + + cpu.memory.write_word(addr, val)?; } // Loads a 16-bit literal value into reg, setting the bottom 16 bits of the diff --git a/emulator/src/emulator/ui/editor.rs b/emulator/src/emulator/ui/editor.rs index e70fc07..7df95b3 100644 --- a/emulator/src/emulator/ui/editor.rs +++ b/emulator/src/emulator/ui/editor.rs @@ -448,7 +448,7 @@ impl Editor { } let mut assembler = Assembler::new(&dsa_path); - compiler.start(); + assembler.start(); // Or block until done self.output = match assembler.output() { diff --git a/emulator/src/main.rs b/emulator/src/main.rs index b074df9..acd4896 100644 --- a/emulator/src/main.rs +++ b/emulator/src/main.rs @@ -1,9 +1,15 @@ +use std::env; use std::path::Path; use std::sync::Arc; use dsa_rs::emulator::{config::Config, misc::rpc::get_rpc_client_or_none}; fn main() -> Result<(), Box> { + if env::args().any(|arg| arg == "--cli") { + dsa_rs::emulator::cli::run_cli()?; + std::process::exit(0); + } + // Initialize channels and read in configuration. let (cmd_sender, cmd_receiver) = std::sync::mpsc::channel(); let (state_sender, state_reciever) = std::sync::mpsc::channel(); diff --git a/resources/dsa/lib/io/serial.dsa b/resources/dsa/lib/io/serial.dsa new file mode 100644 index 0000000..a501019 --- /dev/null +++ b/resources/dsa/lib/io/serial.dsa @@ -0,0 +1,244 @@ +// lib: +// print_serial.dsa + +// usage: +// +// include print_serial "" +// +// usage for print: +// push (register containing address of string) +// push pcx +// jmp print_serial::print +// +// usage for print_byte: +// push (register containing byte) +// push pcx +// jmp print_serial::print_byte +// +// usage for print_word: +// push (register containing word) +// push pcx +// jmp print_serial::print_word +// +// usage for print_hex_byte: +// push (register containing byte) +// push pcx +// jmp print_serial::print_hex_byte +// +// usage for print_hex_word: +// push (register containing word) +// push pcx +// jmp print_serial::print_hex_word +// +// usage for print_num: +// push (register containing number to print in decimal) +// push pcx +// jmp print_serial::print_num +// +// usage for println: +// push (register containing address of string) +// push pcx +// jmp print_serial::println +// + +include maths "../maths/core.dsa" + +dw serial: 0x207D0 // 0x20000 + 2000 + +// ------------------------------------------ +// prints the string at addr(arg[0]) to the serial port. +print: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + +_print_loop: + ldb rg0, acc + cmp acc, zero + jeq _end + stb acc, rg1 + + addi rg0, 1 + jmp _print_loop + +// ------------------------------------------ +// prints the string at addr(arg[0]) followed by a newline to the serial port. +println: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + +_println_loop: + ldb rg0, acc + cmp acc, zero + jeq _println_end + stb acc, rg1 + + addi rg0, 1 + jmp _println_loop + +_println_end: + lli 0x0A, rg2 // newline character + stb rg2, rg1 + jmp _end + +// ------------------------------------------ +// prints the word in arg[0] as 4 raw bytes to the serial port. +print_word: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + + stb rg0, rg1 + shr rg0, 8 + stb rg0, rg1 + shr rg0, 8 + stb rg0, rg1 + shr rg0, 8 + stb rg0, rg1 + jmp _end + +// ------------------------------------------ +// prints the last byte of arg[0] to the serial port. +print_byte: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + + stb rg0, rg1 + jmp _end + +// ------------------------------------------ +// prints the value of arg[0] to the serial port in hex. +print_hex_word: + push bpr + mov spr, bpr + + lwi 0x207D0, rg1 + + ldb bpr, rg0, 8 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 9 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 10 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 11 + push rg0 + call _print_hex_byte + addi spr, 4 + + jmp _end + +// ------------------------------------------ +// prints the last byte of arg[0] to the serial port in hex. +print_hex_byte: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + + call _print_hex_byte + jmp _end + +// function body +_print_hex_byte: + lli 0xF, rg2 + push rg0 + + shr rg0, 4 + and rg0, rg2, rg0 + call _print_hex_nibble + pop rg0 + + and rg0, rg2, rg0 + call _print_hex_nibble + return + +// print a hex digit +_print_hex_nibble: + lli 10, rg3 + cmp rg0, rg3 + jlt _print_hex_nibble_number + addi rg0, 0x37, rg0 + stb rg0, rg1 + return + +_print_hex_nibble_number: + addi rg0, 0x30, rg0 + stb rg0, rg1 + return + +// ------------------------------------------ +// prints arg[0] as a decimal number to the serial port. +print_num: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lli 0, rg5 + + cmp rg0, zero + jne _print_num_extract_digits + + lli 0x30, rg6 + push rg6 + lli 1, rg5 + jmp _print_num_output + +_print_num_extract_digits: + cmp rg0, zero + jeq _print_num_output + + push rg0 + lli 10, rg1 + push rg1 + call maths::divmod + pop rg0 + pop rg1 + + addi rg1, 0x30, rg6 + push rg6 + inc rg5 + + jmp _print_num_extract_digits + +_print_num_output: + lwi 0x207D0, rg1 + +_print_num_output_loop: + cmp rg5, zero + jeq _print_num_done + + pop rg6 + stb rg6, rg1 + dec rg5 + + jmp _print_num_output_loop + +_print_num_done: + // fall through to _end + +// ------------------------------------------ +// return +_end: + mov bpr, spr + pop bpr + return diff --git a/resources/dsa/main.dsa b/resources/dsa/main.dsa index 2499bf3..8906e47 100644 --- a/resources/dsa/main.dsa +++ b/resources/dsa/main.dsa @@ -1,12 +1,143 @@ -// program to just test compute power +// GENERATED BY DSC COMPILER +// Generated at 2026-02-23 17:29:47 -dw large_num: 0x333333 // 333,333 instructions -start: - ldw large_num, rg0 +// Imports +include print: "./lib/io/print.dsa" +include alloc: "./lib/memory/block_alloc.dsa" -// run approx 1m instructions -loop: - dec rg0 - cmp rg0, zero - jgt loop +// Globals & Reserved Memory + +// Entry Point +dw stack: 0x010000 +db message: "Process Exited with code:" +_init: + ldw stack, bpr, 0 + mov bpr, spr + push zero + call main + call print::print_newline + lwi message, rg0 + push rg0 + call print::print + pop zero + call print::print_hex_word + pop zero hlt + +// Return +_ret: + mov bpr, spr + pop bpr + return + +db str_16: "successful free of ptr" +// fn main() -> u32 +main: + push bpr + mov spr, bpr + lli 32, rg0 + lli 64, rg1 + // push arg 1 + push rg0 + // push arg 0 + push rg1 + call alloc::init + pop rg2 + pop zero + push rg2 + // push arg 0 + push rg2 + call print::print_hex_word + pop zero + call print::print_newline + ldw spr, rg0, 0 + stw rg0, spr, 0 + // push arg 0 + push rg0 + call alloc::alloc + pop rg1 + push rg1 + // push arg 0 + push rg1 + call print::print_hex_word + pop zero + lli 200, rg0 + ldw spr, rg1, 0 + stw rg0, rg1, 0 + stw rg1, spr, 0 + call print::print_newline + ldw spr, rg0, 4 + stw rg0, spr, 4 + // push arg 0 + push rg0 + call alloc::alloc + pop rg2 + push rg2 + // push arg 0 + push rg2 + call print::print_hex_word + pop zero + call print::print_newline + ldw spr, rg0, 4 + ldw rg0, rg2, 0 + stw rg0, spr, 4 + // push arg 0 + push rg2 + call print::print_num + pop zero + ldw spr, rg2, 4 + stw rg2, spr, 4 + addi spr, 4, rg3 + ldw spr, rg2, 8 + stw rg2, spr, 8 + // push arg 1 + push rg3 + // push arg 0 + push rg2 + call alloc::free + pop zero + pop zero + ldw spr, rg2, 8 + stw rg2, spr, 8 + // push arg 0 + push rg2 + call alloc::alloc + pop rg3 + push rg3 + call print::print_newline + ldw spr, rg2, 0 + stw rg2, spr, 0 + // push arg 0 + push rg2 + call print::print_hex_word + pop zero + call print::print_newline + ldw spr, rg2, 8 + stw rg2, spr, 8 + // push arg 0 + push rg2 + call print::print_hex_word + pop zero + ldw spr, rg2, 8 + lli 0, rg4 + cmp rg2, rg4 + lli 1, rg5 + jeq _cmp_end_12 + lli 0, rg5 +_cmp_end_12: + cmp rg5, zero + jeq _else_14 +_then_13: + lwi str_16, rg4 + stw rg2, spr, 8 + // push arg 0 + push rg4 + call print::print + pop zero + jmp _end_15 +_else_14: + nop +_end_15: + lli 0, rg4 + stw rg4, bpr, 8 + jmp _ret \ No newline at end of file diff --git a/test/Dsx.toml b/test/Dsx.toml new file mode 100644 index 0000000..4f0b84c --- /dev/null +++ b/test/Dsx.toml @@ -0,0 +1,2 @@ +name = "test" +binaries = [] diff --git a/test/artifacts/out.dsb b/test/artifacts/out.dsb new file mode 100644 index 0000000..dfce6ab Binary files /dev/null and b/test/artifacts/out.dsb differ diff --git a/test/build/lib/maths.dsa b/test/build/lib/maths.dsa new file mode 100644 index 0000000..687ad5d --- /dev/null +++ b/test/build/lib/maths.dsa @@ -0,0 +1,105 @@ + +// multiply.dsa +// usage: +// +// include multiply "" +// +// usage for multiply: +// push (arg1) +// push (arg0) +// call multiply::multiply +// pop (arg0) +// pop (arg1) + +multiply: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 // load op 2 + ldw bpr, rg1, 12 // load op 1 + lwi 0, rg2 // initialise rg2 to zero + +_multiply_loop: + add rg2, rg0, rg2 + dec rg1 + + cmp rg1, zero + jgt _multiply_loop + +_multiply_end: + stw rg2, bpr, 8 + + mov bpr, spr + pop bpr + return + +divmod: + push bpr + mov spr, bpr + + ldw bpr, rg1, 8 // load op 2 + ldw bpr, rg0, 12 // load op 1 + + lli 0, rg3 + +_divmod_loop: + cmp rg0, rg1 + jlt _divmod_end + + sub rg0, rg1, rg0 + inc rg3 + + jmp _divmod_loop + +_divmod_end: + // store div in first arg + // store mod in second arg + stw rg3, bpr, 8 + stw rg0, bpr, 12 + + mov bpr, spr + pop bpr + return + +// multiply.dsa - improved version +// Multiplies two 32-bit numbers using shift-and-add +// +// Usage: +// push operand2 (multiplier) +// push operand1 (multiplicand) +// call multiply::multiply +// pop result +// pop zero (discard second argument) + +new_multiply: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 // rg0 = multiplicand + ldw bpr, rg1, 12 // rg1 = multiplier + + lli 0, rg2 // rg2 = result (accumulator) + lli 32, rg3 // rg3 = bit counter + +mult_loop: + // Check if lowest bit of multiplier is 1 + lli 1, acc + and rg1, acc, acc // acc = rg1 & 1 + cmp acc, zero + jeq skip_add // if (rg1 & 1) == 0, skip addition + + // Add multiplicand to result + add rg2, rg0, rg2 + +skip_add: + shl rg0, 1 // shift multiplicand left + shr rg1, 1 // shift multiplier right + + dec rg3 + cmp rg3, zero + jgt mult_loop + + stw rg2, bpr, 8 // store result + mov bpr, spr + pop bpr + return diff --git a/test/build/lib/print.dsa b/test/build/lib/print.dsa new file mode 100644 index 0000000..ead1d5c --- /dev/null +++ b/test/build/lib/print.dsa @@ -0,0 +1,332 @@ + +// lib: +// print.dsa + +// usage: +// +// include print """ +// +// usage for print: +// push (register containing address of string) +// push pcx +// jmp print::print +// +// usage for reset: +// push pcx +// jmp print::reset +// +// usage for clear: +// push pcx +// jmp print::clear +// +// usage for print_byte: +// push (register containing byte) +// push pcx +// jmp print::print_byte +// +// usage for print_word: +// push (register containing word) +// push pcx +// jmp print::print_word +// +// usage for print_num: +// push (register containing number to print in decimal) +// push pcx +// jmp print::print_num +// + +include maths "./maths.dsa" + +dw display: 0x20000 +dw current: 0x20000 + +// ------------------------------------------ +// prints the string at addr(arg[0]) to the screen. (no trailing whitespace unless explicitly provided) +print: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + ldw current, rg1 + +_print_loop: + ldb rg0, acc + cmp acc, zero + jeq _end + stb acc, rg1 + + addi rg0, 1 + addi rg1, 1 + + jmp _print_loop + +// ------------------------------------------ +println: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + ldw current, rg1 + +_println_loop: + ldb rg0, acc + cmp acc, zero + jeq _println_end + stb acc, rg1 + + addi rg0, 1 + addi rg1, 1 + + jmp _println_loop + +_println_end: + call print_newline + jmp _end + +// ------------------------------------------ +// prints the value of arg[0] to the screen. +print_word: + // initialise + push bpr + mov spr, bpr + + // load byte into acc + ldw bpr, rg0, 8 + ldw current, rg1 + + addi rg1, 3 + + stb rg0, rg1 + subi rg1, 1 + shr rg0, 8 + stb rg0, rg1 + subi rg1, 1 + shr rg0, 8 + stb rg0, rg1 + subi rg1, 1 + shr rg0, 8 + stb rg0, rg1 + + addi rg1, 4 + jmp _end + +// ------------------------------------------ +// prints the last byte of arg[0] to the screen. +print_byte: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + ldw current, rg1 + + stb rg0, rg1 + addi rg1, 1 + jmp _end + +// ------------------------------------------ +// prints the value of arg[0] to the screen in hex. +print_hex_word: + push bpr + mov spr, bpr + + ldw current, rg1 + + ldb bpr, rg0, 8 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 9 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 10 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 11 + push rg0 + call _print_hex_byte + addi spr, 4 + + jmp _end + +// ------------------------------------------ +// prints the last byte of arg[0] to the screen in hex. +print_hex_byte: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + ldw current, rg1 + + call _print_hex_byte + jmp _end + +// function body +_print_hex_byte: + // mask to get lower nibble + lli 0xF, rg2 + // save rg0 state + push rg0 + + shr rg0, 4 + and rg0, rg2, rg0 + call _print_hex_nibble + pop rg0 + + and rg0, rg2, rg0 + call _print_hex_nibble + return + +// print a hex digit +_print_hex_nibble: + lli 10, rg3 + cmp rg0, rg3 + jlt _print_hex_nibble_number + addi rg0, 0x37, rg0 + stb rg0, rg1 + addi rg1, 1 + return + +// helper function. +_print_hex_nibble_number: + addi rg0, 0x30, rg0 + stb rg0, rg1 + addi rg1, 1 + return + +// ------------------------------------------ +// print whitespace +print_whitespace: + push bpr + mov spr, bpr + + ldw current, rg1 + lli 0x20, rg0 + stb rg0, rg1 + addi rg1, 1 + jmp _end + +// ------------------------------------------ +// print newline +print_newline: + push bpr + mov spr, bpr + + // load variables into registers + ldw display, rg0 + ldw current, rg1 + + // get the offset from the display base + sub rg1, rg0, rg0 + + lwi 80, rg2 + pusha 3 + push rg0 + push rg2 + call maths::divmod + pop zero // result + pop rg3 // remainder + popa 3 + + sub rg1, rg3, rg2 + addi rg2, 80, rg1 + + // _end saves the display state + jmp _end + +// ------------------------------------------ +// prints arg[0] as a decimal number to the screen. +print_num: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 // load number to print + lli 0, rg5 // rg5 = digit counter + + // check if number is zero + cmp rg0, zero + jne _print_num_extract_digits + + // special case: print '0' for zero + lli 0x30, rg6 + push rg6 // push digit to stack buffer + lli 1, rg5 // we have 1 digit + jmp _print_num_output + +_print_num_extract_digits: + // divide by 10 repeatedly to get digits + cmp rg0, zero + jeq _print_num_output + + // call divmod(rg0, 10) + push rg0 // dividend + lli 10, rg1 + push rg1 // divisor (10) + call maths::divmod + pop rg0 // quotient (continue dividing this) + pop rg1 // remainder (the digit) + + // convert digit to ASCII and push to stack buffer + addi rg1, 0x30, rg6 // convert to ASCII + push rg6 // push digit to stack + inc rg5 // increment digit counter + + jmp _print_num_extract_digits + +_print_num_output: + // now print digits (pop them off in reverse order) + ldw current, rg1 // get display pointer + +_print_num_output_loop: + // check if we've printed all digits + cmp rg5, zero + jeq _print_num_done + + // pop digit and print it + pop rg6 + stb rg6, rg1 + addi rg1, 1 + dec rg5 + + jmp _print_num_output_loop + +_print_num_done: + jmp _end + +// ------------------------------------------ +// resets the cursor position on the screen to 0x20000. (0,0) +reset: + push bpr + mov spr, bpr + ldw display, rg1 + jmp _end + +// ------------------------------------------ +// clears the screen +clear: + push bpr + mov spr, bpr + // display size = 2000 bytes / 500 words + lli 500 rg0 + ldw display, rg1 + +_clear_loop: + dec rg0 + stw zero, rg1 + addi rg1, 4 + cmp rg0, zero + jgt _clear_loop + jmp _end + +// ------------------------------------------ +// return +_end: + stw rg1, current + + mov bpr, spr + pop bpr + return diff --git a/test/build/lib/serial.dsa b/test/build/lib/serial.dsa new file mode 100644 index 0000000..2ef966e --- /dev/null +++ b/test/build/lib/serial.dsa @@ -0,0 +1,244 @@ +// lib: +// print_serial.dsa + +// usage: +// +// include print_serial "" +// +// usage for print: +// push (register containing address of string) +// push pcx +// jmp print_serial::print +// +// usage for print_byte: +// push (register containing byte) +// push pcx +// jmp print_serial::print_byte +// +// usage for print_word: +// push (register containing word) +// push pcx +// jmp print_serial::print_word +// +// usage for print_hex_byte: +// push (register containing byte) +// push pcx +// jmp print_serial::print_hex_byte +// +// usage for print_hex_word: +// push (register containing word) +// push pcx +// jmp print_serial::print_hex_word +// +// usage for print_num: +// push (register containing number to print in decimal) +// push pcx +// jmp print_serial::print_num +// +// usage for println: +// push (register containing address of string) +// push pcx +// jmp print_serial::println +// + +include maths "./maths.dsa" + +dw serial: 0x207D0 // 0x20000 + 2000 + +// ------------------------------------------ +// prints the string at addr(arg[0]) to the serial port. +print: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + +_print_loop: + ldb rg0, acc + cmp acc, zero + jeq _end + stb acc, rg1 + + addi rg0, 1 + jmp _print_loop + +// ------------------------------------------ +// prints the string at addr(arg[0]) followed by a newline to the serial port. +println: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + +_println_loop: + ldb rg0, acc + cmp acc, zero + jeq _println_end + stb acc, rg1 + + addi rg0, 1 + jmp _println_loop + +_println_end: + lli 0x0A, rg2 // newline character + stb rg2, rg1 + jmp _end + +// ------------------------------------------ +// prints the word in arg[0] as 4 raw bytes to the serial port. +print_word: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + + stb rg0, rg1 + shr rg0, 8 + stb rg0, rg1 + shr rg0, 8 + stb rg0, rg1 + shr rg0, 8 + stb rg0, rg1 + jmp _end + +// ------------------------------------------ +// prints the last byte of arg[0] to the serial port. +print_byte: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + + stb rg0, rg1 + jmp _end + +// ------------------------------------------ +// prints the value of arg[0] to the serial port in hex. +print_hex_word: + push bpr + mov spr, bpr + + lwi 0x207D0, rg1 + + ldb bpr, rg0, 8 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 9 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 10 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 11 + push rg0 + call _print_hex_byte + addi spr, 4 + + jmp _end + +// ------------------------------------------ +// prints the last byte of arg[0] to the serial port in hex. +print_hex_byte: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + + call _print_hex_byte + jmp _end + +// function body +_print_hex_byte: + lli 0xF, rg2 + push rg0 + + shr rg0, 4 + and rg0, rg2, rg0 + call _print_hex_nibble + pop rg0 + + and rg0, rg2, rg0 + call _print_hex_nibble + return + +// print a hex digit +_print_hex_nibble: + lli 10, rg3 + cmp rg0, rg3 + jlt _print_hex_nibble_number + addi rg0, 0x37, rg0 + stb rg0, rg1 + return + +_print_hex_nibble_number: + addi rg0, 0x30, rg0 + stb rg0, rg1 + return + +// ------------------------------------------ +// prints arg[0] as a decimal number to the serial port. +print_num: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lli 0, rg5 + + cmp rg0, zero + jne _print_num_extract_digits + + lli 0x30, rg6 + push rg6 + lli 1, rg5 + jmp _print_num_output + +_print_num_extract_digits: + cmp rg0, zero + jeq _print_num_output + + push rg0 + lli 10, rg1 + push rg1 + call maths::divmod + pop rg0 + pop rg1 + + addi rg1, 0x30, rg6 + push rg6 + inc rg5 + + jmp _print_num_extract_digits + +_print_num_output: + lwi 0x207D0, rg1 + +_print_num_output_loop: + cmp rg5, zero + jeq _print_num_done + + pop rg6 + stb rg6, rg1 + dec rg5 + + jmp _print_num_output_loop + +_print_num_done: + jmp _end + +// ------------------------------------------ +// return +_end: + mov bpr, spr + pop bpr + return diff --git a/test/build/main.dsa b/test/build/main.dsa new file mode 100644 index 0000000..2a2d599 --- /dev/null +++ b/test/build/main.dsa @@ -0,0 +1,42 @@ +// GENERATED BY DSC COMPILER +// Generated at 2026-02-23 18:58:53 + +// Imports +include print: "./lib/print.dsa" + +// Globals & Reserved Memory + +// Entry Point +dw stack: 0x010000 +db message: "Process Exited with code:" +_init: + ldw stack, bpr, 0 + mov bpr, spr + push zero + call main + call print::print_newline + lwi message, rg0 + push rg0 + call print::print + pop zero + call print::print_hex_word + pop zero + hlt + +// Return +_ret: + mov bpr, spr + pop bpr + return + +db str_1: "Hello, World!" +// fn main() -> void +main: + push bpr + mov spr, bpr + lwi str_1, rg0 + // push arg 0 + push rg0 + call print::print + pop zero + jmp _ret \ No newline at end of file diff --git a/test/build/main.dsc b/test/build/main.dsc new file mode 100644 index 0000000..ec8c8aa --- /dev/null +++ b/test/build/main.dsc @@ -0,0 +1,5 @@ +include print: "./lib/print.dsa"; + +fn main() { + print::print("Hello, World!"); +} diff --git a/test/src/lib/maths.dsa b/test/src/lib/maths.dsa new file mode 100644 index 0000000..687ad5d --- /dev/null +++ b/test/src/lib/maths.dsa @@ -0,0 +1,105 @@ + +// multiply.dsa +// usage: +// +// include multiply "" +// +// usage for multiply: +// push (arg1) +// push (arg0) +// call multiply::multiply +// pop (arg0) +// pop (arg1) + +multiply: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 // load op 2 + ldw bpr, rg1, 12 // load op 1 + lwi 0, rg2 // initialise rg2 to zero + +_multiply_loop: + add rg2, rg0, rg2 + dec rg1 + + cmp rg1, zero + jgt _multiply_loop + +_multiply_end: + stw rg2, bpr, 8 + + mov bpr, spr + pop bpr + return + +divmod: + push bpr + mov spr, bpr + + ldw bpr, rg1, 8 // load op 2 + ldw bpr, rg0, 12 // load op 1 + + lli 0, rg3 + +_divmod_loop: + cmp rg0, rg1 + jlt _divmod_end + + sub rg0, rg1, rg0 + inc rg3 + + jmp _divmod_loop + +_divmod_end: + // store div in first arg + // store mod in second arg + stw rg3, bpr, 8 + stw rg0, bpr, 12 + + mov bpr, spr + pop bpr + return + +// multiply.dsa - improved version +// Multiplies two 32-bit numbers using shift-and-add +// +// Usage: +// push operand2 (multiplier) +// push operand1 (multiplicand) +// call multiply::multiply +// pop result +// pop zero (discard second argument) + +new_multiply: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 // rg0 = multiplicand + ldw bpr, rg1, 12 // rg1 = multiplier + + lli 0, rg2 // rg2 = result (accumulator) + lli 32, rg3 // rg3 = bit counter + +mult_loop: + // Check if lowest bit of multiplier is 1 + lli 1, acc + and rg1, acc, acc // acc = rg1 & 1 + cmp acc, zero + jeq skip_add // if (rg1 & 1) == 0, skip addition + + // Add multiplicand to result + add rg2, rg0, rg2 + +skip_add: + shl rg0, 1 // shift multiplicand left + shr rg1, 1 // shift multiplier right + + dec rg3 + cmp rg3, zero + jgt mult_loop + + stw rg2, bpr, 8 // store result + mov bpr, spr + pop bpr + return diff --git a/test/src/lib/print.dsa b/test/src/lib/print.dsa new file mode 100644 index 0000000..ead1d5c --- /dev/null +++ b/test/src/lib/print.dsa @@ -0,0 +1,332 @@ + +// lib: +// print.dsa + +// usage: +// +// include print """ +// +// usage for print: +// push (register containing address of string) +// push pcx +// jmp print::print +// +// usage for reset: +// push pcx +// jmp print::reset +// +// usage for clear: +// push pcx +// jmp print::clear +// +// usage for print_byte: +// push (register containing byte) +// push pcx +// jmp print::print_byte +// +// usage for print_word: +// push (register containing word) +// push pcx +// jmp print::print_word +// +// usage for print_num: +// push (register containing number to print in decimal) +// push pcx +// jmp print::print_num +// + +include maths "./maths.dsa" + +dw display: 0x20000 +dw current: 0x20000 + +// ------------------------------------------ +// prints the string at addr(arg[0]) to the screen. (no trailing whitespace unless explicitly provided) +print: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + ldw current, rg1 + +_print_loop: + ldb rg0, acc + cmp acc, zero + jeq _end + stb acc, rg1 + + addi rg0, 1 + addi rg1, 1 + + jmp _print_loop + +// ------------------------------------------ +println: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + ldw current, rg1 + +_println_loop: + ldb rg0, acc + cmp acc, zero + jeq _println_end + stb acc, rg1 + + addi rg0, 1 + addi rg1, 1 + + jmp _println_loop + +_println_end: + call print_newline + jmp _end + +// ------------------------------------------ +// prints the value of arg[0] to the screen. +print_word: + // initialise + push bpr + mov spr, bpr + + // load byte into acc + ldw bpr, rg0, 8 + ldw current, rg1 + + addi rg1, 3 + + stb rg0, rg1 + subi rg1, 1 + shr rg0, 8 + stb rg0, rg1 + subi rg1, 1 + shr rg0, 8 + stb rg0, rg1 + subi rg1, 1 + shr rg0, 8 + stb rg0, rg1 + + addi rg1, 4 + jmp _end + +// ------------------------------------------ +// prints the last byte of arg[0] to the screen. +print_byte: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + ldw current, rg1 + + stb rg0, rg1 + addi rg1, 1 + jmp _end + +// ------------------------------------------ +// prints the value of arg[0] to the screen in hex. +print_hex_word: + push bpr + mov spr, bpr + + ldw current, rg1 + + ldb bpr, rg0, 8 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 9 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 10 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 11 + push rg0 + call _print_hex_byte + addi spr, 4 + + jmp _end + +// ------------------------------------------ +// prints the last byte of arg[0] to the screen in hex. +print_hex_byte: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + ldw current, rg1 + + call _print_hex_byte + jmp _end + +// function body +_print_hex_byte: + // mask to get lower nibble + lli 0xF, rg2 + // save rg0 state + push rg0 + + shr rg0, 4 + and rg0, rg2, rg0 + call _print_hex_nibble + pop rg0 + + and rg0, rg2, rg0 + call _print_hex_nibble + return + +// print a hex digit +_print_hex_nibble: + lli 10, rg3 + cmp rg0, rg3 + jlt _print_hex_nibble_number + addi rg0, 0x37, rg0 + stb rg0, rg1 + addi rg1, 1 + return + +// helper function. +_print_hex_nibble_number: + addi rg0, 0x30, rg0 + stb rg0, rg1 + addi rg1, 1 + return + +// ------------------------------------------ +// print whitespace +print_whitespace: + push bpr + mov spr, bpr + + ldw current, rg1 + lli 0x20, rg0 + stb rg0, rg1 + addi rg1, 1 + jmp _end + +// ------------------------------------------ +// print newline +print_newline: + push bpr + mov spr, bpr + + // load variables into registers + ldw display, rg0 + ldw current, rg1 + + // get the offset from the display base + sub rg1, rg0, rg0 + + lwi 80, rg2 + pusha 3 + push rg0 + push rg2 + call maths::divmod + pop zero // result + pop rg3 // remainder + popa 3 + + sub rg1, rg3, rg2 + addi rg2, 80, rg1 + + // _end saves the display state + jmp _end + +// ------------------------------------------ +// prints arg[0] as a decimal number to the screen. +print_num: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 // load number to print + lli 0, rg5 // rg5 = digit counter + + // check if number is zero + cmp rg0, zero + jne _print_num_extract_digits + + // special case: print '0' for zero + lli 0x30, rg6 + push rg6 // push digit to stack buffer + lli 1, rg5 // we have 1 digit + jmp _print_num_output + +_print_num_extract_digits: + // divide by 10 repeatedly to get digits + cmp rg0, zero + jeq _print_num_output + + // call divmod(rg0, 10) + push rg0 // dividend + lli 10, rg1 + push rg1 // divisor (10) + call maths::divmod + pop rg0 // quotient (continue dividing this) + pop rg1 // remainder (the digit) + + // convert digit to ASCII and push to stack buffer + addi rg1, 0x30, rg6 // convert to ASCII + push rg6 // push digit to stack + inc rg5 // increment digit counter + + jmp _print_num_extract_digits + +_print_num_output: + // now print digits (pop them off in reverse order) + ldw current, rg1 // get display pointer + +_print_num_output_loop: + // check if we've printed all digits + cmp rg5, zero + jeq _print_num_done + + // pop digit and print it + pop rg6 + stb rg6, rg1 + addi rg1, 1 + dec rg5 + + jmp _print_num_output_loop + +_print_num_done: + jmp _end + +// ------------------------------------------ +// resets the cursor position on the screen to 0x20000. (0,0) +reset: + push bpr + mov spr, bpr + ldw display, rg1 + jmp _end + +// ------------------------------------------ +// clears the screen +clear: + push bpr + mov spr, bpr + // display size = 2000 bytes / 500 words + lli 500 rg0 + ldw display, rg1 + +_clear_loop: + dec rg0 + stw zero, rg1 + addi rg1, 4 + cmp rg0, zero + jgt _clear_loop + jmp _end + +// ------------------------------------------ +// return +_end: + stw rg1, current + + mov bpr, spr + pop bpr + return diff --git a/test/src/lib/serial.dsa b/test/src/lib/serial.dsa new file mode 100644 index 0000000..2ef966e --- /dev/null +++ b/test/src/lib/serial.dsa @@ -0,0 +1,244 @@ +// lib: +// print_serial.dsa + +// usage: +// +// include print_serial "" +// +// usage for print: +// push (register containing address of string) +// push pcx +// jmp print_serial::print +// +// usage for print_byte: +// push (register containing byte) +// push pcx +// jmp print_serial::print_byte +// +// usage for print_word: +// push (register containing word) +// push pcx +// jmp print_serial::print_word +// +// usage for print_hex_byte: +// push (register containing byte) +// push pcx +// jmp print_serial::print_hex_byte +// +// usage for print_hex_word: +// push (register containing word) +// push pcx +// jmp print_serial::print_hex_word +// +// usage for print_num: +// push (register containing number to print in decimal) +// push pcx +// jmp print_serial::print_num +// +// usage for println: +// push (register containing address of string) +// push pcx +// jmp print_serial::println +// + +include maths "./maths.dsa" + +dw serial: 0x207D0 // 0x20000 + 2000 + +// ------------------------------------------ +// prints the string at addr(arg[0]) to the serial port. +print: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + +_print_loop: + ldb rg0, acc + cmp acc, zero + jeq _end + stb acc, rg1 + + addi rg0, 1 + jmp _print_loop + +// ------------------------------------------ +// prints the string at addr(arg[0]) followed by a newline to the serial port. +println: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + +_println_loop: + ldb rg0, acc + cmp acc, zero + jeq _println_end + stb acc, rg1 + + addi rg0, 1 + jmp _println_loop + +_println_end: + lli 0x0A, rg2 // newline character + stb rg2, rg1 + jmp _end + +// ------------------------------------------ +// prints the word in arg[0] as 4 raw bytes to the serial port. +print_word: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + + stb rg0, rg1 + shr rg0, 8 + stb rg0, rg1 + shr rg0, 8 + stb rg0, rg1 + shr rg0, 8 + stb rg0, rg1 + jmp _end + +// ------------------------------------------ +// prints the last byte of arg[0] to the serial port. +print_byte: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + + stb rg0, rg1 + jmp _end + +// ------------------------------------------ +// prints the value of arg[0] to the serial port in hex. +print_hex_word: + push bpr + mov spr, bpr + + lwi 0x207D0, rg1 + + ldb bpr, rg0, 8 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 9 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 10 + push rg0 + call _print_hex_byte + addi spr, 4 + + ldb bpr, rg0, 11 + push rg0 + call _print_hex_byte + addi spr, 4 + + jmp _end + +// ------------------------------------------ +// prints the last byte of arg[0] to the serial port in hex. +print_hex_byte: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lwi 0x207D0, rg1 + + call _print_hex_byte + jmp _end + +// function body +_print_hex_byte: + lli 0xF, rg2 + push rg0 + + shr rg0, 4 + and rg0, rg2, rg0 + call _print_hex_nibble + pop rg0 + + and rg0, rg2, rg0 + call _print_hex_nibble + return + +// print a hex digit +_print_hex_nibble: + lli 10, rg3 + cmp rg0, rg3 + jlt _print_hex_nibble_number + addi rg0, 0x37, rg0 + stb rg0, rg1 + return + +_print_hex_nibble_number: + addi rg0, 0x30, rg0 + stb rg0, rg1 + return + +// ------------------------------------------ +// prints arg[0] as a decimal number to the serial port. +print_num: + push bpr + mov spr, bpr + + ldw bpr, rg0, 8 + lli 0, rg5 + + cmp rg0, zero + jne _print_num_extract_digits + + lli 0x30, rg6 + push rg6 + lli 1, rg5 + jmp _print_num_output + +_print_num_extract_digits: + cmp rg0, zero + jeq _print_num_output + + push rg0 + lli 10, rg1 + push rg1 + call maths::divmod + pop rg0 + pop rg1 + + addi rg1, 0x30, rg6 + push rg6 + inc rg5 + + jmp _print_num_extract_digits + +_print_num_output: + lwi 0x207D0, rg1 + +_print_num_output_loop: + cmp rg5, zero + jeq _print_num_done + + pop rg6 + stb rg6, rg1 + dec rg5 + + jmp _print_num_output_loop + +_print_num_done: + jmp _end + +// ------------------------------------------ +// return +_end: + mov bpr, spr + pop bpr + return diff --git a/test/src/main.dsc b/test/src/main.dsc new file mode 100644 index 0000000..ec8c8aa --- /dev/null +++ b/test/src/main.dsc @@ -0,0 +1,5 @@ +include print: "./lib/print.dsa"; + +fn main() { + print::print("Hello, World!"); +}