Compare commits

2 Commits

Author SHA1 Message Date
zxq5 e9329eca95 update roadmap and ISA spec 2026-02-07 18:21:37 +00:00
zxq5 250b780e14 fix broken build system commit 2026-02-07 18:20:59 +00:00
4 changed files with 1146 additions and 274 deletions
File diff suppressed because it is too large Load Diff
+73 -45
View File
@@ -12,7 +12,9 @@ The Damn Simple Architecture (DSA) is a 32-bit RISC-style architecture designed
| Halfword | 16 bits | 2-byte aligned | | Halfword | 16 bits | 2-byte aligned |
| Word | 32 bits | 4-byte aligned | | Word | 32 bits | 4-byte aligned |
All multi-byte values use little-endian byte order. **Note on Endianness:**
- Instructions and numeric data in memory: Little-endian
- Data defined via `db/dh/dw` directives: Big-endian (assembler-specific)
## Registers ## Registers
@@ -23,33 +25,32 @@ DSA provides 32 programmer-accessible registers plus several internal system reg
| Hex | Register | Type | Description | | Hex | Register | Type | Description |
|-----|----------|------|-------------| |-----|----------|------|-------------|
| 0x00-0x0F | **rg0-rgf** | General Purpose | 16 general-purpose registers for variables and temporary values | | 0x00-0x0F | **rg0-rgf** | General Purpose | 16 general-purpose registers for variables and temporary values |
| 0x10 | **acc** | Special | Accumulator for calculations and temporary storage<br/>⚠️ May be overwritten by pseudo-instructions | | 0x10 | **acc** | Special | Accumulator for calculations and temporary storage<br/>⚠️ Used as scratch by pseudo-instructions - volatile |
| 0x11 | **spr** | Special | Stack pointer - points to top of stack | | 0x11 | **spr** | Special | Stack pointer - points to top of stack |
| 0x12 | **bpr** | Special | Base pointer - used for stack frame management | | 0x12 | **bpr** | Special | Base pointer - used for stack frame management |
| 0x13 | **ret** | Special | Return address register - stores function return addresses | | 0x13 | **ret** | Special | Return address register - used for function returns |
| 0x14 | **idr** | Privileged | Interrupt descriptor table address<br/>Read/write triggers protection fault in user mode | | 0x14 | **idr** | Privileged | Interrupt descriptor table address<br/>Read/write triggers protection fault in user mode |
| 0x15 | **mmr** | Privileged | Hardware memory map table address<br/>Read/write triggers protection fault in user mode | | 0x15 | **mmr** | Privileged | Hardware memory map table address<br/>Read/write triggers protection fault in user mode |
| 0x16 | **zero** | Read-only | Constant zero value<br/>Reads always return 0, writes are discarded | | 0x16 | **zero** | Read-only | Constant zero value<br/>Reads always return 0, writes are discarded |
| 0x17 | **noreg** | Placeholder | Indicates unused register field<br/>Read/write triggers illegal instruction fault | | 0x17 | **noreg** | Placeholder | Indicates unused register field<br/>Read/write triggers illegal instruction fault<br/>Can also be referenced as **null** |
| 0x18-0x1F | - | Reserved | Reserved for future use | | 0x18-0x1F | - | Reserved | Reserved for future use |
**System Registers (indices 0x18-0x1C):**
These exist in the encoding space but are internal to the CPU implementation:
| Hex | Register | Description |
|-----|----------|-------------|
| 0x18 | **mar** | Memory Address Register (CPU internal) |
| 0x19 | **mdr** | Memory Data Register (CPU internal) |
| 0x1A | **sts** | Status Register (CPU internal) |
| 0x1B | **cir** | Current Instruction Register (CPU internal) |
| 0x1C | **pcx** | Program Counter (read-only, special access) |
**Note on PCX (Program Counter):** **Note on PCX (Program Counter):**
- PCX is a read-only system register that can be accessed in some contexts - PCX can be read in certain contexts (e.g., stored during CALL)
- Writing to PCX triggers a protection fault - Writing to PCX triggers a protection fault
- PCX is automatically updated by jump and branch instructions - PCX is automatically updated by jump and branch instructions
### System Registers (Internal)
These registers are used internally by the CPU and are not directly accessible via assembly instructions:
| Register | Description |
|----------|-------------|
| **MAR** | Memory Address Register - holds address for memory operations |
| **MDR** | Memory Data Register - holds data for memory transfers |
| **CIR** | Current Instruction Register - holds instruction being executed |
| **STS** | Status Register - stores comparison and arithmetic flags |
| **PCX** | Program Counter - stores address of next instruction |
### Status Register (STS) Layout ### Status Register (STS) Layout
The status register is a 32-bit register with the following flag bits: The status register is a 32-bit register with the following flag bits:
@@ -108,7 +109,7 @@ Used for operations with a 16-bit immediate value.
**Usage:** **Usage:**
- Arithmetic: Immediate is a signed value - Arithmetic: Immediate is a signed value
- Memory access: Immediate is a signed byte offset from base address - Memory access: Immediate is a signed byte offset from base address
- Branches: Immediate is a signed offset from current PCX - Branches: Immediate is a signed offset added to base register
- Literal loads: Immediate is unsigned 16-bit value - Literal loads: Immediate is unsigned 16-bit value
### J-Type (Jump) Instructions ### J-Type (Jump) Instructions
@@ -131,7 +132,7 @@ Used for absolute jumps with large address ranges.
**Jump Range:** 256MB region around current PC (±128MB) **Jump Range:** 256MB region around current PC (±128MB)
**Note:** J-type instructions are defined but currently unused. Use I-type JMP with register addressing for long jumps. **Note:** J-type instructions are defined but currently unused. Use I-type JMP with register addressing for all jumps.
## Hardware Instructions ## Hardware Instructions
@@ -197,8 +198,8 @@ In machine code: SrcReg (SrcReg field), BaseReg (DestReg field), Offset (Immedia
| Hex | Mnemonic | Type | Operands | Description | | Hex | Mnemonic | Type | Operands | Description |
|-----|----------|------|----------|-------------| |-----|----------|------|----------|-------------|
| 0x0B | **LLI** | I | DestReg, Value | Load 16-bit value into lower 16 bits<br/>⚠️ **CLEARS upper 16 bits!** | | 0x0B | **LLI** | I | Value, DestReg | Load 16-bit value into lower 16 bits<br/>⚠️ **CLEARS upper 16 bits!** |
| 0x0C | **LUI** | I | DestReg, Value | Load 16-bit value into upper 16 bits<br/>Lower 16 bits unchanged | | 0x0C | **LUI** | I | Value, DestReg | Load 16-bit value into upper 16 bits<br/>Lower 16 bits unchanged |
**Usage for 32-bit Values:** **Usage for 32-bit Values:**
``` ```
@@ -208,29 +209,38 @@ LUI 0xABCD, rg0 ; rg0 = 0xABCD1234
**⚠️ CRITICAL:** Always execute LLI before LUI, as LLI clears the upper 16 bits! **⚠️ CRITICAL:** Always execute LLI before LUI, as LLI clears the upper 16 bits!
**Note on LUI:** The assembler may shift the immediate value right by 16 bits when encoding, so specify the upper 16 bits directly (e.g., `LUI 0xABCD, rg0` not `LUI 0xABCD0000, rg0`).
**Encoding Note:** **Encoding Note:**
In machine code: Value (Immediate field), DestReg field (SrcReg unused, set to noreg) In machine code: Value (Immediate field), DestReg (SrcReg field for LLI, SrcReg field for LUI)
### Jump and Branch Instructions ### Jump and Branch Instructions
| Hex | Mnemonic | Type | Operands | Description | | Hex | Mnemonic | Type | Operands | Description |
|-----|----------|------|----------|-------------| |-----|----------|------|----------|-------------|
| 0x0D | **JMP** | I | DestReg, Offset | Unconditional jump to (DestReg + Offset) | | 0x0D | **JMP** | I | Offset, BaseReg | Unconditional jump to (BaseReg + Offset) |
| 0x0E | **JEQ** | I | DestReg, Offset | Jump if Equal flag set | | 0x0E | **JEQ** | I | Offset, BaseReg | Jump if Equal flag set |
| 0x0F | **JNE** | I | DestReg, Offset | Jump if Equal flag NOT set | | 0x0F | **JNE** | I | Offset, BaseReg | Jump if Equal flag NOT set |
| 0x10 | **JGT** | I | DestReg, Offset | Jump if GreaterThan flag set | | 0x10 | **JGT** | I | Offset, BaseReg | Jump if GreaterThan flag set |
| 0x11 | **JGE** | I | DestReg, Offset | Jump if GreaterThan OR Equal flag set | | 0x11 | **JGE** | I | Offset, BaseReg | Jump if GreaterThan OR Equal flag set |
| 0x12 | **JLT** | I | DestReg, Offset | Jump if LessThan flag set | | 0x12 | **JLT** | I | Offset, BaseReg | Jump if LessThan flag set |
| 0x13 | **JLE** | I | DestReg, Offset | Jump if LessThan OR Equal flag set | | 0x13 | **JLE** | I | Offset, BaseReg | Jump if LessThan OR Equal flag set |
**Jump Calculation:** **Jump Calculation:**
- Target address = DestReg + SignExtend(Offset) - Target address = BaseReg + SignExtend(Offset)
- If DestReg = zero, this becomes absolute addressing with Offset - If BaseReg = zero, this becomes absolute addressing with Offset
- If DestReg = pcx, this becomes PC-relative addressing - If BaseReg = ret, this becomes return-style addressing
- Conditional jumps check flags in STS register - Conditional jumps check flags in STS register
**Common Patterns:**
```
JMP label, zero ; Absolute jump to label address
JMP 0, ret ; Jump to address in ret register
JMP 4, ret ; Jump to (ret + 4)
```
**Encoding Note:** **Encoding Note:**
In machine code: DestReg field, Offset (Immediate field) (SrcReg unused, set to noreg) In machine code: Offset (Immediate field), BaseReg (SrcReg field) (DestReg unused, set to noreg)
### Comparison ### Comparison
@@ -265,8 +275,8 @@ DestReg and ShiftAmt fields unused (set to noreg and 0)
- Other flags undefined after arithmetic (use CMP for comparisons) - Other flags undefined after arithmetic (use CMP for comparisons)
**Encoding Notes:** **Encoding Notes:**
- INC/DEC: Reg in SrcReg1 field, also copied to DestReg field - INC/DEC: Reg in SrcReg1 field, DestReg set to noreg
- IADD/ISUB: Immediate is signed 16-bit value - IADD/ISUB: Immediate is signed 16-bit value, all three operands required
### Bitwise Logical Operations ### Bitwise Logical Operations
@@ -285,7 +295,7 @@ DestReg and ShiftAmt fields unused (set to noreg and 0)
- Other flags undefined - Other flags undefined
**Encoding Note:** **Encoding Note:**
NOT uses only Src and Dest; SrcReg2 unused (set to noreg) NOT uses only Src (SrcReg1) and Dest (DestReg); SrcReg2 unused (set to noreg)
### Shift Operations ### Shift Operations
@@ -295,17 +305,22 @@ NOT uses only Src and Dest; SrcReg2 unused (set to noreg)
| 0x18 | **SHR** | R | Reg, ShiftAmount | Shift Reg right by ShiftAmount bits<br/>Zero-fill from left (logical shift) | | 0x18 | **SHR** | R | Reg, ShiftAmount | Shift Reg right by ShiftAmount bits<br/>Zero-fill from left (logical shift) |
**Shift Amount:** **Shift Amount:**
- Can be a 5-bit literal (0-31) in ShiftAmt field - **Literal shifts**: ShiftAmount is a 5-bit literal (0-31) in assembly
- Can be a register value (low 5 bits used) - Stored in ShiftAmt field of instruction
- If using register: Place in SrcReg2, set ShiftAmt to 0 - SrcReg2 set to noreg
- If using literal: Place in ShiftAmt field, set SrcReg2 to noreg - **Register shifts**: ShiftAmount is a register containing shift value
- Register specified in SrcReg2 field
- ShiftAmt field must be 0
- Only low 5 bits of register value used
**Note:** Current assembler implementation may only support literal shifts. Check assembler documentation.
**Flag Effects:** **Flag Effects:**
- Zero flag set if result is zero - Zero flag set if result is zero
**Encoding Notes:** **Encoding Notes:**
- Reg in both SrcReg1 and DestReg fields - Reg in both SrcReg1 and DestReg fields (shifted in place)
- For literal shifts: ShiftAmt field contains shift count - For literal shifts: ShiftAmt field contains shift count, SrcReg2 = noreg
- For register shifts: SrcReg2 contains register, ShiftAmt must be 0 - For register shifts: SrcReg2 contains register, ShiftAmt must be 0
### System and Control Instructions ### System and Control Instructions
@@ -331,6 +346,17 @@ NOT uses only Src and Dest; SrcReg2 unused (set to noreg)
- INT: InterruptCode in low 8 bits of Immediate field - INT: InterruptCode in low 8 bits of Immediate field
- IRT/HLT: All register fields set to noreg, ShiftAmt to 0 - IRT/HLT: All register fields set to noreg, ShiftAmt to 0
### Meta Instructions (Assembler/Linker)
These instructions are used by the assembler and linker but may not represent real CPU operations.
| Hex | Mnemonic | Description |
|-----|----------|-------------|
| 0x27 | **SEGMENT** | Segment marker (implementation-specific) |
| 0x3E | **DATA** | Raw data embedding |
**Note:** The SEGMENT instruction opcode may vary between implementations (0x27 in assembler, 0x3F in some contexts). Consult your specific toolchain documentation.
## Instruction Summary Table ## Instruction Summary Table
| Opcode | Mnemonic | Type | Category | | Opcode | Mnemonic | Type | Category |
@@ -374,6 +400,8 @@ NOT uses only Src and Dest; SrcReg2 unused (set to noreg)
| 0x24 | HLT | R | System | | 0x24 | HLT | R | System |
| 0x25 | IADD | I | Arithmetic | | 0x25 | IADD | I | Arithmetic |
| 0x26 | ISUB | I | Arithmetic | | 0x26 | ISUB | I | Arithmetic |
| 0x27 | SEGMENT | - | Meta |
| 0x3E | DATA | - | Meta |
## Exception Conditions ## Exception Conditions
@@ -393,9 +421,9 @@ See the DSA Assembly Language Reference for the complete calling convention and
## Notes on Design ## Notes on Design
1. **Word Size:** All addresses and general computation is 32-bit 1. **Word Size:** All addresses and general computation is 32-bit
2. **Endianness:** Little-endian byte order 2. **Endianness:** Little-endian for instructions and runtime data; assembler data directives may use big-endian
3. **Stack Growth:** Stack grows upward (incrementing addresses) 3. **Stack Growth:** Stack grows **downward** (toward lower addresses) - PUSH decrements SPR
4. **Alignment:** Natural alignment required for halfword and word accesses 4. **Alignment:** Natural alignment required for halfword and word accesses
5. **Sign Extension:** All immediate values are sign-extended unless noted 5. **Sign Extension:** All immediate values are sign-extended unless noted
6. **Zero Register:** Provides constant zero, writes are legal but discarded 6. **Zero Register:** Provides constant zero, writes are legal but discarded
7. **Reserved Encodings:** Opcodes 0x27-0x3F reserved for future use 7. **Reserved Encodings:** Opcodes 0x27-0x3D and 0x3F reserved or implementation-specific
+10 -10
View File
@@ -263,12 +263,12 @@
- [ ] Array syntax - [ ] Array syntax
- [ ] Struct syntax - [ ] Struct syntax
- [x] Pointer syntax - [x] Pointer syntax
- [ ] Namespaced call syntax - [x] Namespaced call syntax
- [x] AST node definitions - [x] AST node definitions
- [ ] Error recovery mechanisms - [ ] Error recovery mechanisms
- [ ] Comprehensive parser tests - [ ] Comprehensive parser tests
- [ ] Syntax error message quality testing - [ ] Syntax error message quality testing
- [ ] Implement C frontend by moving lexer/parser from `c_compiler` to the new `compiler` project structure - [x] Implement C frontend by moving lexer/parser from `c_compiler` to the new `compiler` project structure
- [ ] Evaluate possible memory management strategies (e.g., keep all variables on the stack vs spill only when calling functions) - [ ] Evaluate possible memory management strategies (e.g., keep all variables on the stack vs spill only when calling functions)
--- ---
@@ -290,7 +290,7 @@
- [ ] Optimize register allocation further - [ ] Optimize register allocation further
- [x] Implement proper function calling conventions - [x] Implement proper function calling conventions
- [ ] Add constant folding optimization - [ ] Add constant folding optimization
- [ ] Dead code elimination - [x] Dead code elimination
- [ ] Test each feature thoroughly - [ ] Test each feature thoroughly
--- ---
@@ -376,7 +376,7 @@
**Dependencies:** None **Dependencies:** None
**Deliverable:** `docs/build-system-design.md` **Deliverable:** `docs/build-system-design.md`
- [ ] Define project structure conventions - [x] Define project structure conventions
- [ ] Design build manifest format (`dsa-project.toml` or similar) - [ ] Design build manifest format (`dsa-project.toml` or similar)
- [ ] Dependency resolution strategy - [ ] Dependency resolution strategy
- [ ] Build cache design - [ ] Build cache design
@@ -391,12 +391,12 @@
**Dependencies:** 3.1.1, 1.2.2, 1.1.3, 2.1.3 **Dependencies:** 3.1.1, 1.2.2, 1.1.3, 2.1.3
**Deliverable:** `dsa-build` executable **Deliverable:** `dsa-build` executable
- [ ] Create crate: `dsa-build` - [x] Create crate: `dsa-build`
- [ ] Manifest parser - [ ] Manifest parser
- [ ] Dependency graph builder - [ ] Dependency graph builder
- [ ] Task orchestrator - [ ] Task orchestrator
- [ ] Compilation tasks - [x] Compilation tasks
- [ ] Assembly tasks - [x] Assembly tasks
- [ ] Linking tasks - [ ] Linking tasks
- [ ] Build cache implementation - [ ] Build cache implementation
- [ ] Parallel build support - [ ] Parallel build support
@@ -412,11 +412,11 @@
**Dependencies:** 3.1.2 **Dependencies:** 3.1.2
**Deliverable:** Enhanced `dsa-build` with project management **Deliverable:** Enhanced `dsa-build` with project management
- [ ] `dsa new <project>` — Create new project - [x] `dsa new <project>` — Create new project
- [ ] `dsa init` — Initialize in existing directory - [x] `dsa init` — Initialize in existing directory
- [ ] `dsa add <dependency>` — Add dependency - [ ] `dsa add <dependency>` — Add dependency
- [ ] Binary vs library project types - [ ] Binary vs library project types
- [ ] Template system for project scaffolding - [x] Template system for project scaffolding
- [ ] Documentation for each command - [ ] Documentation for each command
--- ---
+589
View File
@@ -0,0 +1,589 @@
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} "<relative path>"
//
// 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 "<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 {
format!(
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
"#
)
}