**
** GCSx object structure and script bytecode format
** Revision: August 16, 2006 Alexis Janson:
**   Added SENDT, SENDTs, QUERYT, QUERYTs (send/query when script type is known)
**   Adding GET, GETs, GETT, GETTf, GETTs, GETTh, GETTa, GETTv
**   Adding SET, SETs, SETT, SETTf, SETTs, SETTh, SETTa, SETTv
**

GET [Stack]/[Local]/[Ptr](#) [String] [Stack](S)/[Local](S)/[Global](G) *@

GET [Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](c$) [Stack](S)/[Local](S)/[Global](G) *@

GETs [Pop]/[Stack]/[Local]/[Ptr](c$) [String] [Stack](S)/[Local](S)/[Global](G) *@

GETs [Pop]/[Stack]/[Local]/[Ptr](c$) [Pop]/[Stack]/[Local]/[Ptr](c$) [Stack](S)/[Local](S)/[Global](G) *@

GETs [String] [String] [Stack](S)/[Local](S)/[Global](G) *@

GETs [String] [Pop]/[Stack]/[Local]/[Ptr](c$) [Stack](S)/[Local](S)/[Global](G) *@

GETT [Stack]/[Local]/[Ptr](#) [Int] [Int] [Int] *@

GETT [Stack]/[Local]/[Ptr](#) [Int] [Int] [Stack]/[Local]/[Ptr](#) *@

GETTf [Stack]/[Local]/[Ptr](#) [Int] [Int] [Float] *@

GETTf [Stack]/[Local]/[Ptr](#) [Int] [Int] [Stack]/[Local]/[Ptr](%) *@

GETTs [Stack]/[Local]/[Ptr](#) [Int] [Int] [String] *@

GETTs [Stack]/[Local]/[Ptr](#) [Int] [Int] [Stack]/[Local]/[Ptr](c$) *@

GETTh [Stack]/[Local]/[Ptr](#) [Int] [Int] [Stack]/[Local]/[Ptr](h) *@

GETTa [Stack]/[Local]/[Ptr](#) [Int] [Int] [Stack]/[Local]/[Ptr](a) *@

GETTv [Stack]/[Local]/[Ptr](#) [Int] [Int] [Stack](S)/[Local](S)/[Global](G) *@

SET [Stack]/[Local]/[Ptr](#) [String] [Stack](S)/[Local](S)/[Global](G) *@

SET [Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](c$) [Stack](S)/[Local](S)/[Global](G) *@

SETs [Pop]/[Stack]/[Local]/[Ptr](c$) [String] [Stack](S)/[Local](S)/[Global](G) *@

SETs [Pop]/[Stack]/[Local]/[Ptr](c$) [Pop]/[Stack]/[Local]/[Ptr](c$) [Stack](S)/[Local](S)/[Global](G) *@

SETs [String] [String] [Stack](S)/[Local](S)/[Global](G) *@

SETs [String] [Pop]/[Stack]/[Local]/[Ptr](c$) [Stack](S)/[Local](S)/[Global](G) *@

SETT [Stack]/[Local]/[Ptr](#) [Int] [Int] [Int] *@

SETT [Stack]/[Local]/[Ptr](#) [Int] [Int] [Stack]/[Local]/[Ptr](#) *@

SETTf [Stack]/[Local]/[Ptr](#) [Int] [Int] [Float] *@

SETTf [Stack]/[Local]/[Ptr](#) [Int] [Int] [Stack]/[Local]/[Ptr](%) *@

SETTs [Stack]/[Local]/[Ptr](#) [Int] [Int] [String] *@

SETTs [Stack]/[Local]/[Ptr](#) [Int] [Int] [Stack]/[Local]/[Ptr](c$) *@

SETTh [Stack]/[Local]/[Ptr](#) [Int] [Int] [Stack]/[Local]/[Ptr](h) *@

SETTa [Stack]/[Local]/[Ptr](#) [Int] [Int] [Stack]/[Local]/[Ptr](a) *@

SETTv [Stack]/[Local]/[Ptr](#) [Int] [Int] [Stack](S)/[Local](S)/[Global](G) *@

0) Introduction

This document describes the exact internal structure of objects and code in a GCSx game. The exact structure and storage of an object script or function library is detailed from its original text (source) format, to pre-link bytecode format, (compiled when the code is saved) to fully-linked bytecode format, ready for game play.

When stored to a file, integer values are stored in Intel format (little-endian) and floating point values are stored in standard IEEE formats. Pointers are stored in whatever format and size is native to the platform being run on. Pointers are never saved to a file.

1) Object Structure

1A) Global Variable Structure

Variables are stored as an 8-bit code that denotes the type of variable, and a 32-bit (or larger) value representing the variable. Integer and floating point values are stored as 32-bit values; strings, arrays, and hashes as pointers to more complex structures. String structures in memory are stored as either C-style NUL-terminated strings, or STL String objects, depending on circumstances. Array and hash structures include a reference count, counting the number of stack entries and variables that point to them.

The value or pointer for a variable is always in the same "place", regardless of variable type, so miscompiled code could accidentally read a pointer as an integer value, or vice-versa. Bytecode must be carefully compiled to know what value type will be present, or check this at runtime.

Variable type is 1 for integer, 2 for floating point, 4 for string, +8 for array, +16 for hash. An undefined variable will have a type of 0. This is not currently supported by the GCSx language directly, but can be used by certain bytecode constructs.

1B) Internal Stack Structure

Note that local variables of all types are stored on the stack.

An object stack can hold any number of items, accessed in a LIFO manner. An item consists of an 8-bit code denoting what that stack item contains, followed by a 32-bit value. (on platforms where pointers are larger than 32 bits, a stack value will be the size of a pointer instead.) Note that stack items and global variables have exactly the same format except for stack types 128, 129, and 130. Codes and values include-

Most bytecode opcodes assume stack items are of a certain type, but the type of an element can be checked if necessary. This is mostly needed if you are clearing up the stack, as you must deallocate/dereference structures and release other objects waiting on return values (type 129) or objects that expect to reply (type 130).

When a function is called within an object (for whatever reason, internal or external) the stack should have an optional 129 entry defining where a return value should go if on another object, followed by zero or more parameters for the function, follwed by a 128 entry defining where the return will jump.

1C) Internal Object Structure

Internally, an object stores the following data-

The actual, original code is stored elsewhere, and is not even loaded during gameplay. The bytecode is also stored elsewhere, as multiple objects can use the same bytecode.

1D) File Variable, Stack, Object Structures

(...notes on pointer storage/conversion...)

2) Function Library (and Codebase) Structure

A "function library" is used to store code that can be called from any object. A "codebase" is code that is assigned to one or more objects as their primary script. They are stored and structure almost identically.

2A) Internal Function Library (and Codebase) Structure

Internally, a function library or codebase stores the following data-

2B) File Function Library (and Codebase) Structure

(...notes on pointer storage/conversion...)

2C) Built-In Function Library Codebase

All built-in functions are C++ functions, accessed using the CALL opcode. However, QUERY, SEND, and SUBRs can only jump to bytecode- not directly to a C++ function. To allow these opcodes to call C++ functions, a special wrapper codebase is generated containing bytecode for calling each built-in function, and this bytecode is referenced during QUERY, SEND, and SUBRs opcode handling. Each wrapper is simply a PRECALL, CALL, and RET sequence.

3) Text Source Format

"Text source" is the code for a script or function in its original, human-readable format.

3A) Internal Text Source

The complete size of the text source is stored as a 32-bit integer. Text source is stored as a sequence of NUL-terminated strings. Each string is a single line of code. No newlines are stored. Nothing related to syntax coloring is stored.

3B) File Text Source

The complete size of the text source is stored as a 32-bit integer, followed immediately by the text source in the same format as internally.

4) Compiled Bytecode Format

"Bytecode" is a compiled form of code, designed to be quickly interpreted by the GCSx game engine. Whenever code for an object or function library is edited and saved, bytecode is compiled. This bytecode is kept stored in memory and as part of the world file in a "pre-link" state. This bytecode is mostly compiled, but not quite ready to be used. When the game is played, the code is "linked", which combines all loaded code and function libraries into a ready-to-use codebase. The resulting bytecode is in "post-link" format.

4A) Linking Process

4B) Internal Function Library (and Codebase) Structure - Post-link

After linking, the array of link data is cleared, as is any original source code if present. The array of function names is kept, for dynamic function lookup during gameplay.

5) Bytecode

This section describes how the bytecode works. Note that bytecode is highly optimized for speed, and hence when not in debugging mode, few safety checks are performed.

5A) Registers

Although bytecode looks similar to assembly, there are no registers. (other than the code and stack pointers, to draw assembly parallels) All operations work via the stack.

5B) Operand Types

The following types of operands are available to bytecode commands, also known as bytecode opcodes. Operands marked with * may not have their final value determined until post-link. All operands are 32 bits wide except on platforms with pointers larger than 32 bits.

5B1) [Int]

