Files
2026-02-07 18:21:37 +00:00

1200 lines
33 KiB
Markdown

# 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** - Used as scratch by pseudo-instructions |
| **spr** | Special | Stack pointer | Points to current top of stack |
| **bpr** | Special | Base pointer | Used for stack frames |
| **ret** | Special | Return address | Holds function return addresses |
| **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 |
**Alternative Names:**
- **noreg** can also be referenced as **null** in assembly
**Register Conventions:**
- **acc**: Volatile scratch register used by pseudo-instructions - **never preserved across pseudo-ops**
- **rg0-rge**: Available for general use; calling convention defines preservation rules
- **rgf**: General purpose register, available for use
**⚠️ CRITICAL:** The `acc` register is used internally by label-based memory operations and other pseudo-instructions. Do not assume its value is preserved across any pseudo-instruction!
## 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
```
**Note:** The assembler may process the immediate value for `lui` - specify the upper 16 bits directly (e.g., `lui 0xABCD, rg0` to set upper bits to 0xABCD).
**Loading Addresses:** See `lwi` pseudo-instruction
### Jump and Branch Instructions
```asm
jmp offset, base_reg ; Unconditional jump to (base_reg + offset)
jeq offset, base_reg ; Jump if equal
jne offset, base_reg ; Jump if not equal
jgt offset, base_reg ; Jump if greater than
jge offset, base_reg ; Jump if greater or equal
jlt offset, base_reg ; Jump if less than
jle offset, base_reg ; Jump if less or equal
```
**Target Address Calculation:**
```
target = base_reg + sign_extend(offset)
```
**Jump Modes:**
```asm
; Absolute jump (using zero register)
jmp label, zero ; Jump to label address
; Register-based jump
jmp 0, ret ; Jump to address in ret register
jmp 4, ret ; Jump to (ret + 4)
; PC-relative (if assembler supports label resolution)
jeq loop_start, zero ; Jump to loop_start if equal flag set
```
**Conditional Jumps:** Based on flags set by `cmp` instruction
**Examples:**
```asm
jmp start, zero ; Absolute jump to 'start'
jmp 4, ret ; Jump to (ret + 4)
jeq end, zero ; Jump to 'end' if equal flag set
jgt loop, zero ; Jump to 'loop' if greater than flag set
```
### Comparison
```asm
cmp reg1, reg2 ; Compare reg1 with reg2, set flags
```
**Flags Set:**
- Equal: `reg1 == reg2`
- GreaterThan: `reg1 > reg2` (signed comparison)
- LessThan: `reg1 < reg2` (signed comparison)
- GreaterThanOrEqual: `reg1 >= reg2`
- LessThanOrEqual: `reg1 <= reg2`
**Example:**
```asm
cmp rg0, zero ; Compare rg0 with 0
jeq is_zero, zero ; Branch if rg0 == 0
jgt is_positive, zero ; Branch if rg0 > 0
jlt is_negative, zero ; 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 (DEST REQUIRED!)
isub src, immediate, dest ; dest = src - immediate (DEST REQUIRED!)
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 (in-place)
iadd rg0, 10, rg1 ; rg1 = rg0 + 10 (separate dest)
isub rg1, 5, rg2 ; rg2 = rg1 - 5
inc spr ; spr = spr + 1
dec spr ; spr = spr - 1
```
**⚠️ Note:** For `iadd`/`isub`, the destination register is **required** (not optional). For in-place operations, specify the source register as both source and destination.
### 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:**
- **Literal:** `shl rg0, 2` - shift by constant 2
- **Register:** `shl rg0, rg1` - shift by value in rg1 (low 5 bits)
**⚠️ Note:** Current assembler may only support literal shift amounts. Check your assembler documentation for register shift support.
**Examples:**
```asm
shl rg0, 2 ; rg0 = rg0 << 2
shr rg1, 3 ; rg1 = rg1 >> 3
; shl rg0, rg1 ; May not be supported by assembler
```
**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.
**⚠️ IMPORTANT:** Many pseudo-instructions use the `acc` register as a scratch register. The value in `acc` is **not preserved** across these operations!
### 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 automatically null-terminated and encoded as byte sequences with escape sequences:
- `\n` = newline (0x0A)
- `\t` = tab (0x09)
- `\r` = carriage return (0x0D)
- `\\` = backslash
- `\"` = double quote
- `\0` = null (0x00)
**⚠️ Endianness Note:** Numeric values in data directives are stored in **big-endian** format by the assembler, unlike the little-endian used for instructions.
### 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
```
**⚠️ CRITICAL - Stack Direction:** The DSA stack grows **DOWNWARD** (toward lower memory addresses).
**Expansion:**
```asm
; push rg0 expands to:
subi spr, 4, spr ; spr = spr - 4 (allocate space, stack grows down)
stw rg0, spr, 0 ; Store rg0 to [spr]
; pop rg0 expands to:
ldw spr, rg0, 0 ; Load [spr] into rg0
addi spr, 4, spr ; spr = spr + 4 (deallocate space)
```
**Examples:**
```asm
push rg0 ; Save rg0 on stack
push rg1 ; Save rg1 on stack
; ... do work ...
pop rg1 ; Restore rg1
pop rg0 ; Restore rg0
```
**Stack Diagram:**
```
Higher Memory Addresses
│ (old data)
├─────────────┤
│ rg0 value │ ← After first push
├─────────────┤ ← SPR after first push
│ rg1 value │ ← After second push
├─────────────┤ ← SPR after second push (stack grew down)
Lower Memory Addresses
```
### Push/Pop Multiple Registers
```asm
pusha count ; Push first 'count' general registers (rg0-rgN)
popa count ; Pop first 'count' general registers
```
**Examples:**
```asm
pusha 4 ; Push rg0, rg1, rg2, rg3
; ... do work ...
popa 4 ; Pop rg3, rg2, rg1, rg0
```
**Expansion:**
```asm
; pusha 3 expands to:
subi spr, 12, spr ; Allocate space for 3 words
stw rg0, spr, 0 ; Store rg0
stw rg1, spr, 4 ; Store rg1
stw rg2, spr, 8 ; Store rg2
; popa 3 expands to:
ldw spr, rg0, 0 ; Load rg0
ldw spr, rg1, 4 ; Load rg1
ldw spr, rg2, 8 ; Load rg2
addi spr, 12, spr ; Deallocate space
```
**Note:** Registers are pushed/popped in order (rg0, rg1, rg2, ...).
### 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, 0 ; 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]
```
**⚠️ CRITICAL:** These pseudo-instructions use `acc` as a scratch register! The value in `acc` will be overwritten.
**Expansion:**
```asm
; ldw buffer, rg2 expands to:
lli buffer, acc ; Load lower 16 bits of buffer address into acc
lui buffer, acc ; Load upper 16 bits of buffer address into acc
ldw acc, rg2, 0 ; Load word from address in acc into rg2
; stw rg1, current expands to:
lli current, acc ; Load lower 16 bits of current address into acc
lui current, acc ; Load upper 16 bits of current address into acc
stw rg1, acc, 0 ; Store word from rg1 to address in acc
```
**Examples:**
```asm
dw counter: 0
ldw counter, rg0 ; Load value of counter (clobbers acc!)
addi rg0, 1, rg0 ; Increment
stw rg0, counter ; Store back (clobbers acc!)
```
**⚠️ Warning Example - ACC Clobbering:**
```asm
lli 100, acc ; acc = 100
ldw data, rg0 ; ACC IS NOW OVERWRITTEN!
; acc now contains garbage (address calculation temp)
; The value 100 is LOST!
```
# DSA Assembly Language Reference - Part 2
## Function Call Pseudo-Instructions
```asm
call namespace::function ; Call function from included module
return ; Return from function
```
**⚠️ CRITICAL:** The calling mechanism uses the **STACK** to store return addresses, not just the `ret` register!
**CALL Expansion:**
```asm
; call print::print expands to:
subi spr, 4, spr ; Decrement stack pointer (allocate space)
stw pcx, spr, 0 ; Store return address (PCX) on stack
jmp function_addr, zero ; Jump to function address
```
**RETURN Expansion:**
```asm
; return expands to:
ldw spr, ret, 0 ; Load return address from stack into ret
addi spr, 4, spr ; Increment stack pointer (deallocate)
jmp 4, ret ; Jump to (ret + 4)
```
**Note on RETURN +4 Offset:** The implementation adds 4 to the return address. This may be to account for instruction sizing or pipeline considerations. Consult CPU documentation if modifying.
### 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 stack-based calling convention with downward-growing stack.
### Stack Frame Layout
**⚠️ CRITICAL:** Stack grows DOWNWARD (toward lower addresses)!
```
Higher Memory Addresses ↑
┌─────────────┐
│ Caller's │
│ Frame │
└─────────────┘
│ Arg N │ ← Caller pushed (highest address argument)
│ ... │
│ Arg 2 │
│ Arg 1 │
│ Arg 0 │ ← First argument (lowest address argument)
├─────────────┤
│ Ret Addr │ ← Return address (pushed by CALL)
├─────────────┤
│ Old BPR │ ← Saved base pointer (pushed by callee)
├─────────────┤ ← BPR (current base), SPR (current top)
│ Locals │ ← Local variables (if any)
│ ... │
└─────────────┘ ← SPR grows downward
Lower Memory Addresses ↓
```
**Frame Pointer Offsets** (from BPR):
```
bpr + 0 = Old BPR (saved by function prologue)
bpr + 4 = Return address (pushed by CALL)
bpr + 8 = Argument 0 (first argument)
bpr + 12 = Argument 1
bpr + 16 = Argument 2
bpr + 8 + 4*N = Argument N
```
### Calling Sequence
**Caller Responsibilities:**
1. **Push arguments in reverse order** (last argument first):
```asm
push arg2 ; Push last argument first
push arg1
push arg0 ; First argument pushed last
```
2. **Call the function:**
```asm
call namespace::function ; Pushes return address, jumps to function
```
3. **Clean up arguments** after return:
```asm
pop zero ; Discard arg0 (or pop rg0 to get return value)
pop zero ; Discard arg1
pop zero ; Discard arg2
```
**Callee Responsibilities:**
1. **Set up stack frame:**
```asm
function:
push bpr ; Save caller's base pointer
mov spr, bpr ; Establish new base pointer
```
2. **Access arguments:**
```asm
ldw bpr, rg0, 8 ; Load arg0 from bpr+8
ldw bpr, rg1, 12 ; Load arg1 from bpr+12
ldw bpr, rg2, 16 ; Load arg2 from bpr+16
```
3. **Execute function body:**
```asm
; Function logic here
add rg0, rg1, acc ; Example: acc = arg0 + arg1
```
4. **Store return value** (optional - overwrites arg0 location):
```asm
stw acc, bpr, 8 ; Store result where arg0 was
```
5. **Restore stack frame:**
```asm
mov bpr, spr ; Restore stack pointer to base
pop bpr ; Restore caller's base pointer
```
6. **Return to caller:**
```asm
return ; Pop return address and jump
```
### Stack Evolution During Call
**Before CALL:**
```
┌─────────────┐
│ Arg 2 │
│ Arg 1 │
│ Arg 0 │ ← SPR points here
└─────────────┘
```
**After CALL (before function prologue):**
```
┌─────────────┐
│ Arg 2 │
│ Arg 1 │
│ Arg 0 │
│ Ret Addr │ ← SPR points here (CALL pushed this)
└─────────────┘
```
**After Function Prologue:**
```
┌─────────────┐
│ Arg 2 │
│ Arg 1 │
│ Arg 0 │
│ Ret Addr │
│ Old BPR │ ← SPR and BPR point here
└─────────────┘
```
### Complete Calling Example
```asm
; Function: add two numbers
; Args: arg0, arg1
; Returns: sum (overwrites arg0 position)
add_function:
; Prologue
push bpr ; Save caller's base pointer
mov spr, bpr ; Set up our stack frame
; Load arguments
ldw bpr, rg0, 8 ; rg0 = arg0
ldw bpr, rg1, 12 ; rg1 = arg1
; Perform operation
add rg0, rg1, acc ; acc = arg0 + arg1
; Store return value (overwrites arg0 position)
stw acc, bpr, 8 ; Store result at bpr+8
; Epilogue
mov bpr, spr ; Restore stack pointer
pop bpr ; Restore caller's base pointer
return ; Return to caller
; Caller example:
main:
; Set up stack
lwi stack_base, bpr
mov bpr, spr
; Prepare arguments
lli 5, rg0 ; First argument = 5
lli 7, rg1 ; Second argument = 7
; Push arguments (reverse order!)
push rg1 ; Push arg1 (7) first
push rg0 ; Push arg0 (5) second
; Call function
call local::add_function
; Retrieve result and clean up
pop rg2 ; Get result (12) - was at arg0 position
pop zero ; Discard arg1 slot
hlt
dw stack_base: 0x10000
```
### Register Usage Conventions
| Register(s) | Usage | Preserved Across Calls? |
|-------------|-------|------------------------|
| **rg0-rg3** | Function arguments, temporaries | No (caller-saved) |
| **rg4-rge** | Local variables | Conditional (callee-saved if used) |
| **rgf** | General purpose | Conditional (callee-saved if used) |
| **acc** | Temporary calculations, scratch | **Never** (volatile) |
| **spr** | Stack pointer | Yes (must be restored) |
| **bpr** | Base pointer | Yes (must be restored) |
| **ret** | Return address | Managed by call/return |
**Preservation Rules:**
- **Caller-saved (rg0-rg3, acc):** Caller must save these before calling if needed after
- **Callee-saved (rg4-rgf):** Callee must save/restore if it uses them
- **Always preserved (spr, bpr):** Must be restored to original values
- **Never preserved (acc):** Assume destroyed by any operation
**Example - Callee-Saved Registers:**
```asm
my_function:
push bpr
mov spr, bpr
; We want to use rg4 and rg5 - must save them!
push rg4
push rg5
; Use rg4 and rg5 for local work
lli 100, rg4
lli 200, rg5
add rg4, rg5, acc
; Restore callee-saved registers before returning
pop rg5
pop rg4
mov bpr, spr
pop bpr
return
```
## Complete Examples
### Example 1: Multiplication Library
```asm
// multiply.dsa
// Multiplies two numbers using repeated addition
//
// Usage:
// include multiply "multiply.dsa"
// push multiplicand
// push multiplier
// call multiply::multiply
// pop result ; Result is in arg0 position
// pop zero ; Clean up arg1
multiply:
push bpr
mov spr, bpr
ldw bpr, rg0, 8 ; Load multiplier (arg0)
ldw bpr, rg1, 12 ; Load multiplicand (arg1)
lli 0, acc ; Initialize result to 0
loop_start:
add acc, rg0, acc ; acc += multiplier
dec rg1 ; multiplicand--
cmp rg1, zero
jgt loop_start, zero ; Continue if multiplicand > 0
stw acc, bpr, 8 ; Store result for caller (at arg0)
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 (clobbers acc!)
print_loop:
ldb rg0, acc, 0 ; Load character
stb acc, rg1, 0 ; Store to display
addi rg0, 1, rg0 ; Advance string pointer
addi rg1, 1, rg1 ; Advance cursor
cmp acc, zero ; Check for null terminator
jne print_loop, zero ; Continue if not null
stw rg1, current ; Save cursor position (clobbers acc!)
mov bpr, spr
pop bpr
return
// Reset cursor function
reset:
push bpr
mov spr, bpr
ldw display, rg1 ; Load display base (clobbers acc!)
stw rg1, current ; Reset cursor to start (clobbers acc!)
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 ; Load stack base (clobbers acc!)
mov bpr, spr
start:
// Load string address
lwi string, rg1 ; Load address of string
// Call print function
push rg1
call print::print
pop rg1 ; Clean up (or pop zero to discard)
hlt
```
### Example 4: Conditional Logic
```asm
// Demonstrates comparisons and branching
dw value: 42
main:
ldw value, rg0 ; Load value (clobbers acc!)
cmp rg0, zero
jeq is_zero, zero
jgt is_positive, zero
jlt is_negative, zero
is_zero:
lwi zero_msg, rg1
jmp print_and_exit, zero
is_positive:
lwi positive_msg, rg1
jmp print_and_exit, zero
is_negative:
lwi negative_msg, rg1
jmp print_and_exit, zero
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 ; Set up stack
mov bpr, spr
lli 0, rg0 ; Counter = 0
lli 10, rg1 ; Limit = 10
loop:
// Process counter value
push rg0
call process_value
pop zero
inc rg0 ; Counter++
cmp rg0, rg1 ; Compare with limit
jlt loop, zero ; 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** in the same function
- Set up stack frame (`push bpr; mov spr, bpr`) in every function
- Clean up arguments after function calls (caller's responsibility)
- Use `pop zero` to discard unwanted values
- Remember: stack grows DOWNWARD
### 2. Register Usage
- **Never rely on `acc` being preserved** across ANY operation
- Treat `acc` as write-only temporary storage
- Save caller-saved registers (rg0-rg3) before calls if needed
- Restore callee-saved registers (rg4-rgf) if modified
- Use `zero` register for zero constants
### 3. Memory Access
- Ensure proper alignment for halfword/word access
- Be aware that label-based loads/stores clobber `acc`
- Prefer register-based addressing when `acc` value matters
- Check that labels are defined before use
### 4. Function Design
- **Document calling convention** in function comments
- Always include prologue and epilogue
- Validate input arguments when appropriate
- Use consistent parameter order across related functions
- Return values via stack (overwrite arg0) or designated register
### 5. Code Organization
- Use meaningful label names (e.g., `loop_start` not `l1`)
- Comment complex operations
- Group related functions in modules
- Use includes for code reuse
- Keep functions focused and small
### 6. Performance Considerations
- Minimize memory accesses (use registers when possible)
- Avoid unnecessary comparisons
- Use shifts for multiplication/division by powers of 2:
```asm
shl rg0, 3 ; Multiply by 8 (2^3)
shr rg0, 2 ; Divide by 4 (2^2)
```
- Consider instruction pipelining if CPU supports it
### 7. ACC Register Awareness
**⚠️ CRITICAL - Common Mistakes:**
```asm
; WRONG - acc gets clobbered:
lli 100, acc
ldw data, rg0 ; acc is now GARBAGE!
add acc, rg0, rg1 ; Using garbage value!
; RIGHT - use a different register:
lli 100, rg2
ldw data, rg0 ; acc clobbered (don't care)
add rg2, rg0, rg1 ; rg2 still has 100
; WRONG - assuming acc preserved across call:
lli 42, acc
call some_function
add acc, rg0, rg1 ; acc probably destroyed!
; RIGHT - use caller-saved register:
lli 42, rg0
call some_function
add rg0, rg1, rg2 ; rg0 might be destroyed, so save it first!
```
## Common Patterns
### Loading 32-bit Constants
```asm
lli lower_16_bits, reg
lui upper_16_bits, reg
; Example:
lli 0x1234, rg0 ; rg0 = 0x00001234
lui 0xABCD, rg0 ; rg0 = 0xABCD1234
```
### Zero a Register
```asm
mov zero, reg ; Method 1 (preferred - clearest)
xor reg, reg, reg ; Method 2 (clever but less clear)
lli 0, reg ; Method 3 (works but wastes upper bits clear)
```
### Copy Memory
```asm
ldw src_addr, rg0 ; Load from source (clobbers acc)
stw rg0, dest_addr, 0 ; Store to destination (clobbers acc)
; If you need acc preserved:
lli 0x1234, acc ; Some value in acc
push acc ; Save it
ldw src_addr, rg0 ; acc clobbered
stw rg0, dest_addr, 0 ; acc clobbered again
pop acc ; Restore acc
```
### Multiply/Divide by Power of 2
```asm
shl reg, 3 ; Multiply by 8 (2^3)
shr reg, 2 ; Divide by 4 (2^2)
```
### Boolean NOT
```asm
cmp reg, zero
jeq was_zero, zero ; If reg == 0, result is 1
lli 0, reg
jmp done, zero
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, zero ; If rg0 >= rg1, we're done
mov rg1, rg2 ; rg1 was larger
done:
```
### Array Indexing
```asm
; Access array[i] where array is 32-bit words
; rg0 = array base address
; rg1 = index i
; Result in rg2
shl rg1, 2 ; Convert index to byte offset (i * 4)
add rg0, rg1, acc ; acc = base + offset (use acc for temp)
ldw acc, rg2, 0 ; Load array[i]
```
## Troubleshooting
### Common Errors
**Alignment Fault:**
- Symptom: Exception when loading/storing halfword or word
- Cause: Address not properly aligned
- Fix: Ensure halfword addresses are even, word addresses divisible by 4
**Illegal Instruction:**
- Symptom: Unexpected halt or exception
- Cause: Invalid opcode or malformed instruction
- Fix: Check assembly syntax, verify instruction encoding
**Stack Corruption:**
- Symptom: Function returns to wrong address, registers have wrong values
- Cause: Unbalanced push/pop, incorrect stack frame management
- Fix:
- Verify push/pop balance
- Check function epilogue restores bpr
- Ensure caller cleans up arguments
- Verify stack grows downward (push decrements)
**Wrong Results / Unexpected Values:**
- Symptom: Calculations produce incorrect results
- Cause: ACC clobbering, wrong register assumptions
- Fix:
- Check if acc was assumed preserved (it never is!)
- Verify lli called before lui for 32-bit constants
- Check signed vs unsigned loads (ldb vs ldbs)
- Verify register preservation across calls
**Label Not Found:**
- Symptom: Assembler error about undefined label
- Cause: Label used before definition, typo in label name
- Fix: Define labels before use, check spelling
### Debugging Tips
1. **Add NOP markers** at key points for breakpoints:
```asm
nop ; Breakpoint here
```
2. **Print register values** using display memory:
```asm
stw rg0, debug_out ; Store to known location
```
3. **Use single-step execution** to trace program flow
4. **Verify stack pointer** values at function boundaries:
```asm
; After prologue, check:
; spr == bpr (should point to same location)
; After epilogue, check:
; spr restored to entry value
```
5. **Check label addresses** in disassembly output
6. **Trace ACC usage**:
```asm
lli 0xDEAD, acc ; Marker value
; ... your code ...
cmp acc, zero
jeq acc_was_clobbered, zero ; If acc==0, it was overwritten
```
## 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 - Data** | db, dh, dw, resb, resh, resw |
| **Pseudo - Stack** | push, pop, pusha, popa |
| **Pseudo - Memory** | lwi, ldb/ldh/ldw with labels, stb/sth/stw with labels |
| **Pseudo - Control** | call, return |
| **Pseudo - Module** | include |
## Version History
- **v2.0** - Corrected comprehensive reference based on implementation
- **CORRECTED:** Stack grows downward (not upward)
- **CORRECTED:** CALL/RETURN use stack for return addresses
- **CORRECTED:** Pseudo-instructions use ACC as scratch (not rgf)
- **CORRECTED:** Label loads use ACC for address calculation
- **CORRECTED:** IADD/ISUB require destination (not optional)
- **ADDED:** PUSHA/POPA documentation
- **ADDED:** Detailed ACC volatility warnings throughout
- **ADDED:** Stack evolution diagrams
- **CLARIFIED:** Big-endian data directive encoding
- **CLARIFIED:** Jump instruction semantics
- All examples updated to reflect correct stack direction
- Added extensive troubleshooting for ACC-related issues