Dumping GBA saves
Page 1 of 1

Author:  Hextator [ Sun Jun 21, 2009 10:27 am ]
Post subject:  Dumping GBA saves

Admin note: This thread originally split from viewtopic.php?f=11&t=6820

Cool, glad to see you figured out the problem.

Now you can write something to read and write from and to a game's save battery for me so I can use an unused 64 Kb region of a DS cart to aid in dumping/overwriting the save battery of a GBA cart with the Trainer Toolkit because I don't feel like spending any more money on hardware for efficiently hacking this old GBA game's save files.

...Please? x:

Author:  Tyler24 [ Tue Jun 23, 2009 5:17 pm ]
Post subject:  Re:

Zeld wrote:
Cool, glad to see you figured out the problem.

Now you can write something to read and write from and to a game's save battery for me so I can use an unused 64 Kb region of a DS cart to aid in dumping/overwriting the save battery of a GBA cart with the Trainer Toolkit because I don't feel like spending any more money on hardware for efficiently hacking this old GBA game's save files.

...Please? x:

I'm confused as to what you want me to do... this method works *only* for the R4 card, although it would not be hard at all to modify to suit any other DLDI-ed flash card. What "64k unused area" are you trying to access... the save EEPROM? I've never heard of the trainer toolkit.

There's probably a much better way to hack a GBA save file, no?

Author:  Hextator [ Tue Jun 23, 2009 5:35 pm ]
Post subject: 

I'm sure there is.

I wasn't talking about the R4 card. I'm just assuming the logic is similar since it's a slot 1 device.

DS games have their backup media on their cards. I wanted to know how to read from and write to it. There's documentation, but examples are much easier to follow. I could probably look at how a retail game does it, but that would require going through headaches of deciding whether or not what I'm looking at is what I want.

Here's the info on the relevant registers, from GBATEK:

40001A0h - NDS7/NDS9 - AUXSPICNT - Gamecard ROM and SPI Control
  13    NDS Slot Mode       (0=Parallel/ROM, 1=Serial/SPI-Backup)

40001A2h - NDS7/NDS9 - AUXSPIDATA - Gamecard SPI Bus Data/Strobe (R/W)
  0-7  Data

40001A8h - NDS7/NDS9 - Gamecard bus 8-byte Command Out
  0-7   1st Command Byte (at 40001A8h) (eg. B7h) (MSB)
  8-15  2nd Command Byte (at 40001A9h) (eg. addr bit 24-31)
  16-23 3rd Command Byte (at 40001AAh) (eg. addr bit 16-23)
  24-31 4th Command Byte (at 40001ABh) (eg. addr bit 8-15) (when aligned=even)
  32-39 5th Command Byte (at 40001ACh) (eg. addr bit 0-7)  (when aligned=00h)
  40-47 6th Command Byte (at 40001ADh) (eg. 00h)
  48-57 7th Command Byte (at 40001AEh) (eg. 00h)
  56-63 8th Command Byte (at 40001AFh) (eg. 00h) (LSB)

4100010h - NDS7/NDS9 - Gamecard bus 4-byte Data In (R)
  0-7   1st received Data Byte (at 4100010h)
  8-15  2nd received Data Byte (at 4100011h)
  16-23 3rd received Data Byte (at 4100012h)
  24-31 4th received Data Byte (at 4100013h)

4000204h - NDS9 - EXMEMCNT - 16bit - External Memory Control (R/W)
4000204h - NDS7 - EXMEMSTAT - 16bit - External Memory Status (R/W..R)
  11    17-pin NDS Slot Access Rights     (0=ARM9, 1=ARM7)
  14    Main Memory Interface Mode Switch (0=Async/GBA/Reserved, 1=Synchronous)
  15    Main Memory Access Priority       (0=ARM9 Priority, 1=ARM7 Priority)

Et cetera. Looks a bit complicated. Nothing too difficult, but still, very tedious.

Some clarification would be nice and seeing as how you figured out how to talk to an R4, you might be able to figure out how to talk to retail games' backup media? >.>

Author:  Tyler24 [ Tue Jun 23, 2009 5:42 pm ]
Post subject:  Re:

Using the above code will do nothing but freeze the system when running a retail cart. Also, if you startup the system from GBA mode, I don't think you are able to access the ARM9/DS slot anyways.

Look on GBATEK for the EEPROM/save area... that is what you will need to write/read to/from to do what are you looking to do. It's a completely different protocol...

DS Cartridge Backup

