Difference between revisions of "GOOL Interpreter"

From Crash Bandicoot Hacking Wiki
Jump to navigation Jump to search
(Created page with "In the Crash games, the GOOL Interpreter is responsible for interpreting an object's executable bytecode. This bytecode instructs the interpreter to perform a specific sequenc...")
 
Line 1: Line 1:
 
In the Crash games, the GOOL Interpreter is responsible for interpreting an object's executable bytecode. This bytecode instructs the interpreter to perform a specific sequence of operations that can conditionally or unconditionally alter specific characteristics of the object (ex. appearance, location, status, etc.), and should ultimately render the object according to its changing characteristics. As a result, the player observes a lively, animated, and interactive enemy, box, collectible item, or whatever other kind of in-game object it may be.
 
In the Crash games, the GOOL Interpreter is responsible for interpreting an object's executable bytecode. This bytecode instructs the interpreter to perform a specific sequence of operations that can conditionally or unconditionally alter specific characteristics of the object (ex. appearance, location, status, etc.), and should ultimately render the object according to its changing characteristics. As a result, the player observes a lively, animated, and interactive enemy, box, collectible item, or whatever other kind of in-game object it may be.
  
== Operation ==
+
== Interpretation ==
 
A single ''interpretation'' of an object's bytecode consists of the following steps:
 
A single ''interpretation'' of an object's bytecode consists of the following steps:
 
# ''Fetch'' the instruction at the object's program counter location
 
# ''Fetch'' the instruction at the object's program counter location
Line 10: Line 10:
 
(A suspending instruction is typically either a RET, which marks the end of a block, or ANIF/ANIS, which change the object's current frame of animation.)
 
(A suspending instruction is typically either a RET, which marks the end of a block, or ANIF/ANIS, which change the object's current frame of animation.)
  
'''UNFINISHED'''
+
A single call to the GOOL interpreter routine with some desired object as its first argument results in a single interpretation of that object's bytecode. The GOOL bytecode interpreter routine expects an object that provides the following information:
 +
* The location in its bytecode at which the interpreter shall begin its interpretation = object's program counter
 +
* The location in its memory of a stack frame created for the interpretation = object's frame pointer
 +
* The location in its memory at which the interpreter shall begin pushing the results of/popping the operands for interpreted instructions = object's stack pointer
 +
Prior to an interpretation, each of these fields (program counter, frame pointer, stack pointer) is modified according to the object's current state [descriptor] and the type of interpretation.
  
The main purpose of instantiation for an object is so that, elsewhere among the game code (by a call to the intepreter routine), the interpreter can begin to interpret its bytecode. Thus, the GOOL bytecode interpreter interprets or ''executes'' an object by sequentially fetching instructions (i.e. unsigned longs) located in its bytecode. Additionally, for the interpreter to perform those instructions, the object needs a certain amount of its own stack memory to store the results of their operations and/or to read and use as the operands for subsequent operations.
+
== Types of interpretation ==
 +
Many separate interpretations of an object's bytecode can occur within a single frame. The GOOL interpreter routine can be called with the same object from up to 7 distinct locations in a single iteration of the game loop. Each of the 7 calls to the interpreter routine with that object, and thus the ensuing interpretations of its bytecode, occurs under a separate condition. The 7 interpretations are identified based on the conditions under which they occur, and are as follows:
 +
* One that occurs at a specified rate, i.e. after a specific amount of time has elapsed since its previous occurrence. (Code Block interpretation)
 +
* One that [always] occurs [for each frame]. (Trans Block interpretation)
 +
* One that occurs whenever the object is sent an event and its current state specifies the location of an event service routine. (Event Block interpretation)
 +
* One that occurs whenever the object is sent an event and: its current state does not specify an event service routine or its event service routine failed to return an event, and the event sent maps to an event handler. (Handles Block interpretation)
 +
* One that occurs whenever the object changes state. (Head Block interpretation)
 +
* One that occurs whenever the object changes state and its recent Trans Block interpretation was unsuccessful (*Trans Block interpretation)
 +
* One that occurs when the object minimizes the distance during another object's query to 'find the nearest object', and the event sent for asynchronous handling (from the querying object) maps to an event handler. (*Handles Block interpretation)
  
The GOOL bytecode interpreter routine expects an object that knows the following:
+
Ignoring the details for now, the latter 2 types are just a Trans Block and Handles Block interpretation, respectively, although their calls cross-reference the interpreter routine from different locations. Thus, there are a total of 5 types. These 5 types are more simply viewed as 5 separate <i>threads</i> of execution of the object's bytecode. To demonstrate, here is the expanded game loop routine with only the calls to routines that have a descendant call to the interpreter routine:
* Where to take instruction = object's program counter - the location in the object's bytecode from which the interpreter shall begin interpretation or its process of sequentially fetching instructions and performing their corresponding operations
 
* Where to commit to/read from memory = object's stack pointer - wherein the object's memory the interpreter shall begin pushing the results of/popping the operands for interpreted instructions
 
* How to take instruction = interpreter status flags - how the flow of interpretation might be affected by various instructions
 
An object determines -where- it should begin taking instruction based on the -kind- of interpretation that shall be performed. Up to 5 distinct interpretations of an object's code can
 
  
occur in a single iteration of the main game loop; equivalently, the GOOL bytecode interpreter 
+
<pre>
 +
sub_80011FC4(levelid)
 +
 +
  ...  
 +
  C) sub_80011DD0 - Load entries and/or create universal game objects
 +
                    (HUD/display, Crash, Aku Aku, Shadows, Boxes, Fruit)
 +
    1) sub_8001C6C8 - CreateObject() [called once for each object created]
 +
    ...
 +
  GAME LOOP:
 +
  {
 +
   1) Code for handling game pause/start button press
 +
      A) If start pressed...
 +
          i) If pause menu object does not exist....
 +
            1) sub_8001C6C8 - CreateObject() [create pause menu object]
 +
            ...
 +
            else
 +
            1) sub_80024040 - SendEvent() [destroy pause menu object]
 +
            ...
 +
    2) If Crash object does not exist: 
 +
      A) sub_8002E98C - Create HUD and initialize level
 +
          i) sub_8001C6C8 - CreateObject() [create HUD object]
 +
          ii)sub_80026650 - Reinitialize Level
 +
            1) sub_8001C6C8 - CreateObject() [create Crash object]
 +
            ...
 +
    3) Code for loading a new level if necessary
 +
      A) If new level to load
 +
          ...
 +
          ii)sub_80011DD0 - Load entries and/or create universal game objects
 +
            1) sub_8001C6C8 - CreateObject() [called once for each object created]
 +
            ...
 +
    ...
 +
    5) Spawn all objects in current zone [for those that have their respawn bit set]
 +
      A) sub_80025928 - Spawn objects
 +
          1) sub_8001BCC8 - SpawnObject() [called for each object spawned]
 +
          ...
 +
    ...
 +
     9) Update all objects and create transformed primitives for them
 +
     A) sub_8001D5EC - UpdateObjects()
 +
          1) sub_8001DA0C - UpdateObject() [called for each existing object]
 +
    ...
 +
  } 
 +
}
 +
