Exploring a few alternative Game Boy assemblers
— updated on
RGBDS is considered the premier assembler for developing games for the Game Boy, indeed it's seen popular usage and has grown into a powerful suite over the many years it has existed. It's pretty accessible, and there's loads of genuinely useful things in there, including native character maps, rgbgfx
for png-to-2bpp conversion, easy linker file syntax, among other QoL improvements. But as much as I love it, I feel like something weird is up. The same software culture being applied here, rapid releases which deprecate a lot of stuff really quickly. That becomes a problem when dealing with old projects which I may not have the time to rewrite... funny how it goes needing to rewrite an assembly program just to keep up with the pace of things!
Looking at a Few Alternatives
Despite the main GB dev community positioning RGBDS as The Standard Environment™ for developing Game Boy programs, several alternatives exist. They may or may not be of taste if you're coming from RGBDS like I am, and some even require different assumptions on assembly programming. Just cursory comparisons, and may not even be fair to their actual capabilities. I'm also not really looking at things like ROM mapping and symbol file creation (useful for debugging). If you're using one of these in a pipeline, your mileage may vary.
Vasm
A multi-arch assembler, aiming to be portable. Z80 (and Game Boy ASM) are included as an official module among many other architectures including well-known ones like 6502, M68k and PowerPC, as well as some out-there ones like RPi VideoCore, PDP-11 and the TR3200.
For an assembler, it's got quite a choice when it comes to number prefixes. You can prefix hexadecimal not only with $
, but 0x
and &
. You can also do 2Ah
instead of $2A
. In other words, it's got something for everyone.
Its docs describe the basic syntax of things, but not much examples. It's probably just me, but I'm having a bit of trouble with what's supposed to be bss
sections that doesn't start at 0 (like WRAM needing to start at $C000
), so I'm using "virtual sections" provided by the dsect
feature. Also with regards to wrangling the sections to fit the Game Boy's memory layout and bank switching, I'm using rorg
for them instead.
Since org
specifies entirely new sections, I decided to use align 3
for all the rst and interrupt vectors. align 14
is quite useful for filling in the rest of the bank with a huge swath of nothing.
It's worth noting that ChibiAkumas / Akuyou uses this assembler for his delightfully multi-platform assembly tutorials.
Sample code here.
Bass
Another multi-arch assembler, written by you-know-who. Supports the Game Boy target architecture under the name of gb.cpu
. Aims to be a straight-forward, no-frills, extensible assembler. No linkers, etc, just a plain assembly compiler seemingly following the Unix philosophy. Perfect if you want to make patch ROM hacks (since there's a "modify" switch), though it's also because of this also that you might need to do manual linking.
Funny now that I've said I was trying to use RGBDS for binary hacking, then this exists…
Compared to the others, the writing style for this target is more C-influenced. There's things like scoping, functions as assembler directives, and then some. It supports anonymous local labels up to two within a single scope (any more than that and it refuses to work, because your code smells, probably). Defining memory is a bit tricky here. It does, however, support expressions like while
, which is useful for making unrolled loops. Be wary of gotchas like the fact you need to skip the spaces when doing indirect addressing. Also, I can't seem to find an include
directive?* At least, not what I think might be.
All in all, if you target this assembler, coding feels a bit like you're writing inlined assembly in C. You may need to get or write helper tools if you want to work with it, especially for header checksums.
Sample code here.
NOTE: I've been told that the include
equivalent is called insert
. I'll want to try this out later.
WLA-DX
Yet another multi-arch assembler. But it's a very popular alternative for RGBDS, and for good reason. Although Game Boy ASM is just one of the many architectures it supports, it doesn't just support its assembly language — but also has its own specific features — like being able to define headers easily through .gbheader
and automatically calculating checksums at link time.
Although I probably shouldn't be surprised, since this was originally a Game Boy development suite, which then was extended to all sorts of systems because it was that good. SNES developers can enjoy the same luxuries WLA-DX offers, since it supports all 3 architectures you have to program for it: 65816, SPC-700, and SuperFX.
It has support for all sorts of funny memory layouts. Although it can feel a bit daunting about having to manage these things, it makes it possible for you to specify weird memory layouts if you have to program some other funny systems. It also has a lot of things that RGBDS even aspires to have, like structs (very much useful for virtual sprite management, inventories, or party member data), tables (useful for quickly asserting some fixed data structure), and frankly a lot of other things.
There's loads of options if you want labels. Although local labels are specified with a _
prefix, you can have multiple levels of "local" by specifying a number of @
prefixes. Sometimes, a piece of code may have multiple different parts. A one-level local label is bound to have names like: .okay
, yep
, .ok2
, .done
, which may be confusing in a large file. Multi-level labels might be able to help with this. Anonymous local labels are also available, useful for simple loops.
Point is, this thing is all about control. It's got excellent documentation, and I do recommend reading it. It's got explanations of everything and some examples, which helps if you want to unleash the full power of this beast.
Sample code here.
ASMotor
This is where it all began. Or at least, part of where it all began. RGBDS's history notes that SurfSmurf / Carsten Sørensen developed ASMotor first in 1997, and then in 1999, Otaku no Zoku / Justin Lloyd uses ASMotor to create a compiler for Game Boy assembly, called RGBDS.
Looking around a bit, it would seem that things are a bit confused. Straight from the horse's mouth, it appears that (like WLA-DX) Sørensen made RGBDS first, then extended it as a suite of assemblers called ASMotor. The RGBASM (xAsm) history page mentions 1996-10-01 as the first release, while first ASMotor release is specified as 1997-07-03. Perhaps the fact that ASMotor (and xSomething) is mentioned more than RGBDS gave the impression that ASMotor came first. If it's not that, then an alternative interpretation of ASMotor being RGBDS's "spiritual successor" possibly could be the fact that ASMotor supports more architectures than just the Game Boy. There's also this thread in which both Sørensen and Lloyd explains it from their own sides.
Either way, ASMotor today is very similar to how RGBDS was in its early days. Whereas RGBDS as we know it now deviates a ton and is a lot more modern, ASMotor remains "stable" and faithful to its roots… perhaps a little too faithful.
A comparison to a modern RGBDS workflow is inevitable. For starters, if you want each section to be in its own bank, you'll have to specify them manually (as with other assemblers). In this regard, rgblink
has the upper hand, since its link file can also double as a bird's eye view of the ROM. Some oddities exist, like that endm
has to be indented first. Syntax differs still, such as jp [hl]
but not jp hl
, and ldi [hl], a
ld [hl+], a
but not ld [hli], a
. And as described in my sample code, unused space between sections will always be filled with FF
regardless of what you set with -z
, unless you explicitly use ds
to specify unallocated space. Group names like HOME
and CODE
do need to be in all caps (HOME
== ROM0
, while CODE
and DATA
== ROMX
). Also, importing/exporting symbols manually is a thing you do here, though you can also use the global export marker ::
.
Symbol files are generated with xLink's -m
flag, and while its syntax is compatible with bgb's implementation of the .sym format, not every label is present — seems to be only the ones that are used somewhere else. This does not appear to include local labels.
Its docs help a little bit, but they fall a little short. For example, the HRAM
and VRAM
group symbols don't appear to be documented, and the closest thing would be the GB examples that Sørensen gives in a separate repo. Even the old docs were more complete. Although unlike the old xLink, modern xLink doesn't seem to accept WLA-styled linkfiles anymore.
There is, however, one cutting edge of today's ASMotor: the ability to inline code and data literals. So instead of
Test:
ld de, Text
ld hl, wTilemap + (20 * 2) + 5
jp PrintText
Text:
db "Hello", 0
you can do:
Test:
ld de, { db "Hello", 0 }
ld hl, wTilemap + (20 * 2) + 5
jp PrintText
Very much useful if all you want is to display text and not have to name every single string that appears.
Sørensen also provides xgbfix
separately as part of the ASMotor suite. If you do want to ditch RGBDS entirely (and you're also familiar with the RGBDS of old), this should be a valid choice since all you need (except for rgbgfx
) has its equivalents here.
Sample code here
OVERALL NOTE: If you're feeling salty, right click and view page source to read what I really feel…