Hack76.Program for the Game Boy Advance


Hack 76. Program for the Game Boy Advance

Make your own portable playthings .

What budding game programmer doesn't fantasize about seeing one of his own creations on a console or portable gaming device? Thanks to extensive game development communities and the efforts of independent programmers, this fantasy is no longer such a pipe dream.

In addition to being the prime platform for game companies to re-release their classic 8 and 16-bit titles, the Game Boy Advance has also become a major center for independent, amateur , and homebrew development. The relatively low cost and ease of development for the Game Boy Advance (GBA) has made it a common entry point into console-based programming. In this hack, I'll show you what tools you'll need for development and show you the first steps in writing a program that will use some of the GBA's video modes.

8.5.1. Welcome to the GBA Dev World

To develop games for a console, you typically need a development license from the console vendor and the accompanying software development kit, or SDK. Often the prices for such licenses run into the several thousands of dollars or more. Luckily for hobbyist GBA developers, there is a free alternative available for those who do not have that sort of spare cash lying around. DevKit Advance is a free GBA development kit developed by volunteer programmers. It allows you to compile and link source code into a format understandable to a Game Boy Advance.

Most programming for a GBA is done in C/C++, with some assembly thrown in for the brave and performance-conscious. Unlike the PC world, where most code is compiled for processors in the x86 family, GBA code must be compiled for an ARM processor. This is the main reason you need a special SDK to write software for a GBA. Thankfully, the tools necessary for GBA development, from compilers to image conversion tools, are readily available on the Internet.

8.5.2. Assembling Your Tools