</pre>
 +
Based on the above, consider an arbitrary frame of execution in terms of a single object.
 +
CreateObject() or SpawnObject() will not be called more than once [to create that object] in that frame; that is-the object is not created or spawned more than once, or even spawned after it is created and vice-versa. The object may not even be created/spawned in that frame because it either already exists or does not exist since the game has not requested it be created/spawned. If it is an existing object, it will also be updated only once in that frame. For a single object, the above can be reduced the following series of potential calls:
  
routine ('interpret()') can be called on the same object from up to 5 distinct locations in a single 
+
<pre>
 +
sub_80011FC4(levelid)
 +
 +
  ...  
 +
  GAME LOOP:
 +
  {
 +
    ...
 +
    CreateObject(obj,...) or SpawnObject(obj,...)
 +
    ...
 +
    SendEvent(src,obj,...)
 +
    ...
 +
    UpdateObject(obj)
 +
  } 
 +
}
 +
</pre>
  
iteration of the main game loop: 
+
What the above leaves out, however, are the number of other locations at which a call to the SendEvent() routine reside. In fact, there is nothing preventing the object from being the recipient of more than one event sent (from potentially multiple sources) in a single frame. There may be an interpretation for each event sent to the object during that frame, and there is no limit on the number of events that can be sent. Thus, there may be many calls to SendEvent() [with the object as the recipient] in a single frame, but still only at most one call to CreateObject/SpawnObject() and UpdateObject(). Without getting too far off track, it is simply necessary to understand that each of these routines contains (or calls a routine that calls a routine that contains) a call to the interpreter routine:
  
 +
<pre>
 +
