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
Note
Even boolean flags consume a full INT16 slot. There is no bit-packing at the variable level — use bitwise operations in RPN expressions if you need to pack multiple flags into a single variable.

GBVM Memory Layout

The GBVM virtual machine manages three key memory regions: the shared heap, per-context stacks, and the instruction budget.

ConstantValueDescription
VM_HEAP_SIZE768Shared variable slots (INT16 each)
VM_MAX_CONTEXTS16Concurrent script threads
VM_CONTEXT_STACK_SIZE64Words per thread stack
INSTRUCTIONS_PER_QUANT0x10 (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
Capacity Limit
With 768 slots total and system overhead, large projects can approach the variable limit. Monitor your variable count in the GB Studio editor.

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_CONST or _declareLocal
  • Must be cleaned up with VM_POP before script exit

Function Argument Offsets

ConstantOffsetDescription
FN_ARG0-1First argument (top of caller's stack)
FN_ARG1-2Second argument
FN_ARG2-3Third argument
FN_ARG3-4Fourth argument
FN_ARG4-5Fifth argument
FN_ARG5-6Sixth argument
FN_ARG6-7Seventh argument
FN_ARG7-8Eighth 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.

CommandDescription
VM_SET_CONSTSet variable to a constant value
VM_SETCopy one variable to another
VM_GET_INT8Read a byte from C memory into a variable
VM_SET_INT8Write a variable value to a C memory byte
VM_GET_INT16Read a 16-bit value from C memory into a variable
VM_SET_INT16Write a variable value to a C memory 16-bit location
VM_PUSH_CONSTPush a constant onto the stack
VM_PUSH_VALUEPush a variable's value onto the stack
VM_POPPop 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

ConstantValue
.TRUE1
.FALSE0

Direction Constants

ConstantValueDirection
.DIR_DOWN0Down
.DIR_RIGHT1Right
.DIR_UP2Up
.DIR_LEFT3Left

Actor State Constants

ConstantValueDescription
STATE_DEFAULT0Default animation state
STATE_WALK1Walking 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

ConstantValueDegrees
ANGLE_0DEG0
ANGLE_45DEG3245°
ANGLE_90DEG6490°
ANGLE_180DEG128180°

SIN/COS Reference Table

AngleDegreesDirectionSINCOS
0Up0127
3245°Up-Right9090
6490°Right1270
96135°Down-Right90-90
128180°Down0-128
192270°Left-1280
Easing Curves
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).

OperationMacro / ShiftDescription
Pixel → SubpixelPX_TO_SUBPX(a) = a << 5Multiply by 32
Subpixel → PixelSUBPX_TO_PX(a) = a >> 5Divide by 32
Tile → SubpixelTILE_TO_SUBPX(a) = a << 8Multiply by 256
Subpixel → TileSUBPX_TO_TILE(a) = a >> 8Divide by 256
Pixel → TilePX_TO_TILE(a) = a >> 3Divide by 8
Tile → PixelTILE_TO_PX(a) = a << 3Multiply by 8
Common Mistake
Always use << 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

InterruptFrequencyUse
VBlankEvery frame (~60 Hz)OAM DMA, palette writes, scroll registers
LCD (STAT)Per-scanline (configurable)Parallax, split-screen effects
TimerConfigurableNot 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();
}
Critical: hide_sprites Variable
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 FunctionEnum ValueUse
simple_LCD_isrLCD_simpleBasic scanline interrupt
parallax_LCD_isrLCD_parallaxMulti-layer parallax scrolling
fullscreen_LCD_isrLCD_fullscreenFull-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);
ISR Installation Timing
The scene loading process (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