# open_agb_firm runs GBA ROMs natively on the 3DS's built-in GBA hardware

> open_agb_firm is a bare metal payload for the Nintendo 3DS that runs GBA games and homebrew on the console's own GBA chip, rather than injecting a save into the Virtual Console. It reads .gba files from the SD card, writes saves back to the SD card, picks the save type from a bundled database, and adds the screen corrections that real GBA hardware gives you. It is still labelled beta.

**profi200/open_agb_firm** — open_agb_firm is a bare metal app for running GBA homebrew/games using the 3DS builtin GBA hardware.

- Repository: https://github.com/profi200/open_agb_firm
- Stars: 1,330 · Forks: 81
- Language: C
- License: GPL-3.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/profi200-open-agb-firm

## A bare metal payload instead of a save file inject

open_agb_firm is a bare metal interface for natively running GBA games and homebrew using the 3DS's built-in GBA hardware. That is the whole idea, and the difference from the usual approach is worth stating precisely.

The usual approach on this console is AGB_FIRM, which works by injecting a payload into a Virtual Console save and launching it from the 3DS menu. The README describes open_agb_firm as a complete and better alternative to GBA VC injects, and the list of what that buys is the feature list.

You can launch GBA files directly from the SD card. Save files are written directly to the SD card. The save type is configured automatically using an included database. There is user configuration, such as gamma settings. There is button remapping. There is border support for 1 to 1 scaling mode. There is gamma correction to fix the washed out look of games. And there is colour correction to mimic the look of the GBA or DS phat LCD.

The technical claim underneath those features is the interesting part. Unlike AGB_FIRM, open_agb_firm is not affected by the famous bug where the video output wraps around leaving garbled lines at the bottom of the screen, and the SD cluster size does not matter.

That last point is not a detail. A payload that reads files from the SD card has to work with whatever cluster size the card was formatted with, and getting that wrong is the kind of failure that makes a tool unusable on half the cards in circulation.

## Setup is a file, a folder and a button combination

The setup steps are short and the payload is launched in a way that is worth understanding before you start.

You download the latest release and extract it. You copy the open_agb_firm.firm file to your 3DS's SD card at /luma/payloads if you are using Luma3DS, or elsewhere if you are using fastboot3DS. You copy the 3ds folder to the root of the SD card, merging folders if asked. Then you launch open_agb_firm using Luma3DS by holding START while booting your 3DS, or assign it to a slot if you are using fastboot3DS. Once it launches, you use the file browser to navigate to a .gba ROM to run.

Two things follow from that. The first is that the payload needs a homebrew environment of some kind, since the .firm file is the Luma3DS and fastboot3DS format and the 3ds folder is where the configuration and saves live. The second is that the console has to be in your hands at boot, because the START button is what selects the payload.

The controls are then a GBA controller mapped onto 3DS buttons, A, B, L, R, START and SELECT. SELECT plus Y dumps the hardware frame output to a bitmap under /3ds/open_agb_firm/screenshots with a name of the current date and time from the real-time clock. X plus UP or DOWN adjusts the screen brightness in backlightSteps units, X plus LEFT turns the LCD backlight off and X plus RIGHT turns it back on. Holding X while launching a game skips applying patches, and holding the power button turns the 3DS off.

The file browser is the launcher, which is the simplest possible interface and also the one with the fewest options. There is no cover art, no favourites list and no library scanning.

## One INI file, with a per-game layer on top of it

Settings are stored in /3ds/open_agb_firm/config.ini, and the structure is two layered, which is the part that makes the tool usable with a mixed ROM collection.

The global layer holds the general, video, audio, input and advanced sections. The per-game layer is a file named after the ROM, romName.ini, inside /3ds/open_agb_firm/saves, and the README says the game specific settings are only intended to be used there.

The general section is where the display calibration lives. The backlight setting is a brightness in luminance, cd per square metre, with a default of 64, a range of 20 to 117 on the old 3DS and 16 to 142 on the new one, and a recommendation of values of 64 or less. It also says that hardware calibration from your CTRNAND is required to get the correct brightness for both LCDs, which is a reminder that the 3DS has two screens and both of them are in this calculation. The other general options are directBoot to skip the GBA BIOS intro, useGbaDb to take the save types from gba_db.bin, and useSavesFolder to keep save files in the saves folder instead of the ROM directory.

The per-game layer is where save types are pinned. The saveSlot option selects a slot from 0 to 9, and the saveType option overrides the automatic detection, with options that start with rom_256m intended for 32 MiB games and options ending in rtc enabling the hardware real-time clock. The list runs from eeprom_8k through the flash_512k family, which covers the Atmel, SST, Panasonic, Macronix and Sanyo parts, up to sram_256k, plus none and auto.

In the advanced section, saveOverride opens the save type override menu after selecting a game, and defaultSave sets the type used when the save type is not in gba_db.bin and cannot be auto detected, with sram_256k as the default.

## Colour profiles mimic old screens, and they cost battery

The video section is where this project is more than a launcher, and it is also where the trade-offs are clearest.

The scaler option picks the video scaling, with the default of matrix and none and bilinear as the alternatives. The colorProfile option is the interesting one, with the default of none and a list of targets: gba for Game Boy Advance, gb_micro for Game Boy micro, gba_sp101 for the Game Boy Advance SP, nds for the Nintendo DS phat, ds_lite, nso for Nintendo Switch Online, vba for a full Visual Boy Advance or No$GBA look, and identity for no colour space conversion at all.

The setting exists because most 2DS and 3DS LCDs were not calibrated correctly from the factory, so the look on a 3DS does not match what the same game looked like on the original hardware. Setting the profile to identity is the escape hatch if you only want less saturated colours or a change to contrast or brightness, and the contrast, brightness and saturation options, all with no effect when colorProfile is none, adjust the result further. Contrast is a screen gain defaulting to 1.0, brightness is a screen lift defaulting to 0.0, and saturation defaults to 1.0.

The cost is stated too. Due to a lot of extra RAM access and extra CPU processing per frame, battery runtime is affected with colour profiles other than none. On a handheld that is powered by the same battery for the whole session, that is the difference between a setting you keep on and one you turn on to show someone.

The input section is where the second compromise sits. Each of the twelve 3DS buttons can be mapped, and TOUCH reacts to all touchscreen presses while the CP prefix is short for Circle-Pad. But the README notes that button mappings can cause input lag of up to one frame depending on when the game reads its inputs, and for that reason the default mapping of the Circle-Pad to the D-Pad is no longer provided. The example configuration shows the workaround, mapping both the D-Pad and the Circle-Pad onto the GBA D-Pad.

## Patches, and a build that needs devkitARM

The project applies IPS and UPS patches automatically, and the README explains how to use one, which means a patched ROM works without a separate patch step in the file browser.

Building it is a different matter from using it, and the Makefile is explicit about the requirement.

```text
# "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM"
```

The build produces two binaries, an arm9 and an arm11 one, and combines them into the .firm file. The entry addresses are fixed in the Makefile, 0x08000040 for the arm9 side and 0x1FF89034 for the arm11 side, and the two sections are placed at their own addresses with types that depend on whether the firmtool path or the firmware builder path is used. The version string is derived from git describe against the v tags, so a build reports the tag it was made from.

The Dockerfile shows the toolchain side. It starts from devkitpro/devkitarm, clones and builds two helper projects, dma330as and ctr_firm_builder, copies the resulting binaries into the image, and installs p7zip-full.

The repository layout matches the console's architecture, with arm9/ and arm11/ directories for the two processor sides, source/, include/, resources/, libraries/ and tools/. The licence is GPL 3.0, in LICENSE.txt, and the last push was on 2026-07-30 with the project not archived, while the listed releases are beta builds from 2024.

## Conclusion

open_agb_firm fits someone with a 3DS who owns GBA ROMs and wants them run natively, with saves on the SD card and a screen that looks like the original hardware. It is the wrong choice for a console you cannot take apart of, for anything outside the 3DS family, and for a setup where the 3DS stays closed, because the button combination is how the payload is launched. Verify first that your 3DS model is covered by the listed backlight ranges, and read the known issues, since the screen output can freeze and the project is explicit that it is a beta.

## FAQ

### What does open_agb_firm do on a Nintendo 3DS?

It is a bare metal interface for natively running GBA games and homebrew on the 3DS's built-in GBA hardware, instead of using a Virtual Console save inject like AGB_FIRM. It can launch .gba files from the SD card, write saves to the SD card, detect the save type automatically, and apply IPS and UPS patches.

### How do I install open_agb_firm?

Download the latest release and extract it, copy open_agb_firm.firm to /luma/payloads on the SD card for Luma3DS or elsewhere for fastboot3DS, copy the 3ds folder to the root of the SD card merging if asked, then launch the payload by holding START while booting with Luma3DS or by assigning it to a slot with fastboot3DS.

### Where are the settings and the save files stored?

Settings are in /3ds/open_agb_firm/config.ini, and save files go to /3ds/open_agb_firm/saves rather than the ROM directory when the useSavesFolder option is left at its default of true. Per-game settings use a file named after the ROM in the same saves folder.

### What is the difference between open_agb_firm and AGB_FIRM?

AGB_FIRM works by injecting into a Virtual Console save, and open_agb_firm is a bare metal alternative that runs the games natively. The README says it is not affected by the video output bug where the picture wraps around leaving garbled lines at the bottom of the screen, and that the SD cluster size does not matter.

### Is open_agb_firm stable enough to use daily?

The README calls it currently in beta, says it is relatively stable and safe to use, and notes that some quirks have not been fixed, with a known issues section. One quirk named in the controls is that the screen output can freeze, in which case pressing HOME fixes it. The project also states it is not responsible for damage resulting from use.

## Sources

- [Issues](https://github.com/profi200/open_agb_firm/issues)
- [License: GPL-3.0](https://github.com/profi200/open_agb_firm/blob/master/LICENSE)
- [profi200/open_agb_firm on GitHub](https://github.com/profi200/open_agb_firm)
- [README](https://github.com/profi200/open_agb_firm/blob/master/README.md)
- [Releases](https://github.com/profi200/open_agb_firm/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/profi200-open-agb-firm