sub_80011FC4(levelid)
 +
 +
  ...  
 +
  GAME LOOP:
 +
  {
 +
    ...
 +
    CreateObject(obj,...) or SpawnObject(obj,...)
 +
      InitObject(obj, ...)
 +
        ChangeObjectState(obj, ...)
 +
           CreateObjectStackFrame(obj);
 +
           obj->pc = obj->pchead;
 +
          InterpretObject(obj, 3, &stateref); // # 1
 +
       
 +
    SendEvent(src,obj,...)
 +
        -if there is an event service routine for the object (in its current state)
 +
           CreateObjectStackFrame(obj);
 +
           obj->pc = obj->pcevent;
 +
          InterpretObject(obj, 8, &stateref); // # 2
 +
        -if there is not an event service routine for the object (in its current state)
 +
        or the event service routine failed to return an event
 +
          -if the event maps to an event handler in the event->state/handler map
 +
           CreateObjectStackFrame(obj);
 +
           obj->pc = handlerlocation;
 +
          InterpretObject(obj, 3, &stateref); // # 3
 +
          -else
 +
          ChangeObjectState(obj,state,...); *
  
 +
    UpdateObject(obj)
 +
      -if the object is able to animate
 +
        -if there is a trans block to interpret for the object (in its current state)
 +
           CreateObjectStackFrame(obj);
 +
           obj->pc = obj->pctrans;
 +
          InterpretObject(obj, 3, &stateref); // # 4
 +
        -if at least n ticks have elapsed, where n is given by the 'wait' operand of the
 +
        most recently suspending animation type instruction of the object's code block 
 +
          InterpretObject(obj, 4, &stateref); // # 5
  
  MAIN LOOP:
+
  } 
 
+
}
  {
+
</pre>
 
 
     ...
 
 
 
   2) or 5)
 
 
 
     ...
 
 
 
       initObject(object, ...)
 
 
 
         changeState(object, ...)
 
 
 
           ...
 
 
 
           newFrame(object)
 
 
 
           object->process.pc = object->process.headBlock;
 
 
 
           interpret(object, 3, &ret) // # 1
 
 
 
           ...
 
 
 
          ...
 
 
 
 
 
 
 
        issueEvent(sender, object, ...)
 
 
 
          -if there is an event service routine for the object (in its current state)
 
 
 
            ...
 
 
 
            newFrame(object);
 
 
 
            object->process.pc = object->process.eventBlock;
 
 
 
            interpret(object, 8, &event); // # 2
 
 
 
 
 
 
 
          -if event is non-state changing event
 
 
 
            ...
 
 
 
            newFrame(object);
 
 
 
            object->process.pc = gool_CODE(object, event);
 
 
 
            interpret(object, 3, &ret);  // # 3
 
 
 
 
 
 
 
          -else
 
 
 
            changeState(object, ...);
 
 
 
   9) Execute, animate, and render primitives for all objects (includes physics and collision engine)
 
 
 
        sub_8001D5EC - handleObjects()
 
 
 
          for each existing object do:
 
 
 
            ...
 
 
 
            -if at least one object frame worth of time for this object has elapsed since the following
 
 
 
            has been performed (i.e. since the object has last executed its trans block):
 
 
 
 
 
 
 
              newFrame(object);
 
 
 
              object->process.pc = object->process.transBlock;
 
 
 
              interpret(object, 3, &ret)  // # 4
 
 
 
 
 
 
 
              -if at least n ticks have elapsed, where n is given by the 'wait' operand of the most recently 
 
 
 
               suspending animation type instruction of the object's code block: 
 
 
 
 
 
 
 
               interpret(object, 4, &ret);  // # 5
 
 
 
              ...
 
 
 
  }
 
 
 
 
 
  
 +
<b>UNFINISHED</b>
 
Thus, a potential sequence of instruction reads as a result of multiple calls to the interpret 
 
Thus, a potential sequence of instruction reads as a result of multiple calls to the interpret 
  

Revision as of 10:29, 31 July 2015

In the Crash games, the GOOL Interpreter is responsible for interpreting an object's executable bytecode. This bytecode instructs the interpreter to perform a specific sequence of operations that can conditionally or unconditionally alter specific characteristics of the object (ex. appearance, location, status, etc.), and should ultimately render the object according to its changing characteristics. As a result, the player observes a lively, animated, and interactive enemy, box, collectible item, or whatever other kind of in-game object it may be.

Interpretation

A single interpretation of an object's bytecode consists of the following steps:

  1. Fetch the instruction at the object's program counter location
    • Read the instruction at the object's program counter location
    • Point the object's program counter to the location of the following instruction
  2. Perform the operation specified by the fetched instruction
  3. Continue repeating steps 1 and 2 until a suspending instruction is fetched