A 32-bit signed integer value.

5B2) [Float]

A 32-bit signed floating-point value.

5B4) [Offset]*

A 32-bit signed integer value representing the number of 32-bit dwords to look or move forward or backward in the current code. Positive values move forward, negative move backward. A value of zero represents the current opcode. This value will be adjusted if the code size changes during linking- this will only occur if pointers larger than 32 bits are used by the current system.

5B3) [String]*

Same as [Offset], but contains an offset to a location containing a static string stored within the bytecode. At this location is a NUL-terminated string. Strings are typically stored at the end of bytecode. This value will be adjusted if the code size changes during linking- this will only occur if pointers larger than 32 bits are used by the current system.

5B5) [Ptr]*

A pointer to a memory location. A 32-bit placeholder is stored during pre-link, and during the linking process, an actual pointer to a memory location is stored here. This points to either a built-in (coded in C++) function, a library (bytecode) function, or a global variable. If linked on a system that uses pointers of a size other than 32 bits, this field will expand as necessary, and all [Offset] and [String] values will adjust accordingly.

5B6) [Local]

A 32-bit unsigned integer value that references a stack entry from the bottom-up. 0 references the first added stack entry; 1, the next, and so on. This is used to access non-scoped local variables.

5B6) [Stack]

A 32-bit unsigned integer value that references a stack entry from the top-down. 0 references the most recently added stack entry; 1, one entry back, and so on.

5C) Bytecode Command Format

Each bytecode command consists of-

5D) Opcode Addressing Modes

Addressing modes allow one opcode to function in many ways; in addition, it allows the bytecode interpreter to prepare the operands before selecting a generic routine to perform. For example, although stack entries and global variables are accessed in different ways, the data itself is still the same, so a direct pointer to the data can be created and used to allow the same code to write to a stack entry or a variable. This also allows all debugging checks and assertions to be centralized.

