Programming

Calculon programs are simply more robust functions that can use loops and generally more logic than a function. Calculon can be programmed using a built-in C-like (but typeless) language. For educational (and debugging) purposes, there also remains the possibility to write Calculon programs in a pseudo assembler language.

Writing programs in Assembler

By default, all code inside a Program is considered to be the preferred, high-level language (not ready yet). However, by using the asm keyword, raw assembler code can be injected. Consider the below example:

<Program Name="Square" Parameters="x">
  asm
  {
    move $0, (x * x)
    ret
  }
</Program>

The above shows how to inject assembler code inside a Program block and also shows how to return a value. After a program finishes, the contents of register $0 are the result of the program. Here, the register is set to the square of the parameter.

The ret instruction immediately exits the program (note that none of the code after the instruction will be executed). Its use is optional, the program will exit when the instruction pointer falls outside the code block.

Also, the above is an example how the Calculon assembler language has some features that a normal assembler language hasn’t: you can use the same expression evaluation to move values in the registers. In fact, there only exists a handful of instructions because maths can be done using the expressions.

Architecture

The virtual machine that runs programs is very simple. There are only handful of instructions (see below) and 16 registers, referred as $0 thru $15.

Each of the registers is a one-dimensional array. The first element (index zero) can be accessed by using $0[0] or $0. Each item in the array can contain a result from an expression. However, inside an expression only the first element can be used. The index value is rounded towards the nearest integer.

A value in a register can have a quantity, similar to the expressions. After the program exits, the contents of register $0[0] are considered the result of the program.

Instruction set

An assembler instruction is of form instruction[.condition] DestinationRegister[DestinationIndex], Expression/SourceRegister[SourceIndex] (except for the jmp, ret and throw instructions). The condition part of the instruction is optional, if left out the instruction will execute at all times. However, if the condition is specified, the instruction will only be executed if the condition matches. The conditions are checked against state flags set by the comparison instruction, cmp.

l Executes if the less than flag is set
g Executes if the greater than flag is set
e Executes if the equal flag is set
le Executes if the less than or the equal flag is set
ge Executes if the greater than or the equal flag is set
nl Executes if the less than flag is not set
ng Executes if the greater than flag is not set
ne Executes if the equal flag is not set
lne Executes if the less than nor the equal flag is set
nge Executes if the greater than nor the equal flag is set
cmp

The cmp instruction compares two values and sets the state flags according to the comparison. The first register is compared against the second register (or expression) and if the first register is less than the second, the less than flag is set.

Here is an example of a simple loop:

  move $0, 10
@again
  cmp $0, 0
  jmp.le exit
  move $0, ($0 - 1)
  jmp again
@exit
  ret

The above simply loops ten times. The loop first checks if the register is less than or equal to zero, and if so, it jumps out of the loop. In other cases it will decrement the register and jump back to the comparison.

jmp

The jmp instruction jumps to a label specified after the instruction. Labels are defined and used as follows:

  jmp skip
  move $3, ($3 + 1)
@skip
  ret

The above jumps over the move instruction.

move

The move instruction moves values in a register. It is also used to update the values. For example, the following increments the first value (see below) in register $3 by one:

move $3, ($3 + 1)

The above accesses only the first item in register $3. Below is an alternative way to do the same:

move $3[0], ($3 + 1)

Note that in the expression, it is not possible to specify an index for the register. Only the first element is accessible. However, the index can be any expression and it can use the values in other registers (the first element).

ret

Stops program execution. The contents of the first element of register $0 is considered the result of the program.

throw

Stops program execution and throws a user specified error.

cmp $0, 0
throw.l "Can't take a square root of a negative number"
mov $0, sqrt $0
trap

When a math operation results in an error, and the trap address has been set with the trap instruction, the execution of the program will not stop. Instead, the execution continues at the address specified.

The below example tries to divide by the argument x and if it fails (i.e. x is zero), it will simply return zero (instead of resulting in an error).

  trap error
  move $0, (1 / x)
  ret
@error
  move $0, 0
  ret

Leave a Reply