VM instructions

[ Back ]

When you compile a NaaLaa program, all your code is converted into simple instructions that the NaaLaa virtual machine (VM) can interpret. When Marcus was working on the Raycasting library, he realized that the compiler wasn't always that smart. Yes, he thought that he, the almighty one, could make a better job himself (which is funny, as he was the one who wrote the compiler in the first place). He therefore made some of the VM instructions available for himself and all other NaaLaa programmers.

Commands and registers

Almost every instruction consists of a command, a destination and a source. The destination is usually a register or a variable. The source is often a register, a variable or a constant. You already know what variables and constants are. Registers can be used as temporary variables. There are "a couple" of registers, and you can use five of them without worries. They're referred to as @0 .. @4. The registers are a little faster than variables (especially variables in arrays and local variables ... and references to local array elements).

There's a command named MOV, that moves a value from one place to another. So let's say you wanted to swap the values of two variables, an_int and another_int. You'd probably write:

tmp = an_int
an_int = another_int
another_int = tmp

But this generates a whole bunch of instructions. With the MOV instruction you could use a register as a temporary variable and write:

MOV @0 an_int
MOV an_int another_int
MOV another_int @0

, which is a lot faster.

The stack

Besides from the registers, there's also a stack that can be used as temporary storage. You can push values from variables, registers and constants to this stack, and you can pop values from the stack to variables and registers. You do so with the PSH and POP commands.

Arrays

Many commands let you operate on array elements. However, you can not use the arrays directly. First you need to load an array into a register. What you actually load is a pointer to the first element of the array. You load an array to a register with the normal MOV command. You can then use a command named STP to move forward one or more elements in the array. To set or access the variable that a pointer points at you use square brackets around the register identifier. Ex:

rem Create an array.
my_array[] = [1, 2, 3, 4]
rem Load array into register 0.
MOV @0 my_array
rem Step to last element of array.
STP @0 3
rem Change the value of the element to 42.
MOV [@0] 42
wln my_array[3]

Jumping to labels

There are no loops or selection statements when you work with VM instructions. Instead you do unconditional and conditional jumps to labels. With the CMP command, you can compare a value (variable, register ...) with another value. This command sets a piece of information somewhere that you can use to do a conditional jump. There are many types of jump commands. JLE, for example, does a jump to the specified label only if the first value passed to the CMP command was less than or equal to the second value. You create a label with the @ character followed by a name and a colon. In this example we go through a loop 1000 times:

rem Use register 0 as a counter, set it to 1.
MOV @0 1
rem Our loop label.
@my_loop:
  rem Increase counter by 1.
  ADD @0 1
  rem Compare counter with 1000.
  CMP @0 1000
rem Jump to my_loop if counter is less than or equal to 1000.
JLE my_loop

What about floating points?

Most of the commands listed below work with floating points aswell. A register can store a floating point value, and with some tweaking you can put floats on the stack aswell. To use floating point values you have to add a # character to the command. It will work on most commands but not on all (seriously, the Creator isn't quite sure about the commands that wont work with floating point values, so you'll simply have to test). Here's an example of a loop, like the one above, but with a floating point counter:

MOV# @0 1.0
@my_float_loop:
  ADD# @0 0.5
  CMP# @0 1000.0
JLE# my_float_loop

Instructions

MOV dst src
STP src
ADD dst src
SUB dst src
MUL dst src
DIV dst src
MOD dst src
PSH src
POP dst
CMP dst, src
JMP label
JE label
JNE label
JG label
JGE label
JL label
JLE label
ITF reg
FTI reg

MOV dst src

Move value of src to dst.


STP reg src

Step with src in array reg.


ADD dst src

Add src to dst.


SUB dst src

Subtract src from dst.


MUL dst src

Mutiply dst with src.


DIV dst src

Divide dst with src.


MOD dst src

Let dst become dst mod src.


PSH src

Push value of src to stack.


POP dst

Pop value from stack to dst.


CMP dst, src

Compare dst with src.


JMP label

Jump to label.


JE label

Jump to label if dst was equal to src in last CMP.


JNE label

Jump to label if dst was not equal to src in last CMP.


JG label

Jump to label if dst was greater than src in last CMP.


JGE label

Jump to label if dst was greater than or equal to src in last CMP.


JL label

Jump to label if dst was less than src in last CMP.


JLE label

Jump to label if dst was less than or equal to src in last CMP.


ITF reg

Convert integer value in register reg to float.


FTI reg

Convert float value in register reg to integer.


[ Back ]