Hello VBlank
The simplest GBA program that actually does something is a VBlank loop. This is the heartbeat of every GBA game - wait for the display to finish drawing, then update your game state.
The code
#include <gba/interrupt>
#include <gba/peripherals>
int main() {
// Step 1: Initialise the interrupt handler
gba::irq_handler = {};
// Step 2: Tell the display hardware to fire an interrupt each VBlank
gba::reg_dispstat = { .enable_irq_vblank = true };
// Step 3: Tell the CPU to accept VBlank interrupts
gba::reg_ie = { .vblank = true };
gba::reg_ime = true;
// Step 4: Main loop
for (;;) {
gba::VBlankIntrWait();
// Your game logic goes here
}
}
What is happening?
The GBA display draws 160 lines of pixels (the “active” period), then enters a 68-line “vertical blank” period where no pixels are drawn. The VBlank is your window to safely update video memory without visual tearing.
gba::VBlankIntrWait() puts the CPU to sleep (saving battery) until the VBlank interrupt fires. This is the BIOS SWI 0x05.
Step by step
-
gba::irq_handler = {}installs the default interrupt dispatcher. Without this, BIOS interrupt-wait functions will hang forever. -
gba::reg_dispstat = { .enable_irq_vblank = true }writes to the DISPSTAT register using a designated initialiser. Only the.enable_irq_vblankbit is set; all other fields default to zero. -
gba::reg_ie = { .vblank = true }enables the VBlank interrupt in the interrupt enable register.gba::reg_ime = trueis the master interrupt switch. -
gba::VBlankIntrWait()is a BIOS call that halts the CPU until a VBlank interrupt occurs.
tonclib comparison
The equivalent tonclib code:
#include <tonc.h>
int main() {
irq_init(NULL);
irq_add(II_VBLANK, NULL);
for (;;) {
VBlankIntrWait();
}
}
The key difference is that stdgba uses designated initialisers ({ .vblank = true }) instead of bitfield macros (II_VBLANK). Typos in field names are compile errors; typos in macro names might silently compile to wrong values.
Putting something on screen
The VBlank loop itself produces a blank screen. To prove the program is running, here is a minimal extension that draws a white rectangle in Mode 3:
#include <gba/bios>
#include <gba/interrupt>
#include <gba/video>
int main() {
gba::irq_handler = {};
gba::reg_dispstat = {.enable_irq_vblank = true};
gba::reg_ie = {.vblank = true};
gba::reg_ime = true;
gba::reg_dispcnt = {.video_mode = 3, .enable_bg2 = true};
// Draw a white 40x20 rectangle centered on the 240x160 screen
for (int y = 70; y < 90; ++y) {
for (int x = 100; x < 140; ++x) {
gba::mem_vram[x + y * 240] = 0x7FFF;
}
}
while (true) {
gba::VBlankIntrWait();
}
}

Next steps
- Continue to Hello Graphics and Keypad to draw and move a consteval sprite.
- Then continue to Hello Audio to play a PSG jingle on button press.