IRIS/IRIS++ | Game Development Programming Language

Top Top | Previous Prev | Next Next

Language Specifications


The programming language is made up of one or more statements. A statement can look like one of the following:

// Add 5 to register 0
REG $0
// Print out the value of register 0 on screen
ATB $0

Each statement is made up of instructions, values (strings and integers), objects, commands, and separators. Strings are described by enclosing it with inverted commas (""). You may use \n to insert a carriage return. An integer is a 32 bit decimal value. An instruction is a defined keyword that tells the interpretor to execute a particular set of functions. For instance, ATR substitutes a value into the specified register. Values can be any literal data or registers containing a value. See Registers for more information. Separators are represented by spaces and is used to separate the instruction and values. A statement can also contain comments, represented by double slashes //. Comments will not be interpreted by IRIS and will be deleted during compilation.

// This is a comment
// Comments will not be interpreted

An object is represented by a leading underscore (_) and any child objects or commands are separated by a dot(.) . e.g. _gameEngine.initialize


Registers store the byte position of the loaded code in memory (except for numbers - are stored directly in the registers). Registers can be treated like variables with its values changing in real time. It can store string of characters and numeric values (but limited to a 32 bit integer). In IRIS, you can use up to 256 registers (0 -> 255) each represented by a dollar ($) sign in front of the register number (e.g. $23).

Instruction Sets

NOPNo Operation: VM will do nothing for this instruction
CLRClear Register: Clears the specified register contents
ATRAssign To Register: Assigns a value (either a byte, string or register) to the specified register
HLTHalt: Stops script execution
PTXPrint Text: Outputs a text on screen
ATBAssign To Buffer: Same as ATR, except the value is stored in a string buffer prepared by the VM
JMPJump: Jumps to a label
JMSJump To Subroutine: Same as JMP, but the program counter position is remembered
RETReturn: Returns the program counter back to where JMS is called
SUMSum: Addition of values or registers
SBTSubtract: Subtraction of values or registers
MLTMultiply: Multiplication of values or registers
DIVDivide: Division of values or registers
SKZSkip If Zero: If the specified register value was zero, skips the next instruction
SKLSkip If Less: If the specified register value was less than the specified value, skips the next instruction
SKGSkip If Greater: If the specified register value was greater than the specified value, skips the next instruction
SKESkip If Equal: If the specified register value was equal to the specified value, skips the next instruction
SKNSkip If Not: If the specified register value was not the specified value, skips the next instruction
RUNRun: Runs an external script
IMPImport: Imports an external script (Only used in compilation stage)
REGRegister: Defines which register we are going to use for the coming instructions
CLBClear Buffer: Set an empty string to the internal buffer
PTBPrint Buffer: Prints contents in the internal buffer
ENGUse Engine: Tells the VM to use a specified engine
@...:Defines a subroutine: ... is the subroutine name (e.g. @init:)

Using The Macro

IRIS has 2 types of compile-time modifiers that allow you to replace part of the code with something you have defined. "pre_define.h" file contains definitions of a Macro, a syntax similar to the C language. The replacement occurs before any compilation. "post_define.xml" defines a replacement list in XML form and is called when compiling to byte code.



#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480

Macro file is read from the top of file until it finds the definition. You will need to arrange the definition orders accordingly.

[Erroneous Definition]

// We want to call ID_CHARACTER, but the string ID_CHARACTER exists in the first definition,
// so the latter will not be read
#define ID_CHARACTER_1 1
#define ID_CHARACTER 0

[Re-ordered Definition]

