Last updated: January 24, 2007 13:03
...WORK IN PROGRESS...
Maybe some infos in this document are wrong so take it as is :P
(Pencil art courtesy of
PreyingDantis)

Setting up
You can download a binary version of fpc4gba, or you could try to
rebuild your own copy by hand. I suggest this second way, because you
will get the very latest compiler and library (you know, many bugs
fixed and so on =) )
Binary install:
Download the latest fpc4gba snapshot here:
ftp://ftp.freepascal.org/pub/fpc/snapshot/v21/arm-gba/fpc4gba.zip
or here:
http://itaprogaming.free.fr/download/fpc4gba.zip
Unpack it in a folder of your choice, set the path and edit fpc.cfg
accordingly. Refer to readme.txt for more infos.
More, download the new gbalib for fpc that you can find *here* and copy
all file in your freepascal\units\arm-gba directory.
Source install:
Download a freepascal 2.1.x binary release for your platform (eg. i386-win32) that you can get here:
ftp://ftp.freepascal.org/pub/fpc/snapshot/v21/
Download the binutils for gba here:
http://itaprogaming.free.fr/download/arm-gba-binutils.zip
Get the latest fpc sources here:
ftp://ftp.freepascal.org/pub/fpc/snapshot/v21/source/
Get the FPC4GBA Batch Builder.
Install the freepascal binaries, then unzip the binutils in a directory
of your choice and the fpc sources in another directory. Edit the batch
builder accordingly, by providing a folder where you want to install
your fpc4gba copy. Save and launch the batch file. If you are lucky,
you will get a congratulations message stating that all went right. The
last step is to add your fpc4gba bin/arm-gba directory to your search
PATH.
If you can't get this batch file working, feel free to contact me. In all cases you can follow the step-by-step guide you will find here.
Tools
- A
text
editor of your choice. I use PSPad, but any other editor should be
good.
- A graphic file
editor/converter. I suggest Usenti, that can resolve all your problems.
- A tilemap editor. You can try TileStudio.
GBA Hardware
CPU:
ARM7TDMI
@16.78 mhz. This cpu has two different instruction set: ARM (32 bit)
and THUMB (16 bit). ARM instructions can do more stuff in a single
command, so it should run faster. A THUMB instruction is reduced to 16
bit so it can be executed directly in ROM in a single cycle, because
the bus size to ROM is 16 bit too. Unfortunatley, at the moment I'm
writing this tutorial, fpc can handle ARM instruction set only.
Memory:
The
Gameboy Advance has several memory types: 96kb of video memory; 32kb of
fast internal ram; 256 kb of external ram. This memory is subdivided as
follow:
EWRAM:
(external working ram) starts at $2000000 and is 256kb wide.
It can be accessed in read/write mode at 8, 16 and 32 bit in
two
cycles.
IWRAM:
(internal working ram) starts at $3000000 and is 32kb wide. It can be
accessed in read/write mode in a single cycle (so IWRAM is faster than
EWRAM), at 8, 16 and 32 bit; all your variables and the stack normally
should go here.
IORAM: (IO
registers) starts at $4000000 and is 1kb wide. You can control
all
GBA hardware by setting or unsetting bits in this section.
PALRAM:
(palette memory) starts at $5000000 and is 1kb wide. It contains ttwo
color palettes of 256 entries of 15 bit each. The
first palette is
for the background, the second one is for the sprites.
VRAM:
(video ram) starts at $6000000 and is 96kb wide. According to video
mode, this memory is divided in different way between video memory and
sprite memory. The video bus is 16 bit, so it should be a good
idea to write video memory 16 or 32 bit at time. If you try to
write 8 bit at time, they will be aligned to 16 bit
boundaries, so
the same data will be written two times instead of one. And
this
is BAD!
OAM:
(object attribute memory) starts at $7000000 and is 1kb wide. In this
section you can control sprite attributes.
ROM: (read only
memory) starts at $8000000 and is max 32Mb wide. As its name says, it
can be accessed in read mode only.
GBA has 6 different video mode, 3 allowing single pixel access and 3
allowing handling tiles via hardware. In next few chapters we will see
how to use all these modes.
Bitmap
Modes
Bitmap mode allows to access every single pixel of the screen. You can assign a 16 bit color value or an index value to it, that represent the index in the palette of the color you want. Let's look at bitmap modes a bit deeper.
Mode 3:
This is the easiest mode you can use to put something on the screen. In this mode the screen is made of 240*160 pixel of 15 bit color each. You can think it like an array of 240*160 values, so in order to access a pixel, you should access the (width * y + x)th element of the video array, where width is the screen width, x and y the position on the screen. But let see an example: we will try to put a red pixel in the middle of the screen. Look at this code:
program main;
uses
gba_regs, gba_video;
begin
SetMode(MODE_3 or BG2_ENABLE);
// Set Mode3 and activates BG2
VideoBuffer[240 * 80 + 120] := RGB(31,0,0); // Put a red pixel
end.
That's all! Pretty simple, don't you think so? Now let try to fill the whole screen with a blue color:
program main;
uses
gba_regs, gba_types, gba_video;
var
i: u32;
begin
SetMode(MODE_3 or BG2_ENABLE);
for i := 0 to 240*160 - 1 do // loop trough all pixels
VideoBuffer[i] := RGB(0,0,31);
end.
Nice, but slooowww! Don't worry, You will use this mode for static screens only. You should have noticed that it is possible to see screen rows being updated, and that looks bad... A trick could be to set Mode 3 *after* that you have filled the video memory. Note that I am using an u32 for a loop varialbe: the gba is a 32 bit machine, so works faster with 32 bits variables.
As last example, we'll try to use mode3 for what it was made: displaying pictures. We need a tool to translate images in a way that our compiler can understand: for this purpose we'll use Usenti. Usenti comes with an exhaustive help, so if you are in trouble, RTFM! :P So let's start: open Usenti and load an image you want to show on the gba (240*160 of course), then export it (image->export...), chose a name (something like 'mode3bmp') then click 'Save'. It should popup a new form with some settings to do:
- let checked 'Image' only
- select 'bitmap', bpp='16', cprs='none'
- file type='GAS (*.s)'
- check the 'u16' button
- press 'Ok'.

