VM_INVOKE
is a GBVM instruction that allows you to call specific, pre-defined C functions within the Game Boy engine until they signal completion (return true). This provides a bridge between your GBVM script and lower-level engine functionalities written in C.
Purpose:
While GBVM provides a robust scripting environment, certain complex or performance-critical operations are often implemented directly in C for efficiency and direct hardware control. VM_INVOKE
is used to:
Syntax:
VM_INVOKE BANK, ADDR, N, PARAMS
BANK
: The memory bank number where the C function (ADDR
) is located.ADDR
: The address (label) of the C function to be invoked. The documentation mentions two examples:
_wait_frames
: Waits for a specified number of VBlank intervals (frames)._camera_shake
: Shakes the camera a specified number of times.N
: The number of arguments that the C function expects. These arguments must be pushed onto the GBVM stack before VM_INVOKE
is called. After the C function returns, these N
values will be removed from the stack.PARAMS
: This is not a direct parameter but rather indicates that the C function will read its parameters from the GBVM stack. You must push the required arguments onto the stack before calling VM_INVOKE
.Usage Example: Implementing a Camera Shake Effect
Let’s say you want to shake the camera when the player takes damage or a significant event occurs. The engine provides a _camera_shake
C function for this.
; In your damage routine or event script:
; Assume _camera_shake expects 1 argument: the number of shakes
; Push the number of shakes onto the stack
VM_PUSH_CONST 5 ; Shake the camera 5 times
; Invoke the _camera_shake C function
VM_INVOKE BANK(_camera_shake), _camera_shake, 1, 0
; BANK(_camera_shake): The bank where _camera_shake is located.
; _camera_shake: The address of the function.
; 1: The number of arguments (5) to remove from the stack after invocation.
; 0: (PARAMS) This indicates that the parameters are on the stack.
; ... continue script after camera shake completes ...
; Example of how _camera_shake might be defined in C (conceptual):
; // In a C source file (e.g., src/core/camera.c)
; #include <gb/gb.h>
; #include "vm.h"
; UBYTE camera_shake_count;
; UBYTE camera_shake_timer;
; UBYTE camera_shake_active = FALSE;
;
; // This function is called by VM_INVOKE
; UBYTE _camera_shake(SCRIPT_CTX * THIS) { // SCRIPT_CTX is the VM context
; if (!camera_shake_active) {
; camera_shake_count = SCRIPT_POP_BYTE(THIS); // Get argument from stack
; camera_shake_timer = 0;
; camera_shake_active = TRUE;
; }
;
; // Logic to move camera based on camera_shake_count and camera_shake_timer
; // ...
;
; if (camera_shake_count == 0) {
; camera_shake_active = FALSE;
; return TRUE; // Signal completion
; } else {
; return FALSE; // Not yet complete
; }
; }
In this example, VM_PUSH_CONST 5
places the argument for _camera_shake
onto the stack. VM_INVOKE
then calls the C function. The GBVM script will pause at this point, and _camera_shake
will execute frame by frame until its internal logic determines it’s finished (by returning TRUE
). Once it returns TRUE
, VM_INVOKE
removes the argument from the stack, and the GBVM script resumes execution.
Analogy to other programming languages:
This is analogous to using a Foreign Function Interface (FFI) in languages like Python (ctypes
) or Java (JNA
) to call functions written in C or C++. You are explicitly telling the high-level scripting environment to temporarily hand over control to a low-level, pre-compiled function, often passing arguments and expecting a return value or a completion signal.