All addressing modes return a pointer to an area of memory or structure containing the data, to be directly read or modified by the bytecode processor.

Each addressing mode is listed as the numeric value associated with it, as well as codes used to clarify which addressing modes apply to each format in the opcode reference, for [Stack], [Ptr], and [Local] operands. Also listed is what the entry is translated to at runtime before running the opcode.

Remember that [Local] mode is zero-based- 0 points to the bottom stack entry. [Stack] is also zero-based- 0 points to the top stack entry. [Offset] and [String] are zero-based- 0 points to the current opcode- and measured in 32-bit dwords.

5E) Opcodes

This section describes the available bytecode commands available. Commands marked with * may cause a task switch. (switch to another object) Commands marked with @ may cause a preemption check- if the current object's timeslice has taken too long, a task switch may occur. Task switches will never occur at any other time. Each opcode is listed with all addressing modes it supports. Opcodes may have many variations for different data types- these variations are technically different opcodes, but will have the same name except for extra lowercase letters to denote type.

All opcodes automatically have asserts based on their addressing modes to verify that the proper type of data has been given or referenced for storage, and that it is valid and within any range or boundaries as appropriate. (for example, a stack index is asserted to be within the current size of the stack minus local variables.) Any additional assertions are listed with the opcode. Assertions are only checked when debugging is compiled into GCSx. Any assertions that mention the stack apply only to the non-local-variable portion of the stack. Other non-specific assertions are also made, such as a script running past the end of its codebase, or an invalid opcode or addressing mode or combination.

Unlike normal C assertions, these assertions merely cause the current object to stop running and produce an error message. They do not cause GCSx itself to exit.

All opcodes that have been utilized by the bytecode compiler have been highlighted. Current opcode count: 96

