# VM This is an outline of the VM that drives this language. # Primitives * Numbers may be big endian (BE) or little endian (LE) at the byte level. This guide will use LE. * Addresses point to single bytes. * Signed numbers use two's complement. | Type | Size (bits) | | - | - | | Address | 64 | | Word | 64 | | Halfword | 32 | | Byte | 8 | # Registers CPU registers are addressed by a value between 0-63 (6 bits). All registers are 64 bits wide. * IP - Instruction pointer * SP - Stack pointer * FP - Frame pointer * FLAGS - CPU flags * (9 unused registers) * STATUS - Generic status code * R0-R49 ## CPU Flags CPU flags are addressed by bit index, going from right to left. * `00` - Halt flag * `01` - Compare flag ### Flag ideas * "Trace" flag - halts the CPU when certain conditions are met that may be causing undesired behavior - for debugging * Overwriting a register without its value being used * Mixing arithmetic with bit twiddling on the same target # Instructions ## Arithmetic Arithmetic instructions store their result in the first register specified. Overflow is handled by wrapping around to 0. * Add * **Params**: REG1, REG2 * `REG1 = REG1 + REG2` * Unsigned addition * Mul * **Params**: REG1, REG2 * `REG1 = REG1 * REG2` * Unsigned multiplication * Div * **Params**: REG1, REG2 * `REG1 = REG1 / REG2` * Unsigned division * Mod * **Params**: REG1, REG2 * `REG1 = REG1 % REG2` (exact semantics TBD) * INeg * **Params**: REG1 * `REG1 = REG1 * -1` * Signed negative * And * **Params**: REG1, REG2 * `REG1 = REG1 & REG2` * Or * **Params**: REG1, REG2 * `REG1 = REG1 | REG2` * Inv * **Params**: REG1 * `REG1 = ~REG1` * Not * **Params**: REG1 * ``` if REG1 == 0 { REG1 = 0; } else { REG1 = 1; } ``` * Boolean NOT; equivalent of C's `!` unary operator * Xor * **Params**: REG1, REG2 * `REG1 = REG1 ^ REG2` * Shl * **Params**: REG1, REG2 * `REG1 = REG1 << REG2` * Shr * **Params**: REG1, REG2 * `REG1 = REG1 >> REG2` * Does not sign extend ### TODO * Add signed instructions (iadd, imul, etc) * Sign-extending SHR * Overflow flag? ## Control flow * CmpEq * **Params**: REG1, REG2 * ``` if REG1 == REG2 { FLAGS[1] = 1; } else { FLAGS[1] = 0; } ``` * Sets the COMPARE flag to 1 if REG1 == REG2 * CmpLt * **Params**: REG1, REG2 * ``` if REG1 < REG2 { FLAGS[1] = 1; } else { FLAGS[1] = 0; } ``` * Sets the COMPARE flag to 1 if REG1 < REG2 * Jz * **Params**: REG1 * ``` if FLAGS[1] == 0 { IP = REG1; } ``` * Jumps to the address in REG1 if COMPARE flag is 0. * Jnz * **Params**: REG1 * ``` if FLAGS[1] != 0 { IP = REG1; } ``` * Jumps to the address in REG1 if COMPARE flag is 1. ## Data movement * Load * **Params**: REG1, REG2 * ``` REG1 = MEM[REG2]; ``` * Sets REG1 to the value at the memory address in REG2. * Store * **Params**: REG1, REG2 * ``` MEM[REG2] = REG1; ``` * Sets the value at the memory address in REG2 to the value in REG1. * StoreImm32 * **Params**: REG1, IMM_32 * `REG1 = IMM_32` * Sets REG1 to the specified 32-bit number. * MemCopy * **Params**: REG1, REG2 * `MEM[REG1] = MEM[REG2]` * Copies the value at the memory address in REG2 to the memory address in REG1. * RegCopy * **Params**: REG1, REG2 * `REG1 = REG2` * Copies the value in REG2 into REG1. ## Miscellaneous * Halt * **Params**: (none) * `FLAGS[0] = 1` * Halts the machine * Nop * **Params**: (none) * Does nothing ## Other instructions TODO * Call * Takes address and number of bytes on the stack that are for args(?) * Updates SP, FP, IP, storing previous values starting at the new FP * Ret * Uses FP to determine previous SP, FP, and IP and restores them * Push * Pop * More immediate stores? # General TODO * Interrupts * MMIO regions * Execution pipeline * Helps to define when certain side effects happen (e.g. when the IP increments) * Paging?