(A suspending instruction is typically either a RET, which marks the end of a block, or ANIF/ANIS, which change the object's current frame of animation.)

A single call to the GOOL interpreter routine with some desired object as its first argument results in a single interpretation of that object's bytecode. The GOOL bytecode interpreter routine expects an object that provides the following information:

  • The location in its bytecode at which the interpreter shall begin its interpretation = object's program counter
  • The location in its memory of a stack frame created for the interpretation = object's frame pointer
  • The location in its memory at which the interpreter shall begin pushing the results of/popping the operands for interpreted instructions = object's stack pointer

Prior to an interpretation, each of these fields (program counter, frame pointer, stack pointer) is modified according to the object's current state [descriptor] and the type of interpretation.

Types of interpretation

Many separate interpretations of an object's bytecode can occur within a single frame. The GOOL interpreter routine can be called with the same object from up to 7 distinct locations in a single iteration of the game loop. Each of the 7 calls to the interpreter routine with that object, and thus the ensuing interpretations of its bytecode, occurs under a separate condition. The 7 interpretations are identified based on the conditions under which they occur, and are as follows:

  • One that occurs at a specified rate, i.e. after a specific amount of time has elapsed since its previous occurrence. (Code Block interpretation)
  • One that [always] occurs [for each frame]. (Trans Block interpretation)
  • One that occurs whenever the object is sent an event and its current state specifies the location of an event service routine. (Event Block interpretation)
  • One that occurs whenever the object is sent an event and: its current state does not specify an event service routine or its event service routine failed to return an event, and the event sent maps to an event handler. (Handles Block interpretation)
  • One that occurs whenever the object changes state. (Head Block interpretation)
  • One that occurs whenever the object changes state and its recent Trans Block interpretation was unsuccessful (*Trans Block interpretation)
  • One that occurs when the object minimizes the distance during another object's query to 'find the nearest object', and the event sent for asynchronous handling (from the querying object) maps to an event handler. (*Handles Block interpretation)

Ignoring the details for now, the latter 2 types are just a Trans Block and Handles Block interpretation, respectively, although their calls cross-reference the interpreter routine from different locations. Thus, there are a total of 5 types. These 5 types are more simply viewed as 5 separate threads of execution of the object's bytecode. To demonstrate, here is the expanded game loop routine with only the calls to routines that have a descendant call to the interpreter routine:

 sub_80011FC4(levelid)
 { 
   ...	  
   C) sub_80011DD0 - Load entries and/or create universal game objects 
                     (HUD/display, Crash, Aku Aku, Shadows, Boxes, Fruit)
     1) sub_8001C6C8 - CreateObject() [called once for each object created]
     ...
   GAME LOOP:
   {
     1) Code for handling game pause/start button press
       A) If start pressed...
          i) If pause menu object does not exist....
             1) sub_8001C6C8 - CreateObject() [create pause menu object]
             ...
             else
             1) sub_80024040 - SendEvent() [destroy pause menu object]
             ...
     2) If Crash object does not exist: 
       A) sub_8002E98C - Create HUD and initialize level
          i) sub_8001C6C8 - CreateObject() [create HUD object]
          ii)sub_80026650 - Reinitialize Level
             1) sub_8001C6C8 - CreateObject() [create Crash object]
             ...
     3) Code for loading a new level if necessary
       A) If new level to load
          ...
          ii)sub_80011DD0 - Load entries and/or create universal game objects 
             1) sub_8001C6C8 - CreateObject() [called once for each object created]
             ...
     ...
     5) Spawn all objects in current zone [for those that have their respawn bit set]
       A) sub_80025928 - Spawn objects
          1) sub_8001BCC8 - SpawnObject() [called for each object spawned]
          ...
     ...
     9) Update all objects and create transformed primitives for them
       A) sub_8001D5EC - UpdateObjects()
          1) sub_8001DA0C - UpdateObject() [called for each existing object]
     ...
   } 
 }

Based on the above, consider an arbitrary frame of execution in terms of a single object. CreateObject() or SpawnObject() will not be called more than once [to create that object] in that frame; that is-the object is not created or spawned more than once, or even spawned after it is created and vice-versa. The object may not even be created/spawned in that frame because it either already exists or does not exist since the game has not requested it be created/spawned. If it is an existing object, it will also be updated only once in that frame. For a single object, the above can be reduced the following series of potential calls:

 sub_80011FC4(levelid)
 { 
   ...	  
   GAME LOOP:
   {
     ...
     CreateObject(obj,...) or SpawnObject(obj,...)
     ...
     SendEvent(src,obj,...)
     ...
     UpdateObject(obj)
   } 
 }