The first thing you will need for your new Game Boy Advance development shop is the SDK. DevKit Advance (http://devkitadv.sourceforge.net/) can be downloaded at http:// sourceforge .net/project/showfiles.php?group_id=67315. For most development in C++ you will need to download the following files to the same directory on your hard driveI use C:\ gbadev (the actual names are version-dependent, so the file names may vary):

agb-win- core -r5.zip
agb-win-binutils-r4.zip
agb-win-gcc-r4.zip
agb-win-newlib-r4.zip
agb-win-libstdcpp-r4.zip
agb-win-patch-r4.zip

Once these files are downloaded, unzip them while making sure to preserve their directory structure. They should all unzip to a subdirectory called devkitadv .

While it's certainly possible to write all your code in a simple text editor like Notepad or vi and then compile it manually using DevKit Advance's compiler and linker, it's probably wise to use some sort of Integrated Development Environment (IDE) to organize your projects. But if you want to use the command-line tools this way, you'll need to follow the instructions in devkitadv\ windows .txt to prepare your environment. Since that document assumes you will be using C:\devkitadv , you may want to move the devkitadv subdirectory to the root of your hard drive so you don't have to adjust the instructions.

Emacs, Eclipse, and Microsoft Visual Studio are all workable solutions for making development more convenient . The Newbie's Guide to Game Boy Advance Development by VerticalE provides a good tutorial for how to set up Visual Studio 6.0 to work with DevKit Advance, and it can be found at http://www.gbadev.org/download.php?section=docs&filename=NGGBA.zip.

If you plan to develop without an IDE, you may wish to visit a few tutorials on using makefiles and a GCC-like compiler, like the one used in DevKit Advance. The following sites should offer enough information to get you started:

http://galton.uchicago.edu/~gosset/Compdocs/gcc.html
http://users.actcom.co.il/~choo/lupg/tutorials/writing-makefiles/writing-makefiles.html

To test your new creations, you can either copy your newly compiled GBA ROMs to a flash cart and run it on a GBA or you can use a GBA emulator.

Ideally, you should do both. An emulator allows you the convenience of quick testing from your PC while flash cart testing will show you how your program runs on real hardware and expose problems not evident on emulators (which are generally more forgiving ).

You can find information on purchasing and using GBA Flash carts at http://www.linker4u.com/ and http://www.lik-sang.com/.

There are several free GBA emulators [Hack #44] available on the Internet. A fairly decent list of them can be found at http://www.devrs.com/gba/software.php#emus. I find that Visual Boy (http://vba.ngemu.com/) works well for my purposes.

You will also need tools that allow you to convert images and sounds to a format usable by a GBA. You can download graphic conversion utilities at http://www.gbadev.org/tools.php?section=gfx and you can download sound conversion utilities at http://www.gbadev.org/tools.php?section=sou. For the purposes of this hack, I use Digital Inline's pcx2gba 1.10a and Warder1's GBA Map Editor beta 4 for graphics conversion.

8.5.3. Getting Dirty with the Code

The first step performed by almost any GBA program is to set the video mode. The video mode determines how and in what format graphics are presented on the GBA screen. The GBA has four backgrounds (or layers ) on which graphics can be displayed and six video modes that define how the backgrounds can be used. The six video modes can be divided into two different types: tiled and bitmapped. The different video modes are listed in Table 8-1.

Table 8-1. The GBA video modes

Videomode

Type

Backgrounds

Tiled

All four BGs are static and available.

1

Tiled

BG0, BG1, and BG2 are available.

   

BG2 is scalable and rotatable.

2

Tiled

BG2 and BG3 are available.

   

Both are scalable and rotatable.

3

Bitmapped

BG2 is 240x160 pixels in 15-bit color .

4

Bitmapped

BG2 is 240x160 pixels in 8-bit indexed color and features a frame buffer for better animation quality.

5

Bitmapped

BG2 is 160x128 pixels in 15-bit color and features a frame buffer for better animation quality.


Tiled modes allow graphics to be made out of arrays of smaller graphics. This allows large graphics to be stored and executed efficiently as long as they are made up of repeatable tiles. Bitmapped modes allow a background to be accessed directly as an array of bytes. They can be used for displaying highly detailed images at the cost of speed and storage size . Bitmapped modes only use BG2.

To change the video mode, and several other video related settings, you write data to the video control register. As far as GBA programming goes, registers are particular sections of memory where certain events are to happen if data is placed there. The video control register lives at memory location 0x04000000, so to change video settings, you would use code similar to the following:

 *(unsigned short*)0x04000000 = <   value   >; 

This line essentially says to set the unsigned short (a 16-bit value) at memory location 0x04000000 to whatever value you specify. The value you specify is a 16-bit number whose component bits reflect various settings including the video mode. The lowest three bits of the value determine the video mode. Refer to Table 8-2 for the video modes and their corresponding values:

Table 8-2. Values for setting the video mode

Video mode

Hexadecimal

Binary

0x0

000

1

0x1

001

2

0x2

010

3

0x3

011

4

0x4

100

5

0x5

101


For example, to set the video mode to mode 3 you would write the following:

 *(unsigned short*)0x04000000 = 0x3; 

You can also combine these video mode values with values representing other settings in the video control register. Bit 10, for example, determines whether or not BG2 is enabled. Because you are setting the GBA to mode 3, you will need BG2 to be enabled. BG2 can be enabled with the value 0x400 , which you can combine with the value for mode 4 by using a logical OR, like so:

 *(unsigned short*)0x04000000 = 0x400  0x3; 

Table 8-3 provides a list of useful values that can be set in the video control register.

Table 8-3. Useful video control register values

Function

Hexadecimal

Binary

Display frame buffer (in modes 4 and 5)

0x10

1 0000

Clear Screen (turns screen white by clearing the video buffer)

0x80

1000 0000

Enable BG0

0x100

1 0000 0000

Enable BG1

0x200

10 0000 0000

Enable BG2

0x400

100 0000 0000

Enable BG3

0x800

1000 0000 0000

Enable Sprites

0x1000

1 0000 0000 0000


To really understand how this works, you will need a thorough understanding of binary and hexadecimal numbers and binary operations. You can visit http://www.edmagnin.com/CSIS240/reference.html for more information about binary and hexadecimal. Also, the tutorials at http://gbajunkie.co.uk/ provide a useful set of macros for changing settings with the video control register.

With all this information, you are ready to write an extraordinarily simple GBA program. Copy the following code, save it, and then compile it:

 int main(void) {   *(unsigned short*)0x04000000 = 0x3  0x400  0x80; } 

If you are using the command-line compilers, save this program as hello.c . Then, compile it and prepare the ROM image with these two commands:

 gcc -0 hello.elf hello.c objcopy -0 binary hello.elf hello.gba 

You can adapt these two commands for the remaining examples in this hack.


As you can see, this code is a simple one-line program that sets the video mode to Mode 3, enables BG2 and clears the video buffer. If you were successfully able to compile this program, you should be able to run it on a GBA or GBA emulator. When you run it, you should see a blank screen appear on your GBA or emulator. Not exactly brain surgery, but from here on in, things get interesting.

8.5.4. Managing the Video Buffer

To display something on the GBA's screen you must put data in a particular section of memory called the video buffer. In the case of the GBA, the video buffer is the 96KB large section of memory starting at 0x06000000 . Those familiar with programming using graphical DOS modes like 13h will find the process of accessing the video buffer quite familiar.

In general, you can display something on the GBA screen by using code similar to that used to set the video control register. However, it is useful to write functions to abstract the process, as most people would find it easier to think about graphics in terms of pixels than memory coordinates. The following code is an example of a function for writing a particular color ( r, g , and b ) to a particular pixel ( x and y ) in mode 3:

 void SetPixelMode3 (int x, int y, int r, int g, int b) {      unsigned short color = (b << 10)  (g << 5)  r;  unsigned short *pixelAddress = (unsigned short*)0x06000000;    pixelAddress = pixelAddress + (240 * y) + x;    *pixelAddress = color; } 

Mode 3 uses 15-bit true color, so the color of any pixel is determined by a 15-bit value. The video buffer in Mode 3 is actually be made up of 16-bit values, but seems to ignore the highest bit just fine. With this in mind, the code casts the color as an unsigned short, which happens to be a 16-bit number, and casts the pixel address as a pointer to an unsigned short. In terms of code, this is accomplished by the << operator which is used to shift the value of b 10 bits higher and the value of g 5 bits higher. The values for r, g and b are then all combined using a logical OR operation. The construction of a 15-bit color is shown in Figure 8-21.

To determine the address at which to place the color, keep in mind that the video buffer is a continuous array of memory starting at 0x06000000 . In the case of Mode 3, which is 240 _160 16-bit values, every 240 16-bit values can be thought of as a horizontal line on the screen, as seen in Figure 8-22.

Figure 8-21. Construction of a 15-bit color

Figure 8-22. The Mode 3 video buffer

Therefore, from the starting address of 0x06000000 , the memory address at which to place a pixel is y times 240 (the number of values or pixels per line) plus x . With that in mind, try compiling and running a program that takes advantage of this new function, such as the following:

 void SetPixelMode3(int x, int y, int r, int g, int b) {      unsigned short color = (b << 10)  (g << 5)  r;      unsigned short *pixelAddress = (unsigned short*)0x06000000;      pixelAddress = pixelAddress + (240 * y) + x;      *pixelAddress = color; } int main(void) {       *(unsigned short*)0x04000000 = 0x3  0x400;       int i;      //Draw horizontal red line      for(i = 0; i < 100; i++) {            SetPixelMode3 (10 + i, 10, 31, 0, 0);      }      //Draw diagonal green line      for(i = 0; i < 100; i++) {             SetPixelMode3 (10 + i, 10 + i, 0, 31, 0);      }     //Draw vertical blue line     for(i = 0; i < 100; i++) {            SetPixelMode3 (10, 10 + i, 0, 0, 31);     } } 

When you run this program on a GBA or emulator, you should see something similar to Figure 8-23.

Figure 8-23. Hello, Mode 3

8.5.5. Displaying Images on a GBA

Pushing pixels is a decent way to start, but, for real power, you need to be able to load external images and to use double-buffering for smooth animation. For that, we will harness the power of Mode 4. While it's certainly possible to animate and load external images using Mode 3, it's lack of a secondary region on which to draw graphics before displaying them results in flickering animation. Also, the 8-bit palette-based color system of Mode 4 allows you to save a lot more images to memory compared to Mode 3.

The first step in loading an image on a Game Boy Advance is to create an image that can be easily converted into a format usable by a GBA. You will be using the pcx2gba utility (see "Assembling Your Tools," earlier in this hack) to convert graphics, so first use your preferred image editor create a graphic in PCX format. Mode 4 uses 8-bit color, so the PCX will have to be a 256-color indexed image. To fill the screen, the image should be 240 pixels by 160 pixels. Save your graphic as my_pic.pcx in the same directory as pcx2gba.exe . From a DOS prompt, go to the pcx2gba directory and execute the following command:

 pcx2gba TXT4 my_pic.pcx my_pic.h mypic 

This command tells pcx2gba to convert my_pic.pcx into my_pic.h . There are several different formats a GBA can use for graphics; TXT4 tells pcx2gba to convert into a header file that can be included directly in a GBA program's C/C++ code. The mypic argument is the name used for the data structures defined in the new header file ( my_pic.h ), which contains an array of data for the image palette, mypicpal , and the actual image data, mypicdata .

To add the newly converted image to a GBA program, you include it in your source file using a #include directive. Then you add it to your project or link it at compile time. To actually display the image on the screen, you must first copy the color palette into the palette memory of the GBA, which is located at 0x05000000 . Then, like in Mode 3, you copy the image data into the video buffer, starting at 0x06000000 . In Mode 4, however, bear in mind that the video buffer takes up half the space it does in Mode 3, because it uses 8-bit versus 16-bit color. You will copy half as much data to fill the video buffer.

The following code displays the picture defined in my_pic.h on the screen:

 define USHORT unsigned short #include "my_pic.h" int main(void) {       *(unsigned short*)0x04000000 = 0x4  0x400; int i, x, y; unsigned short *gbaPalette = (unsigned short*)0x05000000; for(i = 0; i < 256; i++) {        gbaPalette[i] = mypicpal[i]; } unsigned short *gbaScreen = (unsigned short*)0x06000000; for(y = 0; y < 160; y++) {      for(x = 0; x < 120; x++) {          gbaScreen[(y * 120) + x] = mypicdata[(y * 120) + x];       }   } } 

The header created by pcx2gba defines the image and palette as arrays of type USHORT . This is not defined by default, so first you must define it as being an unsigned short . Next you need to include the header with the picture data. As before, a simple setting at memory address 0x04000000 sets the video mode, this time to Mode 4. The next section of code defines a pointer to memory address 0x05000000 , the GBA color palette. The program then copies the 256 color entries from the palette in my_pic.h into the GBA palette. The final block of code does a similar operation, except it copies from the image data in my_pic.h into an array starting at the beginning of the video buffer. You may notice that x only iterates to 120 even though the screen is 240 pixels wide. In Mode 4, each pixel is 8 bits long, however the unsigned shorts used to store the image are 16 bits long. Each unsigned short houses the data for two adjacent pixels. So, instead of copying one pixel at a time 240 times, the program copies 2 pixels at a time 120 times. When you compile and run this program, you should see the image you created show up on the GBA's screen.

Mode 4 has half as much memory allotted to the video buffer as Mode 3. The other half is used to implement a frame buffer, a secondary buffer of memory in which to place graphics. You can place images in the frame buffer just as you would to the video buffer. The advantage is that you control when the frame buffer or the video buffer is displayed. This is useful when drawing a complex image or when performing animation. You can wait until your image is ready to be displayed before finally displaying it. Whether the frame buffer or the video buffer is displayed is determined by the fifth bit of the video control register. The frame buffer can be made visible in Mode 4 like so:

 *(unsigned short*)0x04000000 = 0x10  0x4  0x400; 

The following code illustrates putting a second image (create my_pic2.h as you did with my_pic.h ) into the frame buffer and rapidly switching between the frame buffer and the video buffer:

 #define USHORT unsigned short #include "my_pic.h" #include "my_pic2.h" #define VCOUNT (*(volatile unsigned short*)0x04000006) #define vsync() while (VCOUNT != 160); typedef enum bool { false, true } bool; int main(void) {       *(unsigned short*)0x04000000 = 0x4  0x400;       unsigned short *gbaPalette = (unsigned short*)0x05000000;       bool flipped = false;       int i, x, y;       for(i = 0; i < 256; i++) {            gbaPalette[i] = mypicpal[i];       }       unsigned short *gbaScreen = (unsigned short*)0x06000000;       for(y = 0; y < 160; y++) {             for(x = 0; x < 120; x++) {                   gbaScreen[(y * 120) + x] = mypicdata[(y * 120) + x];              }        }       unsigned short *backBuffer = (unsigned short*)0x600A000;           for(y = 0; y < 160; y++) {            for(x = 0; x < 120; x++) {                 backBuffer[(y * 120) + x] = mypic2data[(y * 120) + x];           }     }     while(1) {          vsync();          if(flipped == false) {               *(unsigned short*)0x04000000 = 0x10  0x4  0x400;                flipped = true;          }          else {               *(unsigned short*)0x04000000 = 0x4  0x400;                flipped = false;         }    } } 

The code is fairly straightforward, but there are a few interesting points to note. First, in this scenario both images share the same palette. In fact, only my_pic1 's palette is actually loaded into memory at 0x05000000 . In order for both pictures to look right, you have to make sure they have the same palette when you create them in an image editor. Alternatively, you could load each images' palette just before you display it. This, however, can slow things down and cause odd color distortions on the displayed image as the palette is being changed.

The second point to note is the vsync( ) call. On a GBA, like a monitor, there is a brief period of time during which nothing from the video buffer is being drawn on the screen. It is best to make changes to the video buffer during this time to avoid odd effects (e.g., shearing ) as you switch from one image to the next. Calling vsync( ) aligns you with the period of time, so you can start writing to the video buffer immediately after you call it and, hopefully, finish before the video buffer is drawn on the screen. The following clever bit of code, cribbed from Dev'rs GBA Dev FAQ (http://www.devrs.com/gba/) implements the vsync() call:

 #define VCOUNT (*(volatile unsigned short*)0x04000006) #define vsync() while (VCOUNT != 160); 

Essentially, like the video control register at 0x04000000 , there is a value at memory location 0x04000006 that indicates what line of the screen is currently being drawn. If you wait until the final line of the screen has just been drawn, line 160 in the case of Mode 3 and 4, you can begin modifying the video buffer during the brief interval when the screen is not being displayed.

Robert Ota Dieterich



Retro Gaming Hacks
Retro Gaming Hacks: Tips & Tools for Playing the Classics
ISBN: 0596009178
EAN: 2147483647
Year: 2003
Pages: 150
Authors: Chris Kohler

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net