Kodewerx https://www.kodewerx.org/forum/ |
|
Dumping GBA saves https://www.kodewerx.org/forum/viewtopic.php?f=11&t=6843 |
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: Code: ////////////////////////////// 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... Code: 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 Commands 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. spi.h: Code: #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__ spi.c: Code: #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; spi_wait(); return *(vu16*)0x040001A2; } void inline spi_send(u8 data) { spi_exch(data); } u8 inline spi_recv() { return spi_exch(0xFF); } void inline spi_close() { *(vu16*)0x040001A0 = 0x0000; } u8 inline eeprom_active() { u8 active; spi_open(3); spi_send(0x05); active = (spi_recv() & 0x01); spi_close(); 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. 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: Code: #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: Code: #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): Code: *(vu32*)0x04000204 &= ~0x0800; // Card bus access = ARM9 Or, if you're running from ARM7, run (from ARM9): Code: *(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: Code: spi_open(3); 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! } spi_close(); 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: Code: #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. Code: spi_open(3); 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! } spi_close(); 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. 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. |
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: Code: 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: Code: 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++) { WriteByteBACKUP(fgetc(fin)); } 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 https://www.phpbb.com/ |