MagnumVM is a custom process virtual machine. It was made for queer_hack 2021, where it was given 1st Place (wow, nice). My submission is here.
(This image is not made by me and is licensed under CC-BY. Unfortunately there is not a bisexual version.)
- MagnumVM: a virtual machine for executing Magna (binary executable) files.
- Scriba: a library for writing Magna files.
- Magnum Common: a library with common constants.
MagnumVM targets a custom binary executable format called Magna. This format should have the file extension .magna.
Magna is loosely inspired by ELF.
| Offset | Size (bytes) | Description |
|---|---|---|
0x0 |
3 | MVM in ASCII/0x4D564D in hex; the file signature/magic number. |
0x3 |
1 | 0x0; the target version of MagnumVM. |
0x4 |
8 | 0x35; starting location (in bytes) of text section. |
0xC |
8 | Size (in bytes) of text section. |
0x14 |
8 | Location of read-only section. |
0x1C |
8 | Size of read-only section. |
0x24 |
8 | Location of initialized writable section. |
0x2C |
8 | Size of initialized writable section. |
0x34 |
8 | Size of uninitialized writable memory. |
The text section is right after the header, so it should always have the offset 0x35. This section contains the actual instructions to be executed by MagnumVM. Each instruction is 32-bits.
The read only section is after the text section. This section is intended for non-executable read-only data, such as constants.
The initialized writable section is after the read-only section. This section is intended for non-executable, writable initialized data, such as global variables.
Because uninitialized writable data is uninitialized, it does not need a section, but it does need a specified size for runtime.
Memory is divided into two pools: text and data. In the future, it could also have a heap, but implementing a garbage collector is a lot of work.
The text pool of memory is read-only. The data section of memory is divided into the read-only section, the global writable section, and the stack.
Each instruction is 32-bits, with the first 8 being the opcode. Note all of this is little endian.
| Instruction | Opcode | Arguments | Description |
|---|---|---|---|
nop |
0x00 |
N/A | No operation. |
loadi_b |
0x01 |
Immediate (value): inst[0..7] |
Loads immediate byte onto stack. |
loadi_2b |
0x02 |
Immediate (value): inst[0..15] |
Loads immediate double byte onto stack. |
loadi_4b |
0x03 |
Immediate (value): inst[0..23] |
Loads immediate quadruple byte onto stack. Sign not carried. |
loadi_8b |
0x04 |
Immediate (value): inst[0..23] |
Loads immediate octuple byte onto stack. Sign not carried. |
load |
0x10 |
Immediate (size): inst[16..23], immediate (address): inst[0..15] |
Loads a byte/bytes from memory address onto stack. |
loads |
0x11 |
Immediate (size): inst[16..23], immediate (offset): inst[0..15] |
Loads a byte/bytes from offset of stack base onto stack. |
store |
0x15 |
Immediate (size): inst[16..23], immediate (address): inst[0..15] |
Store a byte/bytes from stack (popping them) to memory location. |
pop |
0x20 |
Immediate (amount of bytes): inst[0..7] |
Pops a byte/bytes from stack. |
func_b |
0x30 |
Function (opcode): inst[0..7] |
Treats byte(s) from stack as u8 and performs function from values (see functions README section). |
sys |
0x40 |
System call (opcode): inst[0..7] |
Performs system call (see system calls README section). |
hlt |
0x50 |
N/A | Halts execution. |
| Function | Function opcode | Description |
|---|---|---|
add |
0x00 |
Addition. |
sub |
0x01 |
Subtraction. |
mul |
0x02 |
Multiplication. |
div |
0x03 |
Division. |
| System call | System call opcode | Description |
|---|---|---|
put_b |
0x00 |
Prints value of byte as a u8. |
put_c |
0x10 |
Prints byte as an ASCII char. |
- Eric Schneider
