This page details the instruction set of the Cord virtual machine project.

Instruction formats

All instructions are 16-bit words, little-endian and naturally aligned. Addresses within instructions are effectively 15 bits: they are encoded with an implicit 0 in the low bit.

p is the program counter.

Name Binary encoding Notes
imm8, opcode 000yyyyy xxxxxxxx Push 8-bit signed immediate x to data stack, then execute opcode y.
opcode, opcode 001yyyyy xxxxxxxx Execute opcode x and then opcode y.
call 01xxxxxx xxxxxxxx Push p to return stack and set p to x.
jmp0 10xxxxxx xxxxxxxx Pop data stack, then set p to x if the value was 0.
jmp 11xxxxxx xxxxxxxx Set p to x.

Note that in the opcode, opcode format x can range from [0, 255]. As we only specify 32 opcodes, this leaves 224 for an optional extensions system (to be specified).

Opcode table

io . dup drop swap over push pop
+ - * /mod u/mod << >> u>>
and or xor not < u< ex ;
w@ s@ c@ w! s! c! w@p s@p

Opcode details

p is the program counter. x and y represent arbitrary 32-bit integers on the data stack.

By design there is no undefined behavior in the instruction set.

Name Inputs Outputs Notes
io varies varies Initiate I/O. 0 io (encoded $0000) is a halt instruction: thus, executing uninitialized memory will halt.
.     No-op.
dup x x x  
drop x   Discard value.
swap x y y x  
over x y x y x  
push x   Push value to return stack.
pop   x Pop value from return stack.
+ x y x+y  
- x y x-y  
* x y x*y Non-widening multiply: works for either unsigned or signed inputs.
/mod x y x%y x/y Signed, truncated modulo and division. Division by 0 results in x -1. Dividing the most negative integer $80000000 by -1 results in 0 $80000000.
u/mod x y x%y x/y Unsigned, truncated modulo and division. Division by 0 results in x -1. Unsigned division cannot overflow.
<< x y x<<y Left shift. If y >​= 32, results in 0. (y is treated as an unsigned value for all shift opcodes.)
>> x y x>>y Signed right shift. If y >​= 32, results in -1 for negative x and 0 otherwise.
u>> x y x>>y Unsigned right shift. If y >​= 32, results in 0.
and x y x&y Bitwise and.
or x y x∣y Bitwise or.
xor x y x^y Bitwise xor.
not x ~x Bitwise not.
< x y x<y If x < y, push -1 to data stack. Otherwise, push 0. x and y are signed.
u< x y x<y If x < y, push -1 to data stack. Otherwise, push 0. x and y are unsigned.
ex     The value at the top of the return stack is swapped with p. A function whose address is on the data stack can be called via push ex.
;     Return from function. Pops return stack into p.
w@ x [x] 32-bit (“word”) load from address x. (Note: Cord is byte-addressed, and addresses that would be out of bounds wrap around.)
s@ x [x] 16-bit (“short”) sign-extending load.
c@ x [x] 8-bit (“char”) sign-extending load.
w! x y   32-bit store (value x into memory address y).
s! x y   16-bit store.
c! x y   8-bit store.
w@p   x 32-bit signed immediate.
s@p   x 16-bit signed immediate.

Prior art

A short and incomplete list of hardware that inspired this instruction set:

Special thanks

Thanks to Ethan Black for his useful comments and suggestions. Check out his SubSky project if you’re interested in a minimalist RISC VM.

✏ Edited