What the above leaves out, however, are the number of other locations at which a call to the SendEvent() routine reside. In fact, there is nothing preventing the object from being the recipient of more than one event sent (from potentially multiple sources) in a single frame. There may be an interpretation for each event sent to the object during that frame, and there is no limit on the number of events that can be sent. Thus, there may be many calls to SendEvent() [with the object as the recipient] in a single frame, but still only at most one call to CreateObject/SpawnObject() and UpdateObject(). Without getting too far off track, it is simply necessary to understand that each of these routines contains (or calls a routine that calls a routine that contains) a call to the interpreter routine:

 sub_80011FC4(levelid)
 { 
   ...	  
   GAME LOOP:
   {
     ...
     CreateObject(obj,...) or SpawnObject(obj,...)
       InitObject(obj, ...)
         ChangeObjectState(obj, ...)
           CreateObjectStackFrame(obj);
           obj->pc = obj->pchead;
           InterpretObject(obj, 3, &stateref); // # 1
         
     SendEvent(src,obj,...)
        -if there is an event service routine for the object (in its current state)
           CreateObjectStackFrame(obj);
           obj->pc = obj->pcevent;
           InterpretObject(obj, 8, &stateref); // # 2
        -if there is not an event service routine for the object (in its current state)
         or the event service routine failed to return an event
          -if the event maps to an event handler in the event->state/handler map
           CreateObjectStackFrame(obj);
           obj->pc = handlerlocation;
           InterpretObject(obj, 3, &stateref); // # 3
          -else
           ChangeObjectState(obj,state,...); *

     UpdateObject(obj)
      -if the object is able to animate
        -if there is a trans block to interpret for the object (in its current state)
           CreateObjectStackFrame(obj);
           obj->pc = obj->pctrans;
           InterpretObject(obj, 3, &stateref); // # 4
        -if at least n ticks have elapsed, where n is given by the 'wait' operand of the 
         most recently suspending animation type instruction of the object's code block 
           InterpretObject(obj, 4, &stateref); // # 5

   } 
 }

UNFINISHED Thus, a potential sequence of instruction reads as a result of multiple calls to the interpret 

routine for the same object in one iteration of the main game loop could be: 


Offset/address | Instruction

---------------|-------------

headBlock: 0x0 |      N

           0x4 |      N

           0x8 |      N

           0xC |      S

.....          |     

eventBlock:0x34|      N

           0x38|      N

           0x3C|      S

.....          |

transBlock:0x50|      N

           0x54|      N

           0x58|      N

           0x5C|      N

           0x60|      N

           0x64|      N

           0x68|      S

.....          |

codeBlock: 0x70|      N

           0x74|      N

           0x78|      N

           0x7C|      N

           0x80|      S


Where the offsets/addresses of the interpreted instructions from the object's instantiating 

executable entries' bytecode item are indicated in the left column along with labels at the offsets 

that are pointed to in their respective object fields of the same name, and whether the instructions 

either did not suspend execution-causing the next instruction in sequence to be read/interpreted 

(indicated by an N)-or did suspend execution-causing the interpreter routine to return to the callee 

(indicated by an S). 


These object fields/pointers (headBlock, eventBlock, transBlock, codeBlock) define, for some object, 

the absolute locations of 'blocks' in the object's code that the object's program counter will be 

changed to point at, prior to each respective type of interpretation [during the iteration of the 

game loop in which the object is created]. Equivalently, they define the respective entry points for 

4 separate -threads- of execution for an object-a head thread, an event thread, a trans thread, and 

a code thread. 3 of these object fields/pointers change whenever the object 'changes state', which 

is accomplished with the 'changeState()' routine. 

Opcode Name Encoding/Format Explicit GOOL ops

in

Explicit IMM. ops

in

Implicit STACK ops

in

Operation Implicit STACK out Explicit

GOOL ops out

Description
0/0x00 ADD 00000000RRRRRRRRRRRRLLLLLLLLLLLL L,R O = L + R O add
1/0x01 SUB 00000001RRRRRRRRRRRRLLLLLLLLLLLL L,R O = L - R O subtract
2/0x02 MUL 00000010RRRRRRRRRRRRLLLLLLLLLLLL L,R O = L * R O multiply
3/0x03 DIV 00000011RRRRRRRRRRRRLLLLLLLLLLLL L,R O = L / R O divide
4/0X04 CEQ 00000100RRRRRRRRRRRRLLLLLLLLLLLL L,R O = (L == R),