ADD [Stack]/[Local]/[Ptr](#) [Int]

ADD [Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Takes the integer value given, adds the second value, and stores it back at the given location.

ADDf [Stack]/[Local]/[Ptr](%) [Float]

ADDf [Stack]/[Local]/[Ptr](%) [Pop]/[Stack]/[Local]/[Ptr](%)

Takes the floating-point value given, adds the second value, and stores it back at the given location.

AND [Stack]/[Local]/[Ptr](#) [Int]

AND [Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Takes the integer values given, performs a bitwise AND, and stores the new value back at the given location.

ARRAY [Stack]/[Local]/[Ptr](a) [Int]

ARRAY [Stack]/[Local]/[Ptr](a) [Pop]/[Stack]/[Local]/[Ptr](#)

Accesses the noted element of the array (the second operand is the index into the array) and pushes the element found onto the stack. If the desired element is outside the array bounds, an empty element (0, 0.0, or "") of the appropriate type is pushed.

CALL [Ptr](B) *

Asserts: PRECALL was previous instruction

Asserts: Returned value is not undefined

Transfers code execution to the given external/built-in function. [Ptr] points to an actual C++ function, of format "int function(Object* object, StackEntry* stacktop)".

This function returns non-zero if the cycle should be ended and the function should be called again next cycle. In this case, the code pointer does not move. The top stack location (from PRECALL) can be used by the function for temporary storage, so that it knows when it is being called repeatedly.

This function should return zero normally. The top stack location (from PRECALL) will contain the return value. The code pointer then moves to the next opcode normally.

CONCAT [Stack]/[Local]/[Ptr]($) [String]

CONCAT [Stack]/[Local]/[Ptr]($) [Pop]/[Stack]/[Local]/[Ptr](c$)

Takes the string value given, concatenate the second value, and store back at the given location.

COPY [Stack](S)

COPY [Local](S)

COPY [Global](G)

If the noted stack entry or variable is an array or hash, creates an entirely new copy and stores it in that location. Optimized so that if the noted hash or array only has one reference, nothing happens. If the noted entry is not an array or hash, nothing happens.

CREATEARRAY [Int]

Asserts: Value is 1, 2, or 4

Creates a new, empty array and pushes it onto the stack. The type of array created is determined by the value given- 1 for Integer, 2 for Float, 4 for String.

CREATEHASH [Int]

Asserts: Value is 1, 2, or 4

Creates a new, empty hash and pushes it onto the stack. The type of hash created is determined by the value given- 1 for Integer, 2 for Float, 4 for String.

DEBUG [Int] [Int]

States the current line and column number within the original source file, for debugging purposes. This opcode is only generated if debugging is enabled.

DISCARD [Int]

DISCARD [Pop]/[Stack]/[Local]/[Ptr](#)

Asserts: Count of items is not larger than current stack

Pops the given number of items off of the stack and discards them. Strings are freed, arrays and hashes are dereferenced, pointers to objects waiting on return values return an empty value, and waiting-for-reply pointers (type 130) modify the target 129 to be a NULL pointer.

DISCARDL [Int]

DISCARDL [Pop]/[Stack]/[Local]/[Ptr](#)

Asserts: Count of items is not larger than current stack

Asserts: Items being discarded are 0, 1, 2, or 128 types only

Pops the given number of items off of the stack and discards them. No checking is done for strings, arrays, hashes, or return types. This is a faster version of DISCARD intended for use when you know the stack entries are simple values.

DIV [Stack]/[Local]/[Ptr](#) [Int]

DIV [Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Takes the integer value given, divides by the second value, and stores the dividend at the given location. Dividing by zero will produce an unpredictable value, but will not cause an error.

DIVf [Stack]/[Local]/[Ptr](%) [Float]

DIVf [Stack]/[Local]/[Ptr](%) [Pop]/[Stack]/[Local]/[Ptr](%)

Takes the floating-point value given, divides by the second value, and stores the dividend at the given location. Dividing by zero will produce an unpredictable value, but will not cause an error.

EQ [Pop]/[Stack]/[Local]/[Ptr](#) [Int]

EQ [Pop]/[Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Compares the two integer values given. If equal, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack.

EQf [Pop]/[Stack]/[Local]/[Ptr](%) [Float]

EQf [Pop]/[Stack]/[Local]/[Ptr](%) [Pop]/[Stack]/[Local]/[Ptr](%)

Compares the two floating-point values given. If equal, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack.

EQs [Pop]/[Stack]/[Local]/[Ptr](c$) [String]

EQs [Pop]/[Stack]/[Local]/[Ptr](c$) [Pop]/[Stack]/[Local]/[Ptr](c$)

Compares the two string values given. If equal, ignoring case, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack.

FTOI [Stack](S)

Asserts: Stack entry is a floating-point value

Converts the given floating-point value to an integer in-place.

FTOS [Stack](S)

Asserts: Stack entry is a floating-point value

Converts the given floating-point value to a string in-place.

FORCEARRAY [Stack](S) [Int]

Asserts: Stack entry is a value or undefined value (not a 128, 129, or 130)

Asserts: Value is 1, 2, or 4

If the stack entry is an array of the proper type, nothing happens. Otherwise, it is replaced by a newly-created, empty array. The type of array created is determined by the value given- 1 for Int, 2 for Float, 4 for String. If the stack entry was an array of an improper type but could be converted, the newly-created array will contain the converted values.

The FORCE* commands are typically used after a QUERY* or SUBRs command, to ensure the return value of an unknown function matches the expected type.

FORCEFLOAT [Stack](S)

Asserts: Stack entry is a value or undefined value (not a 128, 129, or 130)

If the stack entry is a floating-point value, nothing happens. If the entry is an integer or string, it is converted to floating-point. Otherwise, it is replaced by a floating-point value with the value 0.0.

FORCEHASH [Stack](S) [Int]

Asserts: Stack entry is a value or undefined value (not a 128, 129, or 130)

Asserts: Value is 1, 2, or 4

If the stack entry is a hash of the proper type, nothing happens. Otherwise, it is replaced by a newly-created, empty hash. The type of hash created is determined by the value given- 1 for Integer, 2 for Float, 4 for String. If the stack entry was a hash of an improper type but could be converted, the newly-created hash will contain the converted values.

FORCEINT [Stack](S)

Asserts: Stack entry is a value or undefined value (not a 128, 129, or 130)

If the stack entry is an integer, nothing happens. If the entry is a floating-point value or string, it is converted to an integer. Otherwise, it is replaced by an integer with the value 0.

FORCESTRING [Stack](S)

Asserts: Stack entry is a value or undefined value (not a 128, 129, or 130)

If the stack entry is a string, nothing happens. If the entry is a floating-point value or integer, it is converted to a string. Otherwise, it is replaced by an empty string.

GE [Pop]/[Stack]/[Local]/[Ptr](#) [Int]

GE [Pop]/[Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Compares the two integer values given. If the first value is greater than or equal to the second, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack.

GEf [Pop]/[Stack]/[Local]/[Ptr](%) [Float]

GEf [Pop]/[Stack]/[Local]/[Ptr](%) [Pop]/[Stack]/[Local]/[Ptr](%)

Compares the two floating-point values given. If the first value is greater than or equal to the second, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack.

GEs [Pop]/[Stack]/[Local]/[Ptr](c$) [String]

GEs [Pop]/[Stack]/[Local]/[Ptr](c$) [Pop]/[Stack]/[Local]/[Ptr](c$)

Compares the two strings given. If the first string is greater than or equal to the second, ignoring case, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack. 8-bit ASCII collation is used, with lowercase letters sorting as if they were uppercase.

GT [Pop]/[Stack]/[Local]/[Ptr](#) [Int]

GT [Pop]/[Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Compares the two integer values given. If the first value is greater than (but not or equal to) the second, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack.

GTf [Pop]/[Stack]/[Local]/[Ptr](%) [Float]

GTf [Pop]/[Stack]/[Local]/[Ptr](%) [Pop]/[Stack]/[Local]/[Ptr](%)

Compares the two floating-point values given. If the first value is greater than (but not equal to) the second, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack.

GTs [Pop]/[Stack]/[Local]/[Ptr](c$) [String]

GTs [Pop]/[Stack]/[Local]/[Ptr](c$) [Pop]/[Stack]/[Local]/[Ptr](c$)

Compares the two strings given. If the first string is greater than (but not equal to) the second, ignoring case, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack. 8-bit ASCII collation is used, with lowercase letters sorting as if they were uppercase.

HASH [Stack]/[Local]/[Ptr](h) [String]

HASH [Stack]/[Local]/[Ptr](h) [Pop]/[Stack]/[Local]/[Ptr](c$)

Accesses the noted element of the hash (the second operand is the index into the hash) and pushes the element found onto the stack. If no matching element is found, an empty element (0, 0.0, or "") of the appropriate type is pushed.

IFFALSE [Pop]/[Stack]/[Local]/[Ptr](#) [Offset] @

IFFALSEf [Pop]/[Stack]/[Local]/[Ptr](%) [Offset] @

If the value given is 0 or 0.0, transfers code execution to the given location.

IFTRUE [Pop]/[Stack]/[Local]/[Ptr](#) [Offset] @

IFTRUEf [Pop]/[Stack]/[Local]/[Ptr](%) [Offset] @

If the value given is something other than 0 or 0.0, transfers code execution to the given location.

INIT *

Asserts: INIT must be present in all objects (asserted at link-time)

Variable initialization is done. All objects on all scenes get to run once cycle before the game actually starts, during which they are expected to initialize global and local variables and such. INIT marks the end of this segment of code, switching to the next object. This also takes the current stack depth and stores it as the count of local variables. If an object has no initialization code, INIT or STOP should be the first opcode. No restrictions or preemptions are placed on code that runs before INIT, so this code should not enter any infinite loops, etc. or the bytecode engine will hang.

ITOF [Stack](S)

Asserts: Stack entry is an integer value

Converts the given integer to a floating-point value in-place.

ITOS [Stack](S)

Asserts: Stack entry is an integer value

Converts the given integer to a string in-place.

JUMP [Offset] @

Asserts: Valid-looking opcode at given location

Transfers code execution to the given location.

LE [Pop]/[Stack]/[Local]/[Ptr](#) [Int]

LE [Pop]/[Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Compares the two integer values given. If the first value is less than or equal to the second, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack.

LEf [Pop]/[Stack]/[Local]/[Ptr](%) [Float]

LEf [Pop]/[Stack]/[Local]/[Ptr](%) [Pop]/[Stack]/[Local]/[Ptr](%)

Compares the two floating-point values given. If the first value is less than or equal to the second, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack.

LEs [Pop]/[Stack]/[Local]/[Ptr](c$) [String]

LEs [Pop]/[Stack]/[Local]/[Ptr](c$) [Pop]/[Stack]/[Local]/[Ptr](c$)

Compares the two strings given. If the first string is less than or equal to the second, ignoring case, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack. 8-bit ASCII collation is used, with lowercase letters sorting as if they were uppercase.

LT [Pop]/[Stack]/[Local]/[Ptr](#) [Int]

LT [Pop]/[Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Compares the two integer values given. If the first value is less than (but not or equal to) the second, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack.

LTf [Pop]/[Stack]/[Local]/[Ptr](%) [Float]

LTf [Pop]/[Stack]/[Local]/[Ptr](%) [Pop]/[Stack]/[Local]/[Ptr](%)

Compares the two floating-point values given. If the first value is less than (but not equal to) the second, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack.

LTs [Pop]/[Stack]/[Local]/[Ptr](c$) [String]

LTs [Pop]/[Stack]/[Local]/[Ptr](c$) [Pop]/[Stack]/[Local]/[Ptr](c$)

Compares the two strings given. If the first string is less than (but not equal to) the second, ignoring case, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack. 8-bit ASCII collation is used, with lowercase letters sorting as if they were uppercase.

LOGAND

Asserts: Top two entries of stack are values (not 0, 128, 129, or 130)

Pops the top two entries of the stack. If both are true, pushes an integer 1 onto the stack; otherwise, pushes an integer 0 onto the stack. "true" is considered to be anything but a 0 or 0.0 value.

LOGOR

Asserts: Top two entries of stack are values (not 0, 128, 129, or 130)

Pops the top two entries of the stack. If either one is true, pushes an integer 1 onto the stack; otherwise, pushes an integer 0 onto the stack. "true" is considered to be anything but a 0 or 0.0 value.

MOD [Stack]/[Local]/[Ptr](#) [Int]

MOD [Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Takes the integer value given, divide by the second value, and store the remainder back at the given location. Dividing by zero will produce an unpredictable value, but will not cause an error.

MULT [Stack]/[Local]/[Ptr](#) [Int]

MULT [Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Takes the integer value given, multiplys by the second value, and stores the product back at the given location.

MULTf [Stack]/[Local]/[Ptr](%) [Float]

MULTf [Stack]/[Local]/[Ptr](%) [Pop]/[Stack]/[Local]/[Ptr](%)

Takes the floating-point value given, multiplys by the second value, and stores the product back at the given location.

NE [Pop]/[Stack]/[Local]/[Ptr](#) [Int]

NE [Pop]/[Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Compares the two integer values given. If not equal, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack.

NEf [Pop]/[Stack]/[Local]/[Ptr](%) [Float]

NEf [Pop]/[Stack]/[Local]/[Ptr](%) [Pop]/[Stack]/[Local]/[Ptr](%)

Compares the two floating-point values given. If not equal, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack.

NEs [Pop]/[Stack]/[Local]/[Ptr](c$) [String]

NEs [Pop]/[Stack]/[Local]/[Ptr](c$) [Pop]/[Stack]/[Local]/[Ptr](c$)

Compares the two string values given. If not equal, ignoring case, pushes an integer 1 onto the stack. Otherwise, an integer 0 is pushed onto the stack.

NEG [Stack]/[Local]/[Ptr](#)

Takes the integer value given and negates it, storing the new value back at the given location. (negation results in the opposite sign)

NEGf [Stack]/[Local]/[Ptr](%)

Takes the floating-point value given and negates it, storing the new value back at the given location. (negation results in the opposite sign)

NOOP

Does nothing.

NOT

Asserts: Top entry of stack is a value (not 0, 128, 129, or 130)

Pops the top entry of the stack. If it was true, pushes an integer 0 onto the stack; otherwise, pushes an integer 1 onto the stack. "true" is considered to be anything but a 0 or 0.0 value.

OR [Stack]/[Local]/[Ptr](#) [Int]

OR [Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Takes the integer values given, performs a bitwise OR, and stores the new value back at the given location.

PRECALL

Asserts: CALL is next instruction

Pushes an undefined (different from 0) value onto the stack to hold CALL return information. Always used right before a CALL opcode.

This is done separate from the actual CALL so that the code pointer may remain pointed at the CALL instruction with no danger of multiple values being pushed onto the stack- PRECALL will cause one and exactly one return storage value to be pushed.

POP [Stack]/[Local]/[Ptr](#)

Asserts: Top entry of stack exists and is an integer

Asserts: Not popping to [Stack]0

Pops an integer off the stack and stores it in the given location. Note for all POP opcodes- if storing to a stack entry, the position of the entry is "locked in" before the POP occurs, since addressing occurs before any command results.

POPa [Stack]/[Local]/[Ptr](a)

Asserts: Top entry of stack exists and is an array

Asserts: Destination is of same array type as array on top of stack

Asserts: Not popping to [Stack]0

Pops an array off the stack and stores it in the given location. The array already at the current location is dereferenced. Use COPY if you wish to make a new copy and not potentially reference an existing array.

POPf [Stack]/[Local]/[Ptr](%)

Asserts: Top entry of stack exists and is a floating-point value

Asserts: Not popping to [Stack]0

Pops a floating-point value off the stack and stores it in the given location.

POPh [Stack]/[Local]/[Ptr](h)

Asserts: Top entry of stack exists and is a hash

Asserts: Destination is of same hash type as hash on top of stack

Asserts: Not popping to [Stack]0

Pops a hash off the stack and stores it in the given location. The hash already at the current location is dereferenced. Use COPY if you wish to make a new copy and not potentially reference an existing hash.

POPs [Stack]/[Local]/[Ptr]($)

Asserts: Top entry of stack exists and is a string

Asserts: Not popping to [Stack]0

Pops a string off the stack and stores it in the given location. The string already at the current location is freed.

POPv [Stack](S)

POPv [Local](S)

POPv [Global](G)

Asserts: Top entry of stack exists and is not a 0, 128, 129, or 130

Asserts: If storing to a global, it is the proper type or a variant type

Pops any type of value off the stack and stores it in the given location. Use COPY if you wish to make a new copy and not potentially reference an existing array or hash.

POPARRAY [Stack]/[Local]/[Ptr](a) [Int]

POPARRAY [Stack]/[Local]/[Ptr](a) [Pop]/[Stack]/[Local]/[Ptr](#)

Asserts: Top entry of stack exists and is of same type as array

Accesses the noted element of the array (the second operand is the index into the array), pops the top value off of the stack, and stores the popped value into the noted element of the array. If the desired element is beyond the current range of the array, the array is extended until the new element will fit.

POPHASH [Stack]/[Local]/[Ptr](h) [String]

POPHASH [Stack]/[Local]/[Ptr](h) [Pop]/[Stack]/[Local]/[Ptr](c$)

Asserts: Top entry of stack exists and is of same type as hash

Accesses the noted element of the hash (the second operand is the index into the hash), pops the top value off of the stack, and stores the popped value into the noted element of the hash. A new element is created if one doesn't exist.

PUSH [Int]

PUSH [Stack]/[Local]/[Ptr](#)

Takes the given integer and adds it onto the stack as the top item. Note for all PUSH opcodes- if copying from another stack entry, the position of the entry is "locked in" before the PUSH occurs, since addressing occurs before any command results.

PUSHa [Stack]/[Local]/[Ptr](a)

Takes the given array and adds it onto the stack as the top item. Reference count is increased. Use COPY if you wish to make a new copy and not potentially reference an existing array.

PUSHf [Float]

PUSHf [Stack]/[Local]/[Ptr](%)

Takes the given floating-point value and adds it onto the stack as the top item.

PUSHh [Stack]/[Local]/[Ptr](h)

Takes the given hash and adds it onto the stack as the top item. Reference count is increased. Use COPY if you wish to make a new copy and not potentially reference an existing hash.

PUSHs [String]

PUSHs [Stack]/[Local]/[Ptr](c$)

Takes the given string, copies it, and adds it onto the stack as the top item.

PUSHv [Stack](S)

PUSHv [Local](S)

PUSHv [Global](G)

Takes the given stack entry or variable, copies it, and adds it to the stack as the top item. Works on all types of variables/stack entries. Use COPY if you wish to make a new copy and not potentially reference an existing array or hash.

QUERY [Stack]/[Local]/[Ptr](#) [String] [Int] *@

QUERY [Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](c$) [Int] *@

Asserts: [Int] entries exist on stack

Asserts: Next instruction is WAIT

This switches control to another object and calls a function of that object, then waits for a return value.

The next opcode will be WAIT, to prevent resuming code execution until the top value of the stack is no longer type 130, signifying the located object has returned a value.

QUERYs [Pop]/[Stack]/[Local]/[Ptr](c$) [String] [Int] *@

QUERYs [Pop]/[Stack]/[Local]/[Ptr](c$) [Pop]/[Stack]/[Local]/[Ptr](c$) [Int] *@

QUERYs [String] [Pop]/[Stack]/[Local]/[Ptr](c$) [Int] *@

QUERYs [String] [String] [Int] *@

Asserts: [Int] entries exist on stack

Asserts: Next instruction is WAIT

This command is the same as QUERY, except for the third step. Instead of locating an object by ID, an object matching the given name is located. If more than one match is found, one is chosen arbitrarily. (the first one found, although no order is defined)

QUERYT [Stack]/[Local]/[Ptr](#) [Ptr] [Int] [Int] *@

Asserts: Next instruction is WAIT

Asserts: [Int] (last param) entries exist on stack

Asserts: [Int] (third param) respresents a valid script id or zero

Asserts: Parameter types have the proper, matching datatypes

This is the same as QUERY, except a pointer directly to the target function is provided. The first [Int] represents the id of the script being called- this is used simply to verify that the target object is running the right script. This can be a zero value to signify that any script type is valid- this is only valid if [Ptr] points to within a function library.

The second [Int] represents the number of function parameters on the stack. It is assumed that the parameters are all of the proper type and count- no automatic parameter (or return value) conversion is done.

QUERYTs [Pop]/[Stack]/[Local]/[Ptr](c$) [Ptr] [Int] [Int] *@

QUERYTs [String] [Ptr] [Int] [Int] *@

Asserts: Next instruction is WAIT

Asserts: [Int] (last param) entries exist on stack

Asserts: [Int] (third param) respresents a valid script id or zero

Asserts: Parameter types have the proper, matching datatypes

This is a combination of QUERYs and QUERYT. When searching for matching objects, only objects matching the given script id are 'found', unless the given script id is zero.

REPLY [Int] *

Asserts: Top entry of stack is a value (not a 0, 128, 129, or 130)

Asserts: [Int] + 2 entries exist on stack

Asserts: Stack entry pointed to by "object return value" pointer (if one is present) is type 130

This returns a value to an object waiting on a return value. It is similar to RET in operation, without a code jump. [Int] represents the number of parameters and local variables currently on the stack.

Control will switch if a valid object return value pointer was found, and that object switched control to the current object during the current cycle. This is handled internally by the bytecode interpreter and does not involve any state within the stack or object.

RET [Int] [Int] *

Asserts: Top entry of stack is a value (not a 0, 128, 129, or 130)

Asserts: [Int] + [Int] + 2 entries exist on stack

Asserts: 1 entry down is a code location

Asserts: Stack entry pointed to by "object return value" pointer (if one is present) is type 130

This returns from a "function call", using the top entry of the stack as the return value. [Int] represents the number of locals/temporaries currently on the stack and the number of parameters currently on the stack, respectively.

Control may switch back to another object in some situations. This happens when another object was waiting on the return value and switched control to the current object during the current cycle. This is handled internally by the bytecode interpreter and does not involve any state within the stack or object.

SEND [Stack]/[Local]/[Ptr](#) [String] [Int] *@

SEND [Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](c$) [Int] *@

Asserts: [Int] entries exist on stack

This switches control to another object and calls a function of that object, but doesn't wait or accept a return value.

The current object will resume code execution if the located object finishes the function called before a task switch; otherwise, it will resume code execution at the next task switch. This is handled internally by the bytecode interpreter and does not involve any state within the stack or object.

SENDs [Pop]/[Stack]/[Local]/[Ptr](c$) [String] [Int] *@

SENDs [Pop]/[Stack]/[Local]/[Ptr](c$) [Pop]/[Stack]/[Local]/[Ptr](c$) [Int] *@

SENDs [String] [Pop]/[Stack]/[Local]/[Ptr](c$) [Int] *@

SENDs [String] [String] [Int] *@

This command is the same as SEND, except for the second step. Instead of locating an object by ID, an object matching the given name is located. If more than one match is found, then each object has the steps applied individually, except that the stack entries are removed only once. Control switches to the first object affected. All objects affected get a task switch, in turn, before control returns to the original object.

SENDT [Stack]/[Local]/[Ptr](#) [Ptr] [Int] [Int]*@

Asserts: [Int] (last param) entries exist on stack

Asserts: [Int] (third param) respresents a valid script id or zero

Asserts: Parameter types have the proper, matching datatypes

This is the same as SEND, except a pointer directly to the target function is provided. The first [Int] represents the id of the script being called- this is used simply to verify that the target object is running the right script. This can be a zero value to signify that any script type is valid- this is only valid if [Ptr] points to within a function library.

The second [Int] represents the number of function parameters on the stack. It is assumed that the parameters are all of the proper type and count- no automatic parameter (or return value) conversion is done.

SENDTs [Pop]/[Stack]/[Local]/[Ptr](c$) [Ptr] [Int] [Int]*@

SENDTs [String] [Ptr] [Int] [Int]*@

Asserts: [Int] (last param) entries exist on stack

Asserts: [Int] (third param) respresents a valid script id or zero

Asserts: Parameter types have the proper, matching datatypes

This is a combination of SENDs and SENDT. When searching for matching objects, only objects matching the given script id are 'found', unless the given script id is zero.

SHIFTL [Stack]/[Local]/[Ptr](#) [Int]

SHIFTL [Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Takes the integer value given, bitwise shifts left the amount of the second value, and stores the result back at the given location.

SHIFTR [Stack]/[Local]/[Ptr](#) [Int]

SHIFTR [Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Takes the integer value given, bitwise shifts right the amount of the second value, and stores the result back at the given location.

STOF [Stack](S)

Asserts: Stack entry is a string value

Converts the given string to a floating-point value in-place.

STOI [Stack](S)

Asserts: Stack entry is a string value

Converts the given string to an integer in-place.

STOP *

Stop running entirely. The stack is cleared. Strings are freed, arrays and hashes are dereferenced, pointers to objects waiting on return values return an empty value, and waiting-for-reply pointers (type 130) modify the target 129 to be a NULL pointer. Control switches to the next object.

STORE [Stack]/[Local]/[Ptr](#) [Int]

STORE [Stack]/[Local]/[Ptr](#) [Stack]/[Local]/[Ptr](#)

Takes the given integer and stores it at the given location.

STOREa [Stack]/[Local]/[Ptr](a) [Stack]/[Local]/[Ptr](a)

Asserts: Source and destination are of same array type

Takes the given array and stores it at the given location, increasing reference count and dereferencing the array previously there. Use COPY if you wish to make a new copy and not potentially reference an existing array.

STOREf [Stack]/[Local]/[Ptr](%) [Float]

STOREf [Stack]/[Local]/[Ptr](%) [Stack]/[Local]/[Ptr](%)

Takes the given floating-point value and stores it at the given location.

STOREh [Stack]/[Local]/[Ptr](h) [Stack]/[Local]/[Ptr](h)

Asserts: Source and destination are of same hash type

Takes the given hash and stores it at the given location, increasing reference count and dereferencing the hash previously there. Use COPY if you wish to make a new copy and not potentially reference an existing hash.

STOREs [Stack]/[Local]/[Ptr]($) [String]

STOREs [Stack]/[Local]/[Ptr]($) [Stack]/[Local]/[Ptr](c$)

Takes the given string and copies it at the given location, freeing the string currently there.

SUB [Stack]/[Local]/[Ptr](#) [Int]

SUB [Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Takes the integer value given, subtracts the second value, and stores the result back at the given location.

SUBf [Stack]/[Local]/[Ptr](%) [Float]

SUBf [Stack]/[Local]/[Ptr](%) [Pop]/[Stack]/[Local]/[Ptr](%)

Takes the floating-point value given, subtracts the second value, and stores the result back at the given location.

SUBR [Offset] @

SUBR [Ptr](F) @

Asserts: Valid-looking opcode at given location

Push the location of the next instruction onto the stack, then transfer code execution to the given location. Note that, when the code returns, the top stack location will always contain a return value.

Unlike other methods of accessing functions, this one assumes the proper number and type of operands have already been pushed onto the stack.

SUBRs [Stack]/[Local]/[Ptr]($) [Int] @

Asserts: [Int] entries exist on stack

(note- ($) could become (c$) if more efficient)

This calls a function by name, adjusting the parameters on the stack as needed. This opcode is not affected by queueing.

Note that, when the code returns, the top stack location will always contain a return value, which may be undefined.

SWAP [Stack](S) [Stack](S)

Asserts: Source and destination are actual data, not code, object return pointers, or undefined

Takes the given stack entry and swaps it with another stack entry. No copying, freeing, or referencing is required.

TYPEOF [Stack](S)

TYPEOF [Local](S)

TYPEOF [Global](G)

Examines the type of entry or variable at the given location, and pushes an integer value onto the stack representing what it is. The value pushed matches the type values used for stack entries- 1 for integer, 2 for floating point, 4 for string, +8 for array, +16 for hash, 0 for undefined, 128 for code pointer, 129 for return value pointer, and 130 for reply waiting pointer.

WAIT *

Asserts: At least one entry on the stack

Asserts: Previous instruction is a QUERY variant

The code pointer will remain at this opcode until the top entry of the stack is no longer a type 130 value. This causes the object to wait for a RET or REPLY value immediately after a QUERY command.

XOR [Stack]/[Local]/[Ptr](#) [Int]

XOR [Stack]/[Local]/[Ptr](#) [Pop]/[Stack]/[Local]/[Ptr](#)

Takes the integer values given, performs a bitwise XOR, and stores the new value back at the given location.

6) Bytecode Compilation

This section describes how the GCSx language translates into bytecode.

6A) Notes on Global Variables, Constants, and Function Libraries

Globals and function libraries define things outside of the code that they are referenced in. This is handled as follows-

All non-library code is first scanned for global constant and variable declarations, then all code is compiled. Unrecognized identifiers are treated as function library calls. Any missing functions will be noted during linking.

During editing, modifying the code of any object that contains (or contained before editing) global declarations will cause a recompile of all objects. Modifying a function library does not require any recompilation.

6B) Notes on Stack Usage

Many operations and calculations require judicious usage of the stack for temporary storage. During compilation, the current amount of temporary storage being used is carefully tracked line-by-line, opcode-by-opcode. This space is freed up as soon as the operations are complete. Certain structures such as if-else require temporary storage to remain on the stack for an extended block of code; this storage is freed at the earliest possible opportunity. If a goto exists in such a block, the temporary storage must be freed before the goto. If a label exists in such a block, any goto going to that label must first fill the stack with appropriate values matching the expected amount of temporary storage.

The stack is also used for all local and scoped variables.