Game Boy Advance Dev: Logging to the console

Lobsters Hottest Tools

Summary

This article explains how to implement logging to the console in Game Boy Advance development using mGBA's custom memory-mapped registers, allowing printf-style debugging via the emulator's log window or terminal.

<p><a href="https://lobste.rs/s/ujjm68/game_boy_advance_dev_logging_console">Comments</a></p>
Original Article
View Cached Full Text

Cached at: 07/04/26, 06:44 PM

# Game Boy Advance Dev: Logging to the console Source: [https://www.mattgreer.dev/blog/gba-dev-logging/](https://www.mattgreer.dev/blog/gba-dev-logging/) Printing to the console with things like`printf\(\)`in C or`console\.log\(\)`in JavaScript is a useful tool while developing\. When making a GBA game, you might think you have to forego this, or log to the GBA's tiny little screen\. If you use mGBA, you can get`printf\(\)`functionality in your game\. It's simple to do\. ## How it works You often write to registers like`REG\_DISPCNT`when doing GBA dev\. This is a memory mapped register, and it's a simple way for your game to tell the GBA hardware what you want it to do\. This is just an address in the GBA's memory map, and when it is written to, the GBA hardware will take the data it receives and act accordingly\. mGBA added a few more memory mapped registers for the purpose of logging\. They work just like`REG\_DISPCNT`and the like, except they only work when your game is running inside mGBA\. Here are the definitions for these mGBA specific registers\. `` `\#define REG\_LOG\_ENABLE \(vu32\*\)\(0x4FFF780\)` `\#define REG\_LOG\_BUFFER \(vu32\*\)\(0x4FFF600\)` `\#define REG\_LOG\_SEND \(vu32\*\)\(0x4FFF700\)` So to log some data, you first send a special value to`REG\_LOG\_ENABLE`to turn logging on\. Then you write your data to`REG\_LOG\_BUFFER`\. Then when you are done, write the log level to`REG\_LOG\_SEND`and at that point mGBA will take the data you sent it and output it to its log\. This sounds a little complicated, and it kind of is\. But you really just hide all of these details inside of a function and never think about them again\. Here is that function\. `` `\#define REG\_LOG\_ENABLE \(vu32\*\)\(0x4FFF780\)` `\#define REG\_LOG\_BUFFER \(vu32\*\)\(0x4FFF600\)` `\#define REG\_LOG\_SEND \(vu32\*\)\(0x4FFF700\)` `\#define MGBA\_LOG\_MAX\_LINE 256` `\#define ERROR 0x101` `\#define WARNING 0x102` `\#define INFO 0x103` `\#define DEBUG 0x104` `void mgbalog\(u32 level, const char \*msg\) \{` `\*REG\_LOG\_ENABLE = 0xC0DE;` `tonccpy\(\(void \*\)REG\_LOG\_BUFFER, msg, MGBA\_LOG\_MAX\_LINE\);` `\*REG\_LOG\_SEND = level;` `\}` Then you can use it in your game whenever you need to log something\. `` `\#include "mgbalog\.h"` `void myFunc\(\) \{` `\.\.\.` `mgbalog\(DEBUG, "hello mGBA log"\);` `\}` When you run the game, go to`Tools \> View logs\.\.\.`to open up the log window\. ![](https://www.mattgreer.dev/_next/static/media/mgbaLogWindow.d920a68b.png)The mGBA log window with our log message in it\.And there's your message\. When logging, you can choose to log at various levels: error, warning, info or debug\. That's what the`level`business is for in`mgbalog\(\)`\. You write your desired log level to`REG\_LOG\_SEND`, this pulls double duty of telling mGBA at what level to store the message, and that you're done writing the message so mGBA can now process it\. ### Logging to your terminal Opening the log window every time to view your logs can be tedious\. If you launch mGBA from the command line, you can also tell it to write the logs to your terminal\. To do this, go into the log window again and click, on advance settings, then check "Log to console" at the bottom\. ![](https://www.mattgreer.dev/_next/static/media/advanceSettings.bd077ee4.png)Log to console checked in the logging advance settings window\.mGBA itself logs a*lot*of stuff\. You can turn it all on/off in this dialog as well\. Now if you launch your game via the command line, the log output will show up there as well\. You can control which level of log you want with`\-\-log\-level`\. For example, here is how to only log DEBUG to the console\. `` `mgba \-\-log\-level 16 yourRom\.gba` Here are the log levels - 1 \- fatal errors - 2 \- errors - 4 \- warnings - 8 \- info - 16 \- debug - 32 \- stub - 64 \- in\-game errors And you can combine them, so if you want debug and error, do`\-\-log\-level 18` ## Adding formatting Logging a static string is rarely useful\. Thankfully it is easy to add`printf`style formatting\. My blog doesn't allow C includes which use < and \>, so I added spaces to work around that\. I really need to fix that\.\.\. `` `\#include < stdarg\.h \>` `\#include < stdio\.h \>` `static char logBuffer\[MGBA\_LOG\_MAX\_LINE\];` `void mgbalog\(u32 level, const char \*format, \.\.\.\) \{` `\*REG\_LOG\_ENABLE = 0xC0DE;` `va\_list formatArgs;` `va\_start\(formatArgs, format\);` `vsnprintf\(logBuffer, MGBA\_LOG\_MAX\_LINE, format, formatArgs\);` `va\_end\(formatArgs\);` `tonccpy\(\(void \*\)REG\_LOG\_BUFFER, logBuffer, MGBA\_LOG\_MAX\_LINE\);` `\*REG\_LOG\_SEND = level;` `\}` Now you can do things like`mgbalog\(DEBUG, "pos\.x=%i", pos\.x\);`\. ## Only logging during development Logging adds to your binary, uses memory and cpu, and is generally not wanted in the final version of your game\. This is especially true if you add formatting, things like`vsnprintf`from the standard library are pretty expensive on the poor lil GBA\. I hide my logging implementation behind an`MGBALOG`define\. So when I want logging, I define`MGBALOG`\. If it's not defined, nothing related to logging will get added to the binary\. `` `\#ifdef MGBALOG` `void mgbalog\(u32 level, const char \*format, \.\.\.\) \{` `\.\.\.` `\}` `\#endif` That is all well and good, but this means if you don't define`MGBALOG`, then you have to remove all logging calls throughout your codebase, otherwise you will get a compilation error\. This is easily fixed with more define schenanigans\. This is my`mgbalog\.h` `` `\#pragma once` `\#include < tonc\.h \>` `\#define MGBA\_LOG\_MAX\_LINE 256` `\#define ERROR 0x101` `\#define WARNING 0x102` `\#define INFO 0x103` `\#define DEBUG 0x104` `\#define REG\_LOG\_ENABLE \(vu32\*\)\(0x4FFF780\)` `\#define REG\_LOG\_BUFFER \(vu32\*\)\(0x4FFF600\)` `\#define REG\_LOG\_SEND \(vu32\*\)\(0x4FFF700\)` `\#ifdef MGBALOG` `void \_mgbalog\(u32 level, const char \*format, \.\.\.\);` `\#define mgbalog\(\.\.\.\) \_mgbalog\(\_\_VA\_ARGS\_\_\)` `\#else` `\#define mgbalog\(level, format, \.\.\.\)` `\#endif` Then throughout my game I call`mgbalog\(\.\.\.\)`as before whenever I want to log something\. This is now a macro that either calls`\_mgbalog\(\)`if`MGBALOG`is defined, or does nothing at all when it is not defined\. Now you don't need to worry about your log calls, the game will always compile regardless\. With the above header file, here is my implementation\. `` `\#ifdef MGBALOG` `\#include "mgbalog\.h"` `\#include < stdarg\.h \>` `\#include < stdio\.h \>` `static char logBuffer\[MGBA\_LOG\_MAX\_LINE\];` `void \_mgbalog\(u32 level, const char \*format, \.\.\.\) \{` `\*REG\_LOG\_ENABLE = 0xC0DE;` `va\_list formatArgs;` `va\_start\(formatArgs, format\);` `vsnprintf\(logBuffer, MGBA\_LOG\_MAX\_LINE, format, formatArgs\);` `va\_end\(formatArgs\);` `tonccpy\(\(void \*\)REG\_LOG\_BUFFER, logBuffer, MGBA\_LOG\_MAX\_LINE\);` `\*REG\_LOG\_SEND = level;` `\}` `\#endif` ## Conclusion That's all there is to it\. A simple little bit of code adds a lot of utility to your dev experience\. There are mgba logging libraries out there if you don't want to roll your own\. Or if you are using an engine like[Butano](https://github.com/gvaliente/butano), it already has mgba logging built in\. Now when using a logging library, you know what it is doing behind the scenes, which is always a good thing\.

Similar Articles

Emulator Debugging: Area 5150's Lake Effect

Lobsters Hottest

The article details the debugging process for the 'Lake' effect in the Area5150 demo on the MartyPC emulator, explaining the need for a title-specific hack and the subsequent fix using bus sniffing and dynamic clocking to achieve cycle-accurate CGA emulation.

Programming a GBA Game on an iPhone

Hacker News Top

An author documents how they programmed a Game Boy Advance game entirely on an iPhone using tools like iSH, Textastic, Delta, and gba bootstrap, resulting in a short game called TO THE TOWER.

Rust x GBA: Setup and Pixels

Lobsters Hottest

A tutorial guide that walks through setting up a Rust project to build a ROM that runs on the Game Boy Advance, covering project setup and pixel rendering.

Why I Wrote a Game Boy Advance Game in Zig (2024)

Lobsters Hottest

A developer explains why they chose the Zig programming language to create a Game Boy Advance game, highlighting Zig's cross-compilation capabilities and suitability for embedded programming.