thomblog

Tom 'voxel' Purnell's notes

Setting up a Game Boy development environment on a Raspberry Pi

This is a log of how I set up a Game Boy development environment on a raspberry pi. It’s not intended to teach you Z80 assembly, game development, or anything else.

Why

The Game Boy was a popular handheld gaming machine and there are many functional devices still in circulation, not to mention the legions of clones and emulators capable of playing the Game Boy library. Even modest hardware can emulate the Game Boy and gain access to thousands of games, many of which are still enjoyable even today. This results in an enduring audience of players and developers, so making the platform a viable target for publishing games to even in 2023.

Developing for the Game Boy possibly requires even less capable hardware than playing the games, but probably any device capable of viewing this blog post is vastly more powerful than is required to develop for this platform.

The hardware

I’m using a Raspberry Pi 4 with 8GB of RAM as my development machine, running ‘RaspberryPiOS’, which is Debian Linux. I’ll be deploying games to a real Game Boy via a cartridge writing system and compatible cartridges.

Game Boy console and some game cartridges

I also have a Game Boy Color to hand, and developing for either the colour or original monochrome Game Boy is largely an identical process. Both use a custom Z80 family microprocessor, and many Z80 assemblers can output compatible machine code.

Software

To make Game Boy games, the following is needed:

Text editor

You can use whatever editor you like, but I’m using vim. VSCODE (or its free and open source alternative ‘CODE’) are also great choices that I’ve experimented with. Z80 Syntax highlighting is probably available for many programming focused editors.

Assembler and toolchain

The RGBDS suite provides a complete toolchain for assembling Game Boy roms, as well as processing common formats of graphics files into something usable on the game console.

RGBDS isn’t available in the Debian aarch64 repositories, so I’m building it from source.

$ git clone https://github.com/gbdev/rgbds
$ cd rgbds
$ make
$ sudo make install

Graphics Editor

Aseprite or other popular modern pixel focused graphics editors can be made to work on a Raspberry Pi, but I’ve decided to go with Grafx2, a free and open source editor inspired by Deluxe Paint, which is the first ‘serious’ graphics program I ever used. Any tool that can work with indexed graphics formats (where the image file contains a limited palette that all pixels index into) should work.

Grafx2 is available in the Debian repositories, but I built from source for a more up to date version.

$ sudo apt install libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev
$ git clone https://gitlab.com/GrafX2/grafX2
$ cd grafx2/src
$ make release API=sdl2
$ sudo make install API=sdl2

Grafx2 editing a Game Boy tileset

Audio Editor

In the past I’ve used the nice Carillion Game Boy music/sfx editor and driver, which is fun because you’re making the music and sfx on an actual game boy, so you get an accurate representation of how things will sound. It’s not the quickest workflow however, and I’m going to try hUGETracker for this setup, which will let me compose on the raspberry pi.

$ sudo apt install lazarus
$ git clone --recursive https://github.com/SuperDisk/hUGETracker
$ cd hUGETracker
$ lazbuild --add-package-link src/rackctls/RackCtlsPkg.lpk
$ lazbuild --add-package-link src/bgrabitmap/bgrabitmap/bgrabitmappack.lpk
$ ./setup-linux.sh
$ cd src
$ lazbuild hUGETracker.lpi --build-mode="Production Linux"

As of 2023-03-22 this threw some errors about DisabledFontColor being undefined in tracker.pas, so I commented out lines 1178 and 1188 in tracker.pas and rebuilt without issue.

hUGETracker will error out if you try and launch it without its expected collection of bundled files in the right places, so next download the x86 version and unzip it somewhere.

$ cd Downloads
$ wget https://github.com/SuperDisk/hUGETracker/releases/download/v1.0.0/hUGETracker-1.0.0-linux.zip
$ unzip -d hUGETracker hUGETracker-1.0.0-linux.zip

Finally copy our freshly built hUGETracker to the downloaded release, overwriting the incompatible x86 executable

$ cp ~/Src/External/hUGETracker/src/Release/hUGETracker ~/Downloads/hUGETracker/

Finally we can run our aarch64 hUGETracker build.

hUGETracker running

Emulator

I’m using Mesen2, which I set up previously for the Raspberry Pi previously for NES development

Building a test ROM

Now that we have the tools, let’s make some salad. Grab the GB standard definitions file and write your Game Boy code, this example borrowed from https://daid.github.io/rgbds-live

; Simple example to show how to load initial graphics data into VRAM.
; This is only applicable during initial start of your rom, as it disables the LCD
; to get full access to VRAM. This will make the screen white during this time.

INCLUDE "hardware.inc"

SECTION "graphics", ROM0
graphicTiles:
  ; Graphics data, below contains 3x the same 8x8 graphics tile in different formats.
  ; Prefered way would be to use INCBIN with rgbgfx, which is currently not possible in rgbds-live.
opt g.123
  dw `.111111.
  dw `11111111
  dw `1.111.11
  dw `1.111.11
  dw `11111111
  dw `1.1331.1
  dw `1..11..1
  dw `.111111.
.end:

SECTION "entry", ROM0[$100]
  jp start

SECTION "main", ROM0[$150]
start:
  call disableLCD
  call loadTiles
  call loadPalette
  call enableLCD

haltLoop:
  halt
  jp   haltLoop

disableLCD:
  ; Disable the LCD, needs to happen during VBlank, or else we damage hardware
.waitForVBlank:
  ld   a, [rLY]
  cp   144
  jr   c, .waitForVBlank

  xor  a
  ld   [rLCDC], a ; disable the LCD by writting zero to LCDC
  ret

loadPalette:
  ld   a, %11100100
  ld   [rBGP], a
  ret

; Load the graphics tiles into VRAM
loadTiles:
  ld   hl, graphicTiles
  ld   de, graphicTiles.end - graphicTiles  ; We set de to the amount of bytes to copy.
  ld   bc, _VRAM

.copyTilesLoop:
  ; Copy a byte from ROM to VRAM, and increase both hl, bc to the next location.
  ld   a, [hl+]
  ld   [bc], a
  inc  bc
  ; Decrease the amount of bytes we still need to copy and check if the amount left is zero.
  dec  de
  ld   a, d
  or   e
  jp   nz, .copyTilesLoop
  ret

enableLCD:
  ld   a, LCDCF_BGON | LCDCF_BG8000 | LCDCF_ON
  ldh  [rLCDC], a
  ret

We can then build a ROM

$ rgbasm main.asm -o main.o
$ rgblink -o game.gb main.o
$ rgbfix -v game.gb -p 0xff

And run it in an emulator for debugging!

Test rom running in Mesen2 with debuggers

Flashing the ROM to a cartridge

This will differ wildly depending on your flash hardware. Many ‘flash’ carts have an SD card slot or USB socket to allow you to easily copy files onto it - but I’m doing something slightly different and making a ‘one game’ cartridge using a dedicated cart flasher named ‘Joey Joebags’ by BennVenn, which comes with its own python application to interface with the flasher.

Joey Joebags UI window

The cartridge is plugged into the flasher, which is in turn connected to the Raspberry Pi via a USB cable.

Game Boy cartridge flasher connected to a Raspberry Pi

Such a tiny rom takes only a second or two to write to the cart, which can then be inserted into a Game Boy and played like any other game.

Game Boy running the test ROM

Effective Game Boy photography is left as an exercise for the reader.