Files
lopez-repo/blog-post-2.md
2026-03-09 16:53:27 -06:00

136 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Blog Entry #2 — Registers, Addressing Modes & Type Size Modifiers
In this second entry I will cover three fundamental concepts of the
Spider Virtual Machine: the register table, addressing modes, and
type size modifiers. These three systems work together to define how
every single instruction in Spider operates.
---
## Registers
The Spider VM provides two categories of registers.
**General Purpose Registers (16 total)** are directly accessible from
instructions using addressing modes. They can hold either integers or
floats depending on the instruction being executed.
The 16 GP registers have defined roles by convention:
- `RA` — First argument and return value of a function
- `RB`, `RC`, `RD` — Second, third and fourth arguments
- `RX`, `RY` — Free auxiliary registers with no rules
- `R0``R3` — Caller-saved: the called function may overwrite them
- `R4``R7` — Callee-saved: the called function must restore them
- `R8`, `R9` — Fifth and sixth function arguments
**System Registers (8 total)** cannot be accessed directly by addressing
modes. They require dedicated instructions to read or modify them, and
they are always integers.
The most important system registers are:
- `RF` — Flag Register: holds the full state of the VM in 64 bits
- `RI` — Instruction Register: points to the current instruction (program counter)
- `RS` — Stack Register: tracks the current top of the stack
- `RZ` — Stack Base Register: base reference for the current function frame
- `RE` — Exception Register: holds the address of the active exception handler
- `RV` — Interrupt Vector Register: used for both internal and external interrupts
- `RM` — Memory Register: total RAM available to the VM
One of Spider's core design decisions is to prioritize registers over
the stack. Since registers are always available and fast to access,
a well-written Spider program can run with minimal stack usage.
This is especially important on constrained hardware like the ATmega328p,
which only has 2KB of RAM.
---
## Addressing Modes
Each instruction in Spider operates on parameters. An addressing mode
defines **how to interpret** those parameters. The same instruction can
behave very differently depending on the mode.
Spider has 8 addressing modes:
| Mode | Syntax | Meaning |
|-----------|-------------------|------------------------------------------------------|
| Implied | (none) | No parameter needed, operand is implicit |
| Immediate | `42i` | The value is a literal constant |
| Absolute | `0x1000i` | The value is a fixed memory address |
| Register | `RA` | The value is stored in a register |
| Indirect | `[0x1000i]` | Go to that address and use what's stored there |
| Pointer | `[RA]` | The register holds a memory address, follow it |
| Indexed | `[RA + 8i]` | Base register plus a constant offset |
| Scaled | `[RA + RB * 4i]` | Base register plus scaled index register |
| Displaced | `[RA + RB*4i+2i]` | Full address calculation with two registers |
Addressing modes are encoded in 5 bits inside each 2-byte instruction.
When an instruction has two parameters, those 5 bits are split: 2 bits
for the first parameter and 3 bits for the second.
A modifier suffix is used in assembly syntax to specify the mode explicitly:
`.imp`, `.imm`, `.abs`, `.reg`, `.ind`, `.ptr`, `.idx`, `.sca`, `.dis`
For example:
```
MOV.reg RA, RB ; move value from RB into RA using registers
MOV.ptr RA, [RB] ; move value from address stored in RB into RA
MOV.idx RA, [RB + 8i] ; move value from address RB+8 into RA
```
---
## Type Size Modifiers
Every instruction in Spider also carries a **type size modifier** encoded
in the last 2 bits of the 2-byte instruction header. This tells the VM
how many bytes to read or write when executing the instruction.
| Type | Modifier | Size |
|--------|----------|--------|
| Byte | `.B` | 1 byte |
| Short | `.S` | 2 bytes|
| Int | `.I` | 4 bytes|
| Long | `.L` | 8 bytes|
| Float | `.F` | 4 bytes|
| Double | `.D` | 8 bytes|
The type modifier applies to the **entire instruction**, meaning both
operands must be congruent. You cannot mix sizes in a single instruction.
This system is directly tied to Spider's philosophy of **strong typing**:
the programmer always knows exactly how much memory an operation consumes.
There are no hidden conversions or surprises.
Two important behaviors to understand:
**Reading a smaller size** ignores the top bits of the value. For example,
reading a register containing `0x12345678` with `.B` gives you `0x78`.
**Writing a smaller size** only modifies the lower bytes and leaves the
top bits untouched. Writing `0xAB` with `.B` into that same register
gives you `0x123456AB`.
A complete instruction combining all three systems looks like this:
```
MOV.I.reg RA, RB ; move a 4-byte integer from RB into RA
MOV.B.ptr RA, [RB] ; move 1 byte from the address in RB into RA
ADD.L.idx RA, [RB + 8i] ; add 8-byte value at address RB+8 into RA
```
---
## How these three systems connect
These three concepts are not independent. Every instruction in Spider
uses all three simultaneously:
- The **op code** (9 bits) says what to do
- The **addressing mode** (5 bits) says where the data is
- The **type modifier** (2 bits) says how big the data is
Together they fit in exactly 2 bytes per instruction, followed by the
actual parameter data. This compact design is what makes Spider efficient
enough to run on microcontrollers with very limited memory.