www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Writing GBA games with D!

reply Raimondo Mancino <rmancino gmail.com> writes:
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:



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.


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

```sh

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


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

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


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

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-
```



The required steps for this are simple.


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)


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)


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



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.




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


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
next sibling parent reply 12345swordy <alexanderheistermann gmail.com> writes:
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:



 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
parent reply Raimondo Mancino <rmancino gmail.com> writes:
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:



 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
parent reply 12345swordy <alexanderheistermann gmail.com> writes:
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:



 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
parent reply Raimondo Mancino <rmancino gmail.com> writes:
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
parent reply 12345swordy <alexanderheistermann gmail.com> writes:
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
parent reply Raimondo Mancino <rmancino gmail.com> writes:
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
parent reply 12345swordy <alexanderheistermann gmail.com> writes:
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
May 19
next sibling parent Raimondo Mancino <rmancino gmail.com> writes:
On Wednesday, 19 May 2021 at 22:23:00 UTC, 12345swordy wrote:
 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
But, it works for GCC.
May 19
prev sibling parent Raimondo Mancino <rmancino gmail.com> writes:
On Wednesday, 19 May 2021 at 22:23:00 UTC, 12345swordy wrote:
 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
I'll read it anyway; thank you for your suggestion.
May 19
prev sibling next sibling parent reply Raimondo Mancino <rmancino gmail.com> writes:
On Wednesday, 19 May 2021 at 13:52:58 UTC, Raimondo Mancino wrote:

 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.
To make it work correctly, you need to use volatile reference to VRAM. So the correct hello world program is: ```d 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
parent Raimondo Mancino <rmancino gmail.com> writes:
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
prev sibling next sibling parent reply TheGag96 <thegag96 gmail.com> writes:
On Wednesday, 19 May 2021 at 13:52:58 UTC, Raimondo Mancino wrote:
 (snip)
Very cool, from a fellow Nintendo D homebrewer in arms!! I would love to see more people using the language this way. I myself am having a lot of fun writing a game in D for the 3DS - here's a basic [hello world + bindings](https://github.com/TheGag96/3ds-hello-dlang) that I almost certainly need to update. I'm kind of shocked you're not using [devkitARM](https://github.com/devkitPro/gba-examples). Any reason? At first, I modified the toolchain to compile D support into GCC, but like someone already suggested here, I'm using LDC now and am not looking back. Definitely recommend doing so. DevkitARM uses Newlib also, and D unfortunately doesn't yet have bindings for it yet. In my repo, you'll see that I had to kind of study Newlib's header files and try to hack into druntime in myself. LDC made a change recently that reserves the version `CRuntime_Newlib`, so you will probably have to something similar to what I did but call it `CRuntime_Gba` or something. Hopefully it'll get first class support down the road (people actually looking to use it like you and I should probably be taking up the charge, honestly lol). Just curious, what kind of game/app are you looking to make with this?
May 19
parent reply Raimondo Mancino <rmancino gmail.com> writes:
On Thursday, 20 May 2021 at 03:30:40 UTC, TheGag96 wrote:
 Very cool, from a fellow Nintendo D homebrewer in arms!! I 
 would love to see more people using the language this way. I 
 myself am having a lot of fun writing a game in D for the 3DS - 
 here's a basic [hello world + 
 bindings](https://github.com/TheGag96/3ds-hello-dlang) that I 
 almost certainly need to update.
Very interesting!
 I'm kind of shocked you're not using 
 [devkitARM](https://github.com/devkitPro/gba-examples). Any 
 reason? At first, I modified the toolchain to compile D support 
 into GCC, but like someone already suggested here, I'm using 
 LDC now and am not looking back. Definitely recommend doing so.
No particular reason, I just followed the first tutorial I saw and it used GCC. The article in question is this http://nuclear.mutantstargoat.com/articles/gba_dev_tools.html I'm a complete newbie with this :) At first I was doing it with LDC as well, but I didn't manage to build a working binary. I tried with: ```sh ldc2 test.d -betterC -mtriple=arm-none-eabi -float-abi=soft -singleobj -c -nogc -static llvm-strip test.o -o test.o --strip-all --strip-all-gnu --remove-section .ARM.exidx ld.gold -Tlscript.ld -o out.elf crt0.o test.o gba-objcopy -O binary out.elf test.gba ``` where crt0 is the same one I used in this thread. I used gold and not lld because lld doesn't support (or I didn't manage to make it work) the syntax used in the script. I am not very familiar with linker scripts yet, but I'm trying to write my own for GCC now.
 DevkitARM uses Newlib also, and D unfortunately doesn't yet 
 have bindings for it yet. In my repo, you'll see that I had to 
 kind of study Newlib's header files and try to hack into 
 druntime in myself. LDC made a change recently that reserves 
 the version `CRuntime_Newlib`, so you will probably have to 
 something similar to what I did but call it `CRuntime_Gba` or 
 something. Hopefully it'll get first class support down the 
 road (people actually looking to use it like you and I should 
 probably be taking up the charge, honestly lol).
It's interesting though... I'll look into it. I really didn't make much research, just tried right away.
 Just curious, what kind of game/app are you looking to make 
 with this?
I was considering to create a step sequencer at first so I can get used to audio and gui stuff. I'll then try to write a game with j-rpg mechanics, but it's going to be a little different than your usual j-rpg.
May 20
parent TheGag96 <thegag96 gmail.com> writes:
On Thursday, 20 May 2021 at 09:08:58 UTC, Raimondo Mancino wrote:
 I was considering to create a step sequencer at first so I can 
 get used to audio and gui stuff.

 I'll then try to write a game with j-rpg mechanics, but it's 
 going to be a little different than your usual j-rpg.
I see, nice! Hope you have fun with it. Feel free to post back here from time to time about how things are going. I updated my repo with changes to bindings and druntime/Phobos edits - hopefully it's of some use to you or anyone else.
May 20
prev sibling parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
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.

 [...]
👏
May 20