GameBoy Advance Cartridge Backup Storage
Introduction
I’m writing this post because I had some difficulty understanding the wonderful GBATek’s documentation on the different backup storage types GameBoy Advance games could use while working on my emulator. Hopefully, this information will be useful to someone.
As the GBA did not have any internal persistant storage like modern consoles do, if a game developer wanted to implement a save feature in their game, they had to package storage directly inside the cartridge.
The cartridges could ship with several different types of backup storage. These included SRAM, Flash, and EEPROM. In general, flash can be considered a type of EEPROM, but for the purposes of this article and the GBA, they are two separate categories, because they are read and written with two separate protocols.
What type of backup does my cartridge use?
Standard GBA cartridge dumps will (usually) include the type of backup storage they use as text somewhere in the file. This is a good enough way of determining the type.
To identify the cartridge’s type, search the entire ROM for one of the following strings in this table. This will work most of the time. There are a couple games where it won’t, however. For better accuracy, you should use the regular expression in the Regex column, and for best accuracy, you should keep a game database that you manually update to match save types to games.
String | Regex | Backup Type |
---|---|---|
EEPROM_ | EEPROM_V\d\d\d | Cartridge uses EEPROM. Either 512 bytes or 8KB |
SRAM_ | SRAM_V\d\d\d | Cartridge uses SRAM |
FLASH_ | FLASH_V\d\d\d | Flash 64 kilobytes / 512 kilobits |
FLASH512_ | FLASH512_V\d\d\d | Flash 64 kilobytes / 512 kilobits |
FLASH1M_ | FLASH1M_V\d\d\d | Flash 128 kilobytes / 1 megabit |
See GBATek for more details.
It sounds hacky, but that’s because it is.
SRAM
SRAM is by far the simplest type of backup storage, both for emulator developers and game developers. It’s simply another type of RAM, just one that happens to be on the cartridge instead of internal to the system. Unfortunately, it’s volatile, which means that if it loses power, your save data is gone. Cartridges that used SRAM included a built-in backup battery to keep the RAM powered, and your precious high scores in Pokémon Pinball: Ruby and Sapphire safe.
I’m not going to go into too much detail here, as this is just RAM accessed like any other RAM, it just happens to be in the cartridge address space 0xE000000 through 0xE007FFF. Making this persistent in an emulator is as easy as persisting this space to disk, using either mmap or detecting changes and writing out a save.
Flash
Introduction to Flash
Flash memory in GBA carts has the advantage of being non-volatile, meaning it doesn’t need a battery in the cartridge to keep your progress safe. The chips did have a limited number of write cycles, though this isn’t something an emulator developer needs to worry about.
There were several chips that Nintendo used for Flash, all made by different manufacturers. GBATek lists them all.
Important: You do NOT need to simulate all the different chips! When a developer shipped their code off to Nintendo to be put in a cartridge, they did NOT know what type of chip it would be manufactured with! Different production runs of the same game could even use different manufacturer’s chips! As a consequence of this, all games should support all Flash types. I’m sure there are exceptions, but this means that your emulator need only support one type per size. I chose Sanyo for 128K and Panasonic for 64K, as you can see below in the “Stubbing Flash” section.
As a downside, accessing it is significantly more complicated than for SRAM. The main loop the game will use is made up of sending the chip commands, and taking actions.
Because the commands and actions are taken separately, it can (and should) be implemented as a state machine. This means you’ll need to hold state somewhere of what the Flash chip is currently doing, and treat reads and writes differently based on this state.
Sending Flash commands
Commands are sent by 3 separate, sequential 8-bit writes to memory.
- 0xAA to 0xE005555
- 0x55 to 0xE002AAA
- The byte representing the command to 0xE005555
You can keep track of this by switching your state variable from READY to something like CMD_1 to track the 0xAA write, CMD_2 to track the 0x55 write, and then to a state specified by the command on the third write.
Relevant commands
Here’s a table of all the relevant commands you’ll need to implement. See below for more details. Note: I have omitted Atmel-specific commands. These are the only devices that differ in protocol used to communicate with them, and Nintendo supposedly stopped using them towards the end of the GBA’s lifetime. As far as I know, there are no games that require them. I’ve also omitted the Macronix-only “terminate command after timeout” command.
Unless specified, the chip returns to “ready” state after every command.
8-bit write to 0xE005555 | Command | Notes |
---|---|---|
0x90 | Enter “Chip identification mode” | In chip identification mode, the data in 0x0E000000 and 0x0E000001 are replaced by the flash chip’s manufacturer and device ID, as specified in the “Stubbing Flash” section below. Remember to use a different ID for 64K and 128K! |
0xF0 | Exit “Chip identification mode” | Return the chip back to READY mode. |
0x80 | Prepare to receive erase command | Next command must be one of the following two erase commands. |
0x10 | Erase entire chip | MUST be preceded by a ‘prepare to receive erase command’ |
0x30 | Erase 4 kilobyte sector | MUST be preceded by a ‘prepare to receive erase command.’ The address of the third write is also different than the other commands, and meaningful. See below. Only available on non-Atmel chips. |
0xA0 | Prepare to write single data byte | Next write must be a write of a single byte. Only available on non-Atmel chips. |
0xB0 | Set memory bank | Only works on 128KB flash devices, which are represented as two 64KB banks. 64KB flash devices don’t support bank switching, for reasons that should be obvious. |
0x90 Enter Chip Identification mode
In chip identification mode, the data in 0x0E000000 and 0x0E000001 are replaced by the flash chip’s manufacturer and device ID, as specified in the “Stubbing Flash” section below. The chip stays in this mode until the “exit chip identification mode” command is issued.
As far as I know, the game can issue other commands while in this mode, so for that purpose it should be treated the same as “ready” mode.
0xF0 Exit Chip Identification mode
When this command is issued, the device returns to “ready” mode, and reads from the first two addresses are normal again.
0x80 Prepare to receive erase command
This command must be issued before issuing one of the two erase commands specified below. In fact, the next command must be an erase command.
0x10 Erase entire chip
Erases the entire chip. Note that this is not done with zeroes, but every byte in memory will equal 0xFF when done. This normally takes a decent amount of time, but it’s probably fine to do it instantly. The game will wait until a read from 0x0E000000 returns 0xFF, so if you want to go for some kind of cycle-accuracy here, make sure to erase that byte last.
0x30 Erase 4KB sector
Instead of the standard 0xE005555, 0xE002AAA, 0xE005555 sequence that every other command uses, this one is a little special.
The first two writes happen normally. However, instead of the third write being to 0xE005555, the game will write 0x30 to 0x0E00n000, where n is a number representing the page to be erased.
For example, if the game writes 0x30 to 0x0E005000, then all bytes from 0x0E005000 through 0x0E005FFF should be erased, and replaced with 0xFF (same value as in the above command. Flash chips erase to 0xFF, not to 0x00.)
The game will then wait until the value at address 0x0E00n000 reads 0xFF. Again, if you’re trying for cycle accuracy here and not doing this all at once, erase this last.
0xA0 Prepare to write single data byte
After this command, the game should issue one write to a flash address between 0x0E000000 and 0x0E00FFFF. You can emulate this as happening instantly, but games will wait until that value appears before continuing.
0xB0 Set memory bank
This allows 128KB flash chips to expose their full size to the game, even though the address bus they’re connected to only supports 64KB of address space. After issuing this command, the game will write either the value 0 or 1 to the address 0x0E000000. This determines which bank ALL commands that access the memory use.
- Erase 4KB sector
- Write single data byte
- Data reads
Special: Terminate write/erase command
When a game determines it’s been waiting long enough for a write to happen, it can terminate the wait period by writing 0xF0 to 0x0E005555. This is not part of any command sequence, but occurs as a single write. When this write happens after a write command has been issued and completed, it’s safe to return the chip to Ready mode. If this write occurs while the chip is already in ready mode, it can be ignored.
Reading data out of Flash
This part is easy. A read from an address in Flash space reads that index in the flash backup. Any address between 0x0E000000 - 0x0E00FFFF will work. For 128KB devices, this takes into account the bank-switching mechanism. Note that when the chip is in “chip identification mode,” reads from the first and second address will return different data.
Note: Stubbing Flash
If you want to test games like Pokémon Emerald in your emulator, but aren’t quite ready for the full experience of implementing Flash, there’s a quick and easy way to stub it. Obviously, saving the game won’t work, but, assuming nothing else is wrong, you’ll be able to go in-game.
In your memory bus, simply return the following values on 8-bit reads to the specified addresses.
For 128K flash, Sanyo IDs:
8-bit read address | Value | Meaning |
---|---|---|
0x0E000000 | 0x62 | Sanyo manufacturer ID |
0x0E000001 | 0x13 | Sanyo device ID |
For 64K flash, Panasonic IDs:
8-bit read address | Value | Meaning |
---|---|---|
0x0E000000 | 0x32 | Panasonic manufacturer ID |
0x0E000001 | 0x1B | Panasonic device ID |
And with that, you should have enough information to implement flash backups in your emulator.