Difference between revisions of "GOOL Interpreter"

From Crash Bandicoot Hacking Wiki
Jump to navigation Jump to search
Line 71: Line 71:
 
  }
 
  }
 
</pre>
 
</pre>
Based on the above, consider an arbitrary frame of execution in terms of a single 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:
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:
 
  
 
<pre>
 
<pre>
Line 90: Line 89:
 
</pre>
 
</pre>
  
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:
+
What the above leaves out, however, are the number of other locations at which a call to the SendEvent() routine reside. Some are located before the CreateObject or SpawnObject routine calls, some after, some within the UpdateObject routine itself, and even some within the interpreter routine, since there exist several types of GOOL instructions that allow another object to send an event to the recipient object. 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>
 
<pre>
Line 102: Line 101:
 
       InitObject(obj, ...)
 
       InitObject(obj, ...)
 
         ChangeObjectState(obj, ...)
 
         ChangeObjectState(obj, ...)
           CreateObjectStackFrame(obj);
+
           CreateObjectStackFrame(obj); // *create initial stack frame
           obj->pc = obj->pchead;
+
          -if there is a head block to interpret for the object
          InterpretObject(obj, 3, &stateref); // # 1
+
            CreateObjectStackFrame(obj);
 +
             obj->pc = obj->pchead;
 +
            InterpretObject(obj, 0x13, &stateref); // # 1 (head block interpretation)
 
          
 
          
     SendEvent(src,obj,...)
+
     SendEvent(src,obj,...) // there may be multiple calls to this
 
         -if there is an event service routine for the object (in its current state)
 
         -if there is an event service routine for the object (in its current state)
 
           CreateObjectStackFrame(obj);
 
           CreateObjectStackFrame(obj);
 
            obj->pc = obj->pcevent;
 
            obj->pc = obj->pcevent;
           InterpretObject(obj, 8, &stateref); // # 2
+
           InterpretObject(obj, 0x8, &stateref);   // # 2 (event block interpretation)
 
        -if there is not an event service routine for the object (in its current state)
 
        -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
 
         or the event service routine failed to return an event
Line 116: Line 117:
 
           CreateObjectStackFrame(obj);
 
           CreateObjectStackFrame(obj);
 
            obj->pc = handlerlocation;
 
            obj->pc = handlerlocation;
           InterpretObject(obj, 3, &stateref); // # 3
+
           InterpretObject(obj, 0x3, &stateref);   // # 3 (handles block interpretation)
 
           -else
 
           -else
           ChangeObjectState(obj,state,...); *
+
           ChangeObjectState(obj,state,...); // *
  
 
     UpdateObject(obj)
 
     UpdateObject(obj)
Line 125: Line 126:
 
           CreateObjectStackFrame(obj);
 
           CreateObjectStackFrame(obj);
 
            obj->pc = obj->pctrans;
 
            obj->pc = obj->pctrans;
           InterpretObject(obj, 3, &stateref); // # 4
+
           InterpretObject(obj, 0x3, &stateref);   // # 4 (trans block interpretation)
 
        -if at least n ticks have elapsed, where n is given by the 'wait' operand of the  
 
        -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 
 
         most recently suspending animation type instruction of the object's code block 
           InterpretObject(obj, 4, &stateref); // # 5
+
           InterpretObject(obj, 0x4, &stateref);   // # 5 (code block interpretation)
  
 
    } 
 
    } 
 
  }
 
  }
</pre>
+
</pre> 
 +
Notice that immediately prior to each call [to the interpreter routine], with the exception of #5, a new stack frame is created for the object (which includes pointing its frame pointer and stack pointer, respectively, to the beginning and ending of its new frame), and its program counter is pointed to a distinct location within its bytecode. If each bytecode instruction is represented by a pseudo-instruction, a potential sequence of calls to the interpreter routine in a single frame [for only that object] might be represented by the following:
  
<b>UNFINISHED</b>
+
<pre>
Thus, a potential sequence of instruction reads as a result of multiple calls to the interpret 
+
label  | address | pseudo-instruction
  
routine for the same object in one iteration of the main game loop could be
+
pcevent: 0x34      N  // call A
 
+
        0x38      N
 
+
         0x3C      S
 
+
...
Offset/address | Instruction
+
pchead:   0x0     N // call B
 
+
          0x4     N
<nowiki>---------------</nowiki>|-------------
+
          0x8     N
 
+
          0xC     S
headBlock: 0x0 |      N
+
...
 
+
pctrans: 0x50      N // call C
           0x4 |      N
+
         0x54      N
 
+
         0x58      N
           0x8 |      N
+
        0x5C      N
 
