Variables & Memory
GBVM variable system, heap layout, stack memory, constants, math utilities, and the interrupt system. The variable system uses C for engine code and the GBVM virtual machine at runtime.
Variable System Overview
All game variables in GB Studio are INT16 (signed 16-bit integers), with a range of -32768 to 32767. They are stored in the GBVM heap and referenced by index (0–767). Variables are the primary mechanism for tracking game state, scores, flags, positions, and any other mutable data.
- Type:
INT16(signed 16-bit) - Range: -32768 to 32767
- Storage: GBVM heap memory
- Addressing: by index (0–767)
- Used for: game state, scores, flags, positions, counters
GBVM Memory Layout
The GBVM virtual machine manages three key memory regions: the shared heap, per-context stacks, and the instruction budget.
| Constant | Value | Description |
|---|---|---|
VM_HEAP_SIZE | 768 | Shared variable slots (INT16 each) |
VM_MAX_CONTEXTS | 16 | Concurrent script threads |
VM_CONTEXT_STACK_SIZE | 64 | Words per thread stack |
INSTRUCTIONS_PER_QUANT | 0x10 (16) | Instructions executed per time slice |
Heap Organization
The heap is a flat array of 768 INT16 slots. Global game variables occupy the lower indices; system and temporary storage use higher indices.
Index 0-N: Global game variables (defined in project)
Index N+1...: System variables, temporary storage
Total: 768 slots × 2 bytes = 1536 bytes
Stack Memory
Each of the 16 script contexts has its own 64-word stack, used for local variables, function arguments, and RPN calculations.
- Stack grows downward (negative offsets from stack pointer)
- Local variables are allocated via
VM_PUSH_CONSTor_declareLocal - Must be cleaned up with
VM_POPbefore script exit
Function Argument Offsets
| Constant | Offset | Description |
|---|---|---|
FN_ARG0 | -1 | First argument (top of caller's stack) |
FN_ARG1 | -2 | Second argument |
FN_ARG2 | -3 | Third argument |
FN_ARG3 | -4 | Fourth argument |
FN_ARG4 | -5 | Fifth argument |
FN_ARG5 | -6 | Sixth argument |
FN_ARG6 | -7 | Seventh argument |
FN_ARG7 | -8 | Eighth argument |
Variable References
In Event Scripts (.gbsres JSON)
Variables are referenced by their ID (UUID or index) in event JSON files.
// Direct variable reference
{"type": "variable", "value": "12"}
// In expressions — $ID$ syntax references variable by index
{"type": "expression", "value": "$12$ + 1"}
// Multiple variables in one expression
{"type": "expression", "value": "$07$ >= 6"}
In GBVM Assembly
GBVM instructions use VAR_* defines to reference variables by name.
VM_SET_CONST VAR_MY_VARIABLE, 42 ; Set variable to constant
VM_GET_INT8 VAR_RESULT, _my_c_var ; Read C variable into GBVM variable
VM_SET_INT8 _my_c_var, VAR_SOURCE ; Write GBVM variable to C variable
In Event Plugin JavaScript
Event plugins use the helpers API to work with variables.
// Reading a variable field from the event input
const varAlias = helpers.getVariableAlias(input.myVariable);
// Setting a variable from a script value
helpers.variableSetToScriptValue(varAlias, input.myValue);
// Using in RPN calculations
helpers._rpn()
.ref(varAlias) // Push variable value
.int16(10) // Push constant
.operator(".ADD") // Add top two values
.refSet(varAlias) // Pop and store result
.stop();
GBVM Variable Commands
Core GBVM instructions for reading, writing, and manipulating variables.
| Command | Description |
|---|---|
VM_SET_CONST | Set variable to a constant value |
VM_SET | Copy one variable to another |
VM_GET_INT8 | Read a byte from C memory into a variable |
VM_SET_INT8 | Write a variable value to a C memory byte |
VM_GET_INT16 | Read a 16-bit value from C memory into a variable |
VM_SET_INT16 | Write a variable value to a C memory 16-bit location |
VM_PUSH_CONST | Push a constant onto the stack |
VM_PUSH_VALUE | Push a variable's value onto the stack |
VM_POP | Pop N values from the stack |
Usage Examples
; Set a variable to 100
VM_SET_CONST VAR_SCORE, 100
; Copy one variable to another
VM_SET VAR_BACKUP, VAR_SCORE
; Read a C byte variable into GBVM
VM_GET_INT8 .LOCAL_RESULT, _game_over
; Write GBVM variable to C memory
VM_SET_INT8 _elevator_done, VAR_STATUS
; Stack operations for RPN
VM_PUSH_CONST 0 ; Push initial value
VM_PUSH_VALUE VAR_SCORE ; Push variable value
VM_POP 2 ; Clean up stack
Constants and Defines
Common GBVM Constants
| Constant | Value |
|---|---|
.TRUE | 1 |
.FALSE | 0 |
Direction Constants
| Constant | Value | Direction |
|---|---|---|
.DIR_DOWN | 0 | Down |
.DIR_RIGHT | 1 | Right |
.DIR_UP | 2 | Up |
.DIR_LEFT | 3 | Left |
Actor State Constants
| Constant | Value | Description |
|---|---|---|
STATE_DEFAULT | 0 | Default animation state |
STATE_WALK | 1 | Walking animation state |
Math Utilities
Angle System (math.h)
GB Studio uses a 256-unit angle system where the full circle spans 0–255. Trigonometric functions are provided as BANKED lookup tables returning INT8 values.
- Full circle: 256 units (0–255)
SIN(angle)/COS(angle)— BANKED functions- Return type:
INT8(range -128 to 127)
Angle Constants
| Constant | Value | Degrees |
|---|---|---|
ANGLE_0DEG | 0 | 0° |
ANGLE_45DEG | 32 | 45° |
ANGLE_90DEG | 64 | 90° |
ANGLE_180DEG | 128 | 180° |
SIN/COS Reference Table
| Angle | Degrees | Direction | SIN | COS |
|---|---|---|---|---|
| 0 | 0° | Up | 0 | 127 |
| 32 | 45° | Up-Right | 90 | 90 |
| 64 | 90° | Right | 127 | 0 |
| 96 | 135° | Down-Right | 90 | -90 |
| 128 | 180° | Down | 0 | -128 |
| 192 | 270° | Left | -128 | 0 |
SIN(0..64) traces a quarter-sine for asymmetric easing (gentle ease-in, strong ease-out). COS(0..128) gives a symmetric S-curve. Both are useful for animation and scroll easing.
Fixed-Point Math
GB Studio uses fixed-point arithmetic to simulate fractional values on integer hardware. The primary format is 12.5 (12 integer bits, 5 fractional bits).
| Operation | Macro / Shift | Description |
|---|---|---|
| Pixel → Subpixel | PX_TO_SUBPX(a) = a << 5 | Multiply by 32 |
| Subpixel → Pixel | SUBPX_TO_PX(a) = a >> 5 | Divide by 32 |
| Tile → Subpixel | TILE_TO_SUBPX(a) = a << 8 | Multiply by 256 |
| Subpixel → Tile | SUBPX_TO_TILE(a) = a >> 8 | Divide by 256 |
| Pixel → Tile | PX_TO_TILE(a) = a >> 3 | Divide by 8 |
| Tile → Pixel | TILE_TO_PX(a) = a << 3 | Multiply by 8 |
<< 5 / >> 5 for pixel-subpixel conversion, never << 4. The 12.5 format means 32 subpixels per pixel, not 16.
Some plugins use 8.8 fixed-point (8 integer bits, 8 fractional bits) for smoother interpolation, particularly in camera shake and easing calculations.
Random Numbers
UINT8 r = rand(); // 0-255 pseudo-random
// For range [min, max]:
UINT8 val = min + (rand() % (max - min + 1));
Interrupt System
The Game Boy provides several hardware interrupts. GB Studio primarily uses VBlank and LCD (STAT) interrupts.
Available Interrupts
| Interrupt | Frequency | Use |
|---|---|---|
| VBlank | Every frame (~60 Hz) | OAM DMA, palette writes, scroll registers |
| LCD (STAT) | Per-scanline (configurable) | Parallax, split-screen effects |
| Timer | Configurable | Not typically used by GB Studio |
VBlank ISR
The VBlank interrupt fires once per frame during the vertical blanking period. This is the only safe time to write to VRAM, OAM, and palette registers.
void VBL_isr(void) NONBANKED {
if ((WY_REG = win_pos_y) < MENU_CLOSED_Y)
SHOW_WIN;
else
HIDE_WIN;
if (hide_sprites) HIDE_SPRITES;
else SHOW_SPRITES;
scroll_shadow_update();
}
VBL_isr checks the hide_sprites variable every VBlank and overrides the LCDC register accordingly. Calling the HIDE_SPRITES macro alone (which writes LCDC directly) will be immediately overridden on the next VBlank. You must set hide_sprites = TRUE to persist sprite hiding across frames.
LCD ISR
LCD interrupts are triggered at configurable scanlines via the LYC (Line Y Compare) register. GB Studio provides three built-in LCD ISRs:
| ISR Function | Enum Value | Use |
|---|---|---|
simple_LCD_isr | LCD_simple | Basic scanline interrupt |
parallax_LCD_isr | LCD_parallax | Multi-layer parallax scrolling |
fullscreen_LCD_isr | LCD_fullscreen | Full-screen effects |
ISR Management
// Remove all standard LCD ISRs
remove_LCD_ISRs();
// Install a custom ISR
CRITICAL {
add_LCD(my_custom_LCD_isr);
}
// Remove a specific ISR
remove_LCD(my_custom_LCD_isr);
core.c) installs a standard LCD ISR based on scene_LCD_type after load_scene(). If your plugin uses a custom ISR, call remove_LCD_ISRs() followed by add_LCD() in state_init() to replace it. Also clear parallax_rows and set scene_LCD_type = LCD_simple to prevent conflicts.
The LYC sync value used by GB Studio is 150, which falls within the VBlank period and is safe for register changes.
Common Patterns
Using Variables as Flags
; Set a flag
VM_SET_CONST VAR_MY_FLAG, 1
; Check flag (branch if not equal to 0)
VM_IF_CONST .NE, VAR_MY_FLAG, 0, label_true, 0
Reading Engine Fields into Variables
Engine fields are global C variables exposed to the GBVM script system. They can be read into game variables or written from script values.
// From event JSON:
// EVENT_ENGINE_FIELD_STORE — reads a C variable into a game variable
// EVENT_ENGINE_FIELD_SET — writes a value to a C variable
Bridging C and GBVM Variables
// In C code: read a GBVM variable
INT16 val = *(script_memory + VAR_MY_VARIABLE);
// In GBVM: read a C variable
VM_GET_INT8 VAR_RESULT, _my_c_byte_var
VM_GET_INT16 VAR_RESULT, _my_c_word_var
// In GBVM: write to a C variable
VM_SET_INT8 _my_c_byte_var, VAR_SOURCE
VM_SET_INT16 _my_c_word_var, VAR_SOURCE
RPN Stack Calculations
; Calculate: result = (score * 2) + bonus
VM_RPN
.R_REF VAR_SCORE
.R_INT16 2
.R_OPERATOR .MUL
.R_REF VAR_BONUS
.R_OPERATOR .ADD
.R_REF_SET VAR_RESULT
.R_STOP