Home CPSC 305

Introduction to the Game Boy Advance

Overview

This page covers the basics of programming for the Game Boy Advance (GBA). Developing for the GBA is very different for a few reasons:


Hello World

Because the GBA has no operating system, there is no printf function, or any equivalent way of printing text. The following "hello world" program does not actually print hello world, but instead draws some colored pixels to the screen.

This file contains the source code of the hello world program, and the rest of this page describes what is happening.


Compiling and Running GBA Games

GBA code cannot be compiled with the regular gcc compiler. (Actually it can because it is just regular C code, but it will promptly crash from accessing invalid memory locations!)

Instead, we need a cross-compiler which is a compiler which produces code for another machine architecture. We also can't run GBA games directly on our typical computers, we need to either run it in an emulator, or the actual GBA hardware.

Instructions on compiling GBA games and running them in an emulator can be found here.

Games can also be run on the actual GBA hardware following these instructions.


Graphics Modes

The GBA has multiple graphics modes. Which mode the GBA is in determines the way that pixels actually appear on the screen. There are 6 modes to choose from:

The tiled modes are more complex and we will cover them later on. The bitmap modes are simpler, and mode 3 is the simplest of all. The hello world program above is in mode 3.

Notice that none of the modes give you everything you'd want. The tiled modes make you choose between more layers, or more capable layers. The bitmap modes make you give up either double buffering, full resolution, or full color choice.


The Display Control Register

The GBA uses "memory registers" to control various things. These are just regular memory locations where we can store data. Their values are read by the GBA hardware and control various things.

The "display control" register is at memory address 0x4000000 and controls various aspects of the display, including the mode. Most of the bits control things we won't use.

The first three bits of the register control the mode. Because there are 6 modes, three bits are sufficient to store each possibility. We also need to deal with the backgrounds. There are four backgrounds which are turned on by putting 1's into bits 8, 9, 10 and 11.

Mode 3 programs need background 2 on, or else nothing will actually display. The hello world program above sets the display control register using:


#define MODE3 0x0003
#define BG2 0x0400

/* the display control pointer points to the gba graphics register */
volatile unsigned long* display_control = (volatile unsigned long*) 0x4000000;

/* we set the mode to mode 3 with background 2 on */
*display_control = MODE3 | BG2;

Note that the constant 0x0400 is a hexadecimal number which has a '1' in bit position 10. This turns on background 2. The number 0x0003 has the number 3 stored in the left-most bits. By OR-ing them together, we get a number with all of those bits set.


Volatile Variables

The display_control pointer above is declared as volatile which is a keyword in C, C++ and Java. It means that a variable may be accessed even though the compiler can't see how.

When you compile code, the compiler tries to optimize it. If you set a variable, but the compiler cannot see that it is ever read from, the compiler will remove your code which sets that variable.

Likewise, if you read a variable inside of a loop, but the compiler thinks the variable does not change inside of the loop, it will move the read outside of the loop to improve the speed.

On the GBA, the memory registers are sometimes set and read by the hardware directly, which the compiler has no clue about. These optimizations can thus break otherwise correct programs.

By marking a variable as volatile, we tell the compiler that it is outside of the compiler's purview and it should not change code which deals with it.


15-Bit Color

Like everything in a computer, colors are represented with binary numbers. The number of bits we use for the representation controls how many distinct colors we have. Very old computers used 1 bit which only gives us black or white color.

The original Game Boy had 2 bit color which gave use black, white, and two shades of grey in between.

Modern computers use 24-bits to represent colors, which gives more colors than the human eye can distinguish. This is sometimes called "true color".

The GBA uses 15-bits for colors, which is sometimes called "high color" and yields over 32 thousand distinct colors.

15-bit colors are stored in 16-bit integers with the most significant bit being unused.

15-bit color values contain 5 bits for each of the red, green and blue color components. On the GBA, the low bits store the red and the hight bits store the blue:

--BBBBB GGGGG RRRRR

The hello world program contains a function called "make_color" which builds a 16-bit integer out of separate red, green and blue components:


/* compute a 16-bit integer color based on the three components */
unsigned short make_color(unsigned char r, unsigned char g, unsigned char b) {
    unsigned short color = (b & 0x1f) << 10;
    color |= (g & 0x1f) << 5;
    color |= (r & 0x1f);
    return color;
}

This code uses the bitwise operators to construct the color value. AND-ing each parameter with 0xf1 makes sure it is only a 5-bit value, because that is 11111 in binary. The blue component is shifted into place, then the green added next, and finally the red.


Screen Memory

In mode 3, the GBA screen is set at resolution 240 by 160, and stores a 16-bit number for each pixel. This is 38,400 colors which is 76,800 bytes.

The screen is accessed by writing into memory starting at address 0x6000000. Because there are 76,800 bytes, the screen goes up until memory address 0x6012c00.

The hello world program defines the screen as a pointer to the start of this memory block:


/* the screen is simply a pointer into memory at a specific address this
 * pointer points to 16-bit colors of which there are 240x160 */
volatile unsigned short* screen = (volatile unsigned short*) 0x6000000;

Drawing a pixel is then as simple as indexing this memory location with the appropriate index:


/* place a pixel of a given color on the screen */
void put_pixel(int row, int col, unsigned short color) {
    /* set the screen location to this color */
    screen[row * WIDTH + col] = color;
}

The screen is layed-out row by row, so we multiply the row number by the width of the screen (which is 240 columns), and add the column number.


The Main Function

The last important piece of the program above is the main function which sets the display control register, and then loops through each pixel of the screen and places a color on each one:


/* loop through each row of the screen */
for (int row = 0; row < HEIGHT; row++) { 

    /* make a color in the range of black to bright blue based on the row */
    unsigned short color = make_color(0, 0, row % 32);

    /* loop through each column of the screen */
    for (int col = 0; col < WIDTH; col++) {
        put_pixel(row, col, color);
    }
}

The color changes for each row and just starts at (0, 0, 0) which is black and increments up to (0, 0, 31) which is bright blue. This is just so we can see something vaguely interesting on the screen!

Finally the main function just loops forever proudly displaying its image:


/* we now loop forever displaying the image */
while (1) {
    /* do nothing */
}

GBA games do not ever really "halt", because there is no operating system. There is nothing to return back to. The only way to stop them is to turn off the console, so having an infinite loop is normal.

Copyright © 2018 Ian Finlayson | Licensed under a Creative Commons Attribution 4.0 International License.