# DSA Assembly Language Reference ## Overview This document is the comprehensive reference for writing DSA assembly programs. It covers assembly syntax, pseudo-instructions, directives, the module system, calling conventions, and provides complete examples. **Related Documents:** - For hardware instruction details and encoding: See *DSA ISA Specification* - For build system and toolchain: See project documentation ## Assembly Syntax ### General Rules - **Case Insensitive:** Mnemonics can be uppercase or lowercase (`mov` = `MOV`) - **Comments:** Use `//` for line comments or `/* */` for block comments - **Labels:** Identifier followed by colon (e.g., `main:`, `loop:`) - **Whitespace:** Flexible spacing between operands - **Numbers:** - Decimal: `100`, `255` - Hexadecimal: `0x10`, `0xFFFF` - Binary: `0b1010` (if supported by assembler) ### Operand Order Convention DSA assembly uses **GAS-style syntax** (source → destination): ```asm mov rg0, rg1 ; Copy rg0 TO rg1 (destination is last) add rg0, rg1, rg2 ; rg2 = rg0 + rg1 (destination is last) ``` For load/store with immediates: ```asm lli 0x1234, rg0 ; Load immediate 0x1234 INTO rg0 ldw rg0, rg1, 8 ; Load from (rg0+8) INTO rg1 stw rg0, rg1, 8 ; Store rg0 TO address (rg1+8) ``` ## Registers | Register(s) | Type | Description | Usage Notes | |-------------|------|-------------|-------------| | **rg0-rgf** | General | 16 general-purpose registers | Use for variables, temporaries | | **acc** | Special | Accumulator | ⚠️ Volatile - pseudo-instructions may overwrite | | **spr** | Special | Stack pointer | Points to top of stack | | **bpr** | Special | Base pointer | Used for stack frames | | **ret** | Special | Return address | Holds return address for functions | | **zero** | Read-only | Always zero | Reads return 0, writes discarded | | **pcx** | Read-only | Program counter | Cannot be written directly | | **idr** | Privileged | Interrupt descriptor table | Kernel mode only | | **mmr** | Privileged | Memory map register | Kernel mode only | | **noreg** | Placeholder | No register | Used in encoding, triggers fault if accessed | **Register Conventions:** - **acc**: Used by pseudo-instructions for temporary values - do not rely on it being preserved - **rgf**: Used by label-addressing pseudo-instructions as a scratch register - **rg0-rge**: Available for general use; calling convention defines which are preserved ## Hardware Instructions This section shows assembly syntax. For encoding details, see the ISA Specification. ### Data Movement ```asm mov src_reg, dest_reg ; Copy value from src_reg to dest_reg movs src_reg, dest_reg ; Copy with sign extension ``` **Examples:** ```asm mov rg0, rg1 ; rg1 = rg0 movs acc, rg2 ; rg2 = sign_extend(acc) ``` ### Memory Load Instructions ```asm ldb base_reg, dest_reg [, offset] ; Load byte (zero-extend) ldbs base_reg, dest_reg [, offset] ; Load byte (sign-extend) ldh base_reg, dest_reg [, offset] ; Load halfword (zero-extend) ldhs base_reg, dest_reg [, offset] ; Load halfword (sign-extend) ldw base_reg, dest_reg [, offset] ; Load word ``` **Offset:** Optional signed 16-bit offset (defaults to 0) **Examples:** ```asm ldb rg0, rg1 ; Load byte from address in rg0 ldw rg0, rg1, 8 ; Load word from (rg0 + 8) ldhs rg2, rg3, -4 ; Load signed halfword from (rg2 - 4) ``` **Alignment Requirements:** - `ldb/ldbs`: No alignment required - `ldh/ldhs`: Must be 2-byte aligned - `ldw`: Must be 4-byte aligned ### Memory Store Instructions ```asm stb src_reg, base_reg [, offset] ; Store byte sth src_reg, base_reg [, offset] ; Store halfword stw src_reg, base_reg [, offset] ; Store word ``` **Examples:** ```asm stb rg0, rg1 ; Store byte to address in rg1 stw rg0, rg1, 12 ; Store word to (rg1 + 12) sth acc, spr, -2 ; Store halfword to (spr - 2) ``` **Alignment Requirements:** Same as loads ### Immediate Load Instructions ```asm lli immediate, dest_reg ; Load lower 16 bits (CLEARS upper 16!) lui immediate, dest_reg ; Load upper 16 bits (preserves lower 16) ``` **⚠️ CRITICAL:** `lli` clears the upper 16 bits! Always use `lli` before `lui`. **Loading 32-bit Constants:** ```asm lli 0x1234, rg0 ; rg0 = 0x00001234 lui 0xABCD, rg0 ; rg0 = 0xABCD1234 ``` **Loading Addresses:** See `lwi` pseudo-instruction ### Jump and Branch Instructions ```asm jmp addr [, offset_reg] ; Unconditional jump jeq addr [, offset_reg] ; Jump if equal jne addr [, offset_reg] ; Jump if not equal jgt addr [, offset_reg] ; Jump if greater than jge addr [, offset_reg] ; Jump if greater or equal jlt addr [, offset_reg] ; Jump if less than jle addr [, offset_reg] ; Jump if less or equal ``` **Jump Modes:** ```asm ; Absolute jump (using zero register) jmp label, zero ; Jump to label address jmp 0x4000, zero ; Jump to absolute address 0x4000 ; Register-based jump jmp 0, ret ; Jump to address in ret register jmp 4, ret ; Jump to (ret + 4) ; PC-relative (if assembler supports) jeq loop_start ; Jump to loop_start if equal flag set ``` **Conditional Jumps:** Based on flags set by `cmp` instruction ### Comparison ```asm cmp reg1, reg2 ; Compare reg1 with reg2, set flags ``` **Flags Set:** - Equal: `reg1 == reg2` - GreaterThan: `reg1 > reg2` - LessThan: `reg1 < reg2` - GreaterThanOrEqual: `reg1 >= reg2` - LessThanOrEqual: `reg1 <= reg2` **Example:** ```asm cmp rg0, zero ; Compare rg0 with 0 jeq is_zero ; Branch if rg0 == 0 jgt is_positive ; Branch if rg0 > 0 jlt is_negative ; Branch if rg0 < 0 ``` ### Arithmetic Instructions ```asm add src1, src2, dest ; dest = src1 + src2 sub src1, src2, dest ; dest = src1 - src2 iadd src, immediate, dest ; dest = src + immediate isub src, immediate, dest ; dest = src - immediate inc reg ; reg = reg + 1 dec reg ; reg = reg - 1 ``` **Examples:** ```asm add rg0, rg1, rg2 ; rg2 = rg0 + rg1 sub rg0, rg1, rg2 ; rg2 = rg0 - rg1 iadd rg0, 10, rg0 ; rg0 = rg0 + 10 isub rg1, 5, rg2 ; rg2 = rg1 - 5 inc spr ; spr = spr + 1 dec spr ; spr = spr - 1 ``` **Note:** For `iadd`/`isub`, destination can be the same as source for in-place operations. ### Bitwise Logical Operations ```asm and src1, src2, dest ; dest = src1 & src2 or src1, src2, dest ; dest = src1 | src2 xor src1, src2, dest ; dest = src1 ^ src2 not src, dest ; dest = ~src nand src1, src2, dest ; dest = ~(src1 & src2) nor src1, src2, dest ; dest = ~(src1 | src2) xnor src1, src2, dest ; dest = ~(src1 ^ src2) ``` **Examples:** ```asm and rg0, rg1, rg2 ; rg2 = rg0 & rg1 or rg0, rg1, rg2 ; rg2 = rg0 | rg1 not rg0, rg1 ; rg1 = ~rg0 xor rg0, rg0, rg0 ; rg0 = 0 (XOR register with itself) ``` ### Shift Operations ```asm shl reg, shift_amount ; Shift left by amount (0-31) shr reg, shift_amount ; Shift right by amount (0-31) ``` **Shift Amount:** - Can be a literal: `shl rg0, 2` (shift by 2) - Can be a register: `shl rg0, rg1` (shift by value in rg1, uses low 5 bits) **Examples:** ```asm shl rg0, 2 ; rg0 = rg0 << 2 shr rg1, 3 ; rg1 = rg1 >> 3 shl rg0, rg1 ; rg0 = rg0 << (rg1 & 0x1F) ``` **Note:** Shift right is logical (zero-fill), not arithmetic ### System and Control Instructions ```asm hlt ; Halt processor nop ; No operation int interrupt_code ; Trigger interrupt (8-bit code) irt ; Return from interrupt ``` **Examples:** ```asm hlt ; Stop execution nop ; Do nothing (timing/alignment) int 0x21 ; Trigger interrupt 0x21 irt ; Return from interrupt handler ``` ## Pseudo-Instructions Pseudo-instructions are assembly-level constructs that expand into one or more hardware instructions. ### Data Definition Directives ```asm db label: value1 [, value2, ...] ; Define bytes dh label: value1 [, value2, ...] ; Define halfwords (16-bit) dw label: value1 [, value2, ...] ; Define words (32-bit) ``` **Examples:** ```asm db message: "Hello, World!", 0 ; String with null terminator db bytes: 0x01, 0x02, 0x03 ; Array of bytes dh numbers: 1000, 2000, 3000 ; Array of halfwords dw stack_base: 0x10000 ; Single word value dw table: 0, 0, 0, 0 ; Array of 4 words ``` **String Encoding:** Strings are encoded as byte sequences with escape sequences: - `\n` = newline (0x0A) - `\t` = tab (0x09) - `\r` = carriage return (0x0D) - `\\` = backslash - `\"` = double quote - `\0` = null (0x00) ### Memory Reservation Directives ```asm resb label: size ; Reserve 'size' bytes resh label: size ; Reserve 'size' halfwords resw label: size ; Reserve 'size' words ``` **Examples:** ```asm resb buffer: 256 ; Reserve 256 bytes resh array: 100 ; Reserve 100 halfwords (200 bytes) resw heap: 1024 ; Reserve 1024 words (4096 bytes) ``` **Note:** Reserved memory is uninitialized (contents undefined). ### Stack Operations ```asm push reg ; Push register onto stack pop reg ; Pop stack into register ``` **Expansion:** ```asm ; push rg0 expands to: iadd spr, 4, spr ; spr = spr + 4 (stack grows up) stw rg0, spr, 0 ; Store rg0 to [spr] ; pop rg0 expands to: ldw spr, rg0, 0 ; Load [spr] into rg0 isub spr, 4, spr ; spr = spr - 4 ``` **Note:** DSA stack grows upward (toward higher addresses). **Examples:** ```asm push rg0 ; Save rg0 on stack push rg1 ; Save rg1 on stack ; ... do work ... pop rg1 ; Restore rg1 pop rg0 ; Restore rg0 ``` ### Load Address Pseudo-Instruction ```asm lwi label, dest_reg ; Load address of label into register ``` **Expansion:** ```asm ; lwi message, rg0 expands to: lli message, rg0 ; Load lower 16 bits of address lui message, rg0 ; Load upper 16 bits of address ``` **Example:** ```asm db message: "Hello!", 0 lwi message, rg0 ; rg0 = address of message ldb rg0, rg1 ; rg1 = first byte of message ('H') ``` ### Memory Access with Labels Load and store instructions can use labels directly: ```asm ldb label, dest_reg [, offset] ldh label, dest_reg [, offset] ldw label, dest_reg [, offset] stb src_reg, label [, offset] sth src_reg, label [, offset] stw src_reg, label [, offset] ``` **Expansion (uses rgf as scratch):** ```asm ; ldb buffer, rg2 expands to: lli buffer, rgf ; Load lower 16 bits of buffer address lui buffer, rgf ; Load upper 16 bits of buffer address ldb rgf, rg2, 0 ; Load byte from address in rgf ; stw rg1, current expands to: lli current, rgf ; Load lower 16 bits of current address lui current, rgf ; Load upper 16 bits of current address stw rg1, rgf, 0 ; Store word to address in rgf ``` **⚠️ Important:** These pseudo-instructions use `rgf` as a scratch register! Do not use `rgf` for other purposes when using label-based memory access. **Examples:** ```asm dw counter: 0 ldw counter, rg0 ; Load value of counter iadd rg0, 1, rg0 ; Increment stw rg0, counter ; Store back ``` ### Function Call Pseudo-Instructions ```asm call namespace::function ; Call function from included module return ; Return from function ``` **Expansion:** ```asm ; call print::print expands to: lwi print::print, ret ; Load function address into ret jmp 0, ret ; Jump to function (saves return in pcx) ; (The assembler/linker resolves namespace::function to address) ; return expands to: jmp 0, ret ; Jump to address in ret register ``` **Note:** The actual return address handling may be more complex depending on the calling convention. ### Module System ```asm include namespace "path/to/file.dsa" ``` **Example:** ```asm include print "lib/print.dsa" include math "lib/math.dsa" ; Can now call: call print::print call math::multiply ``` **Namespace Resolution:** - Functions in included modules are accessible via `namespace::label` - Namespace is the identifier before the filename - Labels in included files are prefixed with the namespace ## Calling Convention DSA uses a standard calling convention for function calls. ### Stack Frame Layout ``` Higher Addresses ├─────────────┤ │ Arg N │ ← spr + (8 + 4*(N-1)) │ ... │ │ Arg 2 │ ← spr + 16 │ Arg 1 │ ← spr + 12 │ Arg 0 │ ← spr + 8 (first argument) ├─────────────┤ │ Ret Addr │ ← spr + 4 (return address) ├─────────────┤ │ Old BPR │ ← spr + 0 (saved base pointer) ├─────────────┤ ← bpr, spr (current frame) │ Locals │ (local variables, if any) Lower Addresses ``` ### Calling Sequence **Caller Responsibilities:** 1. **Push arguments in reverse order** (last argument first): ```asm push arg2 push arg1 push arg0 ``` 2. **Call the function:** ```asm call namespace::function ``` 3. **Clean up arguments** after return: ```asm pop zero ; Discard or retrieve arg0 pop zero ; Discard arg1 pop zero ; Discard arg2 ``` **Callee Responsibilities:** 1. **Set up stack frame:** ```asm function: push bpr ; Save old base pointer mov spr, bpr ; Establish new base pointer ``` 2. **Access arguments:** ```asm ldw bpr, rg0, 8 ; Load arg0 from spr+8 ldw bpr, rg1, 12 ; Load arg1 from spr+12 ldw bpr, rg2, 16 ; Load arg2 from spr+16 ``` 3. **Execute function body:** ```asm ; Function logic here add rg0, rg1, acc ; Example: acc = arg0 + arg1 ``` 4. **Store return value** (optional, overwrites arg0): ```asm stw acc, bpr, 8 ; Store result where arg0 was ``` 5. **Restore stack frame:** ```asm mov bpr, spr ; Restore stack pointer pop bpr ; Restore old base pointer ``` 6. **Return to caller:** ```asm return ``` ### Complete Example ```asm ; Function: add two numbers ; Args: arg0, arg1 ; Returns: sum in arg0 position add_function: push bpr ; Save base pointer mov spr, bpr ; Set up stack frame ldw bpr, rg0, 8 ; Load arg0 ldw bpr, rg1, 12 ; Load arg1 add rg0, rg1, acc ; acc = arg0 + arg1 stw acc, bpr, 8 ; Store result mov bpr, spr ; Restore stack pop bpr ; Restore base pointer return ; Caller: main: lwi stack_base, bpr mov bpr, spr lli 5, rg0 lli 7, rg1 push rg1 ; Push arg1 (7) push rg0 ; Push arg0 (5) call local::add_function pop rg2 ; Get result (12) pop zero ; Discard arg1 hlt dw stack_base: 0x10000 ``` ### Register Usage Conventions | Register(s) | Usage | Preserved? | |-------------|-------|------------| | rg0-rg3 | Function arguments, temporaries | No (caller-saved) | | rg4-rge | Local variables | Yes (callee-saved if used) | | rgf | Scratch (used by label addressing) | No | | acc | Temporary calculations | No | | spr | Stack pointer | Yes (must be restored) | | bpr | Base pointer | Yes (must be restored) | | ret | Return address | Managed by call/return | **Notes:** - Functions should save and restore rg4-rge if they use them - rg0-rg3 may be overwritten by called functions - acc and rgf are volatile - assume they're overwritten ## Complete Examples ### Example 1: Multiplication Library ```asm // multiply.dsa // Multiplies two numbers using repeated addition // // Usage: // include multiply "multiply.dsa" // push arg1 // push arg0 // call multiply::multiply // pop result // pop zero ; discard second argument multiply: push bpr mov spr, bpr ldw bpr, rg0, 8 ; Load multiplier ldw bpr, rg1, 12 ; Load multiplicand lli 0, acc ; Initialize result to 0 loop_start: add acc, rg0, acc ; acc += multiplier dec rg1 ; multiplicand-- cmp rg1, zero jgt loop_start ; Continue if multiplicand > 0 stw acc, bpr, 8 ; Store result for caller mov bpr, spr pop bpr return ``` ### Example 2: Print Library ```asm // print.dsa // Prints null-terminated string to display memory // // Usage: // include print "print.dsa" // // push string_address // call print::print // pop zero // // call print::reset ; Reset cursor (no args) dw display: 0x20000 ; Display memory base address dw current: 0x20000 ; Current cursor position // Print function print: push bpr mov spr, bpr ldw bpr, rg0, 8 ; Get string address argument ldw current, rg1 ; Get current cursor position print_loop: ldb rg0, acc ; Load character stb acc, rg1 ; Store to display iadd rg0, 1, rg0 ; Advance string pointer iadd rg1, 1, rg1 ; Advance cursor cmp acc, zero ; Check for null terminator jne print_loop ; Continue if not null stw rg1, current ; Save cursor position mov bpr, spr pop bpr return // Reset cursor function reset: push bpr mov spr, bpr ldw display, rg1 ; Load display base stw rg1, current ; Reset cursor to start mov bpr, spr pop bpr return ``` ### Example 3: Main Program ```asm // main.dsa // Demonstrates using included libraries include print "./print.dsa" dw stack: 0x10000 db string: "'To confuse your enemy, you must first confuse yourself' - Probably Sun Tzu.", 0 init: // Set up stack ldw stack, bpr mov bpr, spr start: // Load string address lwi string, rg1 // Call print function push rg1 call print::print pop rg1 ; Clean up (rg1 now contains arg we passed) hlt ``` ### Example 4: Conditional Logic ```asm // Demonstrates comparisons and branching dw value: 42 main: ldw value, rg0 cmp rg0, zero jeq is_zero jgt is_positive jlt is_negative is_zero: // Handle zero case lwi zero_msg, rg1 jmp print_and_exit is_positive: // Handle positive case lwi positive_msg, rg1 jmp print_and_exit is_negative: // Handle negative case lwi negative_msg, rg1 jmp print_and_exit print_and_exit: push rg1 call print::print pop zero hlt db zero_msg: "Value is zero", 0 db positive_msg: "Value is positive", 0 db negative_msg: "Value is negative", 0 ``` ### Example 5: Loop with Counter ```asm // Count from 0 to 9 dw stack: 0x10000 main: ldw stack, bpr mov bpr, spr lli 0, rg0 ; Counter = 0 lli 10, rg1 ; Limit = 10 loop: // Do something with counter in rg0 push rg0 call process_value pop zero inc rg0 ; Counter++ cmp rg0, rg1 ; Compare with limit jlt loop ; Loop if counter < limit hlt process_value: push bpr mov spr, bpr ldw bpr, rg0, 8 ; Get value ; Process value here... mov bpr, spr pop bpr return ``` ## Best Practices ### 1. Stack Management - Always balance push/pop operations - Set up stack frame in every function - Clean up arguments after function calls - Use `pop zero` to discard unwanted values ### 2. Register Usage - Don't rely on `acc` being preserved - Don't use `rgf` for variables (used by label addressing) - Save callee-saved registers if you modify them - Use `zero` register for zero constants ### 3. Memory Access - Ensure proper alignment for halfword/word access - Use label-based addressing for clearer code - Check that labels are defined before use ### 4. Function Design - Document calling convention in comments - Validate input arguments when appropriate - Use consistent parameter order - Return values via stack or designated register ### 5. Code Organization - Use meaningful label names - Comment complex operations - Group related functions in modules - Use includes for code reuse ### 6. Performance - Minimize memory accesses (use registers) - Avoid unnecessary comparisons - Use shifts for multiplication/division by powers of 2 - Consider instruction pipelining if supported ## Assembler Directives ### Alignment (if supported) ```asm .align 4 ; Align to 4-byte boundary .align 2 ; Align to 2-byte boundary ``` ### Origin (if supported) ```asm .org 0x1000 ; Set location counter to 0x1000 ``` ### Section Control (if supported) ```asm .text ; Code section .data ; Data section .bss ; Uninitialized data section ``` **Note:** Assembler directive support depends on the specific DSA assembler implementation. ## Common Patterns ### Loading 32-bit Constants ```asm lli lower_16_bits, reg lui upper_16_bits, reg ``` ### Zero a Register ```asm mov zero, reg ; Method 1 xor reg, reg, reg ; Method 2 lli 0, reg ; Method 3 ``` ### Copy Memory ```asm ldw src_addr, rg0 ; Load from source stw rg0, dest_addr ; Store to destination ``` ### Multiply by Power of 2 ```asm shl reg, 3 ; Multiply by 8 (2^3) ``` ### Divide by Power of 2 ```asm shr reg, 2 ; Divide by 4 (2^2) ``` ### Boolean NOT ```asm cmp reg, zero jeq was_zero ; If reg == 0, result is 1 lli 0, reg jmp done was_zero: lli 1, reg done: ``` ### Min/Max ```asm ; max(rg0, rg1) -> rg2 mov rg0, rg2 ; Assume rg0 is max cmp rg0, rg1 jge done mov rg1, rg2 ; rg1 was larger done: ``` ## Troubleshooting ### Common Errors **Alignment Fault:** - Check that halfword loads/stores use even addresses - Check that word loads/stores use addresses divisible by 4 **Illegal Instruction:** - Verify opcode is valid - Check that shift amount is 0 for non-shift instructions - Ensure you're not using `noreg` as a source/destination **Stack Corruption:** - Verify push/pop balance - Check that functions restore `bpr` before returning - Ensure caller cleans up arguments **Wrong Results:** - Verify `lli` is called before `lui` when loading constants - Check that you're not relying on `acc` or `rgf` being preserved - Verify signed vs. unsigned loads (ldb vs. ldbs) ### Debugging Tips 1. Add `nop` instructions as breakpoint markers 2. Print register values using display memory 3. Use single-step execution to trace program flow 4. Verify stack pointer values at function boundaries 5. Check label addresses in disassembly ## Appendix: Instruction Quick Reference | Category | Instructions | |----------|-------------| | **Data Movement** | mov, movs | | **Memory Load** | ldb, ldbs, ldh, ldhs, ldw | | **Memory Store** | stb, sth, stw | | **Immediate Load** | lli, lui | | **Jump/Branch** | jmp, jeq, jne, jgt, jge, jlt, jle | | **Comparison** | cmp | | **Arithmetic** | add, sub, iadd, isub, inc, dec | | **Logical** | and, or, xor, not, nand, nor, xnor | | **Shift** | shl, shr | | **System** | hlt, nop, int, irt | | **Pseudo** | db, dh, dw, resb, resh, resw, push, pop, lwi, call, return, include | ## Version History - **v1.0** - Initial comprehensive reference - Combined hardware instructions and pseudo-instructions - Added complete calling convention - Included practical examples - Documented common patterns and best practices