Guidelines and Troubleshooting: GBDK


GBDK Programming Guidelines


Porting Code from GBDK 1.1 to GBDK 2.0


Mixing C and Assembly

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:

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

Multiple Bank Images

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:

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.


Copying Functions to RAM and HIRAM

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:

The second approach is slightly more efficient. Both approaches are illustrated in the ram_fn.c example.


Interrupt Handlers

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:

See irq.c for a complete example.


Initialization Routine

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:


Changing Important Addresses

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

Back
Michael Hope
Last modified: Mon May 10 21:43:28 NZST 1999