// We want to call ID_CHARACTER, and ID_CHARACTER exists in the first definition,
// so the latter will not be read
#define ID_CHARACTER 0
#define ID_CHARACTER_1 1



    <source>do {</source><target>%BREAK%%INC_AUTO_NUM%@condition_label_%FILE_PATH%_%AUTO_NUM%:</target>
    <source>} until</source><target>%BREAK%REG</target>
    <source> loop</source><target>%BREAK%JMP condition_label_%FILE_PATH%_%AUTO_NUM%%DEREF_AUTO_NUM%</target>
    <source>function </source><target>%INC_AUTO_NUM%JMP condition_label_%FILE_PATH%_%AUTO_NUM%%BREAK%@</target>
    <source>() {</source><target>:</target>
    <source>if(</source><target>%BREAK%%INC_AUTO_NUM%REG </target>
    <source>) {</source><target>%BREAK%JMP condition_label_%FILE_PATH%_%AUTO_NUM%</target>

    <source>++</source><target> SUM 1</target>
    <source>--</source><target> SBT 1</target>
    <source>let </source><target>REG </target>
    <source>exec </source><target>RUN </target>
    <source>goto </source><target>JMP </target>
    <source>call </source><target>JMS </target>
      ... etc


Defines the encryption key to encrypt the compiled binary code. Code is encrypted by XOR-ing the byte code with this encryption key.

[irisc::source_code::source], [irisc::source_code::target]

String defined in source will be replaced by target during compilation. There are special compiler variables you can use to control the replacement behavior.

%BREAK%Inserts Carriage Return
%INC_AUTO_NUM%Increment an auto number (internal)
%AUTO_NUM%Inserts the current auto number
%DEREF_AUTO_NUM%Delete auto number reference (internal)
%FILE_PATH%Inserts the current file path
%EMPTY%Inserts empty string (nothing is displayed)
%SPACE%Inserts a single space

[irisc::engine::object::source], [irisc::engine::object::target]

String defined in source will be replaced by target during compilation of object codes.

[irisc::engine::command::source], [irisc::engine::command::target]

String defined in source will be replaced by target during compilation of command codes.


Using the basic IRIS instruction sets might be complicated when programming large applications. By using the Macro feature as described previously, we can improve the readability and time to code. IRIS++ is a programming language that encapsulates IRIS with a more comprehensive programming syntax using the Macro replacement technique. For instance, take a look at the following code:

let $0 = 23 + 5;

We have defined "let " as "REG " and =, +, ; as ATR, SUM, %EMPTY%%BREAK% respectively in post_define.xml. Once compiled, the result will be same as:

REG $0
ATR 23

Following are the pre-defined IRIS++ keywords and statements. Note that IRIS++ can only be used for the main execution script and not for external scripts imported by the import statement.

Language Specifications

IRIS++ is similar to modern structured programming languages. Since the language is based on IRIS, there are many limitations to what it can do. Here are some points on the features and limitations.

  • Statement ends with a semi-colon (;)
  • You can mix IRIS instructions with IRIS++ language
  • Temporary registers are defined as $_rga, $_rgb, $_rgc ... $_rgf as $8, $9, $10 ... $13
  • Global registers are defined as $0, $1, $2 ... $7 as $0, $1, $2 ... $7
  • $_once register is used to determine if the initialization function should be executed or not
  • $_ret register holds the result value from calling another function
  • $_OPTREG register is only used by the compiler to optimize the code, so you should not touch this register
  • Open Brackets ({) must be within the same line as the function statement

Mathematical Operators

You may use the following mathematical operators: +, -, *, / (addition, subtraction, multiplication, division).

Logical Operators

You may use the following logical operators: ==, !=, <, > (equals, not equal to, less than, greater than).

Assignment Operators

You may use the following assignment operators: =, +=, -=, *=, /=, ++, -- (substitution, addition, subtraction, multiplication, division, increment, decrement).

let Keyword

Declares to manipulate on the specified register

// Let register A to be 23
let $_rga = 23;
// Increment register A
let $_rga++;

function Keyword

Defines a function. A function name must end with 2 brackets with a space and { symbol.

function initialize() {
  // Do a function call
  call main();

function main() {
  // Do something and return back to where this function was called

call Keyword

Calls a function. Use the return keyword to return back to where it was called.

call main();

goto Keyword

Jumps to a function (and never returns back).

goto main();

exec Keyword

Executes an external IRIS script. The external script needs to be compiled.

exec "test.bin";

exit Keyword

Halts the program execution.


mem Keyword

mem({memory address} [{assignment operator} {assignment value}]);

Manages the internal memory allocated by the Virtual Machine. Note that the memory keyword uses the UlyGL game library commands to operate on the memory and is not a defined feature in IRIS. The Virtual Machine must support the internal memory access in able to use this keyword.

// memory 0 = 10
// Equivalent to _memory::seek(0); _memory::set(10);
mem(0 [= 10]);

// memory 0 = 12 (Added 2 to the existing value)
mem(0 [+ 2]);

// memory 1 = 8
let $_rgb = 10;
mem(1 [= 23 + 57 / $_rgb]);

// Equivalent to _memory::seek(1);

// Equivalent to _memory::seek(0); let $_rga = _memory::get();
mem(0 let $_rga = _memory::get());

param Keyword

param[{value0}, {value1},, {value2},,, {value3}]@{function_name};

A short-cut to calling a function using parameters. Parameters are separated by increasing number of commas, up to the number of 6 parameters. Each parameter is assigned to the temporary registers from $_rga to $_rgf.

// Shows text
function show_text() {
  // Call function print_text with parameters set
  param[ID_FONT_NORMAL, 0,, 0,,, "Hello, World!"]@print_text;

function print_text() {

if Statement

if([condition]) { }

Switches program flow depending on the condition.

// Assign 23 to register A
let $_rga = 23;
// If register A is 23, return
if($_rga == 23) {

// [Tips]: Using the logical operator while assigning a value to register
if($_rga = _screen::_frame::get() == 30) {
  // if frame count == 30 then ...

do~until~loop Statement

do { } until [condition] loop;

Provides conditional loop functionality.

let $_rga = 0;
// Loop for 3 times (loop until register A equals 3)
do {
  // Increment
  let $_rga++;
} until $_rga == 3 loop;