Writing GBA games with D!

Raimondo Mancino rmancino at gmail.com
Wed May 19 13:52:58 UTC 2021


Hello,

I've been interested into GBA game development because it's a 
fairly interesting device to work with. It features an ARM cpu 
with TDMI extensions.

Here are the steps to begin with:

## 1. You need a GCC toolchain.

I've been trying to make it work with LLVM too but it looks like 
Clang does not support GCC's linker script extensions and 
directives, which are required to build the CRT0 object.
You need to do this because the AGB system does not have a 
runtime.
At the time, games were mostly written in pure assembly or C 
without the standard library.

### Building binutils
I used latest binutils (2.36.1 at the moment of writing this 
thread) and used this configuration:

```sh
# Assuming we're in build/ directory
../configure --target=arm-none-eabi --program-prefix=gba-
```

In older GCC (prior to 4.8) you would use `arm-agb-elf` as target.
As you can guess, this is an embedded SoC device, so `eabi` is 
fine.

### Building GCC and GDC
You need *both* GCC and GDC.

To build them we need to disable a lot of stuff and use _newlib_ 
instead of the standard _glibc_ (since it is an embedded device).

Make sure you have libmpc, limpfr and libgmp installed in your 
system before building.

I used latest GCC (11.1.0) and configured it like this:

```sh
# Assuming we're in build/ directory
../configure \
     --target=arm-none-eabi \
     --program-prefix=gba- \
     --enable-languages=c,d \
     --with-newlib \
     --with-multilib-list=rmprofile \
     --disable-decimal-float \
     --disable-libffi \
     --disable-libgomp \
     --disable-libmudflap \
     --disable-libquadmath \
     --disable-libssp \
     --disable-libstdcxx-pch \
     --disable-nls \
     --disable-shared \
     --disable-threads \
     --disable-tls
```

Notice the line `--with-multilib-list=rmprofile`: without this 
line, the libssp needed for the build of the host toolchain would 
fail.
Reference: 
https://forums.gentoo.org/viewtopic-t-1077292-start-0.html

### Building newlib
Since we specified a custom program prefix (`gba-` in my case) we 
need to set the required environment variables so that the 
configure script of newlib does not break.

I used latest newlib (4.1.0).

```sh
# Assuming we're in build/ directory
CC_FOR_TARGET=/absolute/path/to/gba-gcc \
GCC_FOR_TARGET=/absolute/path/to/gba-gcc \
AR_FOR_TARGET=/absolute/path/to/gba-ar \
AS_FOR_TARGET=/absolute/path/to/gba-as \
LD_FOR_TARGET=/absolute/path/to/gba-ld \
NM_FOR_TARGET=/absolute/path/to/gba-nm \
OBJCOPY_FOR_TARGET=/absolute/path/to/gba-objcopy \
OBJDUMP_FOR_TARGET=/absolute/path/to/gba-objdump \
RANLIB_FOR_TARGET=/absolute/path/to/gba-ranlib \
READELF_FOR_TARGET=/absolute/path/to/gba-readelf \
STRIP_FOR_TARGET=/absolute/path/to/gba-strip \
../configure \
     --target=arm-none-eabi \
     --program-prefix=gba-
```

## 2. Set up your build script

The required steps for this are simple.

### Use an updated crt0
Versions of [Jeff Frohwein](https://www.devrs.com/)'s crt0.s 
prior to 1.28 do not seem to work with latest GCC.
If you're using GCC prior to 4.8 older versions should work too.

Here is the [referenced crt0.s](https://ghostbin.com/paste/iIvGa)

### Use an updated linker script
Same as the previous: versions prior to 1.3 do not seem to work 
with latest GCC.

Here is the [referenced linker 
script](https://ghostbin.com/paste/eHuDy)

### Build chain
We need to produce the object files and then link them together 
with GCC.

Here is a simple hello world program that paints the whole screen 
with red:

```d
@nogc:

enum VRAM = cast(ushort*) 0x6000000;
enum SCREEN_WIDTH = 240;
enum SCREEN_HEIGHT = 160;

enum FRAME_SEL_BIT = 0x10;
enum BG2_ENABLE = 0x400;

enum REG_DISP_CTL = cast(ushort*) 0x4000000;

extern(C) int main() {
     int i;

     *REG_DISP_CTL = 3 | BG2_ENABLE;
     *REG_DISP_CTL &= ~FRAME_SEL_BIT;

     for(i = 0; i < SCREEN_WIDTH * SCREEN_HEIGHT; i++) {
         VRAM[i] = 31;
     }

     for(;;) { }

     return 0;
}
```

The example was written after this C example:

```c
#include <stdint.h>

uint16_t *fb = (void*)0x6000000;
const int xsz = 240;
const int ysz = 160;

#define FRAME_SEL_BIT 0x10
#define BG2_ENABLE 0x400

int main(void) {
     int i;
     static volatile uint16_t * const reg_disp_ctl = 
(void*)0x4000000;

     *reg_disp_ctl = 3 | BG2_ENABLE;
     *reg_disp_ctl &= ~FRAME_SEL_BIT;

     for(i=0; i<xsz * ysz; i++) {
         fb[i] = 31;
     }

     for(;;);

     return 0;
}
```

Notice that `_start` is already defined by crt0, which handles 
I/O initialization and other useful init routines (for example, 
stop sound if cartridge is removed).
Both `main` and `AgbMain` are valid entry points of the program.

First you need to build your D files.
Since we're excluding the D runtime, make sure you have at least 
an empty object.d in your project.

```sh
# You would use gba-gcc with the same options for .c files.
# The crt0 and the linker script seem to support C++ as well.

gba-gdc test.d \
     -O3 \
     -fomit-frame-pointer \
     -marm \
     -mcpu=arm7tdmi \
     -fno-druntime \
     -c \
     -pedantic \
     -Wall
```

As you can notice we don't need the D runtime here.

Then, assemble the crt0:

```sh
gba-as crt0.S -o crt0.o
```

Now that we have built the two object files, we can just link 
them together and produce the ELF binary; then we create the GBA 
image with objcopy.

```
gba-gcc \
     -o out.elf crt0.o test.o \
     -Tlscript.ld \
     -nostartfiles \
     -lm

gba-objcopy -O binary out.elf test.gba
```

At this point you can just load it into an emulator.

## 3. Optional steps

### Edit game metadata
Game fields like title and author are specified by crt0.s but you 
need to update the checksum flag accordingly.
If you don't want to set them manually you can use 
[gbafix](https://github.com/devkitPro/gba-tools/).

## 4. Notice
At the moment, the binary is built correctly, but it does not 
seem to link correctly when using D.

Using GCC (for the C example above) instead of GDC with same 
options works so my guess is that the linker script needs to be 
updated.

I think I need help for this; do you have any suggestions?
Thank you in advance.


More information about the Digitalmars-d mailing list