- added support for args in Builder trait

- added is_lib arg for compiler to produce non-main assembly outputs
- updated templates to use include_str
This commit is contained in:
2026-02-23 22:06:07 +00:00
parent 71b36dc6b5
commit 4bee36eb7f
28 changed files with 889 additions and 525 deletions
+1
View File
@@ -1,3 +1,4 @@
/target
**/*.env
Cargo.lock
.test/
+61
View File
@@ -0,0 +1,61 @@
# Maintainer: zxq5 <zxq5@proton.me>
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
}
+1 -1
View File
@@ -5,7 +5,7 @@ edition.workspace = true
authors.workspace = true
[[bin]]
name = "assembler"
name = "dsa-a"
path = "src/main.rs"
[lib]
+2 -1
View File
@@ -58,6 +58,7 @@ impl From<AssembleError> for BuildError {
impl Builder for Assembler {
type Output = Vec<u8>;
type Args = ();
fn logs(&self) -> Vec<String> {
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;
}
+1 -1
View File
@@ -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) {
+5
View File
@@ -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" }
+29 -20
View File
@@ -20,10 +20,11 @@ pub struct CodeGenerator {
functions: Vec<IB>,
symbols: Vec<String>,
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<String, CompilerError> {
// 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::<Vec<_>>());
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"),
+2 -2
View File
@@ -5,7 +5,7 @@ mod instruction;
mod registers;
mod scope;
pub fn generate_code(ast: &Program) -> Result<String, CompilerError> {
let mut codegen = codegen::CodeGenerator::new(ast.clone());
pub fn generate_code(ast: &Program, is_lib: bool) -> Result<String, CompilerError> {
let mut codegen = codegen::CodeGenerator::new(ast.clone(), is_lib);
codegen.generate()
}
+6 -2
View File
@@ -2,9 +2,13 @@ use crate::model::{CompilerError, Program};
mod dsa;
pub fn compiler_backend(ext: &str, ast: &Program) -> Result<String, CompilerError> {
pub fn compiler_backend(
ext: &str,
ast: &Program,
is_lib: bool,
) -> Result<String, CompilerError> {
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
+34 -29
View File
@@ -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 '['
+5 -4
View File
@@ -21,7 +21,7 @@ pub struct Compiler {
}
impl Compiler {
fn build(&mut self) -> Result<String, Box<dyn std::error::Error>> {
fn build(&mut self, is_lib: bool) -> Result<String, Box<dyn std::error::Error>> {
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<PathBuf>) -> 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())),
}
+4 -2
View File
@@ -7,7 +7,7 @@ fn main() {
// read from input file: syntax "c_compiler <src.c> [output.dsa]"
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
eprintln!("Usage: c_compiler <src.dsc> [output.dsa]");
eprintln!("Usage: c_compiler [--lib | --bin] <src.dsc> [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");
+2 -1
View File
@@ -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<PathBuf>) -> 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<Result<Self::Output, BuildError>>;
+33 -1
View File
@@ -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]);
+9 -4
View File
@@ -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");
+2 -449
View File
@@ -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 "<relative path>""
//
// 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 "<relative path>"
//
// 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
"#,
)
}
+104
View File
@@ -0,0 +1,104 @@
// multiply.dsa
// usage:
//
// include multiply "<relative path>"
//
// 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
+331
View File
@@ -0,0 +1,331 @@
// lib:
// print.dsa
// usage:
//
// include print "<relative path>""
//
// 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
+244
View File
@@ -0,0 +1,244 @@
// lib:
// print_serial.dsa
// usage:
//
// include print_serial "<relative path>"
//
// 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
+4 -2
View File
@@ -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())?;
}
+1 -1
View File
@@ -5,7 +5,7 @@ edition.workspace = true
authors.workspace = true
[[bin]]
name = "dsx_server"
name = "dsx-server"
path = "src/main.rs"
[dependencies]
+3 -2
View File
@@ -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]
+5 -3
View File
@@ -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() {