The syntax for the script targeting the 24-bit interpreter works as follows:
First, an operator is supplied with a byte in the format aaabbbbb. bbbbb is the command to be executed. Commands that cause math to occur look at aaa to decide what sort of argument will follow the command. Other commands are things like "jump to subroutine" which always takes a 16 bit argument, or "return from subroutine", which doesn't take any argument.
The "return from subroutine" command 06 causes the game to pop an item from a stack of 16-bit addresses whose contents build up from address cc11, with the address of the top of the stack being tracked in cc0f-cc10.
When entering a command which does math, commands change meaning until "end of current expression" 1f is found. This second set of commands (which I've been calling operators) also follow a aaabbbbb format, with aaa having the same meaning as it does in the previous set of commands.
Within an expression, the game uses addresses cc05-cc07 as a 24-bit accumulator, while cc08-cc0a are in charge of receiving data from the argument. The interpreter will separate the data type from the operator's id, call a subroutine to grab the argument based on the data type, then call a subroutine to perform the operation requested with the two numbers now loaded.
The data types (aaa part of the commands listed above) appear to be as follows:
00 XX: Direct page. Points to c9XX as an 8 bit number.
20 XX: Direct page. Points to c9XX as a 16 bit number.
60 XX: Indirect page. Points to the data that the 16 bit address stored in c9XX itself points to.
c0 XX: Literal. Loads the number XX from the script.
e0 XX YY: Literal. Loads the number YYXX from the script as a 16 bit number.
Here are data types I haven't seen in action but that I suspect exist:
40 XX: Indirect page. Points to the data in the direct page that the 8 bit address stored in c9XX points to.
80, a0: should exist, but I have no knowledge of what they do. Maybe for 24 bit data? The virtual machine is set up to handle that but the code I've touched hasn't needed it.
Command 03, which is "call a GB-native code subroutine" is used sparingly. It's implementation is pretty cool, though - since the game boy's only CALL opcodes can only take a literal, but the game wants to use to the routine specified by the script, the game instead pushes the intended return address to the stack then jumps using JP (HL).
Here's an example of code I've written:
20 47 target c947 as a 16-bit number. This RAM seems to be unused.
ec 80 dc load the number dc80 - this is the address of a timer for animation; it cycles from 0 to f constantly. I'm going to use it like an RNG, which is probably OK because its current state depends on the exact frame when the player cleared the last text box.
1f save it
00 95 target c995 as a 8-bit number
6c 47 load the thing pointed to by c947. Notice I had to first point at the data and then use it; if there is a "load data from the following 16-bit address" data type, I haven't seen it.
c5 03 and with 03
1f save
05 YY YY call my "make sure robots have stuff" subroutine
05 cf 5d call subroutine 5dcf (does all the heavy lifting; I'm going to run out of space in this section of the ROM soon so continue code over there.)
02 e7 58 jump to 58e7 (i.e. continue on with the game's usual script here)
Inside subroutine 5dcf, here's a conditional where I use my "random" number:
61 47 Begin a conditional, with the left hand side of that conditional contained in the address c947 itself points to.
cf 02 0f seems to be the "<=" operator; a subtraction is performed immediately, and the game marks that 0f was the most recently used comparer in cc03. cf, then, is "compare with the number provided here".
1f Evaluate the expression. If it was true, do the next command. Otherwise, skip that command.
02 78 3f Jump to 3f78, where I handle turning the character into a robot.
The way this system handles data is the main reason I call it a virtual machine rather than a script engine - it has a direct page, it allows indirect indexing, etc, making coding for it feel far more like ASM than like BASIC or whatever. It maintains a stack pointer and an execution pointer, but doesn't seem to have the additional indexing registers processors generally do.