macOS + macports asm include fix


I am trying gba dev with natu on a macOS arm64 machine with macports.

Following the instructions on https://natu.exelo.tl, using arm-none-eabi-gcc I encounter an assembler include problem (which does not appears with devkitARM version of arm-none-eabi-gcc). In order to compile, I need to fix natu/private/acsl.nim with AcslAsmFlags = "-Wa,-I" instead of AcslAsmFlags = "-I".

After that, both natu-examples and xniq compile fine and the gba files can be run with mGBA.

However, when I try the same gba files on a DSI with GBArunner2, the emulator only gives me white or black screen whereas the itch.io version of xqni.gba works fine.

Hope this helps. Thank you for releasing natu !


% nim --version
Nim Compiler Version 1.6.10 [MacOSX: arm64]
Compiled at 2022-12-20
Copyright (c) 2006-2021 by Andreas Rumpf

active boot switches: -d:release

% arm-none-eabi-gcc -v
Using built-in specs.
Target: arm-none-eabi
Configured with: /opt/local/var/macports/build/_opt_bblocal_var_buildworker_ports_build_ports_cross_arm-none-eabi-gcc/arm-none-eabi-gcc/work/gcc-12.2.0/configure --prefix=/opt/local --target=arm-none-eabi --infodir=/opt/local/share/info --mandir=/opt/local/share/man --datarootdir=/opt/local/share/arm-none-eabi-gcc --with-system-zlib --with-gmp=/opt/local --with-mpfr=/opt/local --with-mpc=/opt/local --enable-stage1-checking --enable-multilib --disable-libcc1 --with-newlib --with-libgloss --enable-interwork --disable-newlib-supplied-syscalls --with-multilib-list=rmprofile --enable-languages=c,c++
Thread model: single
Supported LTO compression algorithms: zlib
gcc version 12.2.0 (GCC) 
Assigned to
1 year, 6 months ago
1 year, 6 months ago
No labels applied.

~exelotl 1 year, 6 months ago*

Oh, thanks for the heads up! The -Wa,-I thing was a known issue with the Arm GNU Toolchain (they forgot to compile GCC with the flag that forwards include paths to the assembler), but I thought they'd have fixed it by now. Here was the bug report: https://bugs.linaro.org/show_bug.cgi?id=5835

Weirdly maxmod.nim also uses -I instead of -Wl,-I... did you have to change that too or did it somehow work with no problems?

I wasn't aware about the GBARunner2 issues but I'll take a look soon. The itch version was probably built with devkitARM's gcc + linker scripts + startfiles so there may be a discrepancy there.

~nopid 1 year, 6 months ago

Nope, for maxmod.nim you have to keep -I for it to compile correctly.

Concerning the GBARunner2 issues, I just checked that ROMs produced by https://github.com/AntonioND/gba-bootstrap. They seem to be compatible with mGBA but not with GBARunner2. Maybe something to fix into gba_crt0.s or gba_cart.ld? I don't have a real GBA with me this week to check against real hardware.

~nopid 1 year, 6 months ago

Found what is missing in gba-boostrap/template/source/main.c for it to work with GBARunner2 : IRQ handler should be initialized and VBlank IRQ enabled. I can fix my natu simple graphic test by adding:

dispstat.vblankIrq = true

According to natu/irq.nim the last line is supposed to be handled inside irq.enable but the relevant code is missing 😅 :

var ie* {.importc:"(*(volatile NU16*)(0x4000200))", nodecl.}: set[IrqIndex]
  ## "Interrupt Enable" register.
  ## Setting a bit allows an interrupt to be received. But nothing will
  ## happen unless the relevant bit to request the interrupt is also set,
  ## e.g. `dispcnt.vblankIrq = true`.
  ## .. note::
  ##   `irq.put <#put,IrqIndex,FnPtr>`_ or `irq.enable <#enable,IrqIndex>`_ will take care of this for you.

Adding dispstat.vblankIrq = true to xniq replace white screen by blank screen but still no game with GBARunner2.

~exelotl 1 year, 6 months ago

Hey, I've pushed the -Wa,-I fix!

The dispstat.vblankIrq = true line actually is done for you in put/enable by reading the location of the bit from the senders array and flipping it on. This is something I ported from libtonc, it's done this way because the GBA has bits scattered all over the place in different registers to enable different IRQs, and this is a nice data-centric way of solving it across the board.

GBARunner2 indeed seems to do better when VBlank is enabled, I did some tests last night.

First I tried the main.c from gba-bootstrap:

  • ❌︎ Using vanilla gcc + gba-bootstrap --> white screen
  • ❌︎ Using devkitARM's gcc, makefile, linkerscript, crt0 --> white screen

Then I tried a more typical example, linked against libtonc, using VBlankIntrWait for the main loop:

#include <tonc.h>

int main(int argc, char *argv[]) {
    uint i = 240*80;
    u16 c = 0;
    while (1) {
        m3_mem[0][i % (240*160)] = c;   // plot pixel at new location
    return 0;


  • ✔︎ using vanilla gcc + gba-bootstrap --> Works!
  • ✔︎ using devkitARM's gcc, makefile, linkerscript, crt0 --> Works!

Then I tried some other more recent Natu games:

  • ❌︎ HEXES --> black screen
  • ❌︎ A Rushed Hack Job --> black screen
  • ❌︎ Recent Goodboy build --> white screen

And the Natu examples:

  • ✔︎ anim_coins --> works
  • ✔︎ anim_sprite --> works
  • ❌︎ goodboy_gallery --> white screen
  • ❌︎ hello_world --> white screen
  • ✔︎ move_sprite --> works
  • ❓︎ mystify --> kinda works but only visible in the lower half the screen
  • ❌︎ play_music --> white screen
  • ✔︎ bg_regions --> works
  • ✔︎ test_sram --> works

And some other homebrew games from the GBA Jam 2022:

  • ✔︎ Attack on Voxelberg (devkitARM) --> Works!
  • ❌︎ Chocolate Hunter Runa (devkitARM + Butano) --> White screen
  • ❌︎ Coin Fall (devkitAdvance 🥴︎) --> White screen
  • ❓︎ Bugtris (devkitARM) --> Shows splash screens, goes black before reaching title screen
  • ❌︎ Collie defense (devkitARM + Butano) --> White screen
  • ❌︎ Gnoq (Zig) --> White screen
  • ✔︎ Lane (agbrs) --> Works!
  • ❓︎ Hero Core (devkitARM) --> Works but no sound
  • ✔︎ Ravenia (devkitARM + gbsenpai) --> Works!

And the GBA Jam 2021:

  • ✔︎ Varooom 3D (devkitARM + Butano) --> Works!
  • ✔︎ 587 Squadron Advance (devkitARM) --> Works!
  • ✔︎ Dungeon Advance (devkitARM + gbsenpai) -> Works!
  • ✔︎ Feline (devkitARM + Butano) --> Works!
  • ✔︎ Knight Owls (devkitARM + Butano) --> Works!
  • ✔︎ Pipe Spin (devkitARM) --> Works (with minor graphical bugs)
  • ✔︎ Tigermoth (devkitARM) --> Works!
  • ✔︎ Solar Guard (devkitARM + Butano) --> Works!
  • ❌︎ uCity Advance (libugba - similar base to gba-bootstrap) --> white screen

It's kinda hard to draw a conclusion from the above, only that GBARunner2 doesn't seem to work with most homebrew titles made in the last year. It hates busy loops, and doesn't seem to like games made in 2022 that use Maxmod - although games built a year ago using Maxmod, such as xniq and the Goodboy demo, seemed to work just fine, so maybe the version of GCC has something to do with it? Back then Natu was still using devkitARM, whose GCC tends to be a few months behind mainline GCC, so maybe that's why uCity was affected before all the others.

I've created an issue on the GBARunner2 repo: https://github.com/Gericom/GBARunner2/issues/297

Hopefully the situation will improve! In the meantime I can only suggest to not rely on it for the time being, or see how far you get using VBlankIntrWait() and no audio?

~nopid 1 year, 6 months ago

Oups, sorry about the dispstat setter, I am using natu to learn Nim at the same time and I might have missed some stuff. So what would be the proper way to enable iiVBlank?

Thanks for the detailled tests. For the Natu examples that do not work:

  • goodboy_gallery -> has an IRQ handler irq.put(iiVBlank, onVBlank)
  • play_music -> has an IRQ handler irq.put(iiVBlank, maxmod.vblank)
  • hello_world -> does not enable iiVBlank

It turns out my simple examples trying to set a dummy IRQ handler for iiVBlank with irq.put does not work either for me with arm-none-eabi-gcc 12.2.0 and nim 1.6.10. This might explain maxmod not working either.

I will avoid GBARunner2 and turn to another solution for the time being.

~exelotl 1 year, 6 months ago

So what would be the proper way to enable iiVBlank?

If you don't want a handler and just want to enable VBlank so you can use VBlankIntrWait, then it's as simple as: import the irq module (which automatically calls irq.init() for you) then call irq.enable(iiVBlank).

irq.put is just for if you want to set a handler, but also enables the interrupt for convenience.

Register here or Log in to comment, or comment via email.