Now you should have a new file (mode3bmp.s) that you need to pass to assembler, in order to get an object file to link to our rom. If you want (and I suggest to do so), use the template that you can find in 'examples' directory and modify the makefile as follows:
...
OBJECTS = prt0.o mode3bmp.o main.o
...
In this way, the makefile will do the job for you :) Let's look at the code:
program main;
{$L mode3bmp.o} //Linking the external object file
uses
gba_regs, gba_types, gba_video;
var
//Declaring our external image
mode3bmpBitmap: array [0..240*160-1] of u16; cvar; external;
i: u32;
begin
SetMode(MODE_3 or BG2_ENABLE);
for i := 0 to 240*160-1 do // Loop
VideoBuffer[i] := u32(mode3bmpBitmap[i]); //Note u32!
end.
We need to cast our bitmap to u32, because we should read and write 2 pixel at time. In this example the screen is refreshed very slowly too, but don't worry! As I already have said, we will introduce a method that will decrease drastically memory reading and writing time access.
Mode 4:
If Mode3 is good to show static images, with Mode4 you can put on the screen some smooth animations. In fact, this mode is palettized and there is some space in video memory to draw two screens (a front buffer and a back buffer), so you can use page flipping and double buffering. Let's look at a first example, where we'll put a static image on the screen. We need a tool that can produce images with a separate optimized palette of 256 colors. Usenti is smart enough to do this
stuff: in the 'export' window check 'Pal', start=0, num=256, check 'image', select 'bitmap', bpp=8, cprs=none, type=gas, select 'u32' then press 'Ok'. You should get a new .s file that you should use like seen in mode3. Here the source code:
program main;
{$L mode4.o}
uses
gba_regs, gba_types, gba_video;
var
x: u32;
// From mode4.s, made by usenti
mode4Pal: array [0..0] of u16; cvar; external;
mode4Bitmap: array [0..0] of u16; cvar; external;
begin
SetMode(MODE_4 or BG2_ENABLE);
for x := 0 to 255 do // Filling the palette
BG_COLORS[x] := u32(mode4Pal[x]);
for x := 0 to 19199 do // Filling the screen
VideoBuffer[x] := u32(mode4Bitmap[x]);
end.
Because of VRAM needs to be accessed 2 bytes at time, we need all that typecasts. In the next example we'll try to make a simple animation, by using a page flipping system. Get the frames of an animation you want to show and open the images fame by frame with Usenti. Compared
to previous example, you should check 'append'. In this way, you will get an unique file with palette array and a big array with all images.
program main;
{$L walk.o}
uses
gba_core, gba_regs, gba_types, gba_video;
var
// From walker.s, made by usenti
palettePal: array [0 .. 0] of u16; cvar; external;
bmpwalker: array [0 .. 0] of u16; cvar; external;
x, n: u32;
begin
SetMode(MODE_4 or BG2_ENABLE);
for x := 0 to 255 do
BG_COLORS[x] := u32(palettePal[x]);
while true do //animation stuff
begin
for x := 0 to 19199 do
VideoBuffer[x] := u32(bmpWalker[x + (n * 19200)]);
Flip(); // Flip backbuffer to frontbuffer
n := (n + 1) mod 17; // We have 17 frames in our movie
end;
end.
Aaaargh! Awful. It looks like a slow motion movie... Well, I think that we should do something to improve speed. Let's look at the following example:
program main;
{$L walk.o}
uses
gba_core, gba_regs, gba_types, gba_video;
var
// From walker.s, made by usenti
palettePal: array [0 .. 0] of u16; cvar; external;
bmpwalker: array [0 .. 0] of u32; cvar; external;
var
x, n: u32;
begin
SetMode(MODE_4 or BG2_ENABLE);
for x := 0 to 255 do
BGPal[x] := u32(palettePal[x]);
while true do
begin
Wait(4);
memcpy32(VideoBuffer, @bmpWalker[(n * 9600)], 9600);
Flip();
n:=(n+1) mod 17;
end;
end.
Note that I have added a call to Wait() function in order to slow down the execution speed :). We are using an asm optimized routine (memcpy32) that copies our 32 bits data in the right memory location (for 16 bits data, there is memcpy16). However, for small amounts of data you can use a loop, as you can see in the example for palette filling. This isn't the only way to move around data chuncks, because the GBA BIOS allows DMA copy, that is faster than memcpy, but its execution stops the CPU. This can cause problems with interrupts, so pay attention when you are using it.
Mode 5:
I don't go in too much deep for this mode. In fact, if you have understood Mode 3 and 4, then you know all you need for Mode 5. In fact, here you have a fullcoror screen like Mode 3, but you
have room for a back buffer too. This is possible because in Mode 5 we have only a reduced screen of 160*128 pixels. This screen mode is useful when you have to show a movie in fullcolor: you need more than 256 colors of Mode 4 and you need also a back buffer. You are too smart to want an example here :P
Tile
Modes
Stop jocking now and let start the serious work! Indeed, if you want to make a game, you need to use a tile mode. Unlike bitmap modes, in tile modes you can access several layers (or background), that you can draw, manipulate, scale, rotate, according to the mode you are using. Following table shows layer's features:
|
Standard Mode |
Advanced Mode |
Amount of tiles |
1024*16 colors
256*256 colors |
256*256 colors |
Screen size (tiles) |
32*32; 32*64;
64*32; 64*64 |
16*16; 32*32;
64*64; 128*128 |
Screen size (pixels) |
256x256;
512x256;
256x512;
512x512 |
128x128,
256x256,
512x512,
1024x1024 |
Effects |
Scrolling; flipping;
fading; alpha blending;
mosaic |
Scrolling;
fading; alpha blending;
mosaic;
rotation; scaling
|
In each mode, each background can be accessed in standard mode or in advanced mode, following this table:
|
BG0 |
BG1 |
BG2 |
BG3 |
Mode 0 |
standard |
standard |
standard |
standard |
Mode 1 |
standard |
standard |
avanced |
-- |
Mode 2 |
-- |
-- |
avanced |
avanced |
In each tile mode you need to load both tileset and map in memory, that are stored both in VRAM, starting at $6000000. Whole VRAM can be subdivided in 4 Char Blocks (that store the tiles) or in 32 Screen Blocks (that store the map). Following table helps to have a better understand of VRAM:
Char Blocks (16kb) |
Memory Address (2kb) |
Screen Blocks |
Char Block 0 |
$6000000
$6000800
$6001000
$6001800
$6002000
$6002800
$6003000
$6003800
|
Screen Block 00
Screen Block 01
Screen Block 02
Screen Block 03
Screen Block 04
Screen Block 05
Screen Block 06
Screen Block 07 |
Char Block 1 |
$6004000
$6004800
$6005000
$6005800
$6006000
$6006800
$6007000
$6007800 |
Screen Block 08
Screen Block 09
Screen Block 10
Screen Block 11
Screen Block 12
Screen Block 13
Screen Block 14
Screen Block 15 |
Char Block 2 |
$6008000
$6008800
$6009000
$6009800
$600A000
$600A800
$600B000
$600B800 |
Screen Block 16
Screen Block 17
Screen Block 18
Screen Block 19
Screen Block 20
Screen Block 21
Screen Block 22
Screen Block 23 |
Char Block 3 |
$600C000
$600C800
$600D000
$600D800
$600E000
$600E800
$600F000
$600F800 |
Screen Block 24
Screen Block 25
Screen Block 26
Screen Block 27
Screen Block 28
Screen Block 29
Screen Block 30
Screen Block 31 |
Each screen block is 2kb wide ($800=2048 bites); each char block is 8 screen blocks wide (8 screen block * 2kb = 16kb). Ok, now we know how much memory we have... but how many tiles we can use? Easy to calculate: first of all, a tile is 8*8 pixels wide; in order to specify a palette index for a 16 color pixel, we need values from 0 to 15, so 4 bits are enough. Let's do some calculations:
a 16 colors tile = 8*8 pixels = 64 * 4 bits = 256 bits = 32 bytes
(Phew) So: each 16 color tile takes 32 bytes. What about 256 colors tiles (that you need for advanced mode)? In this case, we need 8 bits in order to specify a 256 index value for the palette.
256 colors tile = 8*8 pixels = 64 * 8 bits = 512 bits = 64 bytes
The table below could be useful as reference:
Block type (Memory) |
16 colors |
256 colors |
1 Screen Block (2kb) |
a map of 64 tiles |
a map of 32 tiles |
1 Char Block (16kb) |
512 tiles |
256 tiles |
Now you should have an idea about how tiles are working in memory. Of course you can't store tiles and maps in the same memory location, so be careful: overlapping is a bad thing! The memory taken by a tilemap varies, according to screen size and standard or advanced mode you are using.
Standard |
Advanced |
Screen size
|
Memory |
Screen size |
Memory |
256*256 |
2kb |
128*128 |
256bit |
512*256 |
4kb |
256*256 |
1kb |
256*512 |
4kb |
512*512 |
4kb |
512*512 |
8kb |
1024*1024 |
16kb |
How much blabbing... Well, now we can try to go deeper in tile mode, showing some small examples. Basically, the three tile modes are pretty similar, so we will analyse the differences between standard and advanced background only.
Standard background
Tiled modes aren't so difficult to understand. In addiction to the code we already have seen for bitmap modes, you should set some attributes to the layer you want to use. I think that a line of code explains better that a thousand of words, so let's show a basic example:
program main;
uses
gba_regs, gba_types, gba_video, gba_bg;
var
i: u32;
pal: pu16;
tiles: pu16;
map: pu16;
begin
pal := MEM_BG_PAL; // pointer to te palette
tiles := MEM_BG_CHAR(0); // pointer to char block
map := MEM_BG_MAP(24); // pointer to screen block (map)
SetMode(MODE_0 or BG0_ENABLE);
// BG0 settings:
REG_BG0CNT^ := (BG_SIZEA_256_256 or // screen size is 256*256 pixels
BG_COLOR_256 or // we want a 256 colors palette
BG_CHARBASE(0) or // we are using char block nr.0
BG_MAPBASE(24)); // we are using screen block nr.24
pal[0] := RGB(0, 0, 0); // stores black color in the palette
pal[1] := RGB(0, 0, 31); // stores blue color in the palette
pal[2] := RGB(0, 31, 0); // stores green color in the palette
// drawing 3 colored tiles in screen block
// remember that in VRAM we are writting 2 pixels at time, so each
// pair of pixels is n + (n shl 8), where n is palette index
for i := 0 to 31 do
tiles[i] := 0 + (0 shl 8);
for i := 32 to 63 do
tiles[i] := 1 + (1 shl 8);
for i := 64 to 95 do
tiles[i] := 2 + (2 shl 8);
map[32 * 9 + 15] := 1; // drawing a tile stored in pos. 1
map[32 * 10 + 15] := 2; // drawing a tile stored in pos. 2
end.
This small demo shows on the screen a couple of tiles. Nothing too fancy... the only things you should note are the register REG_BGxCNT, that is used to set the BGx properties, and the loop used to fill the tiles, that writes two bytes at time in a single step, by adding and shifting here and there. The code is full commented, so you should not have trobules.
Showing something more elaborated paradoxically is even simpler, because we don't need to fill the tiles two bit at time, but we can use some useful gbalib functions. Let's look at a nicer example that uses 3 bg.
Advanced background
To do
Sprites
To do
Text system
To do
Keypad
To do
Advanced techniques
To do
Hardware
graphical effects
To do
GCC
and FPC: differences
FPC does not support some features that you can find in gcc-aimed source code for gba.
- At this time, you can't use THUMB asm instruction set
- It is not possible to relocate variables in pascal source directly. In gcc you can write:
int myVar __attribute__ ((section (".mySection"))
The code above puts myVar in .mySection. A workaround for FPC is to declare the variable in an external asm file:
@ external file ExtVars.s
.section .mySection
.global myVar
...
then call it in pascal source code as external:
...
{$L ExtVars.o} //You need to assemble ExtVars.s and link it
var
myVar: integer; cvar; external;
...
- FPC does not allow volatile declarations. Just ignore it, because fpc compiler does not handle a such optimization.
- GCC preprocessor is a bit more complex than fpc one. All #define that require a parameter can be safely replaced by an inline procedure, declaring the parameter as an integer:
// GCC preprocessor define...
#define SCREEN_BASE(m) ((m) << 8)
// ...in fpc becomes:
function ScreenBase(m: integer): integer; inline;
begin
ScreenBase := m shl 8;
end;
Building
FPC from sources
In order
to build fpc for gba you need a working copy of fpc binaries. Be sure
that fpc path (eg. c:\fpc\bin\i386-win32) is at
the first place in your
search path (because compatibility problems with eg. Delphi make.exe)
Step
1 - Getting
Cross Binutils
You need cross binutils for
arm-gba. You can get it (for win32) from http://itaprogaming.free.fr/download/arm-gba-binutils.zip.
Extract them to some dir in the path on your machine. We will suppose
that you extracted the cross binutils to:
%FreePascal%\bin\arm-gba
where %FreePascal% is the path of your fpc binary
installation.
Step
2 - Making
Cross compiler
Now
you need to build fpc cross compiler for ARM processor. To do that you
need the latest fpc 2.1.x sources and a working installation of fpc
2.0.x.
Get the latest 2.1.x source from Subversion
repository:
http://www.freepascal.org/develop.html#svn
We
will suppose that your Free Pascal 2.1.x source code is located here:
%FreePascal%\sources
Step 3 - The
Build process
In order to build the cross
compiler it is necessary to have a correct PATH environment variable
pointing to cross binutils. In Windows you should add
%FreePascal%\bin\arm-gba to the PATH. Now open a dos prompt, go to
%FreePascal%\source\compiler and run:
make PPC_TARGET=arm
At the end
of building process, you should have a ppcarm.exe file in %FreePascal%\source\compiler
directory. Now copy these files to
%FreePascal%\bin\arm-gba. In the dos prompt go to
%FreePascal%\source\rtl\gba and do
make CPU_TARGET=arm OS_TARGET=gba PP=ppcarm OPT="-Tgba"
At the end of the
compiling process, you can find a new directory called
%FreePascal%\source\rtl\units\arm-gba
Now
copy the directory 'arm-gba' and all files inside
in
%FreePascal%\units
Step
4 - Configuration
file
Now
you need to create fpc.cfg configuration file in
%FreePascal%\bin\arm-gba folder in order to use ppcarm.exe easily.
Create
an empty fpc.cfg file in %FreePascal%\bin\arm-gba folder
and add the following lines to it:
-Tgba
-FuD:\freepascal\units\arm-gba
-XParm-gba-
-FDD:\freepascal\bin\arm-gba
Of
course you should replace D:\freepascal with your fpc installation
directory.