DEV Community

Cover image for Gameboy Advance Dev Workflow in 2020
James
James

Posted on • Originally published at sausage-factory.games on

Gameboy Advance Dev Workflow in 2020

This is a repost from my gamedev blog.

How to write GameBoy Advance (GBA) homebrew using modern tools and systems in 2020. Compilers, editors, debugging and more!

This isn’t the definitive way of doing things, but it’s a way that seems to work for me. Also you might read this and think it a bit convoluted and full on. I didn’t create it all in one go, it’s a workflow that has evolved over time.

Tl;dr summary

I’m going to explain how I used DevkitPro, Visual Studio Code, Windows Subsystem for Linux (WSL), Aseprite and the emulator mGBA to write GameBoy Advance homebrew. At the end I’ll have a system where I can press F5 in Visual Studio Code to compile the code, and after lauching the emulator I’ll be able to do line-by-line debugging with variable watching and breakpoints.

There’s a multitude of ways to do this, this is mine. If you do this differently, let me know! I’m documenting my workflow, not just telling you what tools I’m using.

First I’m going to waffle on a bit about why.

Gameboy Advance?

Why am I writing anything for a 20 year old games console, when there are more exciting devices out there like the Nintendo Switch, or at least the 3DS? Well, rewind time to about 2004 when the GBA was still somewhat current. At the time I owned an original GBA and came across flash carts, and being the type of person I am I realised that maybe my code could go on the cart rather than games. Other people had come to the same conclusion and had created various cross-compilers that could produce code the GBA would run. Some had also tried to document how the system worked. I found it quite difficult to learn though. The Internet in 2004 wasn’t like today, things were hidden away in forum posts beyond Google’s indexing powers, YouTube tutorials weren’t a thing, and the whole “Gameboy Advance homebrew scene” was very new, some of the documentation was inaccurate. I managed to make a short top-down shooting game, and a recreation of Chuckie Egg, but it took a long, long time.

20 years on and things have settled down a lot. Time has allowed all the inaccurate documentation to die off, leaving only a few well written sources of information. The hacked together development tools have all been replaced by one set of tools you can install in five minutes - back in the day you had to compile the compiler yourself, and hope nothing had gone wrong because the only time you’d find out is when your code didn’t run, but was that the compiler’s fault or your own? The emulators have matured to a point where they contain debugging utilities such as memory viewers, graphics viewers and attachments for remote debugging.

The other attraction to writing things on the GBA is the relative simplicity of the system. It’s a basic sprite/tile 2D handheld that really wasn’t designed to do bitmap graphics, there are hardware limitations on how many sprites can be drawn on screen, and the screen resolution is low. Even with my terrible pixel editing skills I can just about manage to create 32x32 pixel sprites that don’t look like garbage. Controlling the hardware is done by directly stuffing values into RAM, there are no abstraction layers or operating system. The whole system is understandable by one person.

Setting up WSL

The Windows Subsystem for Linux (WSL) is being used as it gives a real development environment for running GCC based compilers. I never liked Cygwin/MSys, they felt like illusions that were too easy to break. WSL gives you a more realistic Linux console system where packages can be installed.

Setting up WSL is easy, there’s instructions on Microsoft’s website. The main steps are

  • Open PowerShell as an Admin user and type in

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

  • Reboot
  • Open the Windows Store and install your Linux Distro of choice. I chose Debian.
  • Open WSL by finding the icon respresenting your Linux Distro on the Start Menu, and allow it to initialise itself.

You should now be greeted with a Bash prompt, as a regular Linux user.

Install some packages

We need to install packages, frequently if something isn’t working, it’s because a Linux package isn’t installed. From memory, I had to manually install

  • Make
  • wget
  • libncurses5
  • xz-utils
  • Python 3

There are others, I can’t remember them but it becomes obvious when a package is missing.

Make WSL work nicely with Windows

The Linux Distro and your Windows system are not two isolated systems. In Windows it’s easy to run Linux commands from outside the Bash shell, and from within the Bash shell it’s easy to lauch Windows programs. We’ll use this when it comes to running the emulator, and editing code.

The Microsoft WSL website has a nice section on Interoperability that explains it well.

In Windows, the WSL environment is not a directoty you can open with Windows Explorer - in fact you’re warned not to try, should you find where all the files are being stored. Instead, the environment is available as a fake network drive called \\wsl$ which contains everything.

Development Tools

Development tools include

  • Code editing
  • Compiling
  • Drawing Sprites
  • Emulation

Visual Studio Code

I’m using VS Code because it’s a modern editor that requires minimal effort to install. After installing it, I added the following extensions

Set them both up following their instructions. There’s more configuration specific to each code folder that is done later.

When trying to work with a GBA project, use WSL to connect to your Linux distribution, and then open the folder containing your project rather than opening individual files. VS Code will then use its built in Git utils if you’re using Git, and it will store project settings in a .vscode directory. We’ll need that directory later.

GBA Emulation

While it is possible to run the code on real hardware, doing that for development makes things too time consuming. Instead an emulator is being used. Remember, emulators are not real hardware, always test your code on a real piece of hardware (or at least test it on the emulators most people use!).

The emulator I’m using is mGBA as it offers GDB remote debugging. It’s also still under development/maintenance and is one of the few GBA specific parts of this with dates in the last six months(!)

This website shows how to use GDB with mGBA, it’s mostly a case of

  • Build GBA ROM, load into mGBA
  • In mGBA go to Tools - Start GDB Server
  • Port 2345, address 0.0.0.0 is fine
  • Run GDB using instructions later in this post

DevkitARM Compiler

