- 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:
@@ -1,3 +1,4 @@
|
|||||||
/target
|
/target
|
||||||
**/*.env
|
**/*.env
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
.test/
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ edition.workspace = true
|
|||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "assembler"
|
name = "dsa-a"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ impl From<AssembleError> for BuildError {
|
|||||||
|
|
||||||
impl Builder for Assembler {
|
impl Builder for Assembler {
|
||||||
type Output = Vec<u8>;
|
type Output = Vec<u8>;
|
||||||
|
type Args = ();
|
||||||
|
|
||||||
fn logs(&self) -> Vec<String> {
|
fn logs(&self) -> Vec<String> {
|
||||||
self.logs_rx.logs()
|
self.logs_rx.logs()
|
||||||
@@ -78,7 +79,7 @@ impl Builder for Assembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Start the compilation process in a separate thread
|
/// Start the compilation process in a separate thread
|
||||||
fn start(&mut self) {
|
fn start(&mut self, args: ()) {
|
||||||
if self.is_running {
|
if self.is_running {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ fn main() {
|
|||||||
let output_path = &args[4];
|
let output_path = &args[4];
|
||||||
|
|
||||||
let mut engine = Assembler::new(PathBuf::from(input_path));
|
let mut engine = Assembler::new(PathBuf::from(input_path));
|
||||||
engine.start();
|
engine.start(());
|
||||||
let result = engine.output().expect("assembler failed.");
|
let result = engine.output().expect("assembler failed.");
|
||||||
|
|
||||||
if let Err(e) = fs::write(output_path, result) {
|
if let Err(e) = fs::write(output_path, result) {
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ version.workspace = true
|
|||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "dsa-c"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.43"
|
chrono = "0.4.43"
|
||||||
common = { path = "../dsa_common" }
|
common = { path = "../dsa_common" }
|
||||||
|
|||||||
@@ -20,10 +20,11 @@ pub struct CodeGenerator {
|
|||||||
functions: Vec<IB>,
|
functions: Vec<IB>,
|
||||||
symbols: Vec<String>,
|
symbols: Vec<String>,
|
||||||
allocator: RegisterAllocator,
|
allocator: RegisterAllocator,
|
||||||
|
is_library: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodeGenerator {
|
impl CodeGenerator {
|
||||||
pub fn new(ast: Program) -> Self {
|
pub fn new(ast: Program, is_lib: bool) -> Self {
|
||||||
CodeGenerator {
|
CodeGenerator {
|
||||||
ast,
|
ast,
|
||||||
imports: HashMap::new(),
|
imports: HashMap::new(),
|
||||||
@@ -31,6 +32,7 @@ impl CodeGenerator {
|
|||||||
functions: Vec::new(),
|
functions: Vec::new(),
|
||||||
symbols: Vec::new(),
|
symbols: Vec::new(),
|
||||||
allocator: RegisterAllocator::new(),
|
allocator: RegisterAllocator::new(),
|
||||||
|
is_library: is_lib,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +48,9 @@ impl CodeGenerator {
|
|||||||
|
|
||||||
pub fn generate(&mut self) -> Result<String, CompilerError> {
|
pub fn generate(&mut self) -> Result<String, CompilerError> {
|
||||||
// always include the print library for debugging!
|
// 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 {
|
for block in self.ast.clone().declarations {
|
||||||
match block {
|
match block {
|
||||||
@@ -101,6 +105,7 @@ impl CodeGenerator {
|
|||||||
|
|
||||||
block.extend(self.globals.values().cloned().collect::<Vec<_>>());
|
block.extend(self.globals.values().cloned().collect::<Vec<_>>());
|
||||||
|
|
||||||
|
if !self.is_library {
|
||||||
block.extend(vec![
|
block.extend(vec![
|
||||||
I::Newline,
|
I::Newline,
|
||||||
I::global_comment("Entry Point"),
|
I::global_comment("Entry Point"),
|
||||||
@@ -120,6 +125,10 @@ impl CodeGenerator {
|
|||||||
I::call("print::print_hex_word"),
|
I::call("print::print_hex_word"),
|
||||||
I::pop(Register::Zero),
|
I::pop(Register::Zero),
|
||||||
I::Hlt,
|
I::Hlt,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
block.extend(vec![
|
||||||
I::Newline,
|
I::Newline,
|
||||||
// default return block boilerplate
|
// default return block boilerplate
|
||||||
I::global_comment("Return"),
|
I::global_comment("Return"),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ mod instruction;
|
|||||||
mod registers;
|
mod registers;
|
||||||
mod scope;
|
mod scope;
|
||||||
|
|
||||||
pub fn generate_code(ast: &Program) -> Result<String, CompilerError> {
|
pub fn generate_code(ast: &Program, is_lib: bool) -> Result<String, CompilerError> {
|
||||||
let mut codegen = codegen::CodeGenerator::new(ast.clone());
|
let mut codegen = codegen::CodeGenerator::new(ast.clone(), is_lib);
|
||||||
codegen.generate()
|
codegen.generate()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ use crate::model::{CompilerError, Program};
|
|||||||
|
|
||||||
mod dsa;
|
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 {
|
match ext {
|
||||||
"dsa" => Ok(dsa::generate_code(ast)?),
|
"dsa" => Ok(dsa::generate_code(ast, is_lib)?),
|
||||||
_ => Err(CompilerError::Generic(format!(
|
_ => Err(CompilerError::Generic(format!(
|
||||||
"File type {} not supported",
|
"File type {} not supported",
|
||||||
ext
|
ext
|
||||||
|
|||||||
@@ -722,37 +722,42 @@ impl Parser {
|
|||||||
|
|
||||||
// if the next token isn't the beginning of a struct literal this is just
|
// if the next token isn't the beginning of a struct literal this is just
|
||||||
// an identifier.
|
// an identifier.
|
||||||
if !expect_tt!(self.peek_next()?, LeftBrace).accepted() {
|
// if !expect_tt!(self.peek_next()?, LeftBrace).accepted() {
|
||||||
return ParseResult::Accept(Expression::Variable {
|
// return ParseResult::Accept(Expression::Variable {
|
||||||
|
// name,
|
||||||
|
// expr_type: None,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
ParseResult::Accept(Expression::Variable {
|
||||||
name,
|
name,
|
||||||
expr_type: 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,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 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 => {
|
Token::LeftBracket => {
|
||||||
self.next()?; // consume '['
|
self.next()?; // consume '['
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ pub struct Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 =
|
let input =
|
||||||
std::fs::read_to_string(&self.src_path).expect("Failed to read input file");
|
std::fs::read_to_string(&self.src_path).expect("Failed to read input file");
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ impl Compiler {
|
|||||||
// println!("Parsed AST: {:#?}", ast);
|
// println!("Parsed AST: {:#?}", ast);
|
||||||
|
|
||||||
// Generate the output using the backend with the parsed result.
|
// 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,
|
Ok(result) => result,
|
||||||
Err(err) => return Err(format!("Compilation failed: {err:?}").into()),
|
Err(err) => return Err(format!("Compilation failed: {err:?}").into()),
|
||||||
};
|
};
|
||||||
@@ -57,6 +57,7 @@ impl Compiler {
|
|||||||
|
|
||||||
impl Builder for Compiler {
|
impl Builder for Compiler {
|
||||||
type Output = String;
|
type Output = String;
|
||||||
|
type Args = bool;
|
||||||
|
|
||||||
fn new(src_path: impl Into<PathBuf>) -> Self {
|
fn new(src_path: impl Into<PathBuf>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -66,8 +67,8 @@ impl Builder for Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start(&mut self) {
|
fn start(&mut self, args: bool) {
|
||||||
match self.build() {
|
match self.build(args) {
|
||||||
Ok(x) => self.result = Some(Ok(x)),
|
Ok(x) => self.result = Some(Ok(x)),
|
||||||
Err(err) => self.result = Some(Err(err.into())),
|
Err(err) => self.result = Some(Err(err.into())),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ fn main() {
|
|||||||
// read from input file: syntax "c_compiler <src.c> [output.dsa]"
|
// read from input file: syntax "c_compiler <src.c> [output.dsa]"
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
eprintln!("Usage: c_compiler <src.dsc> [output.dsa]");
|
eprintln!("Usage: c_compiler [--lib | --bin] <src.dsc> [output.dsa]");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,8 +19,10 @@ fn main() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
let is_lib = args.contains(&"--lib".to_string());
|
||||||
|
|
||||||
let mut builder = Compiler::new(PathBuf::from(input_file));
|
let mut builder = Compiler::new(PathBuf::from(input_file));
|
||||||
builder.start();
|
builder.start(is_lib);
|
||||||
let result = builder.output().unwrap();
|
let result = builder.output().unwrap();
|
||||||
|
|
||||||
std::fs::write(output_file, &result).expect("Failed to write output");
|
std::fs::write(output_file, &result).expect("Failed to write output");
|
||||||
|
|||||||
@@ -32,11 +32,12 @@ impl fmt::Display for BuildError {
|
|||||||
|
|
||||||
pub trait Builder {
|
pub trait Builder {
|
||||||
type Output: Clone + std::convert::AsRef<[u8]>;
|
type Output: Clone + std::convert::AsRef<[u8]>;
|
||||||
|
type Args: Clone;
|
||||||
|
|
||||||
fn new(src_path: impl Into<PathBuf>) -> Self;
|
fn new(src_path: impl Into<PathBuf>) -> Self;
|
||||||
|
|
||||||
// starts compilation
|
// starts compilation
|
||||||
fn start(&mut self);
|
fn start(&mut self, args: Self::Args);
|
||||||
|
|
||||||
// non-blocking function, returns output if completed
|
// non-blocking function, returns output if completed
|
||||||
fn poll(&mut self) -> Option<Result<Self::Output, BuildError>>;
|
fn poll(&mut self) -> Option<Result<Self::Output, BuildError>>;
|
||||||
|
|||||||
+33
-1
@@ -1,4 +1,4 @@
|
|||||||
use std::{env, path::PathBuf};
|
use std::{env, fs, path::PathBuf, process::Command};
|
||||||
|
|
||||||
use dsx_common::builder;
|
use dsx_common::builder;
|
||||||
|
|
||||||
@@ -22,6 +22,38 @@ fn main() {
|
|||||||
std::process::exit(1);
|
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."),
|
"package" => todo!("Package manager stub – not implemented yet."),
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!("Unknown command: {}", args[1]);
|
eprintln!("Unknown command: {}", args[1]);
|
||||||
|
|||||||
+9
-4
@@ -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::create_dir_all(src_path.join("lib")).expect("Failed to create lib directory");
|
||||||
fs::write(
|
fs::write(
|
||||||
src_path.join("lib/print.dsa"),
|
src_path.join("./lib/print.dsa"),
|
||||||
templates::create_print_lib(),
|
include_str!("../templates/dsa/print.dsa"),
|
||||||
)
|
)
|
||||||
.expect("Failed to create print.dsa");
|
.expect("Failed to create print.dsa");
|
||||||
fs::write(
|
fs::write(
|
||||||
src_path.join("lib/maths.dsa"),
|
src_path.join("./lib/maths.dsa"),
|
||||||
templates::create_maths_lib(),
|
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");
|
.expect("Failed to create maths.dsa");
|
||||||
|
|
||||||
|
|||||||
+2
-449
@@ -1,3 +1,5 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub trait Template {
|
pub trait Template {
|
||||||
fn lib(project: &str) -> String;
|
fn lib(project: &str) -> String;
|
||||||
fn bin(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
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -60,7 +60,7 @@ pub fn build_project(cwd: &Path) -> Result<(), BuildError> {
|
|||||||
{
|
{
|
||||||
fs::create_dir_all(cwd.join("artifacts"))?;
|
fs::create_dir_all(cwd.join("artifacts"))?;
|
||||||
let mut asm = Assembler::new("./main.dsa");
|
let mut asm = Assembler::new("./main.dsa");
|
||||||
asm.start();
|
asm.start(());
|
||||||
asm.write_result("../artifacts/out.dsb")?;
|
asm.write_result("../artifacts/out.dsb")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,8 +143,10 @@ fn build_all_dsc(path: &Path) -> Result<(), BuildError> {
|
|||||||
let input_path = path;
|
let input_path = path;
|
||||||
let output_path = path.with_extension("dsa");
|
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);
|
let mut compiler = Compiler::new(input_path);
|
||||||
compiler.start();
|
compiler.start(is_lib);
|
||||||
compiler.write_result(output_path.clone())?;
|
compiler.write_result(output_path.clone())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ edition.workspace = true
|
|||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "dsx_server"
|
name = "dsx-server"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
+3
-2
@@ -2,7 +2,7 @@
|
|||||||
name = "emulator"
|
name = "emulator"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
default-run = "emulator"
|
default-run = "dsa"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "dsa_rs"
|
name = "dsa_rs"
|
||||||
@@ -10,7 +10,8 @@ path = "src/lib.rs"
|
|||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "emulator"
|
name = "dsa"
|
||||||
|
path = "src/main.rs"
|
||||||
required-features = ["config"]
|
required-features = ["config"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -426,7 +426,7 @@ impl Editor {
|
|||||||
match path.extension().and_then(|ext| ext.to_str()) {
|
match path.extension().and_then(|ext| ext.to_str()) {
|
||||||
Some("dsa") => {
|
Some("dsa") => {
|
||||||
let mut assembler = Assembler::new(path);
|
let mut assembler = Assembler::new(path);
|
||||||
assembler.start();
|
assembler.start(());
|
||||||
|
|
||||||
// Or block until done
|
// Or block until done
|
||||||
self.output = match assembler.output() {
|
self.output = match assembler.output() {
|
||||||
@@ -440,7 +440,9 @@ impl Editor {
|
|||||||
Some("dsc") => {
|
Some("dsc") => {
|
||||||
let dsa_path = Path::new(path).with_extension("dsa");
|
let dsa_path = Path::new(path).with_extension("dsa");
|
||||||
let mut compiler = Compiler::new(path);
|
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) {
|
if let Err(e) = compiler.write_result(&dsa_path) {
|
||||||
self.error = Some(e.to_string());
|
self.error = Some(e.to_string());
|
||||||
@@ -448,7 +450,7 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut assembler = Assembler::new(&dsa_path);
|
let mut assembler = Assembler::new(&dsa_path);
|
||||||
assembler.start();
|
assembler.start(());
|
||||||
|
|
||||||
// Or block until done
|
// Or block until done
|
||||||
self.output = match assembler.output() {
|
self.output = match assembler.output() {
|
||||||
|
|||||||
Reference in New Issue
Block a user