Introduction
This page contains the descriptions for all virtual machine instructions, as well as other details about its operation.
Datatypes
The virtual machine has the following built-in datatypes:
- Integer: 32-bit fixed size integer
- Float: double-precision floating point numbers
- String: immutable character strings (possibly Unicode)
- Block: integer-indexed array of fixed size, no limit on types
- List: cons-based list, no limit on types
- NULL: uninitialized values; most operations will signal specifically that they were used improperly with uninitialized values
Currently booleans are absent because I'm not fully sure how we are treating them.
Blocks are tagged by type:
- 0 - Array
- 1 - Struct
- 2 - Reference
VM Architecture
Each player essentially has a VM to itself. Each VM consists of a stack, a heap, and an instruction set. The VM maintains a instruction pointer to the current instruction, but this is not directly accessible to the VM assembly language. The VM also maintains a stack pointer. The VM also holds an accumulator, which is used to assists in computation for certain instructions.
Call stack
Arguments are passed in reverse order. Thus, the first argument will be at the top of the stack. The REV instruction assists in adjusting the call stack.
Instructions
The accumulator is denoted by ACC. The stack pointer is denoted by SP. The top of the stack is denoted by S0, the next item by S1, etc. The instruction pointer is represented by IP. Optional parameters are shown in italics.
Arithmetic operations
Binary operations
ADD, SUB, MULT, DIV, DIVI, REMI
ACC := ACC [op] S0
Integers are promoted to floats when one of the arguments is a floating point. In division, the arguments are automatically promoted to floating points if the integers are not evenly divisible. DIVI uses C-style integer division with automatic truncation. REMI computes the remainder, using integers.
Unary operations
NEG, FRAC, INT
ACC := [op] ACC
NEG computes the negative of a number. FRAC and INT take the fractional and integer parts of a floating number.
List operations
CONS
ACC := ACC :: S0 S0 must be a valid list.
CAR, CDR
ACC := [op](ACC)
ISNIL
ACC := 1 iff ACC is nil
NIL
ACC := nil Loads the empty list into the accumulator
Array operations
MAKEBLOCK [n]
ACC := new BLOCK[[ACC]]
Creates an block on the accumulator whose size is specified on the accumulator as input and is of type n
MAKEBLOCKSTATIC [n1] [n2]
ACC := new BLOCK[[n2]](S0, S1, ... ,S(n2-1)
Creates a block of size n2 by popping the arguments off the stack. The top of the stack is the element in the block. The created block is of type n1.
MAKEFILLEDBLOCK [n1]
ACC := new BLOCK[[ACC]](S1, S1, ..., s1)
GETELEM
ACC := ACC[[S0]]; SP--
Takes the element from the array in the accumulator at the index specified in the top stack element and puts it in the accumulator. The index is popped from the stack.
GETELEM [n]
ACC := ACC[n]
Like GETELEM, but the index is supplied statically.
SETELEM
ACC[S0] := S1; SP -= 2
Sets the value in the accumulator array at the index on the top of the stack to the next element down in the stack. The index and value are popped from the stack.
SETELEM [n]
ACC[n] := S0; SP -= 1
Like SETELEM, except the index is supplied statically.
Accumulator operations
CONST _[const value]_
ACC := [const value]
The [const value] must be be a primitive data type (int, float, or string). If there is no parameter, the accumulator is set to NULL.
ACC [n]
ACC := S([n])
Stack operations
PUSH
SP++; S0 := ACC
ACC is copied onto the the top of the stack
POP [n] = 1
SP -= n
POP does not affect the accumulator.
SWAP
temp := S0; S0 := ACC; ACC := temp
Swaps the top of the stack and the accumulator.
REV [n]
Reverses the top n elements of the stack.
Comparison operations
LT,GT,LTE,GTE,EQ,NEQ
ACC := 0 if ACC op S0 is false
ACC := 1 if ACC op S0 is true
Label / Jump operations
[label]:
LN([label]) := [current line #]
Alphanumeric (+underscores) label corresponding to the instruction pointer value for that line. Must begin with a letter or underscore. ACC is unchanged.
JMP [label]
IP := LN([label])
Unconditional jump.
The following jumps are conditional jumps. If the value in the accumulator matches the specified condition, the result is IP := LN([label]). Otherwise, IP is unchanged. ACC is always unchanged.
JZ [label] - jump if zero (float or int)
JNZ [label] - jump if non-zero (float or int)
STOP Stops program execution. Normally not used in AI programs, but otherwise good for testing.
Function calls
RETURN
Return from the previously called function. RETURN restores the stack to its previous state.
PUSH_SF [label]
This operation pushes the current stack frame onto the stack. It uses 2 (maybe 3?) stack spaces. First it pushes the return address. Then it pushes the position of the next stack frame.
CALL [label]
[label](S0, S1, ... S(N-1) )
This calls the labeled function with the requisite arguments on the stack. First the return address needs to be pushed onto the stack. Then, arguments are pushed as specified in the stack section. Functions should leave their return values in the accumulator.
APPLY
(ACC)(S0, S1, ..., S(N-1) )
This calls the function whose name is stored in the accumulator. It is otherwise similar to CALL.
RPC [label]
This will call a remote function, using the same stack convention as for normal calls. A remote procedure call pushes the return value onto the stack. The accumulator contains whether or not the RPC was successful.
Actor state access
GETFIELD [n]
puts the the n-th field from the actor state into the accumulator
SETFIELD [n]
sets the n-th field of the actor state to the accumulator
Debugging instructions
DUMP [n]
prints a stack dump to stderr, namely the accumulator and the top n elements of the stack. By default n is zero. If n is greater than the number of elements on the stack, this instruction just prints the whole stack, rather than returning an error.