+
         0x60      N
           0xC |      S
+
        0x64      N
 
+
        0x68      S
.....          |     
+
...
 
+
*pccode: 0x70      N // call D
eventBlock:0x34|      N
+
        0x74      N
 
+
        0x78      N
           0x38|      N
+
        0x7C      N
 
+
        0x80      S
           0x3C|      S
+
...
 
+
pcevent: 0x34      N // call E
.....          |
+
        0x38      N
 
+
         0x3C      S
transBlock:0x50|      N
+
...
 
+
pcevent: 0x34      N // call F
           0x54|      N
+
        0x38      N
 
+
         0x3C      S
           0x58|      N
+
</pre>
 
 
           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 
+
Each call above to the interpreter routine is represented by the sequence of instructions (represented as pseudo-instructions) fetched during its interpretation. An N pseudo-instruction represents an instruction that did not suspend the interpreter, causing the next instruction in sequence to be fetched and interpreted. An S pseudo-instruction represents an instruction that did suspend the interpreter, causing the interpreter routine to end the interpretation/return to its caller. Each pseudo-instruction is listed with the address of the instruction it represents in the object's bytecode [relative to the beginning].
  
(indicated by an N)-or did suspend execution-causing the interpreter routine to return to the callee 
+
The first instruction fetched in each interpretation is also marked with a label; this label is given the name of the object <i>field</i> that points to its associated instruction. Immediately prior to each interpretation, with the exception of the one marked *pccode, the object's program counter is pointed to the location in ''this'' particular field; it is this operation that causes each interpretation to begin at the location it does. This particular object field is also primarily responsible for determining the type of interpretation:
 +
{| class="article-table"
 +
!Field
 +
!Interpretation Type
 +
|-
 +
|pctrans
 +
|Trans Block Interpretation
 +
|-
 +
|pcevent
 +
|Event Block Intepretation
 +
|-
 +
|pchead
 +
|Head Block Interpretation
 +
|-
 +
|pc
 +
|Code Block Interpretation
 +
|}
 +
The <code>pctrans</code>, <code>pcevent</code>, and <code>pchead</code> fields each contain a pointer to/the absolute location of an individual ''block'' in the object's bytecode. These fields locate the respective entry points for 3 separate ''threads'' of interpretation for the object-a <i>trans</i> thread, an <i>event</i> thread, and a <i>head</i> thread. Whenever the object changes state, its <code>pctrans</code> and <code>pcevent</code> fields are modified according to the state descriptor for its new state; its <code>pchead</code> field is then cleared. (If the object wishes to modify its <code>pchead</code> field, it does so via a MOVC instruction in its bytecode.)
  
(indicated by an S). 
+
Whenever the object changes state, its program counter (<code>pc</code> field) is also set directly to the location of the <i>code</i> block specified by the <code>pccode</code> field of the state descriptor for its new state. The entirety of the object's stack contents are then unwound, or the object's stack pointer is reset to its initial location, and an initial stack frame is [re]created. This program counter location and stack configuration is then restored after each subsequent non-code block interpretation is completed. (That is-since those interpretations require the program counter value to be <i>replaced</i> with a new location and a new stack frame to be created, after they are completed, the original program counter location and initial stack frame are restored). When the interpretation marked #5 in the above pseudo-code (the code block interpretation) finally occurs, the object's program counter will point to its code block and its stack frame will be the initial frame. The code block interpretation is yet another thread of interpretation for the object-a <i>code</i> thread.
 +
[[File:Goolinterp1.png|thumb|236x236px]]
 +
For the sake of explanation, <i>handles</i> blocks are viewed as being equivalent to <i>event</i> blocks, so <i>handles</i> interpretations and threads are simply ignored. The timeline to the right shows an arbitrary frame of execution in terms of the times elapsed for the various interpretations of a single object's bytecode. Towards the beginning of the frame, the interpreter has performed a code block interpretation and a trans block interpretation for the object. Towards the end of the frame, the interpreter has performed an event block interpretation for that object, as it has been sent an event from some [unknown] source. The following timeline extends this timeline [to the right] to 6 arbitrary frames of execution:
 +
[[File:Goolinterp2b.png|thumb|733x733px]]
  
  
  
These object fields/pointers (headBlock, eventBlock, transBlock, codeBlock) define, for some object, 
+
The extended timeline makes it clear that this particular object has been configured to have its code block interpretation occur every other frame. It also shows the performance of more than one event block interpretation for that object during the second frame. The following timeline shows the same 6 frames of execution in terms of the times elapsed for all interpretations of each existing object's bytecode; in this particular instance, there are only 2 existing objects.
 +
[[File:Goolinterp2c.png|thumb|733x733px]]
  
the absolute locations of 'blocks' in the object's code that the object's program counter will be 
+
== Stack Frames (incomplete) ==
 +
A new stack frame is created for an object immediately prior to an interpretation of its bytecode. It is also created when jumping to and linking a bytecode routine via the JAL instruction. An object's ''initial'' stack frame is created when it enters its initial state, and is recreated for each subsequent state change (after the unwinding the stack). The initial stack frame is used for the object's code block interpretation.
  
changed to point at, prior to each respective type of interpretation [during the iteration of the 
+
A stack frame has the following format:
 +
{| class="article-table"
 +
!Offset
 +
!Field
 +
!Size
 +
!Value
 +
|-
 +
|<nowiki>-0x4 x </nowiki>''a''
 +
|**Argument ''a''
 +
|4 bytes
 +
|*
 +
|-
 +
|0x0
 +
|Preserved Interpreter Mode Flags
 +
|4 bytes
 +
|0xFFFF for initial frame; * otherwise
 +
|-
 +
|0x4
 +
|Preserved Program Counter
 +
|4 bytes
 +
|*
 +
|-
 +
|0x8
 +
|Preserved Frame Pointer Relative Offset
 +
|2 bytes
 +
|f
 +
|-
 +
|0xA
 +
|Preserved Stack Pointer Relative Offset
 +
|2 bytes
 +
|s
 +
|-
 +
|0xC
 +
|Frame Data
 +
|* x 4 bytes
 +
|*
 +
|}
 +
(**Arguments come before frames and are not actually 'part' of them)
  
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 
+
'''UNFINISHED'''
  
a code thread. 3 of these object fields/pointers change whenever the object 'changes state', which 
+
[***also, do something with this: (*the ensuing head block interpretation begins at the location referenced by <code>pchead</code>, but preserved from <i>before</i> it is cleared; the interpretation actually occurs after the field is cleared.)]
  
is accomplished with the 'changeState()' routine. 
+
Immediately prior to a subsequent non-code block interpretation, the new stack frame is actually created <i>on top</i> of the initial stack frame. This includes preserving the object's program counter, with the <code>pccode</code>/code block location, in the new frame. The program counter is then pointed to the [non-code] block location, which replaces its current value (i.e. the code block location), and interpretation begins. When the interpretation finishes, the object's program counter is <i>restored</i> to the code block location preserved in the frame created [for the interpretation], and the stack is unwound of that frame.
  
 +
== <br>
 +
GOOL Instruction Table ==
 
<div style="font-family:monospace;font-size:10px;">
 
<div style="font-family:monospace;font-size:10px;">
 
{| 
 
{| 

Revision as of 09:24, 1 August 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. Some are located before the CreateObject or SpawnObject routine calls, some after, some within the UpdateObject routine itself, and even some within the interpreter routine, since there exist several types of GOOL instructions that allow another object to send an event to the recipient object. 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); // *create initial stack frame
           -if there is a head block to interpret for the object
             CreateObjectStackFrame(obj);
             obj->pc = obj->pchead;
             InterpretObject(obj, 0x13, &stateref); // # 1 (head block interpretation)
         
     SendEvent(src,obj,...) // there may be multiple calls to this
        -if there is an event service routine for the object (in its current state)
           CreateObjectStackFrame(obj);
           obj->pc = obj->pcevent;
           InterpretObject(obj, 0x8, &stateref);    // # 2 (event block interpretation)
        -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, 0x3, &stateref);    // # 3 (handles block interpretation)
          -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, 0x3, &stateref);   // # 4 (trans block interpretation)
        -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, 0x4, &stateref);   // # 5 (code block interpretation)

   } 
 }

 

Notice that immediately prior to each call [to the interpreter routine], with the exception of #5, a new stack frame is created for the object (which includes pointing its frame pointer and stack pointer, respectively, to the beginning and ending of its new frame), and its program counter is pointed to a distinct location within its bytecode. If each bytecode instruction is represented by a pseudo-instruction, a potential sequence of calls to the interpreter routine in a single frame [for only that object] might be represented by the following:

label  | address | pseudo-instruction

pcevent: 0x34      N  // call A
         0x38      N
         0x3C      S
...
pchead:   0x0      N  // call B
          0x4      N
          0x8      N
          0xC      S
...
pctrans: 0x50      N  // call C
         0x54      N
         0x58      N
         0x5C      N
         0x60      N
         0x64      N
         0x68      S
...
*pccode: 0x70      N  // call D
         0x74      N
         0x78      N
         0x7C      N
         0x80      S
...
pcevent: 0x34      N  // call E
         0x38      N
         0x3C      S
...
pcevent: 0x34      N  // call F
         0x38      N
         0x3C      S

Each call above to the interpreter routine is represented by the sequence of instructions (represented as pseudo-instructions) fetched during its interpretation. An N pseudo-instruction represents an instruction that did not suspend the interpreter, causing the next instruction in sequence to be fetched and interpreted. An S pseudo-instruction represents an instruction that did suspend the interpreter, causing the interpreter routine to end the interpretation/return to its caller. Each pseudo-instruction is listed with the address of the instruction it represents in the object's bytecode [relative to the beginning].

The first instruction fetched in each interpretation is also marked with a label; this label is given the name of the object field that points to its associated instruction. Immediately prior to each interpretation, with the exception of the one marked *pccode, the object's program counter is pointed to the location in this particular field; it is this operation that causes each interpretation to begin at the location it does. This particular object field is also primarily responsible for determining the type of interpretation:

Field Interpretation Type
pctrans Trans Block Interpretation
pcevent Event Block Intepretation
pchead Head Block Interpretation
pc Code Block Interpretation

The pctrans, pcevent, and pchead fields each contain a pointer to/the absolute location of an individual block in the object's bytecode. These fields locate the respective entry points for 3 separate threads of interpretation for the object-a trans thread, an event thread, and a head thread. Whenever the object changes state, its pctrans and pcevent fields are modified according to the state descriptor for its new state; its pchead field is then cleared. (If the object wishes to modify its pchead field, it does so via a MOVC instruction in its bytecode.)

Whenever the object changes state, its program counter (pc field) is also set directly to the location of the code block specified by the pccode field of the state descriptor for its new state. The entirety of the object's stack contents are then unwound, or the object's stack pointer is reset to its initial location, and an initial stack frame is [re]created. This program counter location and stack configuration is then restored after each subsequent non-code block interpretation is completed. (That is-since those interpretations require the program counter value to be replaced with a new location and a new stack frame to be created, after they are completed, the original program counter location and initial stack frame are restored). When the interpretation marked #5 in the above pseudo-code (the code block interpretation) finally occurs, the object's program counter will point to its code block and its stack frame will be the initial frame. The code block interpretation is yet another thread of interpretation for the object-a code thread.

For the sake of explanation, handles blocks are viewed as being equivalent to event blocks, so handles interpretations and threads are simply ignored. The timeline to the right shows an arbitrary frame of execution in terms of the times elapsed for the various interpretations of a single object's bytecode. Towards the beginning of the frame, the interpreter has performed a code block interpretation and a trans block interpretation for the object. Towards the end of the frame, the interpreter has performed an event block interpretation for that object, as it has been sent an event from some [unknown] source. The following timeline extends this timeline [to the right] to 6 arbitrary frames of execution:


The extended timeline makes it clear that this particular object has been configured to have its code block interpretation occur every other frame. It also shows the performance of more than one event block interpretation for that object during the second frame. The following timeline shows the same 6 frames of execution in terms of the times elapsed for all interpretations of each existing object's bytecode; in this particular instance, there are only 2 existing objects.

Stack Frames (incomplete)

A new stack frame is created for an object immediately prior to an interpretation of its bytecode. It is also created when jumping to and linking a bytecode routine via the JAL instruction. An object's initial stack frame is created when it enters its initial state, and is recreated for each subsequent state change (after the unwinding the stack). The initial stack frame is used for the object's code block interpretation.

A stack frame has the following format:

Offset Field Size Value
-0x4 x a **Argument a 4 bytes *
0x0 Preserved Interpreter Mode Flags 4 bytes 0xFFFF for initial frame; * otherwise
0x4 Preserved Program Counter 4 bytes *
0x8 Preserved Frame Pointer Relative Offset 2 bytes f
0xA Preserved Stack Pointer Relative Offset 2 bytes s
0xC Frame Data * x 4 bytes *

(**Arguments come before frames and are not actually 'part' of them)

...

UNFINISHED

[***also, do something with this: (*the ensuing head block interpretation begins at the location referenced by pchead, but preserved from before it is cleared; the interpretation actually occurs after the field is cleared.)]

Immediately prior to a subsequent non-code block interpretation, the new stack frame is actually created on top of the initial stack frame. This includes preserving the object's program counter, with the pccode/code block location, in the new frame. The program counter is then pointed to the [non-code] block location, which replaces its current value (i.e. the code block location), and interpretation begins. When the interpretation finishes, the object's program counter is restored to the code block location preserved in the frame created [for the interpretation], and the stack is unwound of that frame.

==
GOOL Instruction Table ==

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