O = ((L ^ R) == 0)

O check if equal
5/0x05 ANDL 00000101RRRRRRRRRRRRLLLLLLLLLLLL L,R O = (L && R),

O = (L ? (R > 0) : 0)

O logical and
6/0x06 ORL 00000110RRRRRRRRRRRRLLLLLLLLLLLL L,R O = (L || R) O
logical or
7/0x07 ANDB 00000111RRRRRRRRRRRRLLLLLLLLLLLL L,R O = L & R O bitwise and
8/0x08 ORB 00001000RRRRRRRRRRRRLLLLLLLLLLLL L,R O = L | R O bitwise or
9/0x09 SLT 00001001RRRRRRRRRRRRLLLLLLLLLLLL L,R O = L < R O set less than
10/0x0A SLE 00001010RRRRRRRRRRRRLLLLLLLLLLLL L,R O = (L <= R) O set less than or equal
11/0x0B SGT 00001011RRRRRRRRRRRRLLLLLLLLLLLL L,R O = L > R O set greater than
12/0x0C SGE 00001100RRRRRRRRRRRRLLLLLLLLLLLL L,R O = (L >= R) O set greater than or equal
13/0x0D MOD 00001101RRRRRRRRRRRRLLLLLLLLLLLL L,R O = L % R O modulo
14/0x0E XOR 00001110RRRRRRRRRRRRLLLLLLLLLLLL L,R O = L ^ R O exclusive or
15/0x0F TST 00001111RRRRRRRRRRRRLLLLLLLLLLLL L,R O = (((L & R) ^ R) == 0) O test bit
16/0x10 RND 00010000AAAAAAAAAAAABBBBBBBBBBBB A,B O = B+(rand() % (A - B)) O random
17/0x11 MOVE 00010001SSSSSSSSSSSSDDDDDDDDDDDD S D = S [D] move data
18/0x12 NOTL 00010010SSSSSSSSSSSSDDDDDDDDDDDD S D = (S == 0) D logical not
19/0x13 PATH 00010011AAAAAAAAAAAABBBBBBBBBBBB (A),B R = 0x100 [R,A] varies P B path progress
20/0x14 LEA 00010100SSSSSSSSSSSSDDDDDDDDDDDD S D = &S D load effective address
21/0x15 SHA 00010101RRRRRRRRRRRRLLLLLLLLLLLL L,R O = ((R < 0) ? (L >> -R)

: (L << R))

O arithmetic shift
22/0x16 PSHV 00010110AAAAAAAAAAAABBBBBBBBBBBB [A,[B]] [arg_buf = A]

I = A; J = B;

