Soil consists of three parts of state: registers, memory, and byte code.
Soil is not a von Neumann machine – byte code and memory live in separate worlds. Byte code can only read/write the memory, not byte code itself. You can't reflect on the byte code itself, for example, to store pointers to instructions. This gives Soil implementations the freedom to compile the byte code to machine code on startup.
Soil binaries are files that contain byte code and initial memory.
Soil uses two types of integers. A byte is 8 bits and a word is 8 bytes. Data types in Soil are little endian. Registers and pointers are word-sized.
Soil has 8 registers, all of which hold 64 bits.
name | description |
---|---|
sp |
stack pointer |
st |
status register |
a |
general-purpose register |
b |
general-purpose register |
c |
general-purpose register |
d |
general-purpose register |
e |
general-purpose register |
f |
general-purpose register |
Initially, sp
holds the memory size.
All other registers are initialized to zero.
Soil uses byte-addressed memory. The memory size is defined by the VM implementation.
The Soil VM is sandboxed and only allows interactions with the outside world via system calls ("syscalls"). Syscalls are identified by a byte-sized syscall number. The canonical set of syscalls is defined in another part of the specification.
Byte code consists of a sequence of instructions.
Soil executes the instructions in sequence, starting from the first. Some instructions alter control flow by jumping to other instructions.
These are all instructions:
Does nothing.
End program execution with an error.
If a panic occurs, catches it, resets sp
, and jumps to the catch
address.
Ends a scope started by trystart
.
Sets to
to from
.
Sets to
to from
.
Sets to
to from
.
Interprets from
as an address and sets to
to the word at that address in memory.
Interprets from
as an address and sets to
to the byte at that address in memory.
Interprets to
as an address and sets the 64 bits at that address in memory to from
.
Interprets to
as an address and sets the 8 bits at that address in memory to from
.
Decreases sp
by 8, then runs store sp reg
.
Runs load reg sp
, then increases sp
by 8.
Continues executing at the to
th byte.
Runs jump to
if st
is not 0.
Runs jump target
. Saves the formerly next instruction on an internal stack so that ret
returns.
Returns to the instruction after the matching call
.
Performs a syscall. Behavior depends on the syscall. The syscall can access all registers and memory.
Saves left
- right
in st
.
If st
is 0, sets st
to 1, otherwise to 0.
If st
is less than 0, sets st
to 1; otherwise, sets it to 0.
If st
is greater than 0, sets st
to 1; otherwise, sets it to 0.
If st
is 0 or less, sets st
to 1; otherwise, sets it to 0.
If st
is 0 or greater, sets st
to 1; otherwise, sets it to 0.
If st
is 0, sets st
to 0; otherwise, sets it to 1.
Compares left
and right
by subtracting right
from left
and saving the result in st
.
If st
is 0, sets st
to 1; otherwise, sets it to 0.
If st
is less than 0, sets st
to 1; otherwise, sets it to 0.
If st
is greater than 0, sets st
to 1; otherwise, sets it to 0.
If st
is 0 or less, sets st
to 1; otherwise, sets it to 0.
If st
is 0 or greater, sets st
to 1; otherwise, sets it to 0.
If st
is 0, sets st
to 0; otherwise, sets it to 1.
Interprets reg
as an integer and sets it to a float of about the same value. TODO: specify edge cases.
Interprets reg
as a float and sets it to its integer representation, rounded down. TODO: specify edge cases.
Adds from
to to
.
Subtracts from
from to
.
Multiplies from
and to
, saving the result in to
.
Divides dividend
by divisor
, saving the quotient in dividend
.
Divides dividend
by divisor
, saving the remainder in dividend
.
Adds from
to to
, interpreting both as floats.
Subtracts from
from to
, interpreting both as floats.
Multiplies from
and to
, interpreting both as floats, and saves the result in to
.
Divides dividend
by divisor
, interpreting both as floats, and saves the quotient in dividend
.
Performs a binary AND on to
and from
, saving the result in to
.
Performs a binary OR on to
and from
, saving the result in to
.
Performs a binary XOR on to
and from
, saving the result in to
.
Inverts the bits of to
.