diff --git a/.gitignore b/.gitignore index c0c554f..81af514 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target **/*.env Cargo.lock +.test/ diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 0000000..6e05196 --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,61 @@ +# Maintainer: zxq5 +pkgbase='damn-simple-architecture' +pkgname=('dsa' 'dsx' 'dsa-tools' 'dsx-server') +pkgver=0.1.0 +pkgrel=1 +pkgdesc="Damn Simple Architecture" +arch=('x86_64') +url="https://git.zxq5.dev/zxq5/damn-simple-architecture" +license=('MIT') +makedepends=('rust' 'cargo' 'sed') +sha256sums=('SKIP') + +build() { + cd + + cargo build --release \ + --bin dsa \ + --bin dsx \ + --bin dsa-a \ + --bin dsa-c \ + --bin dsx-server +} + +package_dsa() { + pkgdesc="DSA core binary" + depends=() + + cd "$pkgbase-$pkgver" + install -Dm755 "target/release/dsa" "$pkgdir/usr/bin/dsa" +} + +package_dsx() { + pkgdesc="DSX client" + depends=('dsa') + + cd "$pkgbase-$pkgver" + install -Dm755 "target/release/dsx" "$pkgdir/usr/bin/dsx" +} + +package_dsa-tools() { + pkgdesc="DSA assembler and compiler tools" + depends=() + + cd "$pkgbase-$pkgver" + install -Dm755 "target/release/dsa-a" "$pkgdir/usr/bin/dsa-a" + install -Dm755 "target/release/dsa-c" "$pkgdir/usr/bin/dsa-c" +} + +package_dsx-server() { + pkgdesc="DSX server" + depends=() + + cd "$pkgbase-$pkgver" + install -Dm755 "target/release/dsx-server" "$pkgdir/usr/bin/dsx-server" + + # Example sed usage — patch config paths for system install + sed -i 's|./templates|/usr/share/dsx-server/templates|g' \ + "target/release/dsx-server" 2>/dev/null || true + + install -Dm644 "dsx_server/templates" "$pkgdir/usr/share/dsx-server/templates" 2>/dev/null || true +} diff --git a/core/assembler/Cargo.toml b/core/assembler/Cargo.toml index 64cffb3..6241180 100644 --- a/core/assembler/Cargo.toml +++ b/core/assembler/Cargo.toml @@ -5,7 +5,7 @@ edition.workspace = true authors.workspace = true [[bin]] -name = "assembler" +name = "dsa-a" path = "src/main.rs" [lib] diff --git a/core/assembler/src/assembler/mod.rs b/core/assembler/src/assembler/mod.rs index f70a288..c28cccb 100644 --- a/core/assembler/src/assembler/mod.rs +++ b/core/assembler/src/assembler/mod.rs @@ -58,6 +58,7 @@ impl From for BuildError { impl Builder for Assembler { type Output = Vec; + type Args = (); fn logs(&self) -> Vec { self.logs_rx.logs() @@ -78,7 +79,7 @@ impl Builder for Assembler { } /// Start the compilation process in a separate thread - fn start(&mut self) { + fn start(&mut self, args: ()) { if self.is_running { return; } diff --git a/core/assembler/src/main.rs b/core/assembler/src/main.rs index 0a73473..d1e7b68 100644 --- a/core/assembler/src/main.rs +++ b/core/assembler/src/main.rs @@ -46,7 +46,7 @@ fn main() { let output_path = &args[4]; let mut engine = Assembler::new(PathBuf::from(input_path)); - engine.start(); + engine.start(()); let result = engine.output().expect("assembler failed."); if let Err(e) = fs::write(output_path, result) { diff --git a/core/compiler/Cargo.toml b/core/compiler/Cargo.toml index e5d87a0..4ffa032 100644 --- a/core/compiler/Cargo.toml +++ b/core/compiler/Cargo.toml @@ -4,6 +4,11 @@ version.workspace = true edition.workspace = true authors.workspace = true + +[[bin]] +name = "dsa-c" +path = "src/main.rs" + [dependencies] chrono = "0.4.43" common = { path = "../dsa_common" } diff --git a/core/compiler/src/backend/dsa/codegen.rs b/core/compiler/src/backend/dsa/codegen.rs index ae3cb34..552d81d 100644 --- a/core/compiler/src/backend/dsa/codegen.rs +++ b/core/compiler/src/backend/dsa/codegen.rs @@ -20,10 +20,11 @@ pub struct CodeGenerator { functions: Vec, symbols: Vec, allocator: RegisterAllocator, + is_library: bool, } impl CodeGenerator { - pub fn new(ast: Program) -> Self { + pub fn new(ast: Program, is_lib: bool) -> Self { CodeGenerator { ast, imports: HashMap::new(), @@ -31,6 +32,7 @@ impl CodeGenerator { functions: Vec::new(), symbols: Vec::new(), allocator: RegisterAllocator::new(), + is_library: is_lib, } } @@ -46,7 +48,9 @@ impl CodeGenerator { pub fn generate(&mut self) -> Result { // always include the print library for debugging! - self.include("print", "./lib/io/print.dsa"); + if !self.is_library { + self.include("print", "./lib/print.dsa"); + } for block in self.ast.clone().declarations { match block { @@ -101,25 +105,30 @@ impl CodeGenerator { block.extend(self.globals.values().cloned().collect::>()); + if !self.is_library { + block.extend(vec![ + I::Newline, + I::global_comment("Entry Point"), + I::db_word("stack", 0x10000), + I::db_string("message", "Process Exited with code:"), + // init function for stack setup. + I::label("_init"), + I::ldw_label("stack", Register::Bpr), + I::mov(Register::Bpr, Register::Spr), + I::push(Register::Zero), + I::call("main"), + I::call("print::print_newline"), + I::lwi_label("message", Register::Rg0), + I::push(Register::Rg0), + I::call("print::print"), + I::pop(Register::Zero), + I::call("print::print_hex_word"), + I::pop(Register::Zero), + I::Hlt, + ]); + } + block.extend(vec![ - I::Newline, - I::global_comment("Entry Point"), - I::db_word("stack", 0x10000), - I::db_string("message", "Process Exited with code:"), - // init function for stack setup. - I::label("_init"), - I::ldw_label("stack", Register::Bpr), - I::mov(Register::Bpr, Register::Spr), - I::push(Register::Zero), - I::call("main"), - I::call("print::print_newline"), - I::lwi_label("message", Register::Rg0), - I::push(Register::Rg0), - I::call("print::print"), - I::pop(Register::Zero), - I::call("print::print_hex_word"), - I::pop(Register::Zero), - I::Hlt, I::Newline, // default return block boilerplate I::global_comment("Return"), diff --git a/core/compiler/src/backend/dsa/mod.rs b/core/compiler/src/backend/dsa/mod.rs index 37fc52d..4500f3b 100644 --- a/core/compiler/src/backend/dsa/mod.rs +++ b/core/compiler/src/backend/dsa/mod.rs @@ -5,7 +5,7 @@ mod instruction; mod registers; mod scope; -pub fn generate_code(ast: &Program) -> Result { - let mut codegen = codegen::CodeGenerator::new(ast.clone()); +pub fn generate_code(ast: &Program, is_lib: bool) -> Result { + let mut codegen = codegen::CodeGenerator::new(ast.clone(), is_lib); codegen.generate() } diff --git a/core/compiler/src/backend/mod.rs b/core/compiler/src/backend/mod.rs index 9e912e8..cac9049 100644 --- a/core/compiler/src/backend/mod.rs +++ b/core/compiler/src/backend/mod.rs @@ -2,9 +2,13 @@ use crate::model::{CompilerError, Program}; mod dsa; -pub fn compiler_backend(ext: &str, ast: &Program) -> Result { +pub fn compiler_backend( + ext: &str, + ast: &Program, + is_lib: bool, +) -> Result { match ext { - "dsa" => Ok(dsa::generate_code(ast)?), + "dsa" => Ok(dsa::generate_code(ast, is_lib)?), _ => Err(CompilerError::Generic(format!( "File type {} not supported", ext diff --git a/core/compiler/src/frontend/dsc/parser.rs b/core/compiler/src/frontend/dsc/parser.rs index 2409ea5..f6cfb31 100644 --- a/core/compiler/src/frontend/dsc/parser.rs +++ b/core/compiler/src/frontend/dsc/parser.rs @@ -722,37 +722,42 @@ impl Parser { // if the next token isn't the beginning of a struct literal this is just // an identifier. - if !expect_tt!(self.peek_next()?, LeftBrace).accepted() { - return ParseResult::Accept(Expression::Variable { - name, - expr_type: None, - }); - } - - let _ = self.next()?; - - let mut fields = Vec::new(); - while !expect_tt!(self.peek_next()?, RightBrace).accepted() { - let name = expect_value!(self.next()?, Identifier)?; - let _ = expect_tt!(self.next()?, Colon)?; - let expr = self.parse_expression()?; - - fields.push((name, expr)); - - if expect_tt!(self.peek_next()?, Comma).accepted() { - self.next()?; - } else { - break; - } - } - - let _ = expect_tt!(self.next()?, RightBrace)?; - - ParseResult::Accept(Expression::StructLiteral { + // if !expect_tt!(self.peek_next()?, LeftBrace).accepted() { + // return ParseResult::Accept(Expression::Variable { + // name, + // expr_type: None, + // }); + // } + // + ParseResult::Accept(Expression::Variable { name, - fields, - type_id: None, + expr_type: None, }) + + // let _ = self.next()?; + + // let mut fields = Vec::new(); + // while !expect_tt!(self.peek_next()?, RightBrace).accepted() { + // let name = expect_value!(self.next()?, Identifier)?; + // let _ = expect_tt!(self.next()?, Colon)?; + // let expr = self.parse_expression()?; + + // fields.push((name, expr)); + + // if expect_tt!(self.peek_next()?, Comma).accepted() { + // self.next()?; + // } else { + // break; + // } + // } + + // let _ = expect_tt!(self.next()?, RightBrace)?; + + // ParseResult::Accept(Expression::StructLiteral { + // name, + // fields, + // type_id: None, + // }) } Token::LeftBracket => { self.next()?; // consume '[' diff --git a/core/compiler/src/lib.rs b/core/compiler/src/lib.rs index 31e378d..650a609 100644 --- a/core/compiler/src/lib.rs +++ b/core/compiler/src/lib.rs @@ -21,7 +21,7 @@ pub struct Compiler { } impl Compiler { - fn build(&mut self) -> Result> { + fn build(&mut self, is_lib: bool) -> Result> { let input = std::fs::read_to_string(&self.src_path).expect("Failed to read input file"); @@ -46,7 +46,7 @@ impl Compiler { // println!("Parsed AST: {:#?}", ast); // Generate the output using the backend with the parsed result. - let result = match backend::compiler_backend("dsa", &ast) { + let result = match backend::compiler_backend("dsa", &ast, is_lib) { Ok(result) => result, Err(err) => return Err(format!("Compilation failed: {err:?}").into()), }; @@ -57,6 +57,7 @@ impl Compiler { impl Builder for Compiler { type Output = String; + type Args = bool; fn new(src_path: impl Into) -> Self { Self { @@ -66,8 +67,8 @@ impl Builder for Compiler { } } - fn start(&mut self) { - match self.build() { + fn start(&mut self, args: bool) { + match self.build(args) { Ok(x) => self.result = Some(Ok(x)), Err(err) => self.result = Some(Err(err.into())), } diff --git a/core/compiler/src/main.rs b/core/compiler/src/main.rs index efeebe6..66b9452 100644 --- a/core/compiler/src/main.rs +++ b/core/compiler/src/main.rs @@ -7,7 +7,7 @@ fn main() { // read from input file: syntax "c_compiler [output.dsa]" let args: Vec = std::env::args().collect(); if args.len() < 2 { - eprintln!("Usage: c_compiler [output.dsa]"); + eprintln!("Usage: c_compiler [--lib | --bin] [output.dsa]"); return; } @@ -19,8 +19,10 @@ fn main() { }; { + let is_lib = args.contains(&"--lib".to_string()); + let mut builder = Compiler::new(PathBuf::from(input_file)); - builder.start(); + builder.start(is_lib); let result = builder.output().unwrap(); std::fs::write(output_file, &result).expect("Failed to write output"); diff --git a/core/dsa_common/src/build.rs b/core/dsa_common/src/build.rs index ee14c70..65e9d8d 100644 --- a/core/dsa_common/src/build.rs +++ b/core/dsa_common/src/build.rs @@ -32,11 +32,12 @@ impl fmt::Display for BuildError { pub trait Builder { type Output: Clone + std::convert::AsRef<[u8]>; + type Args: Clone; fn new(src_path: impl Into) -> Self; // starts compilation - fn start(&mut self); + fn start(&mut self, args: Self::Args); // non-blocking function, returns output if completed fn poll(&mut self) -> Option>; diff --git a/dsx/dsx/src/main.rs b/dsx/dsx/src/main.rs index c6a3689..e0bf7fc 100644 --- a/dsx/dsx/src/main.rs +++ b/dsx/dsx/src/main.rs @@ -1,4 +1,4 @@ -use std::{env, path::PathBuf}; +use std::{env, fs, path::PathBuf, process::Command}; use dsx_common::builder; @@ -22,6 +22,38 @@ fn main() { std::process::exit(1); } } + "clean" => { + if let Some(dir) = find_project_root() { + fs::remove_dir_all(dir.join("build")) + .expect("failed to remove build dir"); + println!("Build directory cleaned..."); + fs::remove_dir_all(dir.join("artifacts")) + .expect("failed to remove artifacts dir"); + println!("Artifacts directory cleaned..."); + } else { + eprintln!("No Dsx.toml found"); + std::process::exit(1); + } + } + "run" => { + if let Some(dir) = find_project_root() { + builder::build_project(&dir).expect("Run failed!"); + + // start process and call emulator + let mut child = Command::new("dsa") + .arg("--cli") + .arg("--bin") + .arg(dir.join("./artifacts/out.dsb")) + .spawn() + .expect("Failed to start emulator"); + + // wait for emulator to finish + child.wait().expect("Failed to wait for emulator"); + } else { + eprintln!("No Dsx.toml found"); + std::process::exit(1); + } + } "package" => todo!("Package manager stub – not implemented yet."), _ => { eprintln!("Unknown command: {}", args[1]); diff --git a/dsx/dsx/src/new.rs b/dsx/dsx/src/new.rs index 8ff5d54..8f7d755 100644 --- a/dsx/dsx/src/new.rs +++ b/dsx/dsx/src/new.rs @@ -37,13 +37,18 @@ pub fn new_project(args: &[String]) { fs::create_dir_all(src_path.join("lib")).expect("Failed to create lib directory"); fs::write( - src_path.join("lib/print.dsa"), - templates::create_print_lib(), + src_path.join("./lib/print.dsa"), + include_str!("../templates/dsa/print.dsa"), ) .expect("Failed to create print.dsa"); fs::write( - src_path.join("lib/maths.dsa"), - templates::create_maths_lib(), + src_path.join("./lib/maths.dsa"), + include_str!("../templates/dsa/maths.dsa"), + ) + .expect("Failed to create maths.dsa"); + fs::write( + src_path.join("./lib/serial.dsa"), + include_str!("../templates/dsa/serial.dsa"), ) .expect("Failed to create maths.dsa"); diff --git a/dsx/dsx/src/templates.rs b/dsx/dsx/src/templates.rs index bb997ab..9e1a175 100644 --- a/dsx/dsx/src/templates.rs +++ b/dsx/dsx/src/templates.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + pub trait Template { fn lib(project: &str) -> String; fn bin(project: &str) -> String; @@ -138,452 +140,3 @@ fn main() -> u32 {{ ) } } - -pub fn create_print_lib() -> String { - String::from( - r#" -// 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 -"#, - ) -} - -pub fn create_maths_lib() -> String { - String::from( - r#" -// 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/dsx/dsx/templates/dsa/maths.dsa b/dsx/dsx/templates/dsa/maths.dsa new file mode 100644 index 0000000..7640c04 --- /dev/null +++ b/dsx/dsx/templates/dsa/maths.dsa @@ -0,0 +1,104 @@ +// 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/dsx/dsx/templates/dsa/print.dsa b/dsx/dsx/templates/dsa/print.dsa new file mode 100644 index 0000000..f66d4b7 --- /dev/null +++ b/dsx/dsx/templates/dsa/print.dsa @@ -0,0 +1,331 @@ +// 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/dsx/dsx/templates/dsa/serial.dsa b/dsx/dsx/templates/dsa/serial.dsa new file mode 100644 index 0000000..54510a7 --- /dev/null +++ b/dsx/dsx/templates/dsa/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/dsx/dsx_common/src/builder.rs b/dsx/dsx_common/src/builder.rs index 914a638..986b0d0 100644 --- a/dsx/dsx_common/src/builder.rs +++ b/dsx/dsx_common/src/builder.rs @@ -60,7 +60,7 @@ pub fn build_project(cwd: &Path) -> Result<(), BuildError> { { fs::create_dir_all(cwd.join("artifacts"))?; let mut asm = Assembler::new("./main.dsa"); - asm.start(); + asm.start(()); asm.write_result("../artifacts/out.dsb")?; } @@ -143,8 +143,10 @@ fn build_all_dsc(path: &Path) -> Result<(), BuildError> { let input_path = path; let output_path = path.with_extension("dsa"); + let is_lib = !(input_path.file_stem().unwrap().to_str().unwrap() == "main"); + let mut compiler = Compiler::new(input_path); - compiler.start(); + compiler.start(is_lib); compiler.write_result(output_path.clone())?; } diff --git a/dsx/dsx_server/data/repos/example/Package.toml b/dsx/dsx_repo_data/repos/example/Package.toml similarity index 100% rename from dsx/dsx_server/data/repos/example/Package.toml rename to dsx/dsx_repo_data/repos/example/Package.toml diff --git a/dsx/dsx_server/data/repos/example/repo/Dsx.toml b/dsx/dsx_repo_data/repos/example/repo/Dsx.toml similarity index 100% rename from dsx/dsx_server/data/repos/example/repo/Dsx.toml rename to dsx/dsx_repo_data/repos/example/repo/Dsx.toml diff --git a/dsx/dsx_server/data/repos/example/repo/src/lib/maths.dsa b/dsx/dsx_repo_data/repos/example/repo/src/lib/maths.dsa similarity index 100% rename from dsx/dsx_server/data/repos/example/repo/src/lib/maths.dsa rename to dsx/dsx_repo_data/repos/example/repo/src/lib/maths.dsa diff --git a/dsx/dsx_server/data/repos/example/repo/src/lib/print.dsa b/dsx/dsx_repo_data/repos/example/repo/src/lib/print.dsa similarity index 100% rename from dsx/dsx_server/data/repos/example/repo/src/lib/print.dsa rename to dsx/dsx_repo_data/repos/example/repo/src/lib/print.dsa diff --git a/dsx/dsx_server/data/repos/example/repo/src/main.dsa b/dsx/dsx_repo_data/repos/example/repo/src/main.dsa similarity index 100% rename from dsx/dsx_server/data/repos/example/repo/src/main.dsa rename to dsx/dsx_repo_data/repos/example/repo/src/main.dsa diff --git a/dsx/dsx_server/Cargo.toml b/dsx/dsx_server/Cargo.toml index a7ba928..fa92bb2 100644 --- a/dsx/dsx_server/Cargo.toml +++ b/dsx/dsx_server/Cargo.toml @@ -5,7 +5,7 @@ edition.workspace = true authors.workspace = true [[bin]] -name = "dsx_server" +name = "dsx-server" path = "src/main.rs" [dependencies] diff --git a/emulator/Cargo.toml b/emulator/Cargo.toml index 83e43dc..0c0c6f7 100644 --- a/emulator/Cargo.toml +++ b/emulator/Cargo.toml @@ -2,7 +2,7 @@ name = "emulator" version = "0.1.0" edition = "2024" -default-run = "emulator" +default-run = "dsa" [lib] name = "dsa_rs" @@ -10,7 +10,8 @@ path = "src/lib.rs" crate-type = ["cdylib", "rlib"] [[bin]] -name = "emulator" +name = "dsa" +path = "src/main.rs" required-features = ["config"] [dependencies] diff --git a/emulator/src/emulator/ui/editor.rs b/emulator/src/emulator/ui/editor.rs index 7df95b3..0542c13 100644 --- a/emulator/src/emulator/ui/editor.rs +++ b/emulator/src/emulator/ui/editor.rs @@ -426,7 +426,7 @@ impl Editor { match path.extension().and_then(|ext| ext.to_str()) { Some("dsa") => { let mut assembler = Assembler::new(path); - assembler.start(); + assembler.start(()); // Or block until done self.output = match assembler.output() { @@ -440,7 +440,9 @@ impl Editor { Some("dsc") => { let dsa_path = Path::new(path).with_extension("dsa"); let mut compiler = Compiler::new(path); - compiler.start(); + + let is_lib = false; + compiler.start(is_lib); if let Err(e) = compiler.write_result(&dsa_path) { self.error = Some(e.to_string()); @@ -448,7 +450,7 @@ impl Editor { } let mut assembler = Assembler::new(&dsa_path); - assembler.start(); + assembler.start(()); // Or block until done self.output = match assembler.output() {