The only development tools you should be using is the excellent DevkitARM (Don’t confuse this with ‘DevkitPro’. The tools are called ‘DevkitArm’). To install it, just follow their instructions, they’re well explained.

It pretty much boils down to

If this fails, you are probably missing some debian packages, install them and retry the final bullet point.

I then created ~/.bash_profile and put the following inside it

export DEVKITPRO=/opt/devkitpro
export DEVKITARM=${DEVKITPRO}/devkitARM
export DEVKITPPC=${DEVKITPRO}/devkitPPC

export PATH=${DEVKITPRO}/tools/bin:$PATH

Please, for the sake of your own sanity and everyone else’s, copy the example GBA project folder and use that as a basis for your own code. It has a Makefile, code is organised neatly into folders and typing make in a console Just Works. You get a .gba file to load into the emulator, and a .elf file to load into the debugger.

Aseprite

I’m using Aseprite to create sprites and tiles, mostly because it’s specifically designed for that task and isn’t trying to be “pixel art Photoshop”. I use its ability to tag frames when defining different sprite animations, and the sprite sheet exporting with JSON data files.

Grit

Included with DevkitARM is Grit which converts the Aseprite sprite sheets into data that can be included in the GBA source. I created Python scripts to automate this.

Visual Studio Code Configuration

Debugging

The Native Debug extension needs telling where GDB is, and what it is supposed to be debugging when you run it. This is done inside a file called launch.json which lives in the .vscode folder for your GBA project.

Mine looks like this

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug",
            "type": "gdb",
            "request": "attach",
            "gdbpath": "/opt/devkitpro/devkitARM/bin/arm-none-eabi-gdb",
            "executable": "./test.elf",
            "target": ":2345",
            "cwd": "${workspaceRoot}",
            "remote": true,
            "valuesFormatting": "parseText"
        }
    ]
}

The bit that needs changing per project is the executable elf file. The rest should be the same on your system. You can now use Visual Studio Code’s debugging features and the Native Debug extension to watch variables and put breakpoints. If something doesn’t work, look in the bottom window of VS Code, you’ve probably not got devkitARM installed in the same location as me (in which case you’ve been fiddling and probably broken the whole thing!)

Building with F5 - tasks.json

For this to feel like a real development environment, we need to make compilation happen just by pressing a single key. F5 is a good button, it’s nice and traditional. Problem is, VS Code has something bound to F5 already. You’ll need to remove that mapping by going into File - Preferences - Keyboard Shortcuts. Search for F5 and just delete any function with F5 mapped to it, we won’t need them. Yes, even the ones suspiciously labelled “Debug”.

What we’re going to do is made VS Code run a command from a shell when we press F5. That command is make && /opt/mgba yourgame.gba which will cause Make to run, compile your code and then if - and only if - the compilation is successful, mGBA will launch with your game running inside it. Doing this requires creating some tasks in the .vscode/tasks.json file.

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Make",
            "type": "shell",
            "command": "make",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        {
            "label": "Make Clean",
            "type": "shell",
            "command": "make clean"
        },
        {
            "label": "Run",
            "type": "shell",
            "group": {
                "kind": "test",
                "isDefault": true
            },
            "command": "make && '/mnt/c/Program Files/mGBA/mGBA.exe' test.gba"
        },
    ]
}

There’s a bit of quote shuffling going on in the bottom “command” line due to ‘Program Files’ having a space in it. Obviously replace “test.gba” with whatever your project’s gba file is called.

Keyboard shortcuts

The Visual Studio Code documentation contains a section on how to bind keys to tasks

This is my keybindings.json file (which doesn’t go inside the .vscode folder)

// Place your key bindings in this file to override the defaultsauto[]
[
    {
        "key": "ctrl+shift+alt+f5",
        "command": "workbench.action.debug.continue",
        "when": "inDebugMode"
    },
    {
        "key": "f5",
        "command": "-workbench.action.debug.continue",
        "when": "inDebugMode"
    },
    {
        "key": "ctrl+shift+alt+f5",
        "command": "workbench.action.debug.start",
        "when": "!inDebugMode"
    },
    {
        "key": "f5",
        "command": "-workbench.action.debug.start",
        "when": "!inDebugMode"
    },
    {
        "key": "ctrl+shift+f6",
        "command": "workbench.action.debug.pause",
        "when": "debugState == 'running'"
    },
    {
        "key": "f6",
        "command": "-workbench.action.debug.pause",
        "when": "debugState == 'running'"
    },
    {
        "key": "f6",
        "command": "workbench.action.tasks.build"
    },
    {
        "key": "ctrl+shift+b",
        "command": "-workbench.action.tasks.build"
    },
    {
        "key": "f5",
        "command": "workbench.action.tasks.test"
    }
]

The bottom commands are what call the tasks we created earlier. ‘Build’ is bound to F6 and just runs Make. ‘Test’ is bound to F5 and runs Make followed by the emulator. You have to manually run make clean by pressing Ctrl-Shift-P, then typing in Task and choosing Tasks: Run Task and selecting Make Clean from the list. It’s a bit convoluted, but cleaning out the build is only done when major code refactoring happens so I don’t tend to run it often enough that having a specific key is needed.

Workflow

This is the workflow I use now the above is configured

  1. Copy the DevkitARM gba example project to a new folder using Bash
  2. Copy a .vscode folder from another project into it
  3. Use the WSL extension in VS Code to open that folder
  4. Modify tasks.json to change the gba file
  5. Write code
  6. Press F5 to build code and launch emulator
  7. Make the emulator start GDB Server
  8. Switch into VS Code, set breakpoints in the debugger
  9. Click the green play button to run GDB
  10. Debug code

Right, I think that’ll do for now, this has got far too long.

Top comments (0)