Hardware | Contents | First demo |
There are several development kits available for GBA programming. Most of these contain tools for assembly and C/C++. There are a number of development kits based on the GNU compiler collection (GCC) for the ARM CPU. In order of appearance, these are DevKitAdvance (DKA), HAM and devkitARM (dkARM), all of which are freeware. In the last two years or so, devkitARM (dkARM) has replaced DevKit Advance (DKA) as the toolchain of choice. dkARM is smaller, faster and more up-to-date than DKA and will be the focus of this tutorial.
A different type of beast altogether is HAM. This not only has the toolset, but also a library of useful functions and macros called HAMlib. The IDE for HAM called VisualHAM is included in the package. Of course, there's also the official ARM Development Suite (ADS), but that's rather pricey. There is also a BASIC environment called DragonBASIC. Like all BASICs, it's a lot easier to get something up and running than with C, but also less powerful because it hides all the bit-fiddling. The core functions are all written in a special version of assembly called TIN, and so you can extend the language if you want.
Since I actually like all that low level stuff like bit-fiddling and pointer work my language of choice is C, so that's what this tutorial will use as well. All the techniques presented here can be used in any language, though, only the syntax changes.
There are a number of C/CPP toolchains available, mostly based on the Gnu Compiler Collection (GCC). The de-facto standard for GBA programming is devkitPro (DKP). DevkitPro is actually a more than just for GBA development; it also houses tools for PSP, GameCube and probably Wii in the future too. For GBA development, the specific toolchain is called devkitARM, but the names can be used interchangeably. DevkitArm can also be used for the NDS.
Installing DKP is easy: just download the latest installer from www.devkitpro.org and follow the wizard. The essential tools of DKP are:
The full installation of DKP also comes with a number of other items that, while not essential, are worth having as well.
Most of the options of the installer make sense, so installing shouldn't be too hard. If you do run into trouble at this stage, you can also setup the whole thing manually by downloading each separate component from the devkitPro SourceForge page ( http://prdownloads.sourceforge.net/devkitpro/) and installing the pieces yourself.
And now for the hard part. Well, at least for us Windows-types who are used to a GUI doing all the actual work for us. GCC (and thus DKP too) uses a command-line interface, so instead of dialog boxes and mouse-clicks, you're working with shell commands (with documentation if you're lucky) and a good deal of typing.
Relax, it's not that difficult; you just have to pay a little more attention, that's all. When dealing with command-line tools, there are three things to consider: the name of the tool you want to use, the command-line arguments that it can work with, and the concept of paths. The first two are usually covered by a manual or a tutorial; also, if you know the name you can often find out what kind of arguments it wants by using ‘tool-name --help’. The path of a tool is the full name of a file, including all the directories. The environment needs to know the exact location of the tool, not just the basename. There are many ways of doing this, depending on OS and development environment, so I can't really go into detail about that now. I will give an easy, universal way of dealing with this point before the chapter is done though.
This seems one of the major obstacles of getting things running. The DKP installation sets a few thing up itself and if you're running DKP Sanctioned Makefiles (™, ®, etc) there should be no trouble. However, there is still a lot of non-DKP makefiles and batchfiles out there (including Tonc), so you will have to know a little bit about this. Here are a few ways of setting the path.
As a minimum example of what you'd need to do to get things done in a command-line environment, the following is what you'd have to do to turn the first demo first.c into a working GBA binary. Note, some of these instructions will be Windows specific, but should have comparable commands in your own operating system.
First, open up a command-line environment (Start Menu, Run, type cmd). Then browse through to [TONCDIR]\code\basic\first. Once there, you can compile and link your stuff by entering the following.
@ add path manually; not required if you've set it globally
@ modify for your own dir-structure, of course
PATH= e:\dev\devkitPro\msys\bin;e:\dev\devkitPro\devkitARM\bin;
arm-eabi-gcc -mthumb-interwork -c first.c
arm-eabi-gcc -specs=gba_mb.specs -mthumb-interwork first.o -o first.mb.elf
arm-eabi-objcopy -O binary first.mb.elf first.mb.gba
gbafix first.mb.gba
The first line is how I'd set the path on my system, but I'm absolutely certain it'll be done differently on your system. The rest will be the same for everyone. Fig 2.2 shows exactly what I did in the DOS box, and here's what each line does.
e:\dev\devkitPro\msys\bin e:\dev\devkitPro\devkitARM\binActually, the MSYS directory isn't even necessary here, but will be required later so I added it anyway. Setting the path here is only necessary if you hadn't done it elsewhere, like in autoexec.bat or the system variables.
The tools names hare are arm-eabi-gcc, arm-eabi-objcopy and gbafix, which are made visible by adding devkitARM's bin directory to the path. Things like -c, -o are option flags specific to each tool. For example, it's the -c option that tells the compiler to only compile the source and not create a final executable, and -mthumb-interwork tells the compiler to allow for both ARM and THUMB code in the binary, which is a good thing.
How do you know which ones you need? By looking in the manuals. Or by browsing other peoples stuff. I've collected a few of the more common flags here.
The flags -mthumb-interwork
and -specs=XX
in
the compilation and linking steps are required for dkARM.
Use XX=gba_mb.specs
for multiboot code and
XX=gba.specs
for a normal ROM-based code. (Don't worry if
you don't know what that means right now; just use them as the example
does.)
As of revision r19, dkARM uses the newer ‘eabi’ platform, which is different from and in fact incompatible with the previous ‘elf’ platform. If you're completely new this shouldn't be a problem, but for if you're updating from previous version there are a few things you should know.
I'm sure these issues will be resolved in the future, but if you want to stick with r18 for now, all you have to do is change the prefix back to ‘arm-elf’.
There are two different kinds of gba builds: ‘cart’ builds
and ‘multiboot’ builds. A cart build puts the main code and
data in the 32MB ROM (0800:0000h
) of a cart. A multiboot
build puts that stuff in the 256kB EWRAM (0200:0000
).
Commercial games are obviously cart builds, but make use of multiboot
builds to make single-cart multiplayer possible.
Other than the maximum size, there is little difference in gameplay between both. For homebrew, multiboot does have one advantage, namely that you can load a game up to hardware without the need of an expensive flashcart; you can build your own PC-GBA cable for peanuts. For this reason, all the demos here are set for multiboot builds.
To make the distinction between cart and multiboot binaries clear, they are sometimes different extensions. Cart builds generally use plain 'ol ‘.gba’. Multiboot builds often use ‘.mb’, ‘.mb.gba’ or ‘_mb.gba’.
Right, now that you've seen the basic steps of working with devkitARM via a DOS-box (fig 2.2), let's never do anything like this again. If you're prone to typos (and let's face it, who isn't) or just lazy, you're going to loathe having to type this every time you want to compile your code. There are two ways of making life easier in this respect, the first is to put the commands in a batch (.BAT) file and run that. There is a first.bat in the directory of the first demo that you can just execute and it'll run the commands in the batch-file automatically. No more tedious typing, hoozah!
Well, that's actually only half true. You still need to add the build steps to the batch-file yourself, which is alright at first, but for bigger projects it will become a major hassle. A second point against batchfiles is that they're pretty dumb. Unless you want to comment out specific commands, it'll rebuild every file in the project, which becomes annoyingly time-consuming for bigger projects. And there are other similar disadvantages of batch-files that I won't go into here. Instead, we'll use a third option: makefiles.
Makefiles are a lot like batch-files, only more advanced. For one thing, they check whether or not files need compiling or assembling by themselves. They can also make complex use of variables and have extensive string manipulation and wildcard capabilities that make them much more versatile than batch-files. The problem is getting to grips with their syntax. This isn't quite the time yet for a full exposition, but I'll show you the makefile equivalent of the commands given above in the next section.
While it is true that batchfiles are easier to set up, in the end using makefiles is just so much better. Take a little effort to learn the makefile syntax; it's well worth it.
For the record I know very little about HAM; I use dkARM in combination
with the Visual C++ IDE. I will tell you what I do know, but
I can't guarantee it's 100% accurate.
HAM is a full-fledged GBA development environment that can take care
of a lot of the low level stuff like sprite and map loading, special
effects, timers and all that stuff. It also contains a 3rd
party sound/music library called Krawall and a full IDE with all
the important editing tools. And, of course, a truckload of demos.
Setting up HAM is easy: simply download the freeware version from www.ngine.de and install. And then install again because it's only the installer that you've just installed :P. After the second install everything will be ready, but you'll actually have two copies of each, one of them can safely be removed. Oh, and make sure that the path doesn't have spaces in it, HAM doesn't like that (and you shouldn't either).
After that you should be ready to roll. I say ‘should’ because if you're moving from DKA to HAM you may encounter a small problem building the demos. HAM needs a file called "cygwin1.dll" to run. The DKA r4 self-extractor from the PERN project copies a file of that name into the Windows System folder, which is an older version than HAM expects. If this is the case then building stops even before it really begins. Of course if you've read HAM's faq you already know all about that. In any case, this was the only problem I had when getting HAM working.
Underneath HAM it's still GCC, by the way, meaning that normal GBA code can be ported between devkitARM and HAM's toolchain without too much fuss. HAM also uses the makefile system (a very fine makefile system I might add), but all that stuff will be hidden from sight by the IDE. As you add or remove files from the Visual HAM workspaces (.VHW) it'll update the list accordingly (FYI, the file information is stored in VHO files).
VisualHAM's creator, Peter Schraut has also written an add-on library called HEL. Unlike HAM, some time has been spent on optimizing HEL's code, or at the very least to make it not slow. If you're using HAM, consider using HEL as well.
If C and assembly give you the creeps, you can try the more user-friendly DragonBASIC. If you don't know assembly or C yet, but want to do some GBA programming, you might want to give this a try, because C and assembly aren't exactly the easiest languages to learn. But even if you do, DragonBASIC can be interesting because it is simply the fastest way of getting test-cases up and running.
Although I mainly work in C I know that DragonBASIC is very easy to use. All the core functions are browsable and if you can't figure it out from that there's always the tutorials and there are both online and offline help-files. One area where DragonBASIC is very different from using C is in that it can use bitmaps like BMP and PCX directly, without the need of a converter. This is both a blessing and a curse, it's a blessing because it'll allow you to get something up and running in no-time, but you need to be sure that (a) your bitmap is in the right bitdepth and (b) your bitmap editor can create BMPs and PCXs that leave the palette intact (MS-Paint doesn't, for example). This is a problem with C as well, of course, but at least you can look at the converted C-file and read the bit-depth from it.
There are many ways to get your code compiled. The first would be to type in all the commands manually …
with the usual long list of compiler flags …
per file …
without typos …
for every time you want to build the project.
Yah, I didn't think so either.
Entering the commands manually is, not to put a finer point on it, batshit insane. Fortunately, there are a number of ways to automate the whole build process. The first would be to use a batch-file, which is essentially a file with the relevant commands in them, which when invoked will run all the commands automatically. Sounds good, huh? Well yeah, it may be better than doing everything manually, and it may work well enough for very small projects, but for anything bigger batchfiles are just too messy, difficult to maintain and slow.
A more professional approach is makefiles, which is what we'll be using here. Like batchfiles, makefiles contain the commands to build the project, but they are a lot more structured which is essential for anything bigger than a trivial test project.
For example, with makefiles you can create variables for commonly used items, which makes it easier to maintain. The commands are organised in rules, which has a nice benefit that you can have one rule for types of files. The rules also check whether a file is out of date, so you don't have to wait for each and every file in your project if you've changed just one of them. You also have a large number of pattern substitution techniques at your disposal which can help you automate your work. Lastly, makefiles are platform-independent, which is always a good thing.
The Tonc makefile project is pretty complicated and contains multiple makefiles, controlled by the master makefile tonc.mak. To keep things simple, I'll only show the makefile for the first demo called makefile.mak.
makefile.mak contains the basic commands that you'd need to build first.c into first.gba. Mind you, it is a very minimal makefile, intended to give you a brief look into the syntax of a makefile, not anything you'd want to use for a larger project.
#
# Makefile
#
# a minimal makefile (and I do mean minimal, look to the others
# for real-life use)
PATH := $(DEVKITARM)/bin:$(PATH)
PROJ = first
CC = arm-eabi-gcc
OBJCOPY= arm-eabi-objcopy
.PHONY : build clean
build :
$(CC) -mthumb-interwork -c $(PROJ).c
$(CC) -specs=gba_mb.specs -mthumb-interwork $(PROJ).o -o $(PROJ).mb.elf
$(OBJCOPY) -v -O binary $(PROJ).mb.elf $(PROJ).mb.gba
gbafix $(PROJ).mb.gba
clean :
@rm -fv $(PROJ).o
@rm -fv $(PROJ).mb.gba
@rm -fv $(PROJ).mb.elf
The most common elements of makefiles are comments,
variables, rules and commands. As you
may have guessed, '#
' indicates a comment. A variable is
something like
XX= yy
and works very much like a C macro: whenever
make
encounters $(XX)
it will substitute
yy
in its place. Oh, the parentheses are required,
otherwise make will think you mean ($X)X, which would be bad.
Examples here are PROJ
, CC
and
OBJCOPY
. If by any chance you're wondering whether I
should have used another variable for the prefix used in these last
two, the answer is yes. Good call, by the way; keep it up. The
PATH
‘variable’ here refers to the global
path; because the devkitPro installer creates a system variable for
the devkitARM directory, we can use it to add its bin directory so
that the compiler tools will be recognized. If already modified
the system path, this line isn't necessary. If you're using another
toolchain, the line wouldn't work; but as I said, DKP is the
recommended toolchain anyway.
The lines with colons
target : prerequisite command
indicate rules. The things you want built, the targets,
are on the left-hand side of the colon; the things needed for that,
prerequisites, are on the right. On the following line(s)
are the commands, preceded with a tab. Yes, I do mean an actual tab.
By default, the first rule that make encounters starts the
make-chain. If a prerequisite is out of date, it'll run the command.
Note that prerequisites can be targets themselves, though this file
doesn't illustrate that. This file has two independent rules, one for
build
and one for clean
, which will remove
all the output. You can run this rule by adding it to the
command-line options when you invoke make, more on that later.
NOTE: GNU's make requires tabs before actual commands, not spaces. If you copy-paste, you may have to place the tabs manually.
One thing I haven't told you yet is what ".PHONY
"
means. This is a an example of a directive. Normally the targets
of the rules are file-names. The commands are only run when the target
doesn't exist or is older than its prerequisites. Since none of the
commands of, say, build
actually creates a file called
"build", the file shouldn't ever exist and
commands would always be executed. But what happens when it does
exist? Well, since the rule doesn't have prerequisites the file is
always up to date and the commands never run. To counter this, you tell
make that the file is a PHONY
.
And that's basically what a makefile looks like. Again, this isn't a very advanced makefile; it's just one step above your garden variety batch file (which is why the lines after the ‘build’ rule should be so familiar) and hardly uses any of the things that make makefiles so useful. In a proper makefile you will want to separate the compile and link steps and make files of file variables that contain lists of file names and stuff like that. If you want to know more about how that works, you can read more on makefiles and common compiler flags in this appendix.
Of course, having a makefile means nothing unless you know how to run it. Makefiles are executed by a tool called make, which comes with the msys installation (and also HAM). To run it, you do something like this:
make -f filename target
The -f flag says which makefile to run (in our case makefile.mak. The flagless option target denotes the main rule for the make process, which in this case can be either build or clean.
Make also has a number of interesting default options. For example, if you don't give a target, it'll just run the first rule in the file, in this case 'build'.
Also if you do not specify a filename for the makefile, it'll look for either 'GNUmakefile', 'Makefile' or 'makefile' and run that. Conversely, your makefile is named like this, you won't have to specify the file to run make on. This simplifies the make process, because you don't have to remember what file to call, you just call make by itself in a certain directory and it'll do what's necessary. This is actually the standard procedure for makefiles; but may be a little annoying if you're used to double-clicking to open a file; extensionless files can't be opened like that. There are ways around that, though: you could have the rules in a .mak and let Makefile include that, or you can use some Regedit-fu to open extensionless with your editor of choice (look here for an example of that).
Of course, make has plenty more options, but this is not the time. Just RTFM is you want more info.
Running make from the command line is the most obvious way of doing it, but it's probably not the most convenient way. Most non-trivial text editors and all IDE's will have ways of running shell commands; you could even make a batchfile to call make for all I care.
DevkitPro comes with one of those non-trivial editors (Programmer's Notepad), and it is capable of running external commands. Even better, it already has the commands make and make clean! The short-keys for these are Alt+1 and Alt+2, respectively. Note that these only work for the default makefile names, so something with an extension is right out.
To get IDEs and other editors to run make usually takes a little more doing. Because there are so many of these, setting them up will not be covered here; google or use the help files to figure out what needs to be done for your editor. I have examples for setting up conTEXT, an alternative for PN, and MS Visual Studio (5 and 6) in this appendix. The DKP site also has a few examples in the FAQ
As said, I control the Tonc projects via a master makefile called
tonc.mak. This has a number of rules through which one can
build or clean any or all of the sub-projects, as well as create the
tonc library libtonc.a that's used in the later projects.
In the main tonc-code directory there's also a dummy makefile called
‘Makefile’, which relieves you of the terrible
burden of having to type the 11 characters of
‘-f tonc.mak’. And also reduces the
chance of typos, so it's actually not such a bad idea (why not use
that file as a master makefile? Because you can't open an extensionless
file in Windows Explorer by a double click. Unfortunately). To build
or clean anything you can use one of the commands from table 1.
Note the absence of an -f option, and the initialisation of
variable ‘DEMO
’, which is then used inside the
makefile. Feel free to look inside tonc.mak so see how it's
done, but be aware that you won't readily understand it if you're new
to makefiles or the bash shell.
to ... | run ... |
---|---|
build tonclib.a | make utils |
build foo demo | make DEMO=foo |
clean foo demo | make DEMO=foo clean |
build all demos | make build_all |
clean all demos | make clean_all |
Prev | Contents | Next |
Hardware | First demo |