[I,[J]] push value to stack
23/0x17 NOTB 00010111SSSSSSSSSSSSDDDDDDDDDDDD D = ~S D bitwise not
24/0x18 MOVC 000110000000RRRRRRIIIIIIIIIIIIII R,C see docs O move code pointer
25/0x19 ABS 00011001SSSSSSSSSSSSDDDDDDDDDDDD S D = (S < 0) ? -S: S D absolute value
26/0x1A PAD 00011010000TDDDDSSPPBBBBBBBBBBBB B,P,S,D,T O = testctrls(instr,0) O test controller buttons
27/0x1B SPD 00011011VVVVVVVVVVVVBBBBBBBBBBBB V,B S = B + ((V*gvel) >> 10) S calculate speed
28/0x1C MSC 00011100PPPPSSSSSLLLXXXXXXXXXXXX X P,S,L various; see docs *** *** multi-purpose
29/0x1D PRS 00011101PPPPPPPPPPPPDDDDDDDDDDDD P,D large calc; see docs O driven sine wave
30/0x1E SSAW 00011110DDDDDDDDDDDDPPPPPPPPPPPP M,P O = (M + frameCount) % P O synchronized saw wave
31/0x1F RGL 00011111000000000000IIIIIIIIIIII I O = globals[I >> 8] O read global variable
32/0x20 WGL 00100000SSSSSSSSSSSSIIIIIIIIIIII I,S globals[I << 8] = *S write global variable
33/0x21 ANGD 00100001RRRRRRRRRRRRLLLLLLLLLLLL L,R O = angdist(L,R) angle between
34/0x22 APCH 00100010RRRRRRRRRRRRLLLLLLLLLLLL L,(R) S = 0x100 [R,S] O = approach(L,R,S) O approach a value
35/0x23 CVMR 00100011000IIIIIILLL000000000000 I,L O = obj.link[L].colors[I] color vector or matrix read
36/0x24 CVMW 00100011000IIIIIILLLCCCCCCCCCCCC C I,L obj.link[L].colors[I] = C color vector or matrix write
37/0x25 ROT 00100101RRRRRRRRRRRRLLLLLLLLLLLL L,(R) [R,S] O = rotate(L,R,S,0) O approach an angle
38/0x26 PSHP 001001100AAAAAAAAAAABBBBBBBBBBBB [A,[B]] I = &A; J = &B; [I,[J]] push pointer to stack
39/0X27 ANID 00100111FFFFFFFFFFFFDDDDDDDDDDDD F D=&obj.global.anim[F>>5] D set animation
128/0x80 DBG 10000000RRRRRRRRRRRRLLLLLLLLLLLL L,R debug print ops (beta only)
129/0x81 NOP 10000001000000000000000000000000 no operation
130/0x82 CFL 10000010TTCCRRRRRRIIIIIIIIIIIIII T,C,R,I general control flow
BRA 100000100000RRRRRRVVVVIIIIIIIIII R,V,I branch
BNEZ 100000100001RRRRRRVVVVIIIIIIIIII R,V,I branch not equal zero
BEQZ 100000100010RRRRRRVVVVIIIIIIIIII R,V,I branch equal zero
CST 100000100100RRRRRRSSSSSSSSSSSSSS R,S change state
CSNZ 100000100101RRRRRRSSSSSSSSSSSSSS R,S change state not zero
CSEZ 100000100110RRRRRRSSSSSSSSSSSSSS R,S change state equal zero
RET 100000101000RRRRRRIIIIIIIIIIIIII R,I return
131/0x83 ANIS 10000011HHTTTTTTSSSSSSSSSFFFFFFF F,S,T,H W change animation sequence
132/0x84 ANIF 10000100HHTTTTTTFFFFFFFFFFFFFFFF F T,H W change animation frame
133/0x85 VECA 10000101CCCTTTBBBAAAVVVVVVVVVVVV V T,A,B,C * ** multi-purpose vector calcs
134/0x86 JAL 10000110VVVV000000IIIIIIIIIIIIII I,V jump and link
135/0x87 EVNT 10000111LLLAAARRRRRREEEEEEEEEEEE E L,A,R send an event
136/0x88 RSTT 10001000TTCCRRRRRR************** R,C,T,* **
state return guard = true variant
137/0x89 RSTF 10001001TTCCRRRRRR************** R,C,T,* ** state return guard = false variant
138/0x8A CHLD 10001010AAAATTTTTTTTSSSSSSCCCCCC C,T,S,A [C],

arg[0 to A]

spawn children objects
139/0x8B NTRY 10001011TTTTTTTTTTTTEEEEEEEEEEEE T,E multi-purpose page operation
140/0x8C SNDA 10001100AAAAAAAAAAAABBBBBBBBBBBB A,B adjust audio levels
141/0x8D SNDB 10001101VVVVRRRRRRSSSSSSSSSSSSS
play sound
effect
142/0x8E VECB 10001110CCCTTTBBBAAAVVVVVVVVVVVV V T,A,B,C
multi-purpose vector calcs
143/0x8F EVNB 10001111LLLAAARRRRRREEEEEEEEEEEE E L,A,R
broadcast an event
144/0x90 EVNU 10010000LLLAAARRRRRREEEEEEEEEEEE E L,A,R

send event unknown variant
145/0x91 CHLF 10100000AAAATTTTTTTTSSSSSSCCCCCC C,T,S,A [C],

arg[0 to A]


spawn children objects;

no replacement if obj pool full

Specification Format

A AAAAAAAAAAAA AAAA AAA (EVNT/EVNU/EVNB) AAA (VECA/VECB)
Name Value A Argument Count Argument Count Vector A Index
Specification Format B BBBBBBBBBBBB BBBBBBBBBBBB (PAD) BBBBBBBBBBBB (SPD) BBB
Name Value B Controller Buttons Base Speed Vector B Index
Specification Format C CCCCCCCCCCCC CCCCCC CCC CC
Name Color Value Spawn Count Vector C Index Conditional Check Type
Specification Format D DDDDDDDDDDDD DDDDDDDDDDDD (PRS) DDDD
Name Destination Wave Phase Directional Buttons
Specification Format E EEEEEEEEEEEE (NTRY) EEEEEEEEEEEE (EVNT/EVNU/EVNB)
Name Entry Event
Specification Format F FFFFFFFFFFFFFFFF FFFFFFFFFFFF FFFFFFF
Name Animation Frame Animation Descriptor Offset Animation Frame