SPI Bus Backup Memory
  Type   Total Size  Page Size  Chip/Example      Game/Example
  EEPROM 0.5K bytes   16 bytes  ST M95040-W       (eg. Metroid Demo)
  EEPROM   8K bytes   32 bytes  ST M95640-W       (eg. Super Mario DS)
  EEPROM  64K bytes  128 bytes  ST M95512-W       (eg. Downhill Jam)
  FLASH  256K bytes  256 bytes  ST M45PE20        (eg. Skateland)
  FLASH  512K bytes  256 bytes  ST M25PE40?       (eg. which/any games?)
  FRAM     8K bytes   No limit  ?                 (eg. which/any games?)
  FRAM    32K bytes   No limit  Ramtron FM25L256? (eg. which/any games?)

Lifetime Stats
  Type      Max Writes per Page    Data Retention
  EEPROM    100,000                40 years
  FLASH     100,000                20 years
  FRAM      No limit               10 years

SPI Bus Backup Memory is accessed via Ports 40001A0h and 40001A2h, see
DS Cartridge I/O Ports

For all EEPROM and FRAM types:
  06h WREN  Write Enable                Cmd, no parameters
  04h WRDI  Write Disable               Cmd, no parameters
  05h RDSR  Read Status Register        Cmd, read repeated status value(s)
  01h WRSR  Write Status Register       Cmd, write one-byte value
  9Fh RDID  Read JEDEC ID (not supported on EEPROM/FLASH, returns FFh-bytes)
For 0.5K EEPROM (8+1bit Address):
  03h RDLO  Read from Memory 000h-0FFh  Cmd, addr lsb, read byte(s)
  0Bh RDHI  Read from Memory 100h-1FFh  Cmd, addr lsb, read byte(s)
  02h WRLO  Write to Memory 000h-0FFh   Cmd, addr lsb, write 1..MAX byte(s)
  0Ah WRHI  Write to Memory 100h-1FFh   Cmd, addr lsb, write 1..MAX byte(s)
For 8K..64K EEPROM and for FRAM (16bit Address):
  03h RD    Read from Memory            Cmd, addr msb,lsb, read byte(s)
  02h WR    Write to Memory             Cmd, addr msb,lsb, write 1..MAX byte(s)
Note: MAX = Page Size (see above chip list) (no limit for FRAM).

For FLASH backup, commands should be same as for Firmware FLASH memory:
DS Firmware Serial Flash Memory

Status Register
  0   WIP  Write in Progress (1=Busy) (Read only) (always 0 for FRAM chips)
  1   WEL  Write Enable Latch (1=Enable) (Read only, except by WREN,WRDI)
  2-3 WP   Write Protect (0=None, 1=Upper quarter, 2=Upper Half, 3=All memory)
For 0.5K EEPROM:
  4-7 ONEs Not used (all four bits are always set to "1" each)
For 8K..64K EEPROM and for FRAM:
  4-6 ZERO Not used (all three bits are always set to "0" each)
  7   SRWD Status Register Write Disable (0=Normal, 1=Lock) (Only if /W=LOW)
WEL gets reset on Power-up, WRDI, WRSR, WRITE/LO/HI, and on /W=LOW.
The WRSR command allows to change ONLY the two WP bits, and the SRWD bit (if any), these bits are non-volatile (remain intact during power-down), respectively, the WIP bit must be checked to sense WRSR completion.

Detection (by examining hardware responses)
The overall memory type and bus-width can be detected by RDSR/RDID commands:
  RDSR  RDID          Type         (bus-width)
  FFh,  FFh,FFh,FFh   None         (none)
  F0h,  FFh,FFh,FFh   EEPROM       (with 8+1bit address bus)
  00h,  FFh,FFh,FFh   EEPROM/FRAM  (with 16bit address bus)
  00h,  xxh,xxh,xxh   FLASH        (usually with 24bit address bus)
And, the RD commands can be used to detect the memory size/mirrors (though that won't work if the memory is empty).

Pin-Outs for EEPROM and FRAM chips
  Pin Name Expl.
  1  /S    Chip Select
  2  Q     Data Out
  3  /W    Write-Protect (not used in NDS, wired to VCC)
  4  VSS   Ground
  5  D     Data In
  6  C     Clock
  7  /HOLD Transfer-pause (not used in NDS, wired to VCC)
  8  VCC   Supply 2.5 to 5.5V for M95xx0-W

FRAM (Ferroelectric Nonvolatile RAM) is fully backwards compatible with normal EEPROMs, but comes up with faster write/erase time (no delays), and with lower power consumption, and unlimited number of write/erase cycles. Unlike as for normal RAM, as far as I understand, the data remains intact without needing any battery.

I think it will be fairly straightforward after you find out the size of the save area...

Author:  Hextator [ Tue Jun 23, 2009 5:55 pm ]
Post subject: 

Heh, I already read all that. I guess I'll just have to see how a particular game does it.

I could piece together some code from that documentation alone, but I'm partial to getting as close to correct as possible the first time so I don't have as much debugging to do.

I know for fact I won't get this right the first time. I couldn't even get something as simple as calculating and writing the checksum to the GBA SRAM I was hacking correct the first time. Granted, I only made two small mistakes that were typos and not logical errors...

Author:  Parasyte [ Tue Jun 23, 2009 6:04 pm ]
Post subject:  Re: NDS: Reading from R4 Card, Assembly

Why not utilize the good ol' fashioned swap-trick?

Start your program, running in NDS RAM -> swap whatever GBA hardware (trainer toolkit, GBA flash cart, etc) with GBA game -> program copies GBA SRAM to NDS RAM -> swap GBA game with whatever GBA hardware you are using -> program writes backed-up GBA SRAM data from NDS RAM to GBA hardware (trainer toolkit, flash ROM, what have you ...)

Orders of magnitude simpler than screwing around with the NDS SPI bus. (By the way, I've written several SPI-accessing functions. They are all very small, and currently stored on a backup disc containing many [all?] of my development projects over the years. Sadly, I'm feeling extremely lazy today, so I don't think I will be looking them up right away. If you feel inclined, ask me about it sometime, and I will post some of these googies.)

Author:  Hextator [ Tue Jun 23, 2009 6:29 pm ]
Post subject: 

That was the original idea.

The problem was keeping communication intact, as well as initializing it to begin with, which there is much less documentation for.

Author:  Parasyte [ Tue Jun 23, 2009 6:39 pm ]
Post subject:  Re: NDS: Reading from R4 Card, Assembly

The best alternative is buying an old school GBA and using one of the SRAM manager-type multiboot programs available.

Author:  Hextator [ Wed Jun 24, 2009 1:58 pm ]
Post subject: 

I have an old school GBA. >.>

Anyway, I still would like to know how to "fiddle with the SPI bus" or however you put it.

There may come a time when hacking my DS games' saves will be in order (or perhaps those of my brothers'; you may not have noticed, but before I had come here asking for information on how to recover a save that my brother had produced in which no existing Action Replay code would let him continue from the place loading the save would put him at, which therefore required the save to be altered such that his starting location would differ).

Author:  Parasyte [ Wed Jun 24, 2009 4:30 pm ]
Post subject:  Re: Dumping GBA saves

I found some relevant sources... I also have some of these written in pure ASM. But that's fairly trivial. Even better, I have some source for detecting the backup type and dumping/restoring it, but the source was written for a totally different interface (LPT-> NDS hardware I designed years ago; totally awesome hw/sw project). So it will need some level of rewriting to come up with something useful for you.

Anyway, here's the low-level functions for accessing the card SPI bus directly on the NDS. Note the eeprom_active() function; it gives you the basic idea for doing any kind of SPI access.

#ifndef __SPI_H__
#define __SPI_H__

#include <nds.h>

void inline spi_wait();
void inline spi_open(u8 freq);
u8 inline spi_exch(u8 data);
void inline spi_send(u8 data);
u8 inline spi_recv();
void inline spi_close();
u8 inline eeprom_active();

#endif // __SPI_H__

#include "spi.h"

void inline spi_wait() {
   while (*(vu16*)0x040001A0 & 0x80);

void inline spi_open(u8 freq) {
   *(vu16*)0x040001A0 = 0xA040 | (freq & 0x03);

u8 inline spi_exch(u8 data) {
   *(vu16*)0x040001A2 = data;
   return *(vu16*)0x040001A2;

void inline spi_send(u8 data) {

u8 inline spi_recv() {
   return spi_exch(0xFF);

void inline spi_close() {
   *(vu16*)0x040001A0 = 0x0000;

u8 inline eeprom_active() {
   u8 active;

   active = (spi_recv() & 0x01);

   return active;

Author:  Hextator [ Thu Jun 25, 2009 7:46 am ]
Post subject: 

I looked over that and it doesn't look like it restores any of the I/O registers it clobbers.

Does this mean I should rewrite it to do so if I don't want my games crashing, which would then keep the TT communication from being reached and complicating the dumping/writing process?

But anyway, thanks, this greatly simplifies things. Much better than writing a bunch of code that looks like it follows the documentation and ironing out bugs after discovering that I missed some important tidbit.

Author:  Parasyte [ Thu Jun 25, 2009 9:58 pm ]
Post subject:  Re: Dumping GBA saves

If you don't want your games crashing, don't access SPI while the game does?

The code wasn't written for use in a debugger-type program that runs along side the game. As long as you only touch the SPI bus while there are no accesses being made to the card (***even normal ROM reads***) you should be fine. There are ways to mitigate the problem (checking that it's safe to begin access) but not entirely fool-proof without patching the OS with additional 'thread safety' such that the two programs can never conflict with each other.

Truthfully, nothing is stopping the game from interrupting your SPI access and begin doing its own. This is a two-edge blade. You can't assume SPI accesses are safe just by avoiding register clobbers.

Author:  Hextator [ Fri Jun 26, 2009 1:07 pm ]
Post subject: 

Good point. Threading is probably the biggest issue, though. Restoring states isn't really necessary because the game's functions likely make no assumptions and do all of the proper initializations for each access session.

But really, it doesn't matter if the game crashes, for the most part. As long as the DS to PC connection is intact, I don't give a damn what happens to the game. :P

I have some more questions, by the way. I ran the code you provided and it returned true. Shouldn't it return false if the media isn't EEPROM? I know for fact that it's Flash 512k, but I don't know if "FRAM" means that. I thought I saw "Flash" listed separately from FRAM, so it's probably not the same thing.

After that, I don't know what the commands are. I understand how to use the commands for 64k or less sized media, but I didn't see a command listing for 512k media. I suppose it's just 3 address bytes instead of 2 or 1? And what are the commands?

Edit: Looks like the commands are just 2 and 3, like for the other types. I'll fiddle with it while I await clarification lest I save us some time.

Edit, again: I finally found the notes I was looking for. They said that the firmware accessing commands should be similar for flash. I had looked at the flash commands and thought "these look like what I need, but it says it's for the firmware". I didn't realize it was for both.

Another edit: I found where the game uses the read command and was able to mimic it from start to finish to read the header of the save file.

Now I just need to do something similar for writing...

But I'm having a problem. When I run spi_close(), it doesn't seem to reset anything. I wonder if it's because I didn't do spi_open(0) again to set bit 6 back to true, signaling the end of the command...

Author:  Parasyte [ Fri Jun 26, 2009 4:51 pm ]
Post subject:  Re: Dumping GBA saves

Command 5 (sent in eeprom_active() is the "read status register" command:

#define BACKUP_CMD_WRSR      0x01
#define BACKUP_CMD_WRITE     0x02
#define BACKUP_CMD_READ      0x03
#define BACKUP_CMD_WRDI      0x04
#define BACKUP_CMD_RDSR      0x05
#define BACKUP_CMD_WREN      0x06

And here are the bits of the status register:
#define STATUS_BP1      0x08
#define STATUS_BP0      0x04
#define STATUS_WEL      0x02
#define STATUS_WIP      0x01

[Sorry, I don't recall the source for these. Definitely was some PDF datasheet that I downloaded from the grand internets, somewhere.]

That specific command works on the serial EEPROMs and serial flash ROMs just the same. The function tests the "Write in Progress" bit. If you're getting TRUE back, it's because you're only ever reading high-impedance (0xFF) on the bus. Or ... because there is an EEPROM/Flash write in progress... which would be unlikely in your situation.

Make sure your CPU has access to the card bus, since it sounds like it probably doesn't. (By default, ARM7 has access.)

The simplest way to give ARM9 the card access is (from ARM9):
*(vu32*)0x04000204 &= ~0x0800; // Card bus access = ARM9

Or, if you're running from ARM7, run (from ARM9):
*(vu32*)0x04000204 |= 0x0800; // Card bus access = ARM7

FRAM is Flash RAM (as opposed to Flash ROM). I know of no games in the wild that use it, but it is supported by the OS. (Of course, it has been a very long time since I've looked at any NDS games, so there's a chance that some have been introduced in the last several years.)

The basic idea to dumping a 4mbit flash ROM is:
spi_send(0x03); // BACKUP_CMD_READ
spi_send(0x00); // Address high
spi_send(0x00); // Address mid
spi_send(0x00); // Address low
for (i = 0; i < 0x80000; i++) { // 4Mbit (512Kbyte)
    next_byte = spi_read(); // Here it comes!

Writing flash is a whole lot more involved. You have to first erase the chip (or specific sectors/pages) then you can begin writing them. Here are the flash-specific commands for erasing:
#define FLASH_PAGE_WRITE      0x0A
#define FLASH_CHIP_ERASE      0xC7
#define FLASH_SECTOR_ERASE    0xD8
#define FLASH_PAGE_ERASE      0xDB

Author:  Hextator [ Fri Jun 26, 2009 7:15 pm ]
Post subject: 

See my edits.

spi_send(0x03); // BACKUP_CMD_READ
spi_send(0x00); // Address high
spi_send(0x00); // Address mid
spi_send(0x00); // Address low
for (i = 0; i < 0x80000; i++) { // 4Mbit (512Kbyte)
    next_byte = spi_read(); // Here it comes!

I watched the game do that, except it was spi_open(0) instead, and for (i = 0; i < 4; i++).

I think I can probably figure out writing. I mostly just found it annoying that nothing was resetting when I did spi_close(), but GBATEK indicates I need to reset bit 6 at 40001A0 to signal the end of the command, so it may not have been terminated correctly.

In the case of a full dump/write, though, you don't really need to reset the bus anyway. I was only trying to so that I could do more tests. :P

Edit: Setting bit 6 didn't help. :/

I can't figure out why it ignores the address parameter the second time I use command 3...

Edit: The save file only uses 0x2000 bytes. Reading starts outputting after only 2 address parameters are set, and a third address byte is ignored. Also, the endianness of the address parameters is backward. I had to give 0xA000 to read from 0x00A0. When I sent 0x00A0 it read from 0x0000, indicating mirroring.

Why doesn't this crap match the documentation?

Edit: I found another interesting tidbit on GBATEK.

Apparently you have to do spi_send(rand()) and then waitByLoop(12 + n) after calling spi_close() before the system is reset appropriately.

I tested this and confirmed that it works. You might want to write that down somewhere. :P

Author:  Parasyte [ Fri Jun 26, 2009 9:06 pm ]
Post subject:  Re: Dumping GBA saves

Now it sounds like it is *NOT* Flash, but 64Kbit EEPROM. If you were to send the write enable command (0x06), close spi, then send the write command (0x02), followed by two address bytes of 0x00, then push a random byte through spi_send() ... I bet you would read that exact random byte out after the next read from address 0x0000. Only EEPROM will act this way; Flash will either ignore the write command until the page is erased, or will only UNSET bits. Erasing is the only way to set bits in a flash ROM.

And what is this about sending a dummy byte after closing SPI access? I don't recall that being anywhere near remotely necessary. None of my SPI-access code does anything similar, and I have written a lot of it...

Also, how were you testing the endianness of the address? Recall that ARM runs in little endian on the NDS, so using a halfword (0xA000) will be physically stored in memory as 0x00,0xA0 -- writing this sequence as a stream of bytes is clearly not what was intended. The high bits are always pushed through SPI first (being a serial interface, and that the registers being written to are 8-bits, this is strictly necessary, functionally). In fact, when dealing with the 4Kbit EEPROM, the high bit (bit 8) of the address is set in bit 3 on the command. Thus, to write to address 0x0100 you do:
spi_send(0x02 | 0x08); // BACKUP_CMD_WRITE + address high bit
spi_send(0x00); // Address low byte

This is an actual snippet from the hardware/software project I briefly spoke of:
            WriteByteBACKUP(BACKUP_CMD_WRITE | ((i >> 5) & 0x08));   // Write Array & Address High
            WriteByteBACKUP(i);                              // Address Low

            // Write one page at a time
            for (j = 0; j < EEPROM_4_PAGE; j++) {

Finally, what "documentation" do you speak of? GBAtek? Are you fucking shitting me? You call that documentation? Do you realize it is written by a sorry sack of shit for whom I hold much contempt? His piss poor "documentation" led me to releasing my implementation of the NDS card encryption algorithms. Strangely enough, I didn't use any of that work for even the slightest reference, though in retrospect I imagine I did indirectly used the material as reference via third parties who got their information from it. Whatever the case, the best documentation you are going to find is in hardware datasheets and source code that isn't a by-product of Martin Korth.

Page 1 of 1 All times are UTC - 8 hours [ DST ]
Powered by phpBB® Forum Software © phpBB Group