diff --git a/arm7/include/gba.h b/arm7/include/gba.h new file mode 100644 index 0000000..8c1c47e --- /dev/null +++ b/arm7/include/gba.h @@ -0,0 +1,9 @@ +#define GBA_H +#ifdef GBA_H + +#include + +void readEeprom(u8 *dst, u32 src, u32 len); +void writeEeprom(u32 dst, u8 *src, u32 len); + +#endif // GBA_H diff --git a/arm7/source/gba.c b/arm7/source/gba.c new file mode 100644 index 0000000..1a44d3e --- /dev/null +++ b/arm7/source/gba.c @@ -0,0 +1,123 @@ +#include "gba.h" + +#include +#include +#include +#include + +#define EEPROM_ADDRESS (0x09FFFF00) +#define REG_EEPROM *(vu16 *)(EEPROM_ADDRESS) +#define DMA3_CR_H *(vu16 *)(0x040000DE) + +void EEPROM_SendPacket(u16 *packet, int size) +{ + REG_EXMEMSTAT = (REG_EXMEMSTAT & 0xFFE3) | 0x000C; + DMA3_SRC = (u32)packet; + DMA3_DEST = EEPROM_ADDRESS; + DMA3_CR = 0x80000000 + size; + while((DMA3_CR_H & 0x8000) != 0); +} + +void EEPROM_ReceivePacket(u16 *packet, int size) +{ + REG_EXMEMSTAT = (REG_EXMEMSTAT & 0xFFE3) | 0x000C; + DMA3_SRC = EEPROM_ADDRESS; + DMA3_DEST = (u32)packet; + DMA3_CR = 0x80000000 + size; + while((DMA3_CR_H & 0x8000) != 0); +} + +void gbaEepromRead8Bytes(u8 *out, u16 addr, bool short_addr) +{ + u16 packet[68]; + + memset(packet, 0, 68 * 2); + + // Read request + packet[0] = 1; + packet[1] = 1; + + // 6 or 14 bytes eeprom address (MSB first) + for(int i = 2, shift = (short_addr ? 5 : 13); i < (short_addr ? 8 : 16); i++, shift--) { + packet[i] = (addr >> shift) & 1; + } + + // End of request + packet[short_addr ? 8 : 16] = 0; + + // Do transfers + EEPROM_SendPacket(packet, short_addr ? 9 : 17); + memset(packet, 0, 68 * 2); + EEPROM_ReceivePacket(packet, 68); + + // Extract data + u16 *in_pos = &packet[4]; + for(int byte = 7; byte >= 0; --byte) { + u8 out_byte = 0; + for(int bit = 7; bit >= 0; --bit) { + // out_byte += (*in_pos++) << bit; + out_byte += ((*in_pos++) & 1) << bit; + } + *out++ = out_byte; + } +} + +void gbaEepromWrite8Bytes(u8 *in, u16 addr, bool short_addr) +{ + u16 packet_length = short_addr ? 73 : 81; + u16 packet[packet_length]; + + memset(packet, 0, packet_length * 2); + + // Write request + packet[0] = 1; + packet[1] = 0; + + // 6 or 14 bytes eeprom address (MSB first) + for(int i = 2, shift = (short_addr ? 5 : 13); i < (short_addr ? 8 : 16); i++, shift--) { + packet[i] = (addr >> shift) & 1; + } + + // Extract data + u16 *out_pos = &packet[short_addr ? 8 : 16]; + for(int byte = 7; byte >= 0; --byte) { + u8 in_byte = *in++; + for(int bit = 7; bit >= 0; --bit) { + *out_pos++ = (in_byte >> bit) & 1; + } + } + + // End of request + packet[packet_length - 1] = 0; + + // Do transfers + EEPROM_SendPacket(packet, packet_length); + + // Wait for EEPROM to finish (should timeout after 10 ms) + while((REG_EEPROM & 1) == 0); +} + +void readEeprom(u8 *dst, u32 src, u32 len) +{ + int start, end; + start = src >> 3; + end = (src + len) >> 3; + u8 buffer[8]; + for (int j = start; j < end; j++) { + gbaEepromRead8Bytes(buffer, j, len == 0x200); + fifoSendDatamsg(FIFO_USER_02, 8, buffer); + } +} + +void writeEeprom(u32 dst, u8 *src, u32 len) +{ + int start, end; + start = dst >> 3; + end = (dst + len) >> 3; + u8 *ptr = src; + for (int j = start; j < end; j++, ptr += 8) { + gbaEepromWrite8Bytes(ptr, j, len == 0x200); + } + + fifoSendValue32(FIFO_USER_02, 0x454E4F44 /* 'DONE' */); +} diff --git a/arm7/source/main.c b/arm7/source/main.c index 7656b83..16a3d09 100644 --- a/arm7/source/main.c +++ b/arm7/source/main.c @@ -30,6 +30,8 @@ #include #include +#include "gba.h" + void my_installSystemFIFO(void); void my_sdmmc_get_cid(int devicenumber, u32 *cid); @@ -177,6 +179,19 @@ int main() { } *(u8*)(0x2FFFD08) = ((*(vu32*)(0x400481C) & BIT(3)) || !(*(vu32*)(0x400481C) & BIT(5))); // Set if there's no SD inserted resyncClock(); + + // Dump EEPROM save + if(fifoCheckAddress(FIFO_USER_01)) { + switch(fifoGetValue32(FIFO_USER_01)) { + case 0x44414552: // 'READ' + readEeprom((u8 *)fifoGetAddress(FIFO_USER_01), fifoGetValue32(FIFO_USER_01), fifoGetValue32(FIFO_USER_01)); + break; + case 0x54495257: // 'WRITE' + writeEeprom(fifoGetValue32(FIFO_USER_01), (u8 *)fifoGetAddress(FIFO_USER_01), fifoGetValue32(FIFO_USER_01)); + break; + } + } + swiWaitForVBlank(); } return 0; diff --git a/arm9/source/dumpOperations.cpp b/arm9/source/dumpOperations.cpp index 504cebc..8fa1a9d 100644 --- a/arm9/source/dumpOperations.cpp +++ b/arm9/source/dumpOperations.cpp @@ -21,23 +21,10 @@ extern bool expansionPakFound; static sNDSHeaderExt ndsCardHeader; -void dumpFailMsg(bool save) { +void dumpFailMsg(const char *msg) { font->clear(false); - font->printf(0, 0, false, Alignment::left, Palette::white, "Failed to dump the %s.", save ? "save" : "ROM"); - font->update(false); - - for (int i = 0; i < 60*2; i++) { - swiWaitForVBlank(); - } -} - -void saveWriteFailMsg(bool gba) { - char sizeError[256]; - snprintf(sizeError, sizeof(sizeError), "The size of this save doesn't match the size of the inserted game %s.\n\nWrite cancelled!", gba ? "pak" : "card"); - - font->clear(false); - font->print(0, 0, false, sizeError, Alignment::left, Palette::red); - font->print(0, font->calcHeight(sizeError) + 1, false, "( OK)"); + font->print(0, 0, false, msg, Alignment::left, Palette::red); + font->print(0, font->calcHeight(msg) + 1, false, "( OK)"); font->update(false); u16 pressed; @@ -216,7 +203,7 @@ void ndsCardSaveDump(const char* filename) { u32 saveSize = cardNandGetSaveSize(); if(saveSize == 0) { - dumpFailMsg(true); + dumpFailMsg("Failed to dump the save."); return; } @@ -240,14 +227,14 @@ void ndsCardSaveDump(const char* filename) { cardRead(cardNandRwStart + src + i, copyBuf + i, true); } if (fwrite(copyBuf, 1, (currentSize >= 0x8000 ? 0x8000 : currentSize), destinationFile) < 1) { - dumpFailMsg(true); + dumpFailMsg("Failed to dump the save."); break; } currentSize -= 0x8000; } fclose(destinationFile); } else { - dumpFailMsg(true); + dumpFailMsg("Failed to dump the save."); } } else { // SPI unsigned char *buffer; @@ -311,11 +298,7 @@ void ndsCardSaveRestore(const char *filename) { u32 saveSize = cardNandGetSaveSize(); if(saveSize == 0) { - font->print(0, 0, false, "Unable to restore the save."); - font->update(false); - for (int i = 0; i < 60 * 2; i++) { - swiWaitForVBlank(); - } + dumpFailMsg("Unable to restore the save."); return; } @@ -327,7 +310,7 @@ void ndsCardSaveRestore(const char *filename) { if(length != saveSize) { fclose(in); - saveWriteFailMsg(false); + dumpFailMsg("The size of this save doesn't match the size of the inserted game card.\n\nWrite cancelled!"); return; } @@ -388,8 +371,7 @@ void ndsCardSaveRestore(const char *filename) { fseek(in, 0, SEEK_SET); if(length != (auxspi ? (int)(LEN * num_blocks) : size)) { fclose(in); - - saveWriteFailMsg(false); + dumpFailMsg("The size of this save doesn't match the size of the inserted game card.\n\nWrite cancelled!"); return; } @@ -478,12 +460,7 @@ void ndsCardDump(void) { } if (cardInit(&ndsCardHeader) != 0) { - font->clear(false); - font->print(0, 0, false, "Unable to dump the save."); - font->update(false); - for (int i = 0; i < 60 * 2; i++) { - swiWaitForVBlank(); - } + dumpFailMsg("Unable to dump the save."); return; } char gameTitle[13] = {0}; @@ -736,14 +713,14 @@ void ndsCardDump(void) { cardRead (src+i, copyBuf+i, false); } if (fwrite(copyBuf, 1, (currentSize>=0x8000 ? 0x8000 : currentSize), destinationFile) < 1) { - dumpFailMsg(false); + dumpFailMsg("Failed to dump the ROM."); break; } currentSize -= 0x8000; } fclose(destinationFile); } else { - dumpFailMsg(false); + dumpFailMsg("Failed to dump the ROM."); } ndsCardSaveDump(destSavPath); //} @@ -758,19 +735,6 @@ void gbaCartSaveDump(const char *filename) { font->update(false); saveTypeGBA type = gbaGetSaveType(); - - if(type == SAVE_GBA_EEPROM_05 || type == SAVE_GBA_EEPROM_8) { - font->clear(false); - font->print(0, 0, false, "EEPROM saves are not currently supported!\n\nUse GBA Backup Tool to backup/restore this game's save.\nhttps://gamebrew.org/wiki/GBA_Backup_Tool\n\n( OK)"); - font->update(false); - - do { - scanKeys(); - swiWaitForVBlank(); - } while(!(keysDown() & KEY_A)); - return; - } - u32 size = gbaGetSaveSize(type); if(size == 0) return; @@ -810,17 +774,8 @@ void gbaCartSaveRestore(const char *filename) { return; FILE *sourceFile = fopen(filename, "rb"); - u8 *buffer = new u8[size]; - if(!buffer || !sourceFile) { - if(buffer) delete[] buffer; - if(sourceFile) fclose(sourceFile); - - font->clear(false); - font->print(0, 0, false, "Failed to open save."); - font->update(false); - - for (int i = 0; i < 60 * 2; i++) - swiWaitForVBlank(); + if(!sourceFile) { + dumpFailMsg("Failed to open save."); return; } @@ -828,23 +783,18 @@ void gbaCartSaveRestore(const char *filename) { size_t length = ftell(sourceFile); fseek(sourceFile, 0, SEEK_SET); if(length != size) { - delete[] buffer; fclose(sourceFile); - saveWriteFailMsg(true); + dumpFailMsg("The size of this save doesn't match the size of the inserted game pak.\n\nWrite cancelled!"); return; } + u8 *buffer = new u8[size]; if (fread(buffer, 1, size, sourceFile) != size) { delete[] buffer; fclose(sourceFile); - font->clear(false); - font->print(0, 0, false, "Failed to read save."); - font->update(false); - - for (int i = 0; i < 60 * 2; i++) - swiWaitForVBlank(); + dumpFailMsg("Failed to read save."); return; } @@ -993,7 +943,7 @@ void gbaCartDump(void) { font->update(false); if (fwrite(GBAROM + src / sizeof(u16), 1, 0x8000, destinationFile) != 0x8000) { - dumpFailMsg(false); + dumpFailMsg("Failed to dump the ROM."); failed = true; break; } @@ -1028,14 +978,14 @@ void gbaCartDump(void) { writeChange(cmd); readChange(); if (fwrite(GBAROM + (0x1000 >> 1), 0x1000, 1, destinationFile) < 1) { - dumpFailMsg(false); + dumpFailMsg("Failed to dump the ROM."); break; } } } fclose(destinationFile); } else { - dumpFailMsg(false); + dumpFailMsg("Failed to dump the ROM."); return; } } diff --git a/arm9/source/gba.cpp b/arm9/source/gba.cpp index c3b7875..1ec8762 100644 --- a/arm9/source/gba.cpp +++ b/arm9/source/gba.cpp @@ -41,11 +41,6 @@ #include #include "gba.h" -// #include "dsCard.h" - -// #include "display.h" -// #include "globals.h" -// #include "strings.h" inline u32 min(u32 i, u32 j) { return (i < j) ? i : j;} inline u32 max(u32 i, u32 j) { return (i > j) ? i : j;} @@ -59,9 +54,6 @@ inline u32 max(u32 i, u32 j) { return (i > j) ? i : j;} #define MAGIC_H1M_ 0x5f4d3148 -#define EEPROM_ADDRESS (0x0DFFFF00) -#define REG_EEPROM *(vu16 *)(EEPROM_ADDRESS) - // ----------------------------------------------------------- bool gbaIsGame() @@ -77,94 +69,40 @@ bool gbaIsGame() return false; } -void EEPROM_SendPacket(u16 *packet, int size) -{ - REG_EXMEMCNT = (REG_EXMEMCNT & 0xFFE3) | 0x000C; - DMA3_SRC = (u32)packet; - DMA3_DEST = EEPROM_ADDRESS; - DMA3_CR = 0x80000000 + size; - while((DMA3_CR & 0x80000000) != 0); -} +void readEeprom(u8 *dst, u32 src, u32 len) { + // EEPROM reading needs to happen on ARM7 + sysSetCartOwner(BUS_OWNER_ARM7); + fifoSendValue32(FIFO_USER_01, 0x44414552 /* 'READ' */); + fifoSendAddress(FIFO_USER_01, dst); + fifoSendValue32(FIFO_USER_01, src); + fifoSendValue32(FIFO_USER_01, len); -void EEPROM_ReceivePacket(u16 *packet, int size) -{ - REG_EXMEMCNT = (REG_EXMEMCNT & 0xFFE3) | 0x000C; - DMA3_SRC = EEPROM_ADDRESS; - DMA3_DEST = (u32)packet; - DMA3_CR = 0x80000000 + size; - while((DMA3_CR & 0x80000000) != 0); -} - -// local function -void gbaEepromRead8Bytes(u8 *out, u16 addr, bool short_addr) -{ - u16 packet[68]; - - memset(packet, 0, 68 * 2); - - // Read request - packet[0] = 1; - packet[1] = 1; - - // 6 or 14 bytes eeprom address (MSB first) - for(int i = 2, shift = (short_addr ? 5 : 13); i < (short_addr ? 8 : 16); i++, shift--) { - packet[i] = (addr >> shift) & 1; - } - - // End of request - packet[short_addr ? 8 : 16] = 0; - - // Do transfers - EEPROM_SendPacket(packet, short_addr ? 9 : 17); - memset(packet, 0, 68 * 2); - EEPROM_ReceivePacket(packet, 68); - - // Extract data - u16 *in_pos = &packet[4]; - for(int byte = 7; byte >= 0; --byte) { - u8 out_byte = 0; - for(int bit = 7; bit >= 0; --bit) { - // out_byte += (*in_pos++) << bit; - out_byte += ((*in_pos++) & 1) << bit; - } - *out++ = out_byte; - } -} - -// local function -void gbaEepromWrite8Bytes(u8 *in, u16 addr, bool short_addr = false) -{ - u16 packet_length = short_addr ? 73 : 81; - u16 packet[packet_length]; - - memset( packet, 0, packet_length * 2); - - // Write request - packet[0] = 1; - packet[1] = 0; - - // 6 or 14 bytes eeprom address (MSB first) - for(int i = 2, shift = (short_addr ? 5 : 13); i < (short_addr ? 8 : 16); i++, shift--) { - packet[i] = (addr >> shift) & 1; - } - - // Extract data - u16 *out_pos = &packet[short_addr ? 8 : 16]; - for(int byte = 7; byte >= 0; --byte) { - u8 in_byte = *in++; - for(int bit = 7; bit >= 0; --bit) { - *out_pos++ = (in_byte >> bit) & 1; + // Read the data from FIFO + u8 *ptr = dst; + while(ptr < dst + len) { + if(fifoCheckDatamsg(FIFO_USER_02)) { + fifoGetDatamsg(FIFO_USER_02, 8, ptr); + ptr += 8; } } - // End of request - packet[packet_length - 1] = 0; + sysSetCartOwner(BUS_OWNER_ARM9); +} - // Do transfers - EEPROM_SendPacket(packet, packet_length); +void writeEeprom(u32 dst, u8 *src, u32 len) { + // EEPROM writing needs to happen on ARM7 + sysSetCartOwner(BUS_OWNER_ARM7); + fifoSendValue32(FIFO_USER_01, 0x54495257 /* 'WRIT' */); + fifoSendValue32(FIFO_USER_01, dst); + fifoSendAddress(FIFO_USER_01, src); + fifoSendValue32(FIFO_USER_01, len); - // Wait for EEPROM to finish (should timeout after 10 ms) - while((REG_EEPROM & 1) == 0); + // Wait for it to finish + while(!(fifoCheckValue32(FIFO_USER_02) && fifoGetValue32(FIFO_USER_02) == 0x454E4F44 /* 'DONE' */)) { + swiWaitForVBlank(); + } + + sysSetCartOwner(BUS_OWNER_ARM9); } saveTypeGBA gbaGetSaveType() { @@ -173,19 +111,17 @@ saveTypeGBA gbaGetSaveType() { for (int i = 0; i < (0x02000000 >> 2); i++, data++) { if (*data == MAGIC_EEPR) { - u8 *buf = new u8[0x2000]; - u8 *ptr = buf; - for (int j = 0; j < 0x400; j++, ptr += 8) { - gbaEepromRead8Bytes(ptr, j, false); - for(int sleep=0;sleep<512000;sleep++); - } + u8 *buffer = new u8[0x2000]; + readEeprom(buffer, 0, 0x2000); + + // Check if first 0x800 bytes are duplicates of the first 8 for(int j = 8; j < 0x800; j += 8) { - if(memcmp(buf, buf + j, 8) != 0) { - delete[] buf; + if(memcmp(buffer, buffer + j, 8) != 0) { + delete[] buffer; return SAVE_GBA_EEPROM_8; } } - delete[] buf; + delete[] buffer; return SAVE_GBA_EEPROM_05; } else if (*data == MAGIC_SRAM) { // *always* 32 kB @@ -236,21 +172,11 @@ uint32 gbaGetSaveSize(saveTypeGBA type) bool gbaReadSave(u8 *dst, u32 src, u32 len, saveTypeGBA type) { int nbanks = 2; // for type 4,5 - bool eeprom_long = true; switch (type) { - case SAVE_GBA_EEPROM_05: { - eeprom_long = false; - } + case SAVE_GBA_EEPROM_05: case SAVE_GBA_EEPROM_8: { - int start, end; - start = src >> 3; - end = (src + len) >> 3; - u8 *ptr = dst; - for (int j = start; j < end; j++, ptr += 8) { - gbaEepromRead8Bytes(ptr, j, !eeprom_long); - for(int sleep=0;sleep<512000;sleep++); - } + readEeprom(dst, src, len); break; } case SAVE_GBA_SRAM_32: { @@ -327,22 +253,11 @@ bool gbaIsAtmel() bool gbaWriteSave(u32 dst, u8 *src, u32 len, saveTypeGBA type) { int nbanks = 2; // for type 4,5 - bool eeprom_long = true; switch (type) { - case SAVE_GBA_EEPROM_05: { - eeprom_long = false; - } + case SAVE_GBA_EEPROM_05: case SAVE_GBA_EEPROM_8: { - /* - int start, end; - start = dst >> 3; - end = (dst + len) >> 3; - u8 *ptr = src; - for (int j = start; j < end; j++, ptr+=8) { - gbaEepromWrite8Bytes(ptr, j, eeprom_long); - } - */ + writeEeprom(dst, src, len); break; } case SAVE_GBA_SRAM_32: { @@ -432,7 +347,7 @@ bool gbaFormatSave(saveTypeGBA type) switch (type) { case SAVE_GBA_EEPROM_05: case SAVE_GBA_EEPROM_8: - // TODO: eeprom is not supported yet + // EEPROM doesn't need erasing break; case SAVE_GBA_SRAM_32: { diff --git a/arm9/source/gba.h b/arm9/source/gba.h index 469973e..55a7566 100644 --- a/arm9/source/gba.h +++ b/arm9/source/gba.h @@ -1,7 +1,7 @@ /* * savegame_manager: a tool to backup and restore savegames from Nintendo * DS cartridges. Nintendo DS and all derivative names are trademarks - * by Nintendo. EZFlash 3-in-1 is a trademark by EZFlash. + * by Nintendo. * * gba.h: header file for gba.cpp * @@ -46,4 +46,4 @@ bool gbaWriteSave(u32 dst, u8 *src, u32 len, saveTypeGBA type); bool gbaFormatSave(saveTypeGBA type); -#endif // __SLOT2_H__ \ No newline at end of file +#endif // __GBA_H__ \ No newline at end of file