From a8c03520c8d906aa47b4b69cd684bb1be294c3fb Mon Sep 17 00:00:00 2001 From: sanni Date: Mon, 12 Sep 2016 15:16:03 +0200 Subject: [PATCH] V19: Add GBA rom dumping (beta) Seems to work fine with the Arduino running on 5V. Still a few read errors on 3.3V. Probably timing related. --- Cart_Reader/Cart_Reader.ino | 58 +- Cart_Reader/FLASH.ino | 27 +- Cart_Reader/GB.ino | 669 ++++++++++++++++++- Cart_Reader/GBA.ino | 413 ++++++++++++ Cart_Reader/N64.ino | 2 - Cart_Reader/NP.ino | 16 +- Cart_Reader/SNES.ino | 1243 +++++++++++++++++++++++++++++++++++ 7 files changed, 2392 insertions(+), 36 deletions(-) create mode 100644 Cart_Reader/GBA.ino create mode 100644 Cart_Reader/SNES.ino diff --git a/Cart_Reader/Cart_Reader.ino b/Cart_Reader/Cart_Reader.ino index 617eb64..5cf4fcd 100644 --- a/Cart_Reader/Cart_Reader.ino +++ b/Cart_Reader/Cart_Reader.ino @@ -2,8 +2,8 @@ Cartridge Reader for Arduino Mega2560 Author: sanni - Date: 2016-09-04 - Version: V18B + Date: 2016-09-12 + Version: V19 SD lib: https://github.com/greiman/SdFat LCD lib: https://github.com/adafruit/Adafruit_SSD1306 @@ -17,7 +17,7 @@ Jeff Saltzman - 4-Way Button Wayne and Layne - Video-Game-Shield menu skaman - SNES enhancements and SA1 sram support - nocash - Nintendo Power commands + nocash - Nintendo Power commands and lots of other info crazynation - N64 bus timing hkz/themanbehindthecurtain - N64 flashram commands jago85 - help with N64 stuff @@ -29,9 +29,11 @@ Snes9x - SuperFX sram fix zzattack - multigame pcb fix Pickle - SDD1 fix + insidegadgets - GBCartRead + lukeskaff - Nintendo DS GBA slot timing **********************************************************************************/ -char ver[5] = "V18B"; +char ver[5] = "V19"; /****************************************** Define Output @@ -44,8 +46,8 @@ char ver[5] = "V18B"; /****************************************** Define Input ******************************************/ -// If you have two buttons on your cart reader change to 1 -#define enable_Button2 0 +// If you have two buttons on your cart reader you can remove the // +//#define enable_Button2 /****************************************** Pinout @@ -114,6 +116,7 @@ SdFile myFile; #define mode_GB 6 #define mode_FLASH8 7 #define mode_FLASH16 8 +#define mode_GBA 9 /****************************************** Variables @@ -175,6 +178,8 @@ byte numBanks = 128; char checksumStr[5]; bool errorLvl = 0; byte romVersion = 0; +char cartID[5]; +unsigned long cartSize; // Variable to count errors unsigned long writeErrors; @@ -322,6 +327,11 @@ const char flashMenuItem1[] PROGMEM = "8bit slot"; const char flashMenuItem2[] PROGMEM = "16bit slot"; const char* const menuOptionsFlash[] PROGMEM = {flashMenuItem1, flashMenuItem2}; +// GBx Submenu +const char gbxMenuItem1[] PROGMEM = "Game Boy Color"; +const char gbxMenuItem2[] PROGMEM = "Game Boy Advance"; +const char* const menuOptionsGBx[] PROGMEM = {gbxMenuItem1, gbxMenuItem2}; + void mainMenu() { // create menu with title and 6 options to choose from unsigned char modeMenu; @@ -373,10 +383,29 @@ void mainMenu() { break; case 3: - display_Clear(); - display_Update(); - setup_GB(); - mode = mode_GB; + // create menu with title and 2 options to choose from + unsigned char gbType; + // Copy menuOptions out of progmem + convertPgm(menuOptionsGBx, 2); + gbType = question_box("Select Game Boy", menuOptions, 2, 0); + + // wait for user choice to come back from the question box menu + switch (gbType) + { + case 0: + display_Clear(); + display_Update(); + setup_GB(); + mode = mode_GB; + break; + + case 1: + display_Clear(); + display_Update(); + setup_GBA(); + mode = mode_GBA; + break; + } break; case 4: @@ -725,10 +754,14 @@ void rgbLed(byte Color) { *****************************************/ // Read button state int checkButton() { - if ((enable_Button2) && (checkButton2() != 0)) +#ifdef enable_Button2 + if (checkButton2() != 0) return 3; else return (checkButton1()); +#else + return (checkButton1()); +#endif } // Read button 1 @@ -1203,6 +1236,9 @@ void loop() { else if (mode == mode_GB) { gbMenu(); } + else if (mode == mode_GBA) { + gbaMenu(); + } else if (mode == mode_NPFlash) { NPFlashMenu(); } diff --git a/Cart_Reader/FLASH.ino b/Cart_Reader/FLASH.ino index 2807693..27fe483 100644 --- a/Cart_Reader/FLASH.ino +++ b/Cart_Reader/FLASH.ino @@ -348,11 +348,6 @@ void setup_Flash16() { // Set Control Pins to Output OE(PH1) BYTE(PH3) WE(PH4) CE(PH6) DDRH |= (1 << 1) | (1 << 3) | (1 << 4) | (1 << 6); - // Setting OE(PH1) BYTE(PH3) WE(PH4) HIGH - PORTH |= (1 << 1) | (1 << 3) | (1 << 4); - // Setting CE(PH6) LOW - PORTH &= ~(1 << 6); - // Set Data Pins (D0-D15) to Input DDRC = 0x00; DDRA = 0x00; @@ -360,6 +355,13 @@ void setup_Flash16() { PORTC = 0x00; PORTA = 0x00; + // Setting OE(PH1) BYTE(PH3) WE(PH4) HIGH + PORTH |= (1 << 1) | (1 << 3) | (1 << 4); + // Setting CE(PH6) LOW + PORTH &= ~(1 << 6); + + delay(10); + // ID flash idFlash16(); resetFlash16(); @@ -407,7 +409,6 @@ void dataOut16() { // Switch data pins to read void dataIn16() { - // Set to Input and activate pull-up resistors DDRC = 0x00; DDRA = 0x00; } @@ -482,7 +483,7 @@ void writeWord_Flash(unsigned long myAddress, word myData) { PORTH |= (1 << 4); // Leave WE high for at least 50ns - __asm__("nop\n\t"); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); } word readWord_Flash(unsigned long myAddress) { @@ -501,9 +502,11 @@ word readWord_Flash(unsigned long myAddress) { // Read word tempWord = ( ( PINA & 0xFF ) << 8 ) | ( PINC & 0xFF ); + __asm__("nop\n\t"); + // Setting OE(PH1) HIGH PORTH |= (1 << 1); - __asm__("nop\n\t"); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); return tempWord; } @@ -587,7 +590,7 @@ void writeFlash29F032() { if (currByte % 2048 == 0) PORTB ^= (1 << 4); - for (unsigned long c = 0; c < 512; c++) { + for (int c = 0; c < 512; c++) { // Write command sequence writeByte_Flash(0x555, 0xaa); writeByte_Flash(0x2aa, 0x55); @@ -792,7 +795,7 @@ void verifyFlash() { for (unsigned long currByte = 0; currByte < flashSize; currByte += 512) { //fill sdBuffer myFile.read(sdBuffer, 512); - for (unsigned long c = 0; c < 512; c++) { + for (int c = 0; c < 512; c++) { if (readByte_Flash(currByte + c) != sdBuffer[c]) { blank++; } @@ -842,7 +845,7 @@ void readFlash() { print_Error(F("Can't create file on SD"), true); } for (unsigned long currByte = 0; currByte < flashSize; currByte += 512) { - for (unsigned long c = 0; c < 512; c++) { + for (int c = 0; c < 512; c++) { sdBuffer[c] = readByte_Flash(currByte + c); } myFile.write(sdBuffer, 512); @@ -1023,7 +1026,7 @@ void verifyFlash16() { for (unsigned long currByte = 0; currByte < flashSize / 2; currByte += 256) { //fill sdBuffer myFile.read(sdBuffer, 512); - for (unsigned long c = 0; c < 256; c++) { + for (int c = 0; c < 256; c++) { word currWord = ((sdBuffer[d] << 8) | sdBuffer[d + 1]); // Swap bytes in word currWord = ((currWord >> 8) & 0x00FF00FF) | ((currWord & 0x00FF00FF) << 8); diff --git a/Cart_Reader/GB.ino b/Cart_Reader/GB.ino index 5a1e5ae..ab291f8 100644 --- a/Cart_Reader/GB.ino +++ b/Cart_Reader/GB.ino @@ -1,23 +1,686 @@ //****************************************** -// GAMEBOY +// GAME BOY //****************************************** +/****************************************** + Variables + *****************************************/ +// Game Boy +int sramBanks; + /****************************************** Menu *****************************************/ +// GB menu items +const char GBMenuItem1[] PROGMEM = "Read Rom"; +const char GBMenuItem2[] PROGMEM = "Read Save"; +const char GBMenuItem3[] PROGMEM = "Write Save"; +const char GBMenuItem4[] PROGMEM = "Reset"; +const char* const menuOptionsGB[] PROGMEM = {GBMenuItem1, GBMenuItem2, GBMenuItem3, GBMenuItem4}; void gbMenu() { - println_Msg(F("WORK IN PROGRESS")); + // Output a high signal on CS(PH3) WR(PH5) RD(PH6) + PORTH |= (1 << 3) | (1 << 5) | (1 << 6); + + // create menu with title and 4 options to choose from + unsigned char mainMenu; + // Copy menuOptions of of progmem + convertPgm(menuOptionsGB, 4); + mainMenu = question_box("GB Cart Reader", menuOptions, 4, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + case 0: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readROM_GB(); + compare_checksum_GB(); + break; + + case 1: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readSRAM_GB(); + break; + + case 2: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + writeSRAM_GB(); + unsigned long wrErrors; + wrErrors = verifySRAM_GB(); + if (wrErrors == 0) { + println_Msg(F("Verified OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(wrErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + break; + + case 3: + asm volatile (" jmp 0"); + break; + } + println_Msg(F("")); println_Msg(F("Press Button...")); display_Update(); wait(); - asm volatile (" jmp 0"); } /****************************************** Setup *****************************************/ void setup_GB() { + // Set RST(PH0) to Input + DDRH &= ~(1 << 0); + // Activate Internal Pullup Resistors + PORTH |= (1 << 0); + + // Set Address Pins to Output + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + + // Set Control Pins to Output CS(PH3) WR(PH5) RD(PH6) + DDRH |= (1 << 3) | (1 << 5) | (1 << 6); + // Output a high signal on all pins, pins are active low therefore everything is disabled now + PORTH |= (1 << 3) | (1 << 5) | (1 << 6); + + // Set Data Pins (D0-D7) to Input + DDRC = 0x00; + // Enable Internal Pullups + //PORTC = 0xFF; + + // Print start page + getCartInfo_GB(); + display_Clear(); + if (strcmp(checksumStr, "00") != 0) { + println_Msg(F("GB Cart Info")); + print_Msg(F("Rom Name: ")); + println_Msg(romName); + print_Msg(F("Rom Type: ")); + switch (romType) { + case 0: print_Msg(F("ROM ONLY")); break; + case 1: print_Msg(F("MBC1")); break; + case 2: print_Msg(F("MBC1+RAM")); break; + case 3: print_Msg(F("MBC1+RAM")); break; + case 5: print_Msg(F("MBC2")); break; + case 6: print_Msg(F("MBC2")); break; + case 8: print_Msg(F("ROM+RAM")); break; + case 9: print_Msg(F("ROM ONLY")); break; + case 11: print_Msg(F("MMM01")); break; + case 12: print_Msg(F("MMM01+RAM")); break; + case 13: print_Msg(F("MMM01+RAM")); break; + case 15: print_Msg(F("MBC3+TIMER")); break; + case 16: print_Msg(F("MBC3+TIMER+RAM")); break; + case 17: print_Msg(F("MBC3")); break; + case 18: print_Msg(F("MBC3+RAM")); break; + case 19: print_Msg(F("MBC3+RAM")); break; + case 21: print_Msg(F("MBC4")); break; + case 22: print_Msg(F("MBC4+RAM")); break; + case 23: print_Msg(F("MBC4+RAM")); break; + case 25: print_Msg(F("MBC5")); break; + case 26: print_Msg(F("MBC5+RAM")); break; + case 27: print_Msg(F("MBC5+RAM")); break; + case 28: print_Msg(F("MBC5+RUMBLE")); break; + case 29: print_Msg(F("MBC5+RUMBLE+RAM")); break; + case 30: print_Msg(F("MBC5+RUMBLE+RAM")); break; + case 252: print_Msg(F("Gameboy Camera")); break; + default: print_Msg(F("Not found")); + } + println_Msg(F(" ")); + print_Msg(F("Rom Size: ")); + switch (romSize) { + case 0: + print_Msg(F("32KByte")); + break; + case 1: + print_Msg(F("64KByte")); + break; + case 2: + print_Msg(F("128KByte")); + break; + case 3: + print_Msg(F("256KByte")); + break; + case 4: + print_Msg(F("512KByte")); + break; + case 5: + if (romType == 1 || romType == 2 || romType == 3) { + print_Msg(F("1MByte")); + } + else { + print_Msg(F("1MByte")); + } + break; + case 6: + if (romType == 1 || romType == 2 || romType == 3) { + print_Msg(F("2MByte")); + } + else { + print_Msg(F("2MByte")); + } + break; + case 7: + print_Msg(F("4MByte")); + break; + case 82: + print_Msg(F("1.1MByten")); + break; + case 83: + print_Msg(F("1.2MByte")); + break; + case 84: + print_Msg(F("1.5MByte")); + break; + default: + print_Msg(F("Not found")); + } + println_Msg(F("")); + print_Msg(F("Banks: ")); + println_Msg(numBanks); + + print_Msg(F("Sram Size: ")); + switch (sramSize) { + case 0: + if (romType == 6) { + print_Msg(F("512 bytes")); + } + else { + print_Msg(F("None")); + } + break; + case 1: + print_Msg(F("2 KBytes")); + break; + case 2: + print_Msg(F("8 KBytes")); + break; + case 3: + print_Msg(F("32 KBytes")); + break; + case 4: + print_Msg(F("128 KBytes")); + break; + default: + print_Msg(F("Not found")); + } + println_Msg(F("")); + print_Msg(F("Checksum: ")); + println_Msg(checksumStr); + display_Update(); + + // Wait for user input + println_Msg(F("Press Button...")); + display_Update(); + wait(); + } + else { + print_Error(F("GAMEPAK ERROR"), true); + } +} + +/****************************************** + I/O Functions + *****************************************/ + +/****************************************** + Low level functions +*****************************************/ +byte readByte_GB(word myAddress) { + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Switch CS(PH3) and RD(PH6) to LOW + PORTH &= ~((1 << 3) | (1 << 6)); + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Read + byte tempByte = PINC; + + // Switch CS(PH3) and RD(PH6) to HIGH + PORTH |= (1 << 3) | (1 << 6); + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + return tempByte; +} + +void writeByte_GB(int myAddress, uint8_t myData) { + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + PORTC = myData; + + // Arduino running at 16Mhz -> one nop = 62.5ns + // Wait till output is stable + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Pull WR(PH5) low + PORTH &= ~(1 << 5); + + // Leave WE low for at least 60ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Pull WR(PH5) HIGH + PORTH |= (1 << 5); + + // Leave WE high for at least 50ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); +} + +/****************************************** + Game Boy functions +*****************************************/ +// Read Cartridge Header +void getCartInfo_GB() { + // Dump name into 8.3 compatible format + byte myByte = 0; + byte myLength = 0; + + for (int addr = 0x0134; addr <= 0x13C; addr++) { + myByte = readByte_GB(addr); + if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 8) { + romName[myLength] = char(myByte); + myLength++; + } + } + // Get Checksum as string + sprintf(checksumStr, "%02X%02X", readByte_GB(0x014E), readByte_GB(0x014F)); + romType = readByte_GB(0x0147); + romSize = readByte_GB(0x0148); + sramSize = readByte_GB(0x0149); + numBanks = 2; // Default 32K + if (romSize == 1) { + numBanks = 4; + } + if (romSize == 2) { + numBanks = 8; + } + if (romSize == 3) { + numBanks = 16; + } + if (romSize == 4) { + numBanks = 32; + } + if (romSize == 5 && (romType == 1 || romType == 2 || romType == 3)) { + numBanks = 63; + } + else if (romSize == 5) { + numBanks = 64; + } + if (romSize == 6 && (romType == 1 || romType == 2 || romType == 3)) { + numBanks = 125; + } + else if (romSize == 6) { + numBanks = 128; + } + if (romSize == 7) { + numBanks = 255; + } + if (romSize == 82) { + numBanks = 72; + } + if (romSize == 83) { + numBanks = 80; + } + if (romSize == 84) { + numBanks = 96; + } + sramBanks = 1; // Default 8K RAM + if (sramSize == 3) { + sramBanks = 4; + } + if (sramSize == 4) { + sramBanks = 16; // GB Camera + } +} + +// Dump ROM +void readROM_GB() { + // Get name, add extension and convert to char array for sd lib + char fileName[26]; + strcpy(fileName, romName); + strcat(fileName, ".GB"); + + // create a new folder for the rom file + EEPROM_readAnything(0, foldern); + sprintf(folder, "ROM/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + //clear the screen + display_Clear(); + println_Msg(F("Creating folder: ")); + println_Msg(folder); + display_Update(); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(0, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("Can't create file on SD"), true); + } + + unsigned int addr = 0; + + // Read x number of banks + for (int y = 1; y < numBanks; y++) { + dataOut(); + // Set ROM bank + writeByte_GB(0x2100, y); + dataIn(); + if (y > 1) { + addr = 0x4000; + } + for (; addr <= 0x7FFF; addr = addr + 512) { + uint8_t readData[512]; + for (int i = 0; i < 512; i++) { + readData[i] = readByte_GB(addr + i); + } + myFile.write(readData, 512); + } + } + // Close the file: + myFile.close(); + + // Signal end of process + print_Msg(F("Saved as ")); + println_Msg(fileName); +} + +unsigned int calc_checksum_GB (char* fileName, char* folder) { + unsigned int calcChecksum = 0; + int calcFilesize = 0; + unsigned long i = 0; + int c = 0; + + if (strcmp(folder, "root") != 0) + sd.chdir(folder); + + // If file exists + if (myFile.open(fileName, O_READ)) { + calcFilesize = myFile.fileSize() * 8 / 1024 / 1024; + for (i = 0; i < (myFile.fileSize() / 512); i++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksum += sdBuffer[c]; + } + } + myFile.close(); + sd.chdir(); + // Subtract checksum bytes + calcChecksum -= readByte_GB(0x014E); + calcChecksum -= readByte_GB(0x014F); + + // Return result + return (calcChecksum); + } + // Else show error + else { + print_Error(F("DUMP ROM 1ST"), false); + return 0; + } +} + +boolean compare_checksum_GB() { + + println_Msg(F("Calculating Checksum")); + display_Update(); + + char fileName[26]; + strcpy(fileName, romName); + strcat(fileName, ".GB"); + + // last used rom folder + EEPROM_readAnything(0, foldern); + sprintf(folder, "ROM/%s/%d", romName, foldern - 1); + + char calcsumStr[5]; + sprintf(calcsumStr, "%04X", calc_checksum_GB(fileName, folder)); + + if (strcmp(calcsumStr, checksumStr) == 0) { + print_Msg(F("Result: ")); + println_Msg(calcsumStr); + println_Msg(F("Checksum matches")); + display_Update(); + return 1; + } + else { + print_Msg(F("Result: ")); + println_Msg(calcsumStr); + print_Error(F("Checksum Error"), false); + return 0; + } +} + +// Read RAM +void readSRAM_GB() { + + // Get name, add extension and convert to char array for sd lib + char fileName[26]; + strcpy(fileName, romName); + strcat(fileName, ".sav"); + + // create a new folder for the save file + EEPROM_readAnything(0, foldern); + sprintf(folder, "SAVE/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(0, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + + // MBC2 Fix (unknown why this fixes it, maybe has to read ROM before RAM?) + readByte_GB(0x0134); + + unsigned int addr = 0; + unsigned int endaddr = 0; + if (romType == 6 && sramSize == 0) { + endaddr = 0xA1FF; // MBC2 512bytes (nibbles) + } + if (sramSize == 1) { + endaddr = 0xA7FF; // 2K RAM + } + if (sramSize > 1) { + endaddr = 0xBFFF; // 8K RAM + } + + // Does cartridge have RAM + if (endaddr > 0) { + dataOut(); + // Initialise MBC + writeByte_GB(0x0000, 0x0A); + + // Switch RAM banks + for (int bank = 0; bank < sramBanks; bank++) { + dataOut(); + writeByte_GB(0x4000, bank); + + // Read RAM + dataIn(); + for (addr = 0xA000; addr <= endaddr; addr = addr + 64) { + uint8_t readData[64]; + for (int i = 0; i < 64; i++) { + readData[i] = readByte_GB(addr + i); + } + myFile.write(readData, 64); + } + } + dataOut(); + // Disable RAM + writeByte_GB(0x0000, 0x00); + dataIn(); + } + else { + print_Error(F("Cart has no SRAM"), false); + } + // Close the file: + myFile.close(); + + // Signal end of process + print_Msg(F("Saved to SAVE/")); + print_Msg(romName); + print_Msg(F("/")); + print_Msg(foldern - 1); + print_Msg(F("/")); + println_Msg(fileName); + display_Update(); +} + +// Write RAM +void writeSRAM_GB() { + + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser("Select sav file"); + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + + //open file on sd card + if (myFile.open(filePath, O_READ)) { + // MBC2 Fix (unknown why this fixes it, maybe has to read ROM before RAM?) + readByte_GB(0x0134); + unsigned int addr = 0; + unsigned int endaddr = 0; + if (romType == 6 && sramSize == 0) { + endaddr = 0xA1FF; // MBC2 512bytes (nibbles) + } + if (sramSize == 1) { + endaddr = 0xA7FF; // 2K RAM + } + if (sramSize > 1) { + endaddr = 0xBFFF; // 8K RAM + } + + // Does cartridge have RAM + if (endaddr > 0) { + dataOut(); + // Initialise MBC + writeByte_GB(0x0000, 0x0A); + + // Switch RAM banks + for (int bank = 0; bank < sramBanks; bank++) { + writeByte_GB(0x4000, bank); + + // Write RAM + for (addr = 0xA000; addr <= endaddr; addr = addr + 64) { + + // Wait for serial input + for (uint8_t i = 0; i < 64; i++) { + // Pull CS(PH3) LOW + PORTH &= ~(1 << 3); + // Write to RAM + writeByte_GB(addr + i, myFile.read()); + asm volatile("nop"); + asm volatile("nop"); + asm volatile("nop"); + // Pull CS(PH3) HIGH + PORTH |= (1 << 3) ; + } + } + } + + // Disable RAM + writeByte_GB(0x0000, 0x00); + Serial.flush(); // Flush any serial data that wasn't processed + } + else { + print_Error(F("Cart has no SRAM"), false); + } + // Set pins to input + dataIn(); + + // Close the file: + myFile.close(); + display_Clear(); + println_Msg(F("SRAM writing finished")); + display_Update(); + } + else { + print_Error(F("File doesnt exist"), false); + } +} + +// Check if the SRAM was written without any error +unsigned long verifySRAM_GB() { + + //open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Variable for errors + writeErrors = 0; + + // MBC2 Fix (unknown why this fixes it, maybe has to read ROM before RAM?) + readByte_GB(0x0134); + + unsigned int addr = 0; + unsigned int endaddr = 0; + if (romType == 6 && sramSize == 0) { + endaddr = 0xA1FF; // MBC2 512bytes (nibbles) + } + if (sramSize == 1) { + endaddr = 0xA7FF; // 2K RAM + } + if (sramSize > 1) { + endaddr = 0xBFFF; // 8K RAM + } + + // Does cartridge have RAM + if (endaddr > 0) { + dataOut(); + // Initialise MBC + writeByte_GB(0x0000, 0x0A); + + // Switch RAM banks + for (int bank = 0; bank < sramBanks; bank++) { + dataOut(); + writeByte_GB(0x4000, bank); + + // Read RAM + dataIn(); + for (addr = 0xA000; addr <= endaddr; addr = addr + 64) { + //fill sdBuffer + myFile.read(sdBuffer, 64); + for (int c = 0; c < 64; c++) { + if (readByte_GB(addr + c) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + dataOut(); + // Disable RAM + writeByte_GB(0x0000, 0x00); + dataIn(); + } + // Close the file: + myFile.close(); + return writeErrors; + } + else { + print_Error(F("Can't open file"), true); + } } //****************************************** diff --git a/Cart_Reader/GBA.ino b/Cart_Reader/GBA.ino new file mode 100644 index 0000000..9a45e43 --- /dev/null +++ b/Cart_Reader/GBA.ino @@ -0,0 +1,413 @@ +//****************************************** +// GAME BOY ADVANCE +//****************************************** + +/****************************************** + Variables + *****************************************/ +char calcChecksumStr[5]; +byte cartBuffer[513]; + +const int nintendoLogo[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x24, 0xFF, 0xAE, 0x51, 0x69, 0x9A, 0xA2, 0x21, 0x3D, 0x84, 0x82, 0x0A, + 0x84, 0xE4, 0x09, 0xAD, 0x11, 0x24, 0x8B, 0x98, 0xC0, 0x81, 0x7F, 0x21, 0xA3, 0x52, 0xBE, 0x19, + 0x93, 0x09, 0xCE, 0x20, 0x10, 0x46, 0x4A, 0x4A, 0xF8, 0x27, 0x31, 0xEC, 0x58, 0xC7, 0xE8, 0x33, + 0x82, 0xE3, 0xCE, 0xBF, 0x85, 0xF4, 0xDF, 0x94, 0xCE, 0x4B, 0x09, 0xC1, 0x94, 0x56, 0x8A, 0xC0, + 0x13, 0x72, 0xA7, 0xFC, 0x9F, 0x84, 0x4D, 0x73, 0xA3, 0xCA, 0x9A, 0x61, 0x58, 0x97, 0xA3, 0x27, + 0xFC, 0x03, 0x98, 0x76, 0x23, 0x1D, 0xC7, 0x61, 0x03, 0x04, 0xAE, 0x56, 0xBF, 0x38, 0x84, 0x00, + 0x40, 0xA7, 0x0E, 0xFD, 0xFF, 0x52, 0xFE, 0x03, 0x6F, 0x95, 0x30, 0xF1, 0x97, 0xFB, 0xC0, 0x85, + 0x60, 0xD6, 0x80, 0x25, 0xA9, 0x63, 0xBE, 0x03, 0x01, 0x4E, 0x38, 0xE2, 0xF9, 0xA2, 0x34, 0xFF, + 0xBB, 0x3E, 0x03, 0x44, 0x78, 0x00, 0x90, 0xCB, 0x88, 0x11, 0x3A, 0x94, 0x65, 0xC0, 0x7C, 0x63, + 0x87, 0xF0, 0x3C, 0xAF, 0xD6, 0x25, 0xE4, 0x8B, 0x38, 0x0A, 0xAC, 0x72, 0x21, 0xD4, 0xF8, 0x07 +}; + +/****************************************** + Menu + *****************************************/ +// GBA menu items +const char GBAMenuItem1[] PROGMEM = "Read Rom"; +const char GBAMenuItem2[] PROGMEM = "Read Save"; +const char GBAMenuItem3[] PROGMEM = "Write Save"; +const char GBAMenuItem4[] PROGMEM = "Reset"; +const char* const menuOptionsGBA[] PROGMEM = {GBAMenuItem1, GBAMenuItem2, GBAMenuItem3, GBAMenuItem4}; + +const char GBARomMenuItem1[] PROGMEM = "4MB"; +const char GBARomMenuItem2[] PROGMEM = "8MB"; +const char GBARomMenuItem3[] PROGMEM = "16MB"; +const char GBARomMenuItem4[] PROGMEM = "32MB"; +const char* const menuOptionsGBARom[] PROGMEM = {GBARomMenuItem1, GBARomMenuItem2, GBARomMenuItem3, GBARomMenuItem4}; + +void gbaMenu() { + // create menu with title and 4 options to choose from + unsigned char mainMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsGBA, 4); + mainMenu = question_box("GBA Cart Reader", menuOptions, 4, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + case 0: + // Read rom + // create submenu with title and 4 options to choose from + unsigned char GBARomMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsGBARom, 4); + GBARomMenu = question_box("Select ROM size", menuOptions, 4, 0); + + // wait for user choice to come back from the question box menu + switch (GBARomMenu) + { + case 0: + // 4MB + cartSize = 0x400000; + break; + + case 1: + // 8MB + cartSize = 0x800000; + break; + + case 2: + // 16MB + cartSize = 0x1000000; + break; + + case 3: + // 32MB + cartSize = 0x2000000; + break; + } + + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readROM_GBA(); + sd.chdir("/"); + compare_checksum_GBA(); + break; + + case 1: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readSAVE_GBA(); + break; + + case 2: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + writeSAVE_GBA(); + unsigned long wrErrors; + wrErrors = verifySAVE_GBA(); + if (wrErrors == 0) { + println_Msg(F("Verified OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(wrErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + break; + + case 3: + asm volatile (" jmp 0"); + break; + } + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +/****************************************** + Setup + *****************************************/ +void setup_GBA() { + // Set address/data pins to OUTPUT + // AD0-AD7 + DDRF = 0xFF; + // AD8-AD15 + DDRK = 0xFF; + // AD16-AD23 + DDRC = 0xFF; + + // Output a HIGH signal + // AD0-AD7 + PORTF = 0xFF; + // AD8-AD15 + PORTK = 0xFF; + // AD16-AD23 + PORTC = 0xFF; + + // Set Control Pins to Output CS_SRAM(PH0) CS_ROM(PH3) WR(PH5) RD(PH6) + // CLK is N/C and IRQ is conected to GND inside the cartridge + DDRH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); + // Output a high signal on CS_SRAM(PH0) CS_ROM(PH3) WR(PH5) RD(PH6) + // At power-on all the control lines are high/disabled + PORTH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); + + // Delay until all is stable + delay(500); + + // Print start page + getCartInfo_GBA(); + display_Clear(); + + println_Msg(F("GBA Cart Info")); + println_Msg(""); + print_Msg(F("Rom Name: ")); + println_Msg(romName); + print_Msg(F("Cart ID: ")); + println_Msg(cartID); + print_Msg(F("Checksum: ")); + println_Msg(checksumStr); + print_Msg(F("Version: 1.")); + println_Msg(romVersion); + println_Msg(""); + + // Wait for user input + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +/****************************************** + Low level functions +*****************************************/ +// Read one word and toggle both CS and RD +word readWord_GBA(unsigned long myAddress) { + //divide address by two since we read two bytes per offset + unsigned long currAddress = myAddress / 2; + + // Output address to address pins, + PORTF = currAddress & 0xFF; + PORTK = (currAddress >> 8) & 0xFF; + PORTC = (currAddress >> 16) & 0xFF; + + // Wait 30ns, Arduino running at 16Mhz -> one operation = 62.5ns + __asm__("nop\n\t"); + + // Pull CS_ROM(PH3) LOW to latch the address + PORTH &= ~(1 << 3); + + // Wait 120ns between pulling CS and RD LOW + __asm__("nop\n\t""nop\n\t"); + + // Set address/data pins to LOW, this is important + PORTF = 0x0; + PORTK = 0x0; + // Set address/data ports to input so we can read, but don't enable pullups + DDRF = 0x0; + DDRK = 0x0; + + // Pull RD(PH6) to LOW to read 16 bytes of data + PORTH &= ~ (1 << 6); + + // Wait 120ns for the cartridge rom to write the data to the datalines + __asm__("nop\n\t""nop\n\t"); + + // Switch CS_ROM(PH3) RD(PH6) to HIGH + PORTH |= (1 << 3) | (1 << 6); + + // Read the data off the data lines on the rising edge of the RD line. + word tempWord = ((PINF << 8) + PINK) & 0xFFFF; + + // Set address/data pins to output + DDRF = 0xFF; + DDRK = 0xFF; + DDRC = 0xFF; + // Output a high signal so there are no floating pins + PORTF = 0XFF; + PORTK = 0XFF; + PORTC = 0XFF; + + // Return the read word + return tempWord; +} + +// Read multiple bytes into an array by toggling both CS and RD for each byte +void readBlock_GBA(unsigned long myAddress, byte myArray[] , int numBytes) { + for (int currByte = 0; currByte < numBytes; currByte += 2) { + unsigned long currAddress = myAddress + currByte; + word currWord = readWord_GBA(currAddress); + myArray[currByte] = (currWord >> 8) & 0xFF; + myArray[currByte + 1] = currWord & 0xFF; + } +} +/****************************************** + Game Boy functions +*****************************************/ +// Read info out of rom header +void getCartInfo_GBA() { + // Read Header into array + readBlock_GBA(0, cartBuffer, 192); + + // Nintendo logo check + for (int currByte = 0x4; currByte < 0xA0; currByte++) { + if (pgm_read_byte(&nintendoLogo[currByte]) != cartBuffer[currByte]) { + print_Error(F("Nintendo Logo Error"), false); + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + break; + } + } + + // Get cart ID + cartID[0] = char(cartBuffer[0xAC]); + cartID[1] = char(cartBuffer[0xAD]); + cartID[2] = char(cartBuffer[0xAE]); + cartID[3] = char(cartBuffer[0xAF]); + + // Dump name into 8.3 compatible format + byte myByte = 0; + byte myLength = 0; + for (int addr = 0xA0; addr <= 0xAB; addr++) { + myByte = cartBuffer[addr]; + if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 8) { + romName[myLength] = char(myByte); + myLength++; + } + } + + // Get ROM version + romVersion = cartBuffer[0xBC]; + + // Get Checksum as string + sprintf(checksumStr, "%02X", cartBuffer[0xBD]); + + // Calculate Checksum + int calcChecksum = 0x00; + for (int n = 0xA0; n < 0xBD; n++) { + calcChecksum -= cartBuffer[n]; + } + calcChecksum = (calcChecksum - 0x19) & 0xFF; + // Turn into string + sprintf(calcChecksumStr, "%02X", calcChecksum); + + // Compare checksum + if (strcmp(calcChecksumStr, checksumStr) != 0) { + print_Msg(F("Result: ")); + println_Msg(calcChecksumStr); + print_Error(F("Checksum Error"), false); + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + } +} + +// Dump ROM +void readROM_GBA() { + // Get name, add extension and convert to char array for sd lib + char fileName[26]; + strcpy(fileName, romName); + strcat(fileName, ".gba"); + + // create a new folder for the rom file + EEPROM_readAnything(0, foldern); + sprintf(folder, "ROM/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + //clear the screen + display_Clear(); + println_Msg(F("Creating folder: ")); + println_Msg(folder); + display_Update(); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(0, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("Can't create file on SD"), true); + } + + // Read rom + for (int myAddress = 0; myAddress < cartSize; myAddress += 512) { + // Fill cartBuffer starting at myAddress and reading 512 bytes into array cartBuffer + readBlock_GBA(myAddress, sdBuffer, 512); + + // Write to SD + myFile.write(sdBuffer, 512); + + // Pause between blocks, increase if you get errors every numBytes bytes + delayMicroseconds(10); + } + + // Close the file: + myFile.close(); + + // Signal end of process + print_Msg(F("Saved as ")); + println_Msg(fileName); +} + +// Calculate the checksum of the dumped rom +boolean compare_checksum_GBA () { + println_Msg(F("Calculating Checksum")); + display_Update(); + + char fileName[26]; + strcpy(fileName, romName); + strcat(fileName, ".gba"); + + // last used rom folder + EEPROM_readAnything(0, foldern); + sprintf(folder, "ROM/%s/%d", romName, foldern - 1); + sd.chdir(folder); + + // If file exists + if (myFile.open(fileName, O_READ)) { + // Read rom header + myFile.read(sdBuffer, 512); + myFile.close(); + + // Calculate Checksum + int calcChecksum = 0x00; + for (int n = 0xA0; n < 0xBD; n++) { + calcChecksum -= sdBuffer[n]; + } + calcChecksum = (calcChecksum - 0x19) & 0xFF; + + // Turn into string + sprintf(calcChecksumStr, "%02X", calcChecksum); + + if (strcmp(calcChecksumStr, checksumStr) == 0) { + println_Msg(F("Checksum matches")); + display_Update(); + return 1; + } + else { + print_Msg(F("Result: ")); + println_Msg(calcChecksumStr); + print_Error(F("Checksum Error"), false); + return 0; + } + } + // Else show error + else { + print_Error(F("Failed to open rom"), false); + return 0; + } +} + +void readSAVE_GBA() { +} + +void writeSAVE_GBA() { +} + +unsigned long verifySAVE_GBA() { +} + +//****************************************** +// End of File +//****************************************** diff --git a/Cart_Reader/N64.ino b/Cart_Reader/N64.ino index acb693a..059b8b8 100644 --- a/Cart_Reader/N64.ino +++ b/Cart_Reader/N64.ino @@ -36,9 +36,7 @@ String button = "N/A"; String lastbutton = "N/A"; // Cartridge ID and size -char cartID[5]; byte saveType; -unsigned long cartSize; // Rom base address unsigned long romBase = 0x10000000; diff --git a/Cart_Reader/NP.ino b/Cart_Reader/NP.ino index 6defb73..8515d02 100644 --- a/Cart_Reader/NP.ino +++ b/Cart_Reader/NP.ino @@ -91,31 +91,31 @@ void NPGameMenu() { if (hasMenu) { // Create submenu options char menuOptionsNPGames[8][20]; - for (int i = 0; i < (numGames - 1); i++) { - strncpy(menuOptionsNPGames[i], gameCode[i + 1], 10); + for (int i = 0; i < (numGames); i++) { + strncpy(menuOptionsNPGames[i], gameCode[i], 10); } - // Create menu with title and numGames - 1 options to choose from + // Create menu with title and numGames options to choose from unsigned char gameSubMenu; // wait for user choice to come back from the question box menu - gameSubMenu = question_box("Select Game", menuOptionsNPGames, numGames - 1, 0); + gameSubMenu = question_box("Select Game", menuOptionsNPGames, numGames, 0); // Switch to game - send_NP(gameSubMenu + 0x81); + send_NP(gameSubMenu + 0x80); delay(200); // Check for successfull switch byte timeout = 0; while (readBank_SNES(0, 0x2400) != 0x7D) { delay(200); // Try again - send_NP(gameSubMenu + 0x81); + send_NP(gameSubMenu + 0x80); delay(200); timeout++; // Abort, something is wrong if (timeout == 5) { display_Clear(); print_Msg(F("Game ")); - print_Msg(gameSubMenu + 0x81, HEX); + print_Msg(gameSubMenu + 0x80, HEX); println_Msg(F(" Timeout")); println_Msg(readBank_SNES(0, 0x2400), HEX); println_Msg(F("")); @@ -1328,7 +1328,7 @@ byte send_NP(byte command) { // Switch to read dataIn(); controlIn_SNES(); - + // Read status NPReady = readBank_SNES(0, 0x2400); diff --git a/Cart_Reader/SNES.ino b/Cart_Reader/SNES.ino new file mode 100644 index 0000000..b784c14 --- /dev/null +++ b/Cart_Reader/SNES.ino @@ -0,0 +1,1243 @@ +//****************************************** +// SUPER NINTENDO +//****************************************** + +/****************************************** + Defines + *****************************************/ +// SNES Hi and LoRom, SA is HI with different Sram dumping +#define EX 4 +#define SA 3 +#define HI 1 +#define LO 0 + +/****************************************** + Variables + *****************************************/ +// Define SNES Cart Reader Variables +int romSpeed = 0; // 0 = SlowROM, 3 = FastROM +int romChips = 0; // 0 = ROM only, 1 = ROM & RAM, 2 = ROM & Save RAM, 3 = ROM & DSP1, 4 = ROM & RAM & DSP1, 5 = ROM & Save RAM & DSP1, 19 = ROM & SFX +// 227 = ROM & RAM & GameBoy data, 246 = ROM & DSP2 +byte romSizeExp = 0; // ROM-Size Exponent +int cartCountry = 255; +boolean NP = false; + +/****************************************** + Menu +*****************************************/ +// SNES menu items +const char SnesMenuItem1[] PROGMEM = "Read Rom"; +const char SnesMenuItem2[] PROGMEM = "Read Save"; +const char SnesMenuItem3[] PROGMEM = "Write Save"; +const char SnesMenuItem4[] PROGMEM = "Test SRAM"; +const char SnesMenuItem5[] PROGMEM = "Cycle cart"; +const char SnesMenuItem6[] PROGMEM = "Reset"; +const char* const menuOptionsSNES[] PROGMEM = {SnesMenuItem1, SnesMenuItem2, SnesMenuItem3, SnesMenuItem4, SnesMenuItem5, SnesMenuItem6}; + +// Manual config menu items +const char confMenuItem1[] PROGMEM = "Use header info"; +const char confMenuItem2[] PROGMEM = "4MB LoRom 256K Sram"; +const char confMenuItem3[] PROGMEM = "4MB HiRom 64K Sram"; +const char confMenuItem4[] PROGMEM = "6MB ExRom 256K Sram"; +const char confMenuItem5[] PROGMEM = "Reset"; +const char* const menuOptionsConf[] PROGMEM = {confMenuItem1, confMenuItem2, confMenuItem3, confMenuItem4, confMenuItem5}; + +// SNES Menu +void snesMenu() { + // create menu with title and 7 options to choose from + unsigned char mainMenu; + // Copy menuOptions of of progmem + convertPgm(menuOptionsSNES, 6); + mainMenu = question_box("SNES Cart Reader", menuOptions, 6, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + case 0: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readROM_SNES(); + compare_checksum(); + break; + + case 1: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readSRAM(); + break; + + case 2: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + writeSRAM(1); + unsigned long wrErrors; + wrErrors = verifySRAM(); + if (wrErrors == 0) { + println_Msg(F("Verified OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(wrErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + break; + + case 3: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readSRAM(); + eraseSRAM(0x00); + eraseSRAM(0xFF); + writeSRAM(0); + wrErrors = verifySRAM(); + if (wrErrors == 0) { + println_Msg(F("Verified OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(wrErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + break; + + case 4: + // For arcademaster1 (Markfrizb) multi-game carts + // Set reset pin to output (PH0) + DDRH |= (1 << 0); + // Switch RST(PH0) to LOW + PORTH &= ~(1 << 0); + display_Clear(); + print_Msg("Resetting..."); + display_Update(); + delay(3000); // wait 3 secs to switch to next game + asm volatile (" jmp 0"); + break; + + case 5: + asm volatile (" jmp 0"); + break; + } + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +// Menu for manual configuration +void confMenu() { + // create menu with title and 5 options to choose from + unsigned char subMenu; + // Copy menuOptions of of progmem + convertPgm(menuOptionsConf, 5); + subMenu = question_box("Choose mapping", menuOptions, 5, 0); + + // wait for user choice to come back from the question box menu + switch (subMenu) + { + case 0: + break; + + case 1: + romType = LO; + numBanks = 128; + sramSize = 256; + strcpy(romName, "LOROM"); + break; + + case 2: + romType = HI; + numBanks = 64; + sramSize = 64; + strcpy(romName, "HIROM"); + break; + + case 3: + romType = EX; + numBanks = 96; + sramSize = 256; + strcpy(romName, "EXROM"); + break; + + case 4: + // Reset + asm volatile (" jmp 0"); + break; + } +} + +/****************************************** + Setup + *****************************************/ +void setup_Snes() { + // Set cicrstPin(PG1) to Output + DDRG |= (1 << 1); + // Output a high signal until we're ready to start + PORTG |= (1 << 1); + // Set cichstPin(PG0) to Input + DDRG &= ~(1 << 0); + + // Adafruit Clock Generator + //clockgen.set_correction(-29000); + clockgen.set_correction(0); + clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0); + clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); + clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); + clockgen.set_freq(2147727200ULL, SI5351_PLL_FIXED, SI5351_CLK0); + clockgen.set_freq(307200000ULL, SI5351_PLL_FIXED, SI5351_CLK2); + clockgen.output_enable(SI5351_CLK0, 1); + clockgen.output_enable(SI5351_CLK1, 0); + clockgen.output_enable(SI5351_CLK2, 1); + + // Set Address Pins to Output + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //BA0-BA7 + DDRL = 0xFF; + //PA0-PA7 + DDRA = 0xFF; + + // Set Control Pins to Output RST(PH0) CS(PH3) WR(PH5) RD(PH6) + DDRH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); + // Switch RST(PH0) and WR(PH5) to HIGH + PORTH |= (1 << 0) | (1 << 5); + // Switch CS(PH3) and RD(PH6) to LOW + PORTH &= ~((1 << 3) | (1 << 6)); + + // Set Refresh(PE5) to Output + DDRE |= (1 << 5); + // Switch Refresh(PE5) to LOW (needed for SA-1) + PORTE &= ~(1 << 5); + + // Set CPU Clock(PH1) to Output + DDRH |= (1 << 1); + //PORTH &= ~(1 << 1); + + // Set IRQ(PH4) to Input + DDRH &= ~(1 << 4); + // Activate Internal Pullup Resistors + //PORTH |= (1 << 4); + + // Set expand(PG5) to Imput + DDRG &= ~(1 << 5); + // Activate Internal Pullup Resistors + //PORTG |= (1 << 5); + + // Set Data Pins (D0-D7) to Input + DDRC = 0x00; + // Enable Internal Pullups + //PORTC = 0xFF; + + // Unused pins + // Set wram(PE4) to Output + DDRE |= (1 << 4); + //PORTE &= ~(1 << 4); + // Set pawr(PJ1) to Output + DDRJ |= (1 << 1); + //PORTJ &= ~(1 << 1); + // Set pard(PJ0) to Output + DDRJ |= (1 << 0); + //PORTJ &= ~(1 << 0); + + // Start CIC by outputting a low signal to cicrstPin(PG1) + PORTG &= ~(1 << 1); + + // Wait for CIC reset + delay(1000); + + // Print all the info + getCartInfo_SNES(); +} + +/****************************************** + I/O Functions + *****************************************/ +// Switch control pins to write +void controlOut_SNES() { + // Switch RD(PH6) and WR(PH5) to HIGH + PORTH |= (1 << 6) | (1 << 5); + // Switch CS(PH3) to LOW + PORTH &= ~(1 << 3); +} + +// Switch control pins to read +void controlIn_SNES() { + // Switch WR(PH5) to HIGH + PORTH |= (1 << 5); + // Switch CS(PH3) and RD(PH6) to LOW + PORTH &= ~((1 << 3) | (1 << 6)); +} + +/****************************************** + Low level functions + *****************************************/ +// Write one byte of data to a location specified by bank and address, 00:0000 +void writeBank_SNES(byte myBank, word myAddress, byte myData) { + PORTL = myBank; + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + PORTC = myData; + + // Arduino running at 16Mhz -> one nop = 62.5ns + // Wait till output is stable + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Switch WR(PH5) to LOW + PORTH &= ~(1 << 5); + + // Leave WR low for at least 60ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Switch WR(PH5) to HIGH + PORTH |= (1 << 5); + + // Leave WR high for at least 50ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); +} + +// Read one byte of data from a location specified by bank and address, 00:0000 +byte readBank_SNES(byte myBank, word myAddress) { + PORTL = myBank; + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + + // Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Read + byte tempByte = PINC; + return tempByte; +} + +/****************************************** + SNES ROM Functions +******************************************/ +void getCartInfo_SNES() { + boolean manualConfig = 0; + + // Print start page + if (checkcart_SNES() == 0) { + // Checksum either corrupt or 0000 + manualConfig = 1; + errorLvl = 1; + rgb.setColor(255, 0, 0); + + display_Clear(); + println_Msg(F("ERROR")); + println_Msg(F("Rom header corrupt")); + println_Msg(F("or missing")); + println_Msg(F("")); + println_Msg(F("")); + println_Msg(F("Press button for")); + println_Msg(F("manual configuration")); + println_Msg(F("or powercycle if SA1")); + display_Update(); + wait(); + // Wait() clears errors but in this case we still have an error + errorLvl = 1; + } + + display_Clear(); + print_Msg(F("Rom Name: ")); + println_Msg(romName); + + print_Msg(F("Type: ")); + if (romType == HI) + print_Msg(F("HiROM")); + else if (romType == LO) + print_Msg(F("LoROM")); + else if (romType == EX) + print_Msg(F("ExHiRom")); + else + print_Msg(romType); + print_Msg(F(" ")); + if (romSpeed == 0) + println_Msg(F("SlowROM")); + else if (romSpeed == 2) + println_Msg(F("SlowROM")); + else if (romSpeed == 3) + println_Msg(F("FastROM")); + else + println_Msg(romSpeed); + + print_Msg(F("ICs: ROM ")); + if (romChips == 0) + println_Msg(F("ONLY")); + else if (romChips == 1) + println_Msg(F("RAM")); + else if (romChips == 2) + println_Msg(F("SAVE")); + else if (romChips == 3) + println_Msg(F("DSP1")); + else if (romChips == 4) + println_Msg(F("DSP1 RAM")); + else if (romChips == 5) + println_Msg(F("DSP1 SAVE")); + else if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) + println_Msg(F("SuperFX")); + else if (romChips == 52) { + println_Msg(F("SA1 RAM")); + romType = SA; + } + else if (romChips == 53) { + println_Msg(F("SA1 RAM BATT")); + romType = SA; + } + else if (romChips == 69) { + println_Msg(F("SDD1 BATT")); + } + else if (romChips == 227) + println_Msg(F("RAM GBoy")); + else if (romChips == 246) + println_Msg(F("DSP2")); + else + println_Msg(F("")); + + print_Msg(F("Rom Size: ")); + print_Msg(romSize); + println_Msg(F("Mbit")); + + print_Msg(F("Banks: ")); + println_Msg(numBanks); + + print_Msg(F("Sram Size: ")); + print_Msg(sramSize); + println_Msg(F("Kbit")); + + print_Msg(F("ROM Version: 1.")); + println_Msg(romVersion); + + print_Msg(F("Checksum: ")); + println_Msg(checksumStr); + display_Update(); + + // Wait for user input + if (enable_OLED) { + println_Msg(F(" ")); + println_Msg(F(" ")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + } + + else if (enable_Serial) { + println_Msg(F(" ")); + } + + // Start manual config + if (manualConfig == 1) { + confMenu(); + } +} + +void checkAltConf() { + char tempStr1[2]; + char tempStr2[5]; + char sizeStr[3]; + char bankStr[3]; + + if (myFile.open("snes.txt", O_READ)) { + while (myFile.available()) { + // Read 4 bytes into String, do it one at a time so byte order doesn't get mixed up + sprintf(tempStr1, "%c", myFile.read()); + strcpy(tempStr2, tempStr1); + sprintf(tempStr1, "%c", myFile.read()); + strcat(tempStr2, tempStr1); + sprintf(tempStr1, "%c", myFile.read()); + strcat(tempStr2, tempStr1); + sprintf(tempStr1, "%c", myFile.read()); + strcat(tempStr2, tempStr1); + + // Check if string is a match + if (strcmp(tempStr2, checksumStr) == 0) { + + // Skip the , in the file + myFile.seekSet(myFile.curPosition() + 1); + + // Read next two bytes into a string + romSize = myFile.read() - 48; + romSize = romSize * 10 + myFile.read() - 48; + + // Skip the , in the file + myFile.seekSet(myFile.curPosition() + 1); + + // Add next two bytes to the string + numBanks = myFile.read() - 48; + numBanks = numBanks * 10 + myFile.read() - 48; + } + // If no match empty string advance by 8 and try again + else { + myFile.seekSet(myFile.curPosition() + 8); + } + } + } + // Close the file: + myFile.close(); +} + +// Read header information +boolean checkcart_SNES() { + // set control to read + dataIn(); + + // Get Checksum as string + sprintf(checksumStr, "%02X%02X", readBank_SNES(0, 65503), readBank_SNES(0, 65502)); + + // Check if ExHiROM + if (readBank_SNES(0, 0xFFD5) == 0x35) { + romType = EX; + } + else { + // Check if LoROM or HiROM + romType = (readBank_SNES(0, 0xFFD5) & 1); + } + + // Check RomSpeed + romSpeed = (readBank_SNES(0, 65493) >> 4); + + // Check RomChips + romChips = readBank_SNES(0, 65494); + + if (romChips == 69) + { + romSize = 48; + numBanks = 96; + romType = HI; + } + else + { + // Check RomSize + byte romSizeExp = readBank_SNES(0, 65495) - 7; + romSize = 1; + while (romSizeExp--) + romSize *= 2; + + if ((romType == EX) || (romType == SA)) { + numBanks = long(romSize) * 2; + } + else { + numBanks = (long(romSize) * 1024 * 1024 / 8) / (32768 + (long(romType) * 32768)); + } + } + + //Check SD card for alt config + checkAltConf(); + + // Dump name into 8.3 compatible format + byte myByte = 0; + byte myLength = 0; + for (unsigned int i = 65472; i < 65492; i++) { + myByte = readBank_SNES(0, i); + if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 8) { + romName[myLength] = char(myByte); + myLength++; + } + } + + // Read sramSizeExp + byte sramSizeExp; + if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { + // SuperFX + if (readBank_SNES(0, 0x7FDA) == 0x33) { + sramSizeExp = readBank_SNES(0, 0x7FBD); + } + else { + if (strncmp(romName, "STARFOX2", 8) == 0) { + sramSizeExp = 6; + } + else { + sramSizeExp = 5; + } + } + } + else { + // No SuperFX + sramSizeExp = readBank_SNES(0, 0xFFD8); + } + + // Calculate sramSize + if (sramSizeExp != 0) { + sramSizeExp = sramSizeExp + 3; + sramSize = 1; + while (sramSizeExp--) + sramSize *= 2; + } + else { + sramSize = 0; + } + + // Check Cart Country + cartCountry = readBank_SNES(0, 65497); + + // ROM Version + romVersion = readBank_SNES(0, 65499); + + // Test if checksum is equal to reverse checksum + if (((word(readBank_SNES(0, 65500)) + (word(readBank_SNES(0, 65501)) * 256)) + (word(readBank_SNES(0, 65502)) + (word(readBank_SNES(0, 65503)) * 256))) == 65535 ) { + if (strcmp("0000", checksumStr) == 0) { + return 0; + } + else { + return 1; + } + } + // Either rom checksum is wrong or no cart is inserted + else { + return 0; + } +} + +unsigned int calc_checksum (char* fileName, char* folder) { + unsigned int calcChecksum = 0; + unsigned int calcChecksumChunk = 0; + int calcFilesize = 0; + unsigned int c = 0; + unsigned long i = 0; + unsigned long j = 0; + + if (strcmp(folder, "root") != 0) + sd.chdir(folder); + + // If file exists + if (myFile.open(fileName, O_READ)) { + calcFilesize = myFile.fileSize() * 8 / 1024 / 1024; + + // Nintendo Power (SF Memory Cassette) + // Read up to 0x60000 then add FFs to 0x80000 + if (NP == true) { + for (i = 0; i < (0x60000 / 512); i++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + calcChecksum = calcChecksumChunk; + } + calcChecksum += 0xF47C; // FFs from 0x60000-0x80000 + } + else if (calcFilesize == 12 || calcFilesize == 20) { + // Divide filesize by 8 to get number of 8Mbit chunks + for (i = 0; i < (calcFilesize / 8); i++ ) { + // Add all the bits in the 8Mbit chunks + for (j = 0; j < (1048576 / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + } + calcChecksum = calcChecksumChunk; + } + calcChecksumChunk = 0; + // Add the 4Mbit rest + for (j = 0; j < (524288 / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + } + calcChecksum += 2 * calcChecksumChunk; + } + else if (calcFilesize == 24 || calcFilesize == 28) { + /* Momotarou Dentestu Happy Fix 3MB (24Mbit) + if ((calcFilesize == 24) && (romSizeExp = 0x0C)) { + for (i = 0; i < (myFile.fileSize() / 512); i++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + } + calcChecksum = 2 * calcChecksumChunk; + } + else {*/ + for (i = 0; i < (calcFilesize / 16); i++ ) { + // Add all the bits in the 16Mbit chunks + for (j = 0; j < (2097152 / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + } + calcChecksum = calcChecksumChunk; + } + calcChecksumChunk = 0; + + // Add the 8Mbit rest + for (j = 0; j < (1048576 / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + } + calcChecksum += 2 * calcChecksumChunk; + //} + } + else if ((calcFilesize == 40) && (romChips == 85)) { + // Daikaijuu Monogatari 2 Fix 5MB (40Mbit) + // Add the 4MB (32Mbit) start + for (j = 0; j < (4194304 / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + calcChecksum = calcChecksumChunk; + } + calcChecksumChunk = 0; + // Add the 1MB (8Mbit) end + for (j = 0; j < (1048576 / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + } + calcChecksum += 4 * calcChecksumChunk; + } + else if (calcFilesize == 48) { + // Star Ocean/Tales of Phantasia Fix 6MB (48Mbit) + // Add the 4MB (32Mbit) start + for (j = 0; j < (4194304 / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + calcChecksum = calcChecksumChunk; + } + calcChecksumChunk = 0; + // Add the 2MB (16Mbit) end + for (j = 0; j < (2097152 / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + } + calcChecksum += 2 * calcChecksumChunk; + } + else { + //calcFilesize == 2 || 4 || 8 || 16 || 32 || 40 || etc + for (i = 0; i < (myFile.fileSize() / 512); i++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + calcChecksum = calcChecksumChunk; + } + } + myFile.close(); + sd.chdir(); + return (calcChecksum); + } + // Else show error + else { + print_Error(F("DUMP ROM 1ST"), false); + return 0; + } +} + +boolean compare_checksum() { + + println_Msg(F("Calculating Checksum")); + display_Update(); + + strcpy(fileName, romName); + strcat(fileName, ".sfc"); + + // last used rom folder + EEPROM_readAnything(0, foldern); + sprintf(folder, "ROM/%s/%d", romName, foldern - 1); + + char calcsumStr[5]; + sprintf(calcsumStr, "%04X", calc_checksum(fileName, folder)); + + if (strcmp(calcsumStr, checksumStr) == 0) { + print_Msg(F("Result: ")); + println_Msg(calcsumStr); + println_Msg(F("Checksum matches")); + display_Update(); + return 1; + } + else { + print_Msg(F("Result: ")); + println_Msg(calcsumStr); + print_Error(F("Checksum Error"), false); + display_Update(); + return 0; + } +} + +// Read rom to SD card +void readROM_SNES() { + // Set control + dataIn(); + controlIn_SNES(); + + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".sfc"); + + // create a new folder for the save file + EEPROM_readAnything(0, foldern); + sprintf(folder, "ROM/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + //clear the screen + display_Clear(); + println_Msg(F("Creating folder: ")); + println_Msg(folder); + display_Update(); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(0, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("Can't create file on SD"), true); + } + + // Check if LoROM or HiROM... + if (romType == LO) { + println_Msg(F("Dumping LoRom...")); + display_Update(); + + // Read up to 96 banks starting at bank 0×00. + for (int currBank = 0; currBank < numBanks; currBank++) { + // Dump the bytes to SD 512B at a time + for (long currByte = 32768; currByte < 65536; currByte += 512) { + for (int c = 0; c < 512; c++) { + sdBuffer[c] = readBank_SNES(currBank, currByte + c); + } + myFile.write(sdBuffer, 512); + } + } + } + // Dump High-type ROM + else if (((romType == HI) || (romType == SA) || (romType == EX)) && (romChips != 69)) { + println_Msg(F("Dumping HiRom...")); + display_Update(); + + for (int currBank = 192; currBank < (numBanks + 192); currBank++) { + for (long currByte = 0; currByte < 65536; currByte += 512) { + for (int c = 0; c < 512; c++) { + sdBuffer[c] = readBank_SNES(currBank, currByte + c); + } + myFile.write(sdBuffer, 512); + } + } + } + // Dump SDD1 High-type ROM + else if ((romType == HI) && (romChips == 69)) { + println_Msg(F("Dumping SDD1 HiRom...")); + display_Update(); + + controlIn_SNES(); + byte initialSOMap = readBank_SNES(0, 18439); + + for (int currMemmap = 0; currMemmap < (numBanks / 16); currMemmap++) { + + dataOut(); + controlOut_SNES(); + + writeBank_SNES(0, 18439, currMemmap); + + dataIn(); + controlIn_SNES(); + + for (int currBank = 240; currBank < 256; currBank++) { + for (long currByte = 0; currByte < 65536; currByte += 512) { + for (int c = 0; c < 512; c++) { + sdBuffer[c] = readBank_SNES(currBank, currByte + c); + } + myFile.write(sdBuffer, 512); + } + } + } + + dataOut(); + controlOut_SNES(); + + writeBank_SNES(0, 18439, initialSOMap); + + dataIn(); + controlIn_SNES(); + } + // Close the file: + myFile.close(); + + // Signal end of process + print_Msg(F("Saved as ")); + println_Msg(fileName); +} + +/****************************************** + SNES SRAM Functions +*****************************************/ +// Write file to SRAM +void writeSRAM (boolean browseFile) { + if (browseFile) { + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser("Select srm file"); + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + display_Clear(); + } + else + sprintf(filePath, "%s", fileName); + + //open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Set pins to output + dataOut(); + + // Set RST RD WR to High and CS to Low + controlOut_SNES(); + + // LoRom + if (romType == LO) { + // Sram size + long lastByte = (long(sramSize) * 128); + for (long currByte = 0; currByte < lastByte; currByte++) { + writeBank_SNES(0x70, currByte, myFile.read()); + } + } + + // HiRom + else if (romType == HI) { + // Writing SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + writeBank_SNES(0x30, currByte, myFile.read()); + } + } + // ExHiRom + else if (romType == EX) { + // Writing SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + writeBank_SNES(0xB0, currByte, myFile.read()); + } + } + // SA1 + else if (romType == SA) { + long lastByte = (long(sramSize) * 0x80); + + // Enable CPU Clock + clockgen.set_freq(357954500ULL, SI5351_PLL_FIXED, SI5351_CLK1); + clockgen.output_enable(SI5351_CLK1, 1); + + // Direct writes to BW-RAM (SRAM) in banks 0x40-0x43 don't work + // Break BW-RAM (SRAM) into 0x2000 blocks + // Use $2225 to map BW-RAM block to 0x6000-0x7FFF + // Writes must be to entire address range 0x0000-0x7FFF + byte lastBlock = 0; + lastBlock = lastByte / 0x2000; + + // Writing SRAM on SA1 needs CS(PH3) to be high + PORTH |= (1 << 3); + + for (byte currBlock = 0; currBlock < lastBlock; currBlock++) { + // Set 0x2225 (SA-1 BMAP) to map SRAM Block to 0x6000-0x7FFF + writeBank_SNES(0, 0x2225, currBlock); + // Set 0x2227 to 0x80 SA-1 SWBE BW-RAM Write Enable + writeBank_SNES(0, 0x2227, 0x80); + for (long currByte = 0x0000; currByte < 0x8000; currByte += 512) { + if (currByte < 0x6000) { + for (int c = 0; c < 512; c++) { + // Shift to bypass protected 1st 0x100 bytes + writeBank_SNES(0, currByte + c, currBlock + lastBlock); + } + } + else { + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + writeBank_SNES(0, currByte + c, sdBuffer[c]); + } + } + } + } + // Set 0x2227 to 0x00 SA-1 SWBE BW-RAM Write Disable + writeBank_SNES(0, 0x2227, 0x00); + // Disable CPU clock + clockgen.output_enable(SI5351_CLK1, 0); + } + + // Set pins to input + dataIn(); + + // Close the file: + myFile.close(); + println_Msg(F("SRAM writing finished")); + display_Update(); + + } + else { + print_Error(F("File doesnt exist"), false); + } +} + +void readSRAM () { + // set control + controlIn_SNES(); + + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".srm"); + + // create a new folder for the save file + EEPROM_readAnything(0, foldern); + sprintf(folder, "SAVE/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + // Signal end of process + print_Msg(F("Reading to SAVE/")); + print_Msg(romName); + print_Msg(F("/")); + print_Msg(foldern); + print_Msg(F("/")); + print_Msg(fileName); + print_Msg(F("...")); + display_Update(); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(0, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + if (romType == LO) { + // Sram size + long lastByte = (long(sramSize) * 128); + for (long currByte = 0; currByte < lastByte; currByte++) { + myFile.write(readBank_SNES(0x70, currByte)); + } + } + else if (romType == HI) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + myFile.write(readBank_SNES(0x30, currByte)); + } + } + else if (romType == EX) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + myFile.write(readBank_SNES(0xB0, currByte)); + } + } + else if (romType == SA) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + for (long currByte = 0x0; currByte < lastByte; currByte++) { + myFile.write(readBank_SNES(0x40, currByte)); + } + } + + // Close the file: + myFile.close(); + + // Signal end of process + println_Msg(F("Done")); + display_Update(); +} + +// Check if the SRAM was written without any error +unsigned long verifySRAM() { + //open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Variable for errors + writeErrors = 0; + + // Set control + controlIn_SNES(); + + if (romType == LO) { + // Sram size + long lastByte = (long(sramSize) * 128); + for (long currByte = 0; currByte < lastByte; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x70, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + else if (romType == HI) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x30, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + else if (romType == EX) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0xB0, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + else if (romType == SA) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + for (long currByte = 0x0; currByte < lastByte; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x40, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + // Close the file: + myFile.close(); + return writeErrors; + } + else { + print_Error(F("Can't open file"), false); + } +} + +// Overwrite the entire SRAM +boolean eraseSRAM (byte b) { + print_Msg(F("0x")); + print_Msg(b, HEX); + print_Msg(F(": ")); + display_Update(); + + // Set pins to output + dataOut(); + + // Set control pins + controlOut_SNES(); + + if (romType == LO) { + + // Sram size + long lastByte = (long(sramSize) * 128); + for (long currByte = 0; currByte < lastByte; currByte++) { + writeBank_SNES(0x70, currByte, b); + } + } + else if (romType == HI) { + // Writing SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + writeBank_SNES(0x30, currByte, b); + + } + } + // ExHiRom + else if (romType == EX) { + // Writing SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + writeBank_SNES(0xB0, currByte, b); + } + } + dataIn(); + + // Variable for errors + writeErrors = 0; + + // Set control + controlIn_SNES(); + + if (romType == LO) { + // Sram size + long lastByte = (long(sramSize) * 128); + for (long currByte = 0; currByte < lastByte; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x70, currByte + c)) != b) { + writeErrors++; + } + } + } + } + else if (romType == HI) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x30, currByte + c)) != b) { + writeErrors++; + } + } + } + } + else if (romType == EX) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0xB0, currByte + c)) != b) { + writeErrors++; + } + } + } + } + if (writeErrors == 0) { + println_Msg(F("OK")); + return 1; + } + else { + println_Msg(F("ERROR")); + return 0; + } + display_Update(); +} + +//****************************************** +// End of File +//******************************************