Soil
A low-level VM
When implementing my programming language Martinaise, I was faced with a difficult challenge. I wanted to achieve both of these goals simultaneously:
Performance: Martinaise programs should be (nearly) as efficient as writing the corresponding machine code. Until now, this was achieved by allowing the developer to write functions in platform-specific assembly.
Platform Independence: All Martinaise code should be platform independent. So, just writing assembly doesn't work.
The solution is pretty obvious:
Any problem in computer science can be solved with another layer of indirection. [...]
So, I invented Soil.
Similar to fantasy consoles such as PICO-8, Soil is a specification for a VM. It is on a similar abstraction level as existing CPU instruction sets: There are registers, memory, and instructions.
Registers have sane names (
a
,b
,c
,d
,e
,f
, stack pointersp
, status registerst
).Instructions are low-level and operate on registers. Soil follows the tradition of RISC instruction sets – there are only a few simple instructions that compose instead of many specialized instructions. For example, jumping based on whether a value is negative is encoded using these instructions:
cmp a 0 isless cjump label
Memory has a fixed size. Code can just access any address within that range.
Because Soil is low-level, it's easy to write interpreters. There already exist a few implementations written in C, Zig, Dart, Rust, and x86_64 assembly.
Now, I can write Martinaise functions in platform-independent Soil assembly:
fun memcopy(from: Address, to: Address, amount: Int) asm {
moveib a 8 add a sp load a a | from
moveib b 16 add b sp load b b | to
moveib c 24 add c sp load c c | amount
moveib e 1
cmp a b isless cjump .right_to_left
.left_to_right: ..loop:
move st c isequal cjump .done
loadb d a storeb b d
add a e add b e sub c e
jump ..loop
.right_to_left:
add a c add b c sub a e sub b e | make a and b point to the last byte
..loop:
move st c isequal cjump .done
loadb d a storeb b d
sub a e sub b e sub c e
jump ..loop
.done: ret
}