Jump to page: 1 2
Thread overview
Writing GBA games with D!
May 19, 2021
Raimondo Mancino
May 19, 2021
12345swordy
May 19, 2021
Raimondo Mancino
May 19, 2021
12345swordy
May 19, 2021
Raimondo Mancino
May 19, 2021
12345swordy
May 19, 2021
Raimondo Mancino
May 19, 2021
12345swordy
May 19, 2021
Raimondo Mancino
May 19, 2021
Raimondo Mancino
May 19, 2021
Raimondo Mancino
May 19, 2021
Raimondo Mancino
May 20, 2021
TheGag96
May 20, 2021
Raimondo Mancino
May 20, 2021
TheGag96
Feb 09, 2022
redthing1
May 20, 2021
Imperatorn
May 19, 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:

# 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:

# 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).

# 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'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

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

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:

@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:

#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.

# 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:

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.

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.

May 19, 2021

On Wednesday, 19 May 2021 at 13:52:58 UTC, Raimondo Mancino wrote:

>

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.

That isn't correct though, you just need to use llvm LLD linker. It supports linker script extensions.

-Alex

May 19, 2021

On Wednesday, 19 May 2021 at 15:06:02 UTC, 12345swordy wrote:

>

On Wednesday, 19 May 2021 at 13:52:58 UTC, Raimondo Mancino wrote:

>

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.

That isn't correct though, you just need to use llvm LLD linker. It supports linker script extensions.

-Alex

I tried to use ld.lld but it doesn't seem to work.
It yells about unknown directives.

May 19, 2021

On Wednesday, 19 May 2021 at 13:52:58 UTC, Raimondo Mancino wrote:

>

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.

Edit: It works!

To make it work correctly, you need to use volatile reference to VRAM.

So the correct hello world program is:

import core.volatile;

@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;

    volatileStore(REG_DISP_CTL, 3 | BG2_ENABLE);
    volatileStore(REG_DISP_CTL, volatileLoad(REG_DISP_CTL) & ~FRAME_SEL_BIT);

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

    for(;;) { }

    return 0;
}

Since we used -fno-druntime, core/volatile.d needs to be included in our project.

May 19, 2021

On Wednesday, 19 May 2021 at 15:59:33 UTC, Raimondo Mancino wrote:

>

To make it work correctly, you need to use volatile reference to VRAM.

I'm sorry, I meant, to REG_DISP_CTL, because it lies in the section at 0x6000000 of the AGB system.

May 19, 2021

On Wednesday, 19 May 2021 at 15:17:13 UTC, Raimondo Mancino wrote:

>

On Wednesday, 19 May 2021 at 15:06:02 UTC, 12345swordy wrote:

>

On Wednesday, 19 May 2021 at 13:52:58 UTC, Raimondo Mancino wrote:

>

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.

That isn't correct though, you just need to use llvm LLD linker. It supports linker script extensions.

-Alex

I tried to use ld.lld but it doesn't seem to work.
It yells about unknown directives.

What directives that you are using anyways?

-Alex

May 19, 2021

On Wednesday, 19 May 2021 at 18:28:49 UTC, 12345swordy wrote:

>

What directives that you are using anyways?

-Alex

I didn't write the linker script myself; you can find it here https://ghostbin.com/paste/eHuDy

May 19, 2021

On Wednesday, 19 May 2021 at 21:33:07 UTC, Raimondo Mancino wrote:

>

On Wednesday, 19 May 2021 at 18:28:49 UTC, 12345swordy wrote:

>

What directives that you are using anyways?

-Alex

I didn't write the linker script myself; you can find it here https://ghostbin.com/paste/eHuDy

What error messages are you seeing?

-Alex

May 19, 2021

On Wednesday, 19 May 2021 at 22:09:56 UTC, 12345swordy wrote:

>

What error messages are you seeing?

-Alex

Here is an example:

ld.lld: error: lscript.ld:199: malformed number: :
>>>   OVERLAY : NOCROSSREFS AT (__iwram_overlay_lma)
>>>
May 19, 2021

On Wednesday, 19 May 2021 at 22:15:25 UTC, Raimondo Mancino wrote:

>

On Wednesday, 19 May 2021 at 22:09:56 UTC, 12345swordy wrote:

>

What error messages are you seeing?

-Alex

Here is an example:

ld.lld: error: lscript.ld:199: malformed number: :
>>>   OVERLAY : NOCROSSREFS AT (__iwram_overlay_lma)
>>>

You shouldn't copy and paste someone else's links script without understanding how it works.

Please read the documentation on the linker script.

https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_chapter/ld_3.html

-Alex

« First   ‹ Prev
1 2