GOOL Interpreter
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
A single interpretation of an object's bytecode consists of the following steps:
- 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
- Perform the operation specified by the fetched instruction
- 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.)
UNFINISHED
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.
The GOOL bytecode interpreter routine expects an object that knows the following:
- 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
routine ('interpret()') can be called on the same object from up to 5 distinct locations in a single
iteration of the main game loop:
MAIN LOOP:
{
...
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
...
}
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 |