pub trait Template { fn lib(project: &str) -> String; fn bin(project: &str) -> String; fn create(project: &str, lib: bool) -> String { if lib { Self::lib(project) } else { Self::bin(project) } } } pub struct Dsa; pub struct Dsc; impl Template for Dsa { fn lib(project: &str) -> String { format!( r#"// lib.dsa // usage: // // include {project} "" // // usage for {project}_main: // push (arg1) // push (arg0) // call {project}::{project}_main // pop (arg0) // pop (arg1) // Example data declarations // dw example_data: 0x0000 // Main function template {project}_main: // the correct way to start a function as defined by the calling convention push bpr mov spr, bpr // explanation of how to access args ldw bpr, rg0, 8 // arg 0 ldw bpr, rg0, 12 // arg 1 // your code goes here // Example: load example_data into rg1 // ldw example_data, rg1 // the correct way to end a function as defined by the calling convention mov bpr, spr pop bpr return "#, ) } fn bin(project: &str) -> String { format!( r#" // GENERATED BY DSX-BUILD // Generated at: {timestamp} // Project name: {project} // Imports include print: "./lib/print.dsa" // Globals & Reserved Memory dw stack: 0x10000 db message: "Process Exited with code:" // Entry Point _init: ldw stack, bpr 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 main: push bpr mov spr, bpr // Your code goes here // Return zero stw zero, bpr, 8 mov bpr, spr pop bpr return"#, timestamp = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string() ) } } impl Template for Dsc { fn lib(project: &str) -> String { format!( r#" // GENERATED BY DSX-BUILD // Generated at: {timestamp} // Project name: {project} // Imports include print: "./lib/print.dsa"; // Main Function fn {project}_main() -> u32 {{ return 0; }}"#, timestamp = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string() ) } fn bin(project: &str) -> String { format!( r#" // GENERATED BY DSX-BUILD // Generated at: {timestamp} // Project name: {project} // Imports include print: "./lib/print.dsa"; // Main Function fn main() -> u32 {{ return 0; }}"#, timestamp = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string() ) } } pub fn create_print_lib() -> String { format!( 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 { format!( 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 "# ) }