5.8 KiB
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 functionRB,RC,RD— Second, third and fourth argumentsRX,RY— Free auxiliary registers with no rulesR0–R3— Caller-saved: the called function may overwrite themR4–R7— Callee-saved: the called function must restore themR8,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 bitsRI— Instruction Register: points to the current instruction (program counter)RS— Stack Register: tracks the current top of the stackRZ— Stack Base Register: base reference for the current function frameRE— Exception Register: holds the address of the active exception handlerRV— Interrupt Vector Register: used for both internal and external interruptsRM— 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.