Specification Format

H HH
Name Horizontal Flip

Specification Format

I IIIIIIIIIIIIII IIIIIIIIIIII IIIIIIIIII IIIIII
Name Immediate Code Location Global Variable Index Immediate Branch Offset Color Index

Specification Format

L LLLLLLLLLLLL LLL
Name Left Operand Object Link Index

Specification Format

P PPPPPPPPPPPP PPPP PP
Name Wave Period Primary Operation Subtype Primary Check Type

Specification Format

R RRRRRRRRRRRR RRRRRR
Name Right Operand Object Register Index

Specification Format

S SSSSSSSSSSSSSS SSSSSSSSSSSS SSSSSSSSS SSSSSS
Name State Source Animation Sequence Index Subtype

Specification Format

SSSSS SS
Name Secondary Operation Subtype Secondary Check Type

Specification Format

T TTTTTTTTTTTT TTTTTTTT TTTTTT TTT
Name Operation Subtype (Object) Type Time Operation Subtype

Specification Format

TT T
Name Operation Subtype Truth Invert Toggle

Specification Format

V VVVVVVVVVVVV VVVV VVVV (SNDB)
Name Velocity Variable Count Volume

State Return Instruction Table

Opcode Name Encoding/Format Explicit

IMM. ops

in

Description
136/0x88 RSTT 10001000TTCCRRRRRR************** R,C,T,* state return guard = true variant
RST 100010000100RRRRRRSSSSSSSSSSSSSS R,S state return guard = true
RSNT 100010000101RRRRRRSSSSSSSSSSSSSS R,S state return if nonzero guard = true
RSZT 100010000110RRRRRRSSSSSSSSSSSSSS R,S state return if equal zero guard = true
RSCT 100010000111RRRRRRSSSSSSSSSSSSSS R,S state return eval prev cond guard = true
RNT 100010001000RRRRRRxxxxxxxxxxxxxx R null return guard = true
RNNT 100010001001RRRRRRxxxxxxxxxxxxxx R null return if nonzero guard = true
RNZT 100010001010RRRRRRxxxxxxxxxxxxxx R null return if equal zero guard = true
RNCT 100010001011RRRRRRxxxxxxxxxxxxxx R null return eval prev cond guard = true
GDT 100010001100RRRRRRxxxxxxxxxxxxxx R guard = true
GNT 100010001101RRRRRRxxxxxxxxxxxxxx R if nonzero guard = true
GZT 100010001110RRRRRRxxxxxxxxxxxxxx R if equal zero guard = true
GCT 100010001111RRRRRRxxxxxxxxxxxxxx R eval prev cond guard = true
GBNT 100010000001RRRRRRVVVVIIIIIIIIII R,V,I if nonzero guard = true else branch
GBZT 100010000010RRRRRRVVVVIIIIIIIIII R,V,I if equal zero guard = true else branch
137/0x89 RSTF 10001001TTCCRRRRRR************** R,C,T,* state return guard = false variant
RSF 100010000100RRRRRRSSSSSSSSSSSSSS R,S state return guard = false
RSNF 100010000101RRRRRRSSSSSSSSSSSSSS R,S state return if nonzero guard = false
RSZF 100010000110RRRRRRSSSSSSSSSSSSSS R,S state return if equal zero guard = false
RSCF 100010000111RRRRRRSSSSSSSSSSSSSS R,S state return eval prev cond guard = false
RNF 100010001000RRRRRRxxxxxxxxxxxxxx R null return guard = false
RNNF 100010001001RRRRRRxxxxxxxxxxxxxx R null return if nonzero guard = false
RNZF 100010001010RRRRRRxxxxxxxxxxxxxx R null return if equal zero guard = false
RNCF 100010001011RRRRRRxxxxxxxxxxxxxx R null return eval prev cond guard = false
GDF 100010001100RRRRRRxxxxxxxxxxxxxx R guard = false
GNF 100010001101RRRRRRxxxxxxxxxxxxxx R if nonzero guard = false
GZF 100010001110RRRRRRxxxxxxxxxxxxxx R if equal zero guard = false
GCF 100010001111RRRRRRxxxxxxxxxxxxxx R eval prev cond guard = false
GBNF 100010000001RRRRRRVVVVIIIIIIIIII R,V,I if nonzero guard = false else branch
GBZF 100010000010RRRRRRVVVVIIIIIIIIII R,V,I if equal zero guard = false else branch