The new versions of GBDK use a new directory structure. The GBDK can target pure Z80 systems (and not only the Z80 variant found in the GameBoy). The new directory structure is: /SDK/XX-XX/Y.Y.Y
(Y-Y-Y
on DOS), where XX-XX
is the target and Y.Y.Y
is the version number. An additional benefit is that it is possible to install new GBDK versions without removing the older ones.
A preliminary version of the libraries documentation is available online. For technical informations about the GameBoy hardware, see there.
New: Have a look at the GBDoK documentation page maintained by Jason.
A japanese translation of this page is available here.
The GameBoy Developer's Kit (GBDK), is a set of tools that enable to develop programs for the Nintendo GameBoy system, either in C or in assembly. GBDK includes a set of libraries for the most common requirements and generates image files for use with a real GameBoy or with an emulator like VGB or no$gmb.
GBDK is available for UNIX and DOS. The UNIX version of GBDK has been tested on Solaris 2.6. The DOS version requires a 386 DX processor (or higher), with a DOS Protected Mode Interface (DPMI).
GBDK is currently being used in several projects. The first commercial project that used GBDK is Pro Action Replay II. GBDK also got used for a japanese RPG game (Monster Race from KOEI Co., Ltd.).
GBDK features:
GBsed, a companion sprite editor written in Java, enables to create images and sprites to be included in GameBoy programs. Alternatively, you can use the Win 95/NT BMP2GB program by Ian James that convert BMP files to C code, or the Win 95/NT GameBoy Tile Designer and Map Builder programs that lets you create images and maps for GBDK.
GBDK is freeware for non-commercial developments. If you use it, just send me an e-mail to keep me informed of your work. If you really find it great, you can send me something typical from your country...
If GBDK is used for developing a commercial program, I ask you to mention that the program has been made using GBDK (in the credits), and to send me a copy of the finished product. If you feel generous, you can also send me a copy of any other products that your company has produced for the GameBoy...
maccer
macro preprocessor.
__REG_XXXX
special variables. The optimizer automatically transforms LD
into LDH
when necessary.
cgb.s
library.
get_sprite_tile()
and get_sprite_prop()
.
banks.c
example.
-Wl-yp0x146=0x03
flag.
STOP
instruction.
wait_vbl()
function.
set_win_data()
function so that it is consistent with other functions. This change requires adding 0x80
to the first parameter of this function call in existing programs.
cls()
function to the standard output library (reported by Helmut Schoettner).
-int8
and -int16
) to specify the size of the integer type (default is 8 bits, which produces shorter and faster code).
-Wl-yp0x143=0x80
).
reset()
function to reset the GB (restart the program's execution).
_cpu
that indicated the CPU of the machine on which the code is running (GB, Pocket GB, Color GB).
memset
, malloc
, calloc
, and free
are implemented).
set_bkg_tiles
.
paint.c
) illustrating this library.
LD (HLI),A LD (HLD),A LD A,(HLI) LD A,(HLD) ADD SP,offset LDHL SP,offset
gb-dtmf
example so that it compiles on DOS.
comm.c
example which illustrates how to use communication routines.
-Wl-yn="XXX"
flag (where X
is the name of the program, which can contain up to 16 characters in quotes, including spaces; on Unix, depending on your shell, you must add backslashes before quotes and spaces like in -Wl-yn=\"My\ Game\"
).
memcpy()
and hiramcpy()
functions). The compiler now automatically generates two symbol for the start and the end of each function, named start_X
and end_X
(where X
is the name of the function). This enables to calculate the length of a function when copying it to RAM.
ram_fn.c
example which illustrates how to copy functions to RAM and HIRAM.
irq.c
example which illustrates how to install interrupt handlers.
switch_ram_bank()
function). The switch_bank()
function has been renamed to switch_rom_bank()
. The banks.c
example has been updated. The flags for generating multiple bank images have been modified.
-Wl-g.OAM=#
flag (where #
is the address of the sprite ram). The sprite ram address must begin at an address multiple of 0x100, and is 0xA0 bytes long.
int
comparison.
delay()
function so that it takes a long parameter. It can be used to wait between 1 and 65536 milliseconds (0 = 65536). The pause()
function has been removed.
galaxy.c
) has been added. It is the C version of the space.s
example. sprite.c
has been removed.
banks
example).
-Wl-g.STACK=#
flag (where #
is the address of the stack pointer).
show_bkg()
) have been changed into macros (e.g. SHOW_BKG
).
delay()
function waits exactly 1 millisecond, and the pause()
waits 256 milliseconds.
lib/gb.lib
(lib\gb.lib
on DOS) text file contains a list of modules in which to look for undefined symbols. The linker will parse this file, and link your code with the required modules only. The stdio
library has been split in several object files, and only necessary modules will be added to your code, thus reducing its size.
-Wo-lccdir=GBDK-DIR
flag when invoking lcc.
\GBDK-2.0
directory on DOS machines.
int
is 8 bits.
long
is 16 bits.
Installing GBDK requires the following steps:
You must have gcc, gnu make and /bin/csh for building GBDK.
% gzip -cd SDK.tar.gz | tar xf -
% cd SDK-2.0
% make % make installThis will create a directory (
$HOME/SDK/XX-XX/Y.Y.Y
by default, where XX-XX
is the target and Y.Y.Y
is the version number) with all the files you need in it.
% cd $HOME/SDK/XX-XX/Y.Y.Y/lib % make % cd $HOME/SDK/XX-XX/Y.Y.Y/examples % makeAt that point, you don't need the sources anymore and you can delete them.
You must have at least a 386 DX processor, and a DOS Protected Mode Interface (DPMI). For instance, Windows 95 provides a DPMI. You can also get one from here.
The GBDK distribution includes a batch file (gbdk.bat
, by Mark Rawer) that automates many of the steps below, and that enables to install the GBDK distribution in any directory. You'll have to edit and modify the first lines of this batch file before executing it.
-d
flag when invoking unzip
):
C:> unzip -d GBDK.zipThis will create a directory (
\SDK\XX-XX\Y-Y-Y
, where XX-XX
is the target and Y-Y-Y
is the version number) with all the files you need in it. GBDK must be installed in the root of one of your drives (e.g., C:\SDK
).
TMP
or TEMP
environment variable that points to a temporary directory.
C:> cd \SDK\XX-XX\Y-Y-Y\lib C:> make C:> cd \SDK\XX-XX\Y-Y-Y\examples C:> make
The compiler is based on lcc, a free of charge retargetable compiler for ANSI/ISO C. GBDK includes a code generator for lcc that generates code for the GameBoy custom Z80. For an ehaustive description, read the man page included with the lcc distribution.
The compiler defines the following sizes for basic types:
type | size | min | max |
char | 1 byte | -128 | 127 |
unsigned char | 1 byte | 0 | 255 |
int | 1 byte | -128 | 127 |
unsigned int | 1 byte | 0 | 255 |
long | 2 byte | -32768 | 32767 |
unsigned long | 2 byte | 0 | 65535 |
long long | 4 byte | -2147483648 | 2147483647 |
unsigned long long | 4 byte | 0 | 4294967296 |
float | 4 byte | n/a | n/a |
double | 4 byte | n/a | n/a |
pointer | 2 byte | n/a | n/a |
Since the CPU is an 8-bit processor, working with int
values is much more efficient than working with long
values. But you have to be careful with overflows.
When generating a GameBoy image, the linker will look for undefined symbols in each of the object files listed in the lib/gb.lib
text file. If one of these object files contains the symbol, it will be linked with the main program. If none of these object files contain the symbol, the linker will generate an error. Therefore, there is no need to explicitely link the main program with the standard libraries.
Here are some examples of lcc usage:
source.c
) and generate a GameBoy image (image.gb
):
lcc -o image.gb source.c
source.s
) and generate a GameBoy image (image.gb
):
lcc -o image.gb source.s
source.c
) and generate an object file (object.o
):
lcc -c -o object.o source.c
source.s
) and generate an object file (object.o
):
lcc -c -o object.o source.s
object1.o
and object2.o
) and generate a GameBoy image (image.gb
):
lcc -o image.gb object1.o object2.o
image.gb
) from an assembly source file (source.s
), a C source file (source.c
), and an object file (object.o
):
lcc -o image.gb source.s source.c object.o
The following flags allow to pass options to the preprocessor, the compiler, the assembler, and the linker:
-Wp -Wf -Wa -Wl
A typical useage of these flags is for generating listing and map files:
-Wa-l
flag. The name of this file is the same as the object file, with the .lst
extension. It contains the assembly code and source. If the assembler generates error messages, listing files are necessary for locating these errors.
-Wl-m
flag. The name of this file is the same as the image file, with the .map
extension. It contains informations about where functions and variables are located in ROM. If the linker generates error messages, map files are useful for locating these errors.
-Wl-j
flag. The name of this file is the same as the image file, with the .sym
extension. It contains informations that can be used by the no$gmb built-in debugger.
It is generally a good habit to generate listing and map files.
The assembler is based on ASxxxx Cross Assemblers.
The GameBoy processor is very similar to the Z80, although some of the instructions are missing and some ther have been added. Also, she second set of registers (BC', DE', HL', AF') and the index registers (IX, IY) are missing and, consequently, there are no DD and FD opcode tables. Finally, I/O ports are gone and so are all IN/OUT opcodes. For a descriptions of the changed instructions, read the GameBoy FAQ.
I have modified the name of some of the GB-specific opcodes:
LD (HLI),A -> LD (HL+),A LD (HLD),A -> LD (HL-),A LD A,(HLI) -> LD A,(HL+) LD A,(HLD) -> LD A,(HL-) ADD SP,offset -> LDA SP,offset(SP) LDHL SP,offset -> LDA HL,offset(SP)
The LDA
opcode means "load address", like in 68x00 assembly. I've called these instructions like this because both are orthogonal (they do the same thing on two different registers).
The assembler accepts the following flags:
ASxxxx Assembler V01.75 (GameBoy Z80-like CPU) Usage: [-dqxgalopsf] outfile file1 [file2 file3 ...] d decimal listing q octal listing x hex listing (default) g undefined symbols made global a all user symbols made global l create list output outfile[LST] o create object output outfile[o] s create symbol output outfile[SYM] p disable listing pagination f flag relocatable references by ` in listing file ff flag relocatable references by mode in listing file
For an ehaustive description, read the asmlnk.doc
file in the doc
directory, or this html-ized document.
The linker is based on ASxxxx Cross Assemblers. It has been extended in particular to support generation of GameBoy images.
The linker accepts the following flags:
ASxxxx Linker V01.75 Startup: -- [Commands] Non-interactive command line input -c Command line input -f file[LNK] File input -p Prompt and echo of file[LNK] to stdout (default) -n No echo of file[LNK] to stdout Usage: [-Options] outfile file [file ...] Libraries: -k Library path specification, one per -k -l Library file specification, one per -l Relocation: -b area base address = expression -g global symbol = expression -yo Number of rom banks (default: 2) -ya Number of ram banks (default: 0) -yt MBC type (default: no MBC) -yn Name of program (default: name of output file) -yp# Patch one byte in the output GB file (# is: addr=byte) Map format: -m Map output generated as file[MAP] -j no$gmb symbol file generated as file[SYM] -x Hexidecimal (default) -d Decimal -q Octal Output: -i Intel Hex as file[IHX] -s Motorola S19 as file[S19] -z Gameboy image as file[GB] List: -u Update listing file(s) with link data as file(s)[.RST] End: -e or null line terminates input
For an ehaustive description, read the asmlnk.doc
file in the doc
directory, or this html-ized document.
Several include files are part of GBDK. Some of them only define useful macros (with no code associated), while others define functions implemented in separate object modules. The libraries are split in several small object files in order to reduce the size of the final image file (only the required modules are linked with the main program). The include files and libraries are divided in the following groups:
The crt0.o
object module contains the basic C runtime library, with GameBoy initialization routines, C support and other essential things. This library is required and automatically linked with every program.
The gb.h
include file defines basic GameBoy-related macros and functions. It also includes the hardware.h
file that defines GameBoy hardware registers.
The ctype.h
, stdarg.h
, stdlib.h
, string.h
, and types.h
include files define some functions found in the standard C libraries.
Basic console I/O is provided through a set of functions defined in the console.h
and stdio.h
include files. Note that console I/O uses most of the tiles and sprites of the GameBoy, and thus is not easily mixable with graphics programs.
Simple graphic functions for drawing points and images on the screen are defined in the drawing.h
include file. Note that the graphic library uses most of the tiles and sprites of the GameBoy.
The rand.h
include file defines functions for using the GBDK random generator.
GBDK includes several example programs both in C and in assembly. They are located in the examples
directory, and in its subdirectories. They can be build by typing make
in the correnponding directory.
space.s
The space.s
example is an assembly program that demonstrates the use of sprites, window, background, fixed-point values and more. The following keys are used:
Arrow keys : Change the speed (and direction) of the sprite Arrow keys + A : Change the speed (and direction) of the window Arrow keys + B : Change the speed (and direction) of the background START : Open/close the door SELECT : Basic fading effect
galaxy.c
The galaxy.c
example is a C translation of the space.s
assembly program.
paint.c
The paint.c
example is a painting program. It supports different painting tools, drawing modes, and colors. At the moment, it only paints individual pixels. This program illustrates the use of the full-screen drawing library. It also illustrates the use of generic structures and big sprites. It is definitely worth having a look at its source. The following keys are used:
Arrow keys : Move the cursor SELECT : Display/hide the tools palette A : Select tool
sound.c
The sound.c
example is meant for experimenting with the soung generator of the GameBoy (to use on a real GameBoy). The four different sound modes of the GameBoy are available. It also demonstrates the use of bit fields in C (it's a quick hack, so don't expect too much from the code). The following keys are used:
UP/DOWN : Move the cursor RIGHT/LEFT : Increment/decrement the value RIGHT/LEFT+A : Increment/decrement the value by 10 RIGHT/LEFT+B : Set the value to maximum/minimum START : Play the current mode's sound (or all modes if in control screen) START+A : Play a little music with the current mode's sound SELECT : Change the sound mode (1, 2, 3, 4 and control) SELECT+A : Dump the sound registers to the screen
rpn.c
The rpn.c
example is a basic RPN calculator. Try entering expressions like 12 134*
and then 1789+
.
banks.c
The banks.c
example illustrates how to make multiple-banks programs.
ram_fn.c
The ram_fn.c
example illustrates how to copy functions to RAM or HIRAM, and how to call them from C.
irq.c
The irq.c
example illustrates how to install interrupt handlers.
comm.c
The comm.c
example illustrates how to use communication routines.
gb-dtmf/gb-dtmf.c
The gb-dtmf/gb-dtmf.c
program, written by Osamu Ohashi, is a Dual Tone Multi-Frequency (DTMF) generator.
colorbar/colorbar.c
The colorbar/colorbar.c
program, written by Mr. N.U. of TeamKNOx, illustrates the use of colors on a Color GameBoy.
dscan/dscan.c
Deep Scan (dscan/dscan.c
) is a game written by Mr. N.U. of TeamKNOx that supports the Color GameBoy. Your aim is to destroy the submarines from your boat, and to avoid the projectiles that they send to you. The game should be self-explanatory. The following keys are used:
RIGHT/LEFT : Move your boat A/B : Send a bomb from one side of your boat START : Start game or pause game When game is paused: SELECT : Invert A and B buttons RIGHT/LEFT : Change speed UP/DOWN : Change level
rand.c
The rand.c
program, written by Luc Van den Borre, illustrates the use of the GBDK random generator.
foo(i + 2 - 1);lcc will add 2 and substract 1, instead of just adding 1. In this situation, you should parenthesize then constant expression:
foo(i + (2 - 1));
type.h
):
BYTE UBYTE WORD UWORD LWORD ULWORDor
INT8 UINT8 INT16 UINT16 INT32 UINT32That way, you will always know the size of your variables.
U
, L
and UL
postfixes when necessary. U
specifies that the constant is unsigned, while L
specifies that the constant is long. Consider the following example:
UBYTE i, j = 0; i = j+0x80;The compiler will think that
0x80
is a signed value, and since it is bigger than the biggest signed 8-bit value (0x79), the compiler will treat it as a long constant. Following the C specification, j
will be extended to a long, and added to 0x80
using a long addition (which is costly on an 8-bit processor), before beeing truncated to an 8-bit value and assigned to i
. This example should be written:
UBYTE i, j = 0; i = j+0x80U;
int i1; /* OK : will be located in RAM */ char *s1; /* OK : will be located in RAM */ int i2 = 0; /* Error : will be located in ROM */ char *s2 = "Hi"; /* Error : will be located in ROM */ void main() { ... }
==
and !=
comparison operators to <
, <=
, >
, and >=
. The code will be shorter and quicker. For instance:
for(i = 0; i < 10; i++)is less efficient than
for(i = 0; i != 10; i++)
int
variables to long
if they have to be bigger than 255. If they should only contain values between 0 and 255, use an unsigned int
.
delay
function, you'll have to adapt your delay values.
show_bkg()
is now SHOW_BKG
).
For mixing C and assembly, you must use one file per language (you cannot embed C code with assembly) and link both files together. Here are the things to know:
i
will be called _i
in assembly.
DE
register.
SP+2
because the return address is also saved on the stack).
.globl
directive.
_reg_0xXX
where XX
is the register number (see sound.c
for an example).
HL
(and DE
when the function returns a result).
Here is an example of how to mix assembly with C:
main.c
main() { WORD i; WORD add(WORD, WORD); i = add(1, 3); }
add.s
.globl _add _add: ; WORD add(WORD a, WORD b) ; There is no register to save: ; BC is not used ; DE is the return register ; HL needs never to be saved LDA HL,2(SP) LD E,(HL) ; Get a in DE INC HL LD D,(HL) INC HL LD A,(HL) ; Get b in HL INC HL LD H,(HL) LD L,A ADD HL,DE ; Add DE to HL LD D,H LD E,L ; There is no register to restore RET ; Return result in DE
GBDK can generate multiple bank images (with both multible ROM and RAM banks) for MBC1 and MBC2 memory bank controllers. Multiple RAM banks are only supported by MBC 1.
With multiple ROM banks, addresses 0x0000 to 0x3FFF are reserved for the fixed ROM bank, while addresses 0x4000 to 0x7FFF are switchable, i.e. can be used for any bank. Switchable ROM banks are called _CODE_1
, _CODE_2
,... and the fixed ROM bank is called _CODE
(note that there is no _CODE_0
). The maximum number of ROM banks is 32.
Addresses 0xC000 to 0xDFFF are always reserved for the internal RAM. Addresses 0xA000 to 0xBFFF are reserved for (switchable) external RAM. External RAM banks are called _BSS_0
, _BSS_1
, _BSS_2
,... and internal RAM is called _BSS
. The maximum number of external RAM banks is 4.
When deciding how to populate your RAM banks, remember that local variables are always allocated on the stack, and initialized global variables are located in ROM. Only uninitialized global or static variables are allocated into RAM.
For generating multiple bank images, you have to:
-Wf-bo#
flag (where #
is the number of the bank, greater than 0). If you do not use this flag, the code will be located in the fixed ROM bank.
-Wf-ba#
flag (where #
is the number of the bank, greater or equal to 0). If you do not use this flag, the data will be located in the internal RAM.
-Wl-yo#
(for ROM) and -Wl-ya#
(for RAM) flags (where #
is the number of banks), and the type of MBC used in the cartridge using the -Wl-yt#
flag (where #
is the cartridge type code to be located at address 0x147 of the image).256Kbit = 32KByte = 2 banks 512Kbit = 64KByte = 4 banks 1Mbit = 128KByte = 8 banks 2Mbit = 256KByte = 16 banks 4Mbit = 512KByte = 32 banksStandard supported RAM sizes are:
None 64kBit = 8kB = 1 bank 256kBit = 32kB = 4 banksStandard supported cartridge types are:
0 : ROM ONLY 1 : ROM+MBC1 2 : ROM+MBC1+RAM 3 : ROM+MBC1+RAM+BATTERY 5 : ROM+MBC2 6 : ROM+MBC2+BATTERY
Bank switching is not automatic in programs. You have to explicitely call the switch_rom_bank()
and switch_ram_bank()
functions. See banks.c
for a complete example.
It is possible to copy functions to RAM and HIRAM (using the memcpy()
and hiramcpy()
functions), and execute them from C. The compiler automatically generates two symbol for the start and the end of each function, named start_X
and end_X
(where X
is the name of the function). This enables to calculate the length of a function when copying it to RAM. Ensure you have enough free space in RAM or HIRAM for copying a function.
There are basically two ways for calling a function located in RAM, HIRAM, or ROM:
extern
, and set its address at link time using the -Wl-gXXX=#
flag (where XXX
is the name of the function, and #
is its address).
The second approach is slightly more efficient. Both approaches are illustrated in the ram_fn.c
example.
The GameBoy hardware can generate 5 types of interrupts:
VBL : V-blank LCD : LCDC status TIM : Timer overflow SIO : Serial I/O transfer end JOY : Transition from high to low of joypad
It is possible to install your own interrupt handlers (in C or in assembly) for any of these interrupts. Up to 7 interrupt handlers can be installed for each interrupt. Interrupt handlers are called in sequence. To install a new interrupt handler, do the following:
foo()
) that takes no parameter, and that returns nothing. Remember that the code executed in an interrupt handler must be short.
add_XXX()
function, where XXX
is the interrupt that you want to handle.
set_interrupts()
function. Note that the VBL interrupt is already enabled before the main()
function is called. If you want to set the interrupts before main()
is called, you must install an initialization routine.
See irq.c
for a complete example.
You can install a routine that will be executed before the main()
function is called, and just before the interrupts are enabled. For instance, you can use an initialization routine to modify the interrupt flags and avoid that a VBL IRQ is handled before main()
is executed. For installing an initialization routine, you have to:
foo()
) that takes no parameter, and that returns nothing.
-Wl-g.init=XXX
flag (where XXX
is the name of your function). Remember that your function will have an initial underscore in assembly. In our example, it will be -Wl-g.init=_foo
.
It is possible to change the addresses of some important data at link time using the -Wl-gXXX=YYY
flag (where XXX
is the name of the data, and YYY
is the new address). The addresses that can be changed are:
.OAM : Location of sprite ram (requires 0xA0 bytes) .STACK : Initial stack address .refresh_OAM : Address to which the routine for refreshing OAM will be copied (must be in HIRAM) .init : Initialization routine
Messages of the type:
u 0226 a 0329 u 0333
are error messages from the assembler. To see where these errors occur, you should produce an assembly listing using the -Wa-l
flag of lcc and have a look at this file. If such an error occurs with a file generated by the compiler, send me the C source along with the listing.
Messages of the type:
?ASlink-W-Undefined Global .count referenced by module Demo
are error messages from the linker. An image file is generated, but sould be corrupted. Detailed information about errors can be found in map files (generated using the -Wl-m
flag of lcc).
The DOS shell truncates commmand lines to something like 128 characters. If you have to use a longer line due to the number of flags you pass to the compiler, I strongly encourage you to get bash
, a DOS port of Unix Bourne-Again SHell. It is much more powerful than DOS shell, and does not truncate command lines. A better alternative is to get make
, a DOS port of the Unix make utility, and use makefiles instead of DOS batch files. Of course, you can get both. They are available as part of DJGPP (DOS port of GNU C).
UBYTE tiles[] = { 0x00, 0x01, ..., 0xFF };But declaring the following arrays cause a compiler error:
UBYTE tiles[128];Here is the answer sent to me by Dave Hanson, one of the authors of lcc:
This is actually the most serious problem. ANSI C stipulates 32Kb as the maximum size of any object. lcc does indeed assume that an int can hold 16-bit values, because it uses ints to hold sizes (e.g., in type structures). Accepting long array sizes would be no problem, and it would work OK as long as the machine on which lcc runs has 16-bit ints. But changing "int" to "long" for every variable/field that holds a size would be painful and would involve touching every module.
A workaround for this problem is to declare multiple-dimensions arrays, and to use them as single-dimension arrays:UBYTE tiles[64][8]; /* This declares an array of 64*8 = 512 unsigned bytes */ void foo() { UWORD l; ((UBYTE *)tiles)[500] = 0; l = 500; ((UBYTE *)tiles)[l] = 0; }