# 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.