-
Notifications
You must be signed in to change notification settings - Fork 0
Script interpreter
The script interpreter is very much based on the x86 architecture or more generally said a register-memory-architecture. Calculations are performed as 32 bit signed integer for fixed point and 32 bit float for floating point.
0xIICCCCCC (0xP1P1P1P1) (0xP2P2P2P2)
I = instance id, C = opcode, P1 = optional first parameter, P2 = optional second parameter
Opcodes have variable length depending on how much parameters they take. The opcode itself takes 4 byte and each parameter additional 4 byte. There can be 0 to 2 parameters.
- SREG_SP: Holds the stack pointer. It points to the next free stack address.
- SREG_MAR: Holds a memory address for reading from and writing to memory. Direct memory reads and writes go through this register. An exception are reads to values on the stack, which can be accessed relative to the stack pointer.
- SREG_OP: When calling a member function of an AGS script object, this register holds the memory address of the object.
- SREG_AX, SREG_BX, SREG_CX, SREG_DX: General purpose registers. They hold both int and float values since there are no dedicated floating point registers. I am not sure if CX and DX are even actually used by the compiler.
Parameters for functions are always passed through the stack. The return value is stored in SREG_AX.
The machine distinguishes three kinds of function calls:
- Save program counter to the callstack array
- Set program counter to the entry of the called function
- Restore program counter
- Get instance id of callee script
- Save program counter and stack pointer
- Call function
- Restore program counter and stack pointer
- Parameters are pushed onto the callstack array via SCMD_PUSHREAL
- If the function is a script object member function, set the object with SCMD_CALLOBJ
- SCMD_CALLEXT is called with the address of the C function as first parameter
- Call function, return value is in SREG_AX
- The C function can have between 0 and 9 parameters
Static variables are accessed through C pointer values directly in the code.
The opcodes for accessing an imported variable are:
- Write variable address (SCMD_LITTOREG) into SREG_MAR register
- Note, that this address is subsituted by actual object address during runtime script initialization
- For structs - add (SCMD_ADD) offset to SREG_MAR (only of offset is greater than zero)
The opcodes for accessing a global variable are:
- Write global data offset (SCMD_LITTOREG) into SREG_MAR register
- Note, that this address is subsituted by actual global data address during runtime script initialization
For either type of variable, if that variable is a static array:
- Add (SCMD_ADDREG) element offset to SREG_MAR, copying it from SREG_CX (where it was previously stored)
Then, it is one of the following:
- Writing to variable (SCMD_MEMWRITEPTR, value copied from SREG_AX),
- Reading variable (SCMD_MEMREADPTR, value copied to SREG_AX),
- Store the variable address (SCMD_REGTOREG, address copied to SREG_AX)
Store address of Nth element of global game's array (e.g. 'objects' or 'characters') to the AX:
- SCMD_LITTOREG | SREG_MAR | < ARRAY_PTR >
- SCMD_ADDREG | SREG_MAR | SREG_CX
- SCMD_REGTOREG | SREG_MAR | SREG_AX
Dynamic script objects are represented through "handles". A handle is assigned when an object is created and destroyed when the reference count for the object reaches zero (garbage collection). Opcodes for dynamic objects are somewhat unintuitively named. The "pointer" part of their name has to be understand as an AGS object pointer in the scope of the scripting language and not a C/C++ pointer.
- SCMD_MEMWRITEPTR 47 // m[MAR] = reg1 (adjust ptr addr)
- SCMD_MEMREADPTR 48 // reg1 = m[MAR] (adjust ptr addr)
- SCMD_MEMZEROPTR 49 // m[MAR] = 0 (blank ptr)
- SCMD_MEMINITPTR 50 // m[MAR] = reg1 (but don't free old one)
- SCMD_MEMZEROPTRND 69 // m[MAR] = 0 (blank ptr, no dispose if = ax)
Only the "globaldata" block for each script module and the global script is persisted in save games. This means neither the register contents nor the stack is stored, implying that a savegame cannot be created while a script runs but only at the end of a game frame.