diff --git a/Cart_Reader/NP.ino b/Cart_Reader/NP.ino index b76c404..7b8337f 100644 --- a/Cart_Reader/NP.ino +++ b/Cart_Reader/NP.ino @@ -1,1744 +1,1750 @@ -//****************************************** -// NINTENDO POWER SF MEMORY MODULE -//****************************************** - -#include "options.h" -#ifdef enable_NP - -/****************************************** - SF Memory Clock Source -******************************************/ -// The clock signal for the SF Memory cassette -// is generated with the Adafruit Clock Generator -// or a similar external clock source - -/****************************************** - Variables - *****************************************/ -// SF Memory status -byte sfmReady = 0; - -// SF Memory Menu -boolean hasMenu = true; -byte numGames = 0; - -// Arrays that hold game info -int gameSize[8]; -int saveSize[8]; -byte gameAddress[8]; -byte gameVersion[8]; -char gameCode[8][10]; -boolean hirom[8]; - -/****************************************** - Menu -*****************************************/ -// SFM menu items -static const char sfmMenuItem1[] PROGMEM = "Game Menu"; -static const char sfmMenuItem2[] PROGMEM = "Flash Menu"; -static const char sfmMenuItem3[] PROGMEM = "Reset"; -static const char* const menuOptionsSFM[] PROGMEM = {sfmMenuItem1, sfmMenuItem2, sfmMenuItem3}; - -// SFM flash menu items -static const char sfmFlashMenuItem1[] PROGMEM = "Read Flash"; -static const char sfmFlashMenuItem2[] PROGMEM = "Write Flash"; -static const char sfmFlashMenuItem3[] PROGMEM = "Print Mapping"; -static const char sfmFlashMenuItem4[] PROGMEM = "Read Mapping"; -static const char sfmFlashMenuItem5[] PROGMEM = "Write Mapping"; -static const char sfmFlashMenuItem6[] PROGMEM = "Back"; -static const char* const menuOptionsSFMFlash[] PROGMEM = {sfmFlashMenuItem1, sfmFlashMenuItem2, sfmFlashMenuItem3, sfmFlashMenuItem4, sfmFlashMenuItem5, sfmFlashMenuItem6}; - -// SFM game menu items -static const char sfmGameMenuItem1[] PROGMEM = "Read Sram"; -static const char sfmGameMenuItem2[] PROGMEM = "Read Game"; -static const char sfmGameMenuItem3[] PROGMEM = "Write Sram"; -static const char sfmGameMenuItem4[] PROGMEM = "Switch Game"; -static const char sfmGameMenuItem5[] PROGMEM = "Reset"; -static const char* const menuOptionsSFMGame[] PROGMEM = {sfmGameMenuItem1, sfmGameMenuItem2, sfmGameMenuItem3, sfmGameMenuItem4, sfmGameMenuItem5}; - -void sfmMenu() { - // create menu with title and 3 options to choose from - unsigned char mainMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsSFM, 3); - mainMenu = question_box(F("SF Memory"), menuOptions, 3, 0); - - // wait for user choice to come back from the question box menu - switch (mainMenu) - { - // Game menu - case 0: - sfmGameMenu(); - break; - // Flash menu - case 1: - mode = mode_SFM_Flash; - break; - // Reset - case 2: - resetArduino(); - break; - } -} - -void sfmGameMenu() { - // Switch to hirom all - if (send_SFM(0x04) == 0x2A) { - delay(300); - - // Fill arrays with data - getGames(); - - if (hasMenu) { - // Create submenu options - char menuOptionsSFMGames[8][20]; - for (int i = 0; i < (numGames); i++) { - strncpy(menuOptionsSFMGames[i], gameCode[i], 10); - } - - // 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(F("Select Game"), menuOptionsSFMGames, numGames, 0); - - // Switch to game - send_SFM(gameSubMenu + 0x80); - delay(200); - // Check for successfull switch - byte timeout = 0; - while (readBank_SFM(0, 0x2400) != 0x7D) { - delay(200); - // Try again - send_SFM(gameSubMenu + 0x80); - delay(200); - timeout++; - // Abort, something is wrong - if (timeout == 5) { - display_Clear(); - print_Msg(F("Game ")); - print_Msg(gameSubMenu + 0x80, HEX); - println_Msg(F(" Timeout")); - println_Msg(readBank_SFM(0, 0x2400), HEX); - println_Msg(F("")); - print_Error(F("Powercycle SFM cart"), true); - } - } - // Copy gameCode to romName in case of japanese chars in romName - strcpy(romName, gameCode[gameSubMenu + 1]); - - // Print info - getCartInfo_SFM(); - mode = mode_SFM_Game; - } - else { - // No menu so switch to only game - // Switch to game - send_SFM(0x80); - delay(200); - - // Copy gameCode to romName in case of japanese chars in romName - strcpy(romName, gameCode[0]); - - // Print info - getCartInfo_SFM(); - mode = mode_SFM_Game; - } - } - else { - print_Error(F("Switch to HiRom failed"), false); - } -} - -void sfmGameOptions() { - // create menu with title and 5 options to choose from - unsigned char gameSubMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsSFMGame, 5); - gameSubMenu = question_box(F("SFM Game Menu"), menuOptions, 5, 0); - - // wait for user choice to come back from the question box menu - switch (gameSubMenu) - { - // Read sram - case 0: - display_Clear(); - // Change working dir to root - sd.chdir("/"); - readSRAM(); - break; - - // Read rom - case 1: - display_Clear(); - // Change working dir to root - sd.chdir("/"); - readROM_SFM(); - compare_checksum(); - break; - - // Write sram - 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; - - // Switch game - case 3: - sfmGameMenu(); - break; - - // Reset - case 4: - resetArduino(); - break; - } - if (gameSubMenu != 3) { - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - } -} - -#ifdef enable_FLASH -void sfmFlashMenu() { - // create menu with title and 6 options to choose from - unsigned char flashSubMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsSFMFlash, 6); - flashSubMenu = question_box(F("SFM Flash Menu"), menuOptions, 6, 0); - - // wait for user choice to come back from the question box menu - switch (flashSubMenu) - { - // Read Flash - case 0: - // Clear screen - display_Clear(); - - // Reset to root directory - sd.chdir("/"); - - // Reset to HIROM ALL - romType = 1; - print_Msg(F("Switch to HiRom...")); - display_Update(); - if (send_SFM(0x04) == 0x2A) { - println_Msg(F("OK")); - display_Update(); - - // Reset flash - resetFlash_SFM(0xC0); - resetFlash_SFM(0xE0); - - flashSize = 4194304; - numBanks = 64; - - // Get name, add extension and convert to char array for sd lib - EEPROM_readAnything(0, foldern); - sprintf(fileName, "SFM%d", foldern); - strcat(fileName, ".bin"); - sd.mkdir("NP", true); - sd.chdir("NP"); - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(0, foldern); - - // Read flash - readFlash_SFM(); - } - else { - print_Error(F("Switch to HiRom failed"), false); - } - break; - - // Write Flash - case 1: - // Clear screen - display_Clear(); - - // Print warning - println_Msg(F("Attention")); - println_Msg(F("This will erase your")); - println_Msg(F("NP Cartridge.")); - println_Msg(""); - println_Msg(F("Press Button")); - println_Msg(F("to continue")); - display_Update(); - wait(); - - // Clear screen - display_Clear(); - - filePath[0] = '\0'; - sd.chdir("/"); - // Launch file browser - fileBrowser(F("Select 4MB file")); - display_Clear(); - sprintf(filePath, "%s/%s", filePath, fileName); - flashSize = 2097152; - numBanks = 32; - println_Msg(F("Writing 1st rom")); - display_Update(); - // Program 1st flashrom - write_SFM(0xC0, 0); - display_Clear(); - println_Msg(F("Writing 2nd rom")); - display_Update(); - // Program 2nd flashrom - write_SFM(0xE0, 2097152); - break; - - // Print mapping - case 2: - // Clear screen - display_Clear(); - - // Reset to root directory - sd.chdir("/"); - - // Reset to HIROM ALL - romType = 1; - print_Msg(F("Switch to HiRom...")); - display_Update(); - if (send_SFM(0x04) == 0x2A) { - println_Msg(F("OK")); - display_Update(); - idFlash_SFM(0xC0); - if (strcmp(flashid, "c2f3") == 0) { - idFlash_SFM(0xE0); - if (strcmp(flashid, "c2f3") == 0) { - // Reset flash - resetFlash_SFM(0xC0); - resetFlash_SFM(0xE0); - delay(100); - // Clear screen - display_Clear(); - printMapping(); - resetFlash_SFM(0xC0); - resetFlash_SFM(0xE0); - } - else { - print_Error(F("Error: Wrong Flash ID"), true); - } - } - else { - print_Error(F("Error: Wrong Flash ID"), true); - } - } - else { - print_Error(F("failed"), false); - } - break; - - // Read mapping - case 3: - // Clear screen - display_Clear(); - - // Reset to root directory - sd.chdir("/"); - - // Reset to HIROM ALL - romType = 1; - print_Msg(F("Switch to HiRom...")); - display_Update(); - if (send_SFM(0x04) == 0x2A) { - println_Msg(F("OK")); - display_Update(); - idFlash_SFM(0xC0); - if (strcmp(flashid, "c2f3") == 0) { - idFlash_SFM(0xE0); - if (strcmp(flashid, "c2f3") == 0) { - // Reset flash - resetFlash_SFM(0xC0); - resetFlash_SFM(0xE0); - delay(100); - readMapping(); - resetFlash_SFM(0xC0); - resetFlash_SFM(0xE0); - } - else { - print_Error(F("Error: Wrong Flash ID"), true); - } - } - else { - print_Error(F("Error: Wrong Flash ID"), true); - } - } - else { - print_Error(F("failed"), false); - } - break; - - // Write mapping - case 4: - // Clear screen - display_Clear(); - - // Print warning - println_Msg(F("Attention")); - println_Msg(F("This will erase your")); - println_Msg(F("NP Cartridge.")); - println_Msg(""); - println_Msg(F("Press Button")); - println_Msg(F("to continue")); - display_Update(); - wait(); - - // Clear screen - display_Clear(); - - // Reset to root directory - sd.chdir("/"); - - // Erase mapping - eraseMapping(0xD0); - eraseMapping(0xE0); - print_Msg(F("Blankcheck...")); - display_Update(); - if (blankcheckMapping_SFM()) { - println_Msg(F("OK")); - display_Update(); - } - else { - println_Msg(F("Nope")); - break; - } - - // Clear screen - display_Clear(); - - // Clear filepath - filePath[0] = '\0'; - - // Reset to root directory - sd.chdir("/"); - - // Launch file browser - fileBrowser(F("Select MAP file")); - display_Clear(); - sprintf(filePath, "%s/%s", filePath, fileName); - display_Update(); - - // Write mapping - writeMapping_SFM(0xD0, 0); - writeMapping_SFM(0xE0, 256); - break; - - // Go back - case 5: - mode = mode_SFM; - break; - } - if (flashSubMenu != 5) { - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - } -} -#endif - -// Read the games from the menu area -void getGames() { - // Set data pins to input - dataIn(); - // Set control pins to input - controlIn_SFM(); - - // Check if menu is present - byte menuString[] = {0x4D, 0x45, 0x4E, 0x55, 0x20, 0x50, 0x52, 0x4F, 0x47, 0x52, 0x41, 0x4D}; - for (int i = 0; i < 12; i++) { - if (menuString[i] != readBank_SFM(0xC0, 0x7FC0 + i)) { - hasMenu = false; - } - } - - if (hasMenu) { - // Count number of games - for (word i = 0x0000; i < 0xE000; i += 0x2000) { - if (readBank_SFM(0xC6, i) == numGames ) - numGames++; - } - - // Get game info - for (int i = 0; i < numGames; i++) { - // Read starting address and size - gameAddress[i] = 0xC0 + readBank_SFM(0xC6, i * 0x2000 + 0x01) * 0x8; - gameSize[i] = readBank_SFM(0xC6, i * 0x2000 + 0x03) * 128; - saveSize[i] = readBank_SFM(0xC6, i * 0x2000 + 0x05) / 8; - - //check if hirom - if (readBank_SFM(gameAddress[i], 0xFFD5) == 0x31) { - hirom[i] = true; - } - else if (readBank_SFM(gameAddress[i], 0xFFD5) == 0x21) { - hirom[i] = true; - } - else { - hirom[i] = false; - } - - if (hirom[i]) { - gameVersion[i] = readBank_SFM(gameAddress[i], 0xFFDB); - } else { - gameVersion[i] = readBank_SFM(gameAddress[i], 0x7FDB); - } - - // Read game code - byte myByte = 0; - byte myLength = 0; - for (int j = 0; j < 9; j++) { - myByte = readBank_SFM(0xC6, i * 0x2000 + 0x07 + j); - // Remove funny characters - if (((char(myByte) >= 44 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 9) { - gameCode[i][myLength] = char(myByte); - myLength++; - } - } - // End char array in case game code is less than 9 chars - gameCode[i][myLength] = '\0'; - } - } - else { - //check if hirom - if (readBank_SFM(0xC0, 0xFFD5) == 0x31) { - hirom[0] = true; - } - else { - hirom[0] = false; - } - - if (hirom[0]) { - gameVersion[0] = readBank_SFM(0xC0, 0xFFDB); - gameCode[0][0] = 'G'; - gameCode[0][1] = 'A'; - gameCode[0][2] = 'M'; - gameCode[0][3] = 'E'; - gameCode[0][4] = '-'; - gameCode[0][5] = char(readBank_SFM(0xC0, 0xFFB2)); - gameCode[0][6] = char(readBank_SFM(0xC0, 0xFFB3)); - gameCode[0][7] = char(readBank_SFM(0xC0, 0xFFB4)); - gameCode[0][8] = char(readBank_SFM(0xC0, 0xFFB5)); - gameCode[0][9] = '\0'; - - byte romSizeExp = readBank_SFM(0xC0, 0xFFD7) - 7; - gameSize[0] = 1; - while (romSizeExp--) - gameSize[0] *= 2; - } - else { - gameVersion[0] = readBank_SFM(0xC0, 0x7FDB); - gameCode[0][0] = 'G'; - gameCode[0][1] = 'A'; - gameCode[0][2] = 'M'; - gameCode[0][3] = 'E'; - gameCode[0][4] = '-'; - gameCode[0][5] = char(readBank_SFM(0xC0, 0x7FB2)); - gameCode[0][6] = char(readBank_SFM(0xC0, 0x7FB3)); - gameCode[0][7] = char(readBank_SFM(0xC0, 0x7FB4)); - gameCode[0][8] = char(readBank_SFM(0xC0, 0x7FB5)); - gameCode[0][9] = '\0'; - - byte romSizeExp = readBank_SFM(0xC0, 0x7FD7) - 7; - gameSize[0] = 1; - while (romSizeExp--) - gameSize[0] *= 2; - } - } -} - -/****************************************** - Setup - *****************************************/ -void setup_SFM() { - // Set cicrstPin(PG1) to Output - DDRG |= (1 << 1); - // Output a high signal to disable snesCIC - PORTG |= (1 << 1); - // Set cichstPin(PG0) to Input - DDRG &= ~(1 << 0); - - // Set Address Pins to Output - //A0-A7 - DDRF = 0xFF; - //A8-A15 - DDRK = 0xFF; - //BA0-BA7 - DDRL = 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 IRQ(PH4) to Input - DDRH &= ~(1 << 4); - // Activate Internal Pullup Resistors - //PORTH |= (1 << 4); - - // Set Data Pins (D0-D7) to Input - DDRC = 0x00; - // Enable Internal Pullups - //PORTC = 0xFF; - - // Unused pins - // Set CPU Clock(PH1) to Output - DDRH |= (1 << 1); - //PORTH &= ~(1 << 1); - - // Adafruit Clock Generator - clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0); - clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); - clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); - clockgen.set_freq(2147727200ULL, SI5351_CLK0); - - // start outputting master clock - clockgen.output_enable(SI5351_CLK1, 0); - clockgen.output_enable(SI5351_CLK2, 0); - clockgen.output_enable(SI5351_CLK0, 1); - - // Wait until all is stable - delay(500); - - // Switch to HiRom All - byte timeout = 0; - send_SFM(0x04); - delay(200); - while (readBank_SFM(0, 0x2400) != 0x2A) { - delay(100); - // Try again - send_SFM(0x04); - delay(100); - timeout++; - // Abort, something is wrong - if (timeout == 5) { - println_Msg(F("Hirom All Timeout")); - println_Msg(F("")); - println_Msg(F("")); - print_Error(F("Powercycle SFM cart"), true); - } - } -} - -/****************************************** - I/O Functions - *****************************************/ -// Switch control pins to write -void controlOut_SFM() { - // 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_SFM() { - // 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_SFM(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_SFM(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_SFM() { - // Print start page - if (checkcart_SFM() == 0) { - // Checksum either corrupt or 0000 - errorLvl = 1; - rgb.setColor(255, 0, 0); - display_Clear(); - println_Msg(F("ERROR")); - println_Msg(F("Rom header corrupt")); - println_Msg(F("or missing")); - display_Update(); - wait(); - // Wait() clears errors but in this case we still have an error - errorLvl = 1; - } - - display_Clear(); - print_Msg(F("Name: ")); - println_Msg(romName); - println_Msg(F(" ")); - - print_Msg(F("Version: 1.")); - println_Msg(romVersion); - - print_Msg(F("Checksum: ")); - println_Msg(checksumStr); - - print_Msg(F("Size: ")); - print_Msg(romSize); - println_Msg(F("Mbit ")); - - print_Msg(F("Type: ")); - if (romType == 1) - println_Msg(F("HiROM")); - else if (romType == 0) - println_Msg(F("LoROM")); - else - println_Msg(romType); - - print_Msg(F("Banks: ")); - println_Msg(numBanks); - - print_Msg(F("Sram: ")); - print_Msg(sramSize); - println_Msg(F("Kbit")); - println_Msg(F("Press Button")); - display_Update(); - // Wait for user input - wait(); -} - -// Read header information -boolean checkcart_SFM() { - // set control to read - dataIn(); - - // Get Checksum as string - sprintf(checksumStr, "%02X%02X", readBank_SFM(0, 65503), readBank_SFM(0, 65502)); - - romType = readBank_SFM(0, 0xFFD5); - if ((romType >> 5) != 1) { // Detect invalid romType byte due to too long ROM name (22 chars) - romType = 0; // LoROM // Krusty's Super Fun House (U) 1.0 & Contra 3 (U) - } - else { - romType &= 1; // Must be LoROM or HiROM - } - - // Check RomSize - byte romSizeExp = readBank_SFM(0, 65495) - 7; - romSize = 1; - while (romSizeExp--) - romSize *= 2; - - numBanks = (long(romSize) * 1024 * 1024 / 8) / (32768 + (long(romType) * 32768)); - - //Check SD card for alt config - checkAltConf(); - - // Get name - byte myByte = 0; - byte myLength = 0; - for (unsigned int i = 65472; i < 65492; i++) { - myByte = readBank_SFM(0, i); - if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 15) { - romName[myLength] = char(myByte); - myLength++; - } - } - // If name consists out of all japanese characters use game code - if (myLength == 0) { - // Get rom code - romName[0] = 'S'; - romName[1] = 'H'; - romName[2] = 'V'; - romName[3] = 'C'; - romName[4] = '-'; - for (unsigned int i = 0; i < 4; i++) { - myByte = readBank_SFM(0, 0xFFB2 + i); - if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 4) { - romName[myLength + 5] = char(myByte); - myLength++; - } - } - if (myLength == 0) { - // Rom code unknown - romName[0] = 'U'; - romName[1] = 'N'; - romName[2] = 'K'; - romName[3] = 'N'; - romName[4] = 'O'; - romName[5] = 'W'; - romName[6] = 'N'; - } - } - - // Read sramSizeExp - byte sramSizeExp = readBank_SFM(0, 0xFFD8); - - // Calculate sramSize - if (sramSizeExp != 0) { - sramSizeExp = sramSizeExp + 3; - sramSize = 1; - while (sramSizeExp--) - sramSize *= 2; - } - else { - sramSize = 0; - } - - // ROM Version - romVersion = readBank_SFM(0, 65499); - - // Test if checksum is equal to reverse checksum - if (((word(readBank_SFM(0, 65500)) + (word(readBank_SFM(0, 65501)) * 256)) + (word(readBank_SFM(0, 65502)) + (word(readBank_SFM(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; - } -} - -// Read rom to SD card -void readROM_SFM() { - // Set control - dataIn(); - controlIn_SFM(); - - // 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, "NP/%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 == 0) { - 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_SFM(currBank, currByte + c); - } - myFile.write(sdBuffer, 512); - } - } - } - // Dump High-type ROM - else { - 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_SFM(currBank, currByte + c); - } - myFile.write(sdBuffer, 512); - } - } - } - // Close the file: - myFile.close(); - - // Signal end of process - print_Msg(F("Saved as ")); - println_Msg(fileName); -} - -/****************************************** - 29F1601 flashrom functions (NP) - *****************************************/ -// Reset the MX29F1601 flashrom, startbank is 0xC0 for first and 0xE0 for second flashrom -void resetFlash_SFM(int startBank) { - // Configure control pins - controlOut_SFM(); - // Set data pins to output - dataOut(); - - // Reset command sequence - if (romType) { - writeBank_SFM(startBank, 0x5555L * 2, 0xaa); - writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); - writeBank_SFM(startBank, 0x5555L * 2, 0xf0); - } - else { - writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0xaa); - writeBank_SFM(0, 0x8000 + 0x2AAAL * 2, 0x55); - writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0xf0); - } -} - -// Print flashrom manufacturer and device ID -void idFlash_SFM(int startBank) { - // Configure control pins - controlOut_SFM(); - // Set data pins to output - dataOut(); - - if (romType) { - // ID command sequence - writeBank_SFM(startBank, 0x5555L * 2, 0xaa); - writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); - writeBank_SFM(startBank, 0x5555L * 2, 0x90); - - // Set data pins to input again - dataIn(); - // Set control pins to input - controlIn_SFM(); - - // Read the two id bytes into a string - sprintf(flashid, "%x%x", readBank_SFM(startBank, 0x00), readBank_SFM(startBank, 0x02)); - } - else { - writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0xaa); - writeBank_SFM(0, 0x8000 + 0x2AAAL * 2, 0x55); - writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0x90); - - // Set data pins to input again - dataIn(); - // Set control pins to input - controlIn_SFM(); - - // Read the two id bytes into a string - sprintf(flashid, "%x%x", readBank_SFM(0, 0x8000), readBank_SFM(0, 0x8000 + 0x02)); - } -} - -// Write the flashroms by reading a file from the SD card, pos defines where in the file the reading/writing should start -void writeFlash_SFM(int startBank, uint32_t pos) { - display_Clear(); - print_Msg(F("Writing Bank 0x")); - print_Msg(startBank, HEX); - print_Msg(F("...")); - display_Update(); - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - - // Seek to a new position in the file - if (pos != 0) - myFile.seekCur(pos); - - // Configure control pins - controlOut_SFM(); - // Set data pins to output - dataOut(); - - if (romType) { - // Write hirom - for (int currBank = startBank; currBank < startBank + numBanks; currBank++) { - // Fill SDBuffer with 1 page at a time then write it repeat until all bytes are written - for (unsigned long currByte = 0; currByte < 0x10000; currByte += 128) { - myFile.read(sdBuffer, 128); - // Write command sequence - writeBank_SFM(startBank, 0x5555L * 2, 0xaa); - writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); - writeBank_SFM(startBank, 0x5555L * 2, 0xa0); - - for (byte c = 0; c < 128; c++) { - - // Write one byte of data - writeBank_SFM(currBank, currByte + c, sdBuffer[c]); - - if (c == 127) { - // Write the last byte twice or else it won't write at all - writeBank_SFM(currBank, currByte + c, sdBuffer[c]); - } - } - // Wait until write is finished - busyCheck_SFM(startBank); - } - } - } - else { - // Write lorom - for (int currBank = 0; currBank < numBanks; currBank++) { - for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte += 128) { - myFile.read(sdBuffer, 128); - // Write command sequence - writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0xaa); - writeBank_SFM(0, 0x8000 + 0x2AAAL * 2, 0x55); - writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0xa0); - - for (byte c = 0; c < 128; c++) { - // Write one byte of data - writeBank_SFM(currBank, currByte + c, sdBuffer[c]); - - if (c == 127) { - // Write the last byte twice or else it won't write at all - writeBank_SFM(currBank, currByte + c, sdBuffer[c]); - } - } - // Wait until write is finished - busyCheck_SFM(startBank); - } - } - } - // Close the file: - myFile.close(); - println_Msg(""); - } - else { - print_Error(F("Can't open file on SD"), true); - } -} - -// Delay between write operations based on status register -void busyCheck_SFM(byte startBank) { - // Set data pins to input - dataIn(); - // Set control pins to input and therefore pull CE low and latch status register content - controlIn_SFM(); - - // Read register - readBank_SFM(startBank, 0x0000); - - // Read D7 while D7 = 0 - //1 = B00000001, 1 << 7 = B10000000, PINC = B1XXXXXXX (X = don't care), & = bitwise and - while (!(PINC & (1 << 7))) { - // CE or OE must be toggled with each subsequent status read or the - // completion of a program or erase operation will not be evident. - // Switch RD(PH6) to HIGH - PORTH |= (1 << 6); - - // one nop ~62.5ns - __asm__("nop\n\t"); - - // Switch RD(PH6) to LOW - PORTH &= ~(1 << 6); - - // one nop ~62.5ns - __asm__("nop\n\t"); - } - - // Configure control pins - controlOut_SFM(); - // Set data pins to output - dataOut(); -} - -// Erase the flashrom to 0xFF -void eraseFlash_SFM(int startBank) { - - // Configure control pins - controlOut_SFM(); - // Set data pins to output - dataOut(); - - if (romType) { - // Erase command sequence - writeBank_SFM(startBank, 0x5555L * 2, 0xaa); - writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); - writeBank_SFM(startBank, 0x5555L * 2, 0x80); - writeBank_SFM(startBank, 0x5555L * 2, 0xaa); - writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); - writeBank_SFM(startBank, 0x5555L * 2, 0x10); - } - else { - writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0xaa); - writeBank_SFM(0, 0x8000 + 0x2AAAL * 2, 0x55); - writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0x80); - writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0xaa); - writeBank_SFM(0, 0x8000 + 0x2AAAL * 2, 0x55); - writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0x10); - } - - // Wait for erase to complete - busyCheck_SFM(startBank); -} - -// Check if an erase succeeded, return 1 if blank and 0 if not -byte blankcheck_SFM(int startBank) { - - // Set data pins to input again - dataIn(); - // Set control pins to input - controlIn_SFM(); - - byte blank = 1; - if (romType) { - for (int currBank = startBank; currBank < startBank + numBanks; currBank++) { - for (unsigned long currByte = 0; currByte < 0x10000; currByte++) { - if (readBank_SFM(currBank, currByte) != 0xFF) { - currBank = startBank + numBanks; - blank = 0; - } - } - } - } - else { - for (int currBank = 0; currBank < numBanks; currBank++) { - for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte++) { - if (readBank_SFM(currBank, currByte) != 0xFF) { - currBank = numBanks; - blank = 0; - } - } - } - } - return blank; -} - -// Check if a write succeeded, returns 0 if all is ok and number of errors if not -unsigned long verifyFlash_SFM(int startBank, uint32_t pos) { - unsigned long verified = 0; - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - - // Set file starting position - myFile.seekCur(pos); - - // Set data pins to input - dataIn(); - // Set control pins to input - controlIn_SFM(); - - if (romType) { - for (int currBank = startBank; currBank < startBank + numBanks; currBank++) { - for (unsigned long currByte = 0; currByte < 0x10000; currByte += 512) { - // Fill SDBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if (readBank_SFM(currBank, currByte + c) != sdBuffer[c]) { - verified++; - } - } - } - } - } - else { - for (int currBank = 0; currBank < numBanks; currBank++) { - for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte += 512) { - // Fill SDBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if (readBank_SFM(currBank, currByte + c) != sdBuffer[c]) { - verified++; - } - } - } - } - } - // Close the file: - myFile.close(); - } - else { - // SD Error - verified = 999999; - print_Error(F("Can't open file on SD"), false); - } - // Return 0 if verified ok, or number of errors - return verified; -} - -// Read flashroms and save them to the SD card -void readFlash_SFM() { - // Set data pins to input - dataIn(); - // Set control pins to input - controlIn_SFM(); - - print_Msg(F("Saving as NP/")); - print_Msg(fileName); - println_Msg(F("...")); - display_Update(); - - // Open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("Can't create file on SD"), true); - } - if (romType) { - for (int currBank = 0xC0; currBank < 0xC0 + numBanks; currBank++) { - for (unsigned long currByte = 0; currByte < 0x10000; currByte += 512) { - for (int c = 0; c < 512; c++) { - sdBuffer[c] = readBank_SFM(currBank, currByte + c); - } - myFile.write(sdBuffer, 512); - } - } - } - else { - for (int currBank = 0; currBank < numBanks; currBank++) { - for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte += 512) { - for (int c = 0; c < 512; c++) { - sdBuffer[c] = readBank_SFM(currBank, currByte + c); - } - myFile.write(sdBuffer, 512); - } - } - } - // Close the file: - myFile.close(); - println_Msg(""); - println_Msg(F("Finished reading")); - display_Update(); -} - -// Display protected sectors/banks as 0xc2 and unprotected as 0x00 -void readSectorProtection_SFM(byte startBank) { - - // Configure control pins - controlOut_SFM(); - // Set data pins to output - dataOut(); - - // Display Sector Protection Status - writeBank_SFM(startBank, 0x5555L * 2, 0xaa); - writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); - writeBank_SFM(startBank, 0x5555L * 2, 0x90); - - // Configure control pins - controlIn_SFM(); - // Set data pins to output - dataIn(); - display_Clear(); - for (int i = 0; i <= 0x1F; i++) { - print_Msg(F("Sector: 0x")); - print_Msg(startBank + i, HEX); - print_Msg(F(" Sector Protect: 0x")); - println_Msg(readBank_SFM(startBank + i, 0x04), HEX); - } - display_Update(); -} - -// Read the current mapping from the hidden "page buffer" and print it -void printMapping() { - // Switch to write - dataOut(); - controlOut_SFM(); - - // Reset to defaults - writeBank_SFM(0xC0, 0x0000, 0x38); - writeBank_SFM(0xC0, 0x0000, 0xd0); - // Read Extended Status Register (GSR and PSR) - writeBank_SFM(0xC0, 0x0000, 0x71); - // Page Buffer Swap - writeBank_SFM(0xC0, 0x0000, 0x72); - // Read Page Buffer - writeBank_SFM(0xC0, 0x0000, 0x75); - - // Switch to read - dataIn(); - controlIn_SFM(); - - // Read the mapping out of the first chip - char buffer[3]; - - for (int currByte = 0xFF00; currByte < 0xFF50; currByte += 10) { - for (int c = 0; c < 10; c++) { - itoa (readBank_SFM(0xC0, currByte + c), buffer, 16); - for (int i = 0; i < 2 - strlen(buffer); i++) { - print_Msg("0"); - } - // Now print the significant bits - print_Msg(buffer); - } - println_Msg(""); - } - display_Update(); - - // Switch to write - dataOut(); - controlOut_SFM(); - - // Reset Flash - writeBank_SFM(0xC0, 0x5555L * 2, 0xaa); - writeBank_SFM(0xC0, 0x2AAAL * 2, 0x55); - writeBank_SFM(0xC0, 0x5555L * 2, 0xf0); - - // Reset Flash - writeBank_SFM(0xE0, 0x5555L * 2, 0xaa); - writeBank_SFM(0xE0, 0x2AAAL * 2, 0x55); - writeBank_SFM(0xE0, 0x5555L * 2, 0xf0); - - // Switch to read - dataIn(); - controlIn_SFM(); -} - -// Read the current mapping from the hidden "page buffer" -void readMapping() { - - // Switch to write - dataOut(); - controlOut_SFM(); - - // Reset to defaults - writeBank_SFM(0xC0, 0x0000, 0x38); - writeBank_SFM(0xC0, 0x0000, 0xd0); - // Read Extended Status Register (GSR and PSR) - writeBank_SFM(0xC0, 0x0000, 0x71); - // Page Buffer Swap - writeBank_SFM(0xC0, 0x0000, 0x72); - // Read Page Buffer - writeBank_SFM(0xC0, 0x0000, 0x75); - - // Switch to read - dataIn(); - controlIn_SFM(); - - // Get name, add extension and convert to char array for sd lib - EEPROM_readAnything(0, foldern); - sprintf(fileName, "NP%d", foldern); - strcat(fileName, ".MAP"); - sd.mkdir("NP", true); - sd.chdir("NP"); - - // 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); - } - - // Read the mapping info out of the 1st chip - for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { - myFile.write(readBank_SFM(0xC0, currByte)); - } - - // Switch to write - dataOut(); - controlOut_SFM(); - - // Reset to defaults - writeBank_SFM(0xE0, 0x0000, 0x38); - writeBank_SFM(0xE0, 0x0000, 0xd0); - // Read Extended Status Register (GSR and PSR) - writeBank_SFM(0xE0, 0x0000, 0x71); - // Page Buffer Swap - writeBank_SFM(0xE0, 0x0000, 0x72); - // Read Page Buffer - writeBank_SFM(0xE0, 0x0000, 0x75); - - // Switch to read - dataIn(); - controlIn_SFM(); - - // Read the mapping info out of the 1st chip - for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { - myFile.write(readBank_SFM(0xE0, currByte)); - } - - // Close the file: - myFile.close(); - - // Switch to write - dataOut(); - controlOut_SFM(); - - // Reset Flash - writeBank_SFM(0xC0, 0x5555L * 2, 0xaa); - writeBank_SFM(0xC0, 0x2AAAL * 2, 0x55); - writeBank_SFM(0xC0, 0x5555L * 2, 0xf0); - - // Reset Flash - writeBank_SFM(0xE0, 0x5555L * 2, 0xaa); - writeBank_SFM(0xE0, 0x2AAAL * 2, 0x55); - writeBank_SFM(0xE0, 0x5555L * 2, 0xf0); - - // Switch to read - dataIn(); - controlIn_SFM(); - - // Signal end of process - print_Msg(F("Saved to NP/")); - println_Msg(fileName); - display_Update(); -} - -void eraseMapping(byte startBank) { - - if (unlockHirom()) { - // Get ID - idFlash_SFM(startBank); - if (strcmp(flashid, "c2f3") == 0) { - resetFlash_SFM(startBank); - - // Switch to write - dataOut(); - controlOut_SFM(); - - // Prepare to erase/write Page Buffer - writeBank_SFM(startBank, 0x5555L * 2, 0xaa); - writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); - writeBank_SFM(startBank, 0x5555L * 2, 0x77); - // Erase Page Buffer - writeBank_SFM(startBank, 0x5555L * 2, 0xaa); - writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); - writeBank_SFM(startBank, 0x5555L * 2, 0xe0); - - // Wait until complete - busyCheck_SFM(startBank); - - // Switch to read - dataIn(); - controlIn_SFM(); - } - else { - print_Error(F("Error: Wrong Flash ID"), true); - } - } - else { - print_Error(F("Unlock failed"), true); - } -} - -// Check if the current mapping is all 0xFF -byte blankcheckMapping_SFM() { - byte blank = 1; - - // Switch to write - dataOut(); - controlOut_SFM(); - - // Reset to defaults - writeBank_SFM(0xC0, 0x0000, 0x38); - writeBank_SFM(0xC0, 0x0000, 0xd0); - // Read Extended Status Register (GSR and PSR) - writeBank_SFM(0xC0, 0x0000, 0x71); - // Page Buffer Swap - writeBank_SFM(0xC0, 0x0000, 0x72); - // Read Page Buffer - writeBank_SFM(0xC0, 0x0000, 0x75); - - // Switch to read - dataIn(); - controlIn_SFM(); - - // Read the mapping info out of the 1st chip - for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { - if (readBank_SFM(0xC0, currByte) != 0xFF) { - blank = 0; - } - } - - // Switch to write - dataOut(); - controlOut_SFM(); - - // Reset to defaults - writeBank_SFM(0xE0, 0x0000, 0x38); - writeBank_SFM(0xE0, 0x0000, 0xd0); - // Read Extended Status Register (GSR and PSR) - writeBank_SFM(0xE0, 0x0000, 0x71); - // Page Buffer Swap - writeBank_SFM(0xE0, 0x0000, 0x72); - // Read Page Buffer - writeBank_SFM(0xE0, 0x0000, 0x75); - - // Switch to read - dataIn(); - controlIn_SFM(); - - // Read the mapping info out of the 1st chip - for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { - if (readBank_SFM(0xE0, currByte) != 0xFF) { - blank = 0; - } - } - - // Switch to write - dataOut(); - controlOut_SFM(); - - // Reset Flash - writeBank_SFM(0xC0, 0x5555L * 2, 0xaa); - writeBank_SFM(0xC0, 0x2AAAL * 2, 0x55); - writeBank_SFM(0xC0, 0x5555L * 2, 0xf0); - - // Reset Flash - writeBank_SFM(0xE0, 0x5555L * 2, 0xaa); - writeBank_SFM(0xE0, 0x2AAAL * 2, 0x55); - writeBank_SFM(0xE0, 0x5555L * 2, 0xf0); - - // Switch to read - dataIn(); - controlIn_SFM(); - - return blank; -} - -void writeMapping_SFM(byte startBank, uint32_t pos) { - - if (unlockHirom()) { - // Get ID - idFlash_SFM(startBank); - if (strcmp(flashid, "c2f3") == 0) { - resetFlash_SFM(startBank); - - // Switch to write - dataOut(); - controlOut_SFM(); - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - - // Seek to a new position in the file - if (pos != 0) - myFile.seekCur(pos); - - // Write to Page Buffer - for (unsigned long currByte = 0xFF00; currByte < 0xFFFF; currByte += 128) { - // Prepare to erase/write Page Buffer - writeBank_SFM(startBank, 0x5555L * 2, 0xaa); - writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); - writeBank_SFM(startBank, 0x5555L * 2, 0x77); - // Write Page Buffer Command - writeBank_SFM(startBank, 0x5555L * 2, 0xaa); - writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); - writeBank_SFM(startBank, 0x5555L * 2, 0x99); - - myFile.read(sdBuffer, 128); - - for (byte c = 0; c < 128; c++) { - writeBank_SFM(startBank, currByte + c, sdBuffer[c]); - // Write last byte twice - if (c == 127) { - writeBank_SFM(startBank, currByte + c, sdBuffer[c]); - } - } - busyCheck_SFM(startBank); - } - - // Close the file: - myFile.close(); - println_Msg(""); - } - else { - print_Error(F("Can't open file on SD"), false); - } - - // Switch to read - dataIn(); - controlIn_SFM(); - } - else { - print_Error(F("Error: Wrong Flash ID"), true); - } - } - else { - print_Error(F("Unlock failed"), true); - } -} - -/****************************************** - SF Memory functions -*****************************************/ -// Switch to HiRom All and unlock Write Protection -boolean unlockHirom() { - romType = 1; - print_Msg(F("Switch to HiRom...")); - display_Update(); - if (send_SFM(0x04) == 0x2A) { - println_Msg(F("OK")); - display_Update(); - // Unlock Write Protection - print_Msg(F("Enable Write...")); - display_Update(); - send_SFM(0x02); - if (readBank_SFM(0, 0x2401) == 0x4) { - println_Msg(F("OK")); - display_Update(); - return 1; - } - else { - println_Msg(F("failed")); - display_Update(); - return 0; - } - } - else { - println_Msg(F("failed")); - display_Update(); - return 0; - } -} - -// Send a command to the MX15001 chip -byte send_SFM(byte command) { - // Switch to write - dataOut(); - controlOut_SFM(); - - // Write command - writeBank_SFM(0, 0x2400, 0x09); - - // Switch to read - dataIn(); - controlIn_SFM(); - - // Read status - sfmReady = readBank_SFM(0, 0x2400); - - // Switch to write - dataOut(); - controlOut_SFM(); - - writeBank_SFM(0, 0x2401, 0x28); - writeBank_SFM(0, 0x2401, 0x84); - - // NP_CMD_06h, send this only if above read has returned 7Dh, not if it's already returning 2Ah - if (sfmReady == 0x7D) { - writeBank_SFM(0, 0x2400, 0x06); - writeBank_SFM(0, 0x2400, 0x39); - } - - // Write the command - writeBank_SFM(0, 0x2400, command); - - // Switch to read - dataIn(); - controlIn_SFM(); - - // Read status - sfmReady = readBank_SFM(0, 0x2400); - return sfmReady; -} - -// This function will erase and program the NP cart from a 4MB file off the SD card -void write_SFM(int startBank, uint32_t pos) { - // Switch NP cart's mapping - if (unlockHirom()) { - // Get ID - idFlash_SFM(startBank); - if (strcmp(flashid, "c2f3") == 0) { - print_Msg(F("Flash ID: ")); - println_Msg(flashid); - display_Update(); - resetFlash_SFM(startBank); - delay(1000); - // Erase flash - print_Msg(F("Blankcheck...")); - display_Update(); - if (blankcheck_SFM(startBank)) { - println_Msg(F("OK")); - display_Update(); - } - else { - println_Msg(F("Nope")); - display_Clear(); - print_Msg(F("Erasing...")); - display_Update(); - eraseFlash_SFM(startBank); - resetFlash_SFM(startBank); - println_Msg(F("Done")); - print_Msg(F("Blankcheck...")); - display_Update(); - if (blankcheck_SFM(startBank)) { - println_Msg(F("OK")); - display_Update(); - } - else { - print_Error(F("Could not erase flash"), true); - } - } - // Write flash - writeFlash_SFM(startBank, pos); - - // Reset flash - resetFlash_SFM(startBank); - - // Checking for errors - print_Msg(F("Verifying...")); - display_Update(); - writeErrors = verifyFlash_SFM(startBank, pos); - if (writeErrors == 0) { - println_Msg(F("OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), true); - } - } - else { - print_Error(F("Error: Wrong Flash ID"), true); - } - } - else { - print_Error(F("Unlock failed"), true); - } -} - -#endif - -//****************************************** -// End of File -//****************************************** +//****************************************** +// NINTENDO POWER SF MEMORY MODULE +//****************************************** + +#include "options.h" +#ifdef enable_NP +#include "snes_clk.h" + +/****************************************** + SF Memory Clock Source +******************************************/ +// The clock signal for the SF Memory cassette +// is generated with the Adafruit Clock Generator +// or a similar external clock source + +/****************************************** + Variables + *****************************************/ +// SF Memory status +byte sfmReady = 0; + +// SF Memory Menu +boolean hasMenu = true; +byte numGames = 0; + +// Arrays that hold game info +int gameSize[8]; +int saveSize[8]; +byte gameAddress[8]; +byte gameVersion[8]; +char gameCode[8][10]; +boolean hirom[8]; + +/****************************************** + Menu +*****************************************/ +// SFM menu items +static const char sfmMenuItem1[] PROGMEM = "Game Menu"; +static const char sfmMenuItem2[] PROGMEM = "Flash Menu"; +static const char sfmMenuItem3[] PROGMEM = "Reset"; +static const char* const menuOptionsSFM[] PROGMEM = {sfmMenuItem1, sfmMenuItem2, sfmMenuItem3}; + +// SFM flash menu items +static const char sfmFlashMenuItem1[] PROGMEM = "Read Flash"; +static const char sfmFlashMenuItem2[] PROGMEM = "Write Flash"; +static const char sfmFlashMenuItem3[] PROGMEM = "Print Mapping"; +static const char sfmFlashMenuItem4[] PROGMEM = "Read Mapping"; +static const char sfmFlashMenuItem5[] PROGMEM = "Write Mapping"; +static const char sfmFlashMenuItem6[] PROGMEM = "Back"; +static const char* const menuOptionsSFMFlash[] PROGMEM = {sfmFlashMenuItem1, sfmFlashMenuItem2, sfmFlashMenuItem3, sfmFlashMenuItem4, sfmFlashMenuItem5, sfmFlashMenuItem6}; + +// SFM game menu items +static const char sfmGameMenuItem1[] PROGMEM = "Read Sram"; +static const char sfmGameMenuItem2[] PROGMEM = "Read Game"; +static const char sfmGameMenuItem3[] PROGMEM = "Write Sram"; +static const char sfmGameMenuItem4[] PROGMEM = "Switch Game"; +static const char sfmGameMenuItem5[] PROGMEM = "Reset"; +static const char* const menuOptionsSFMGame[] PROGMEM = {sfmGameMenuItem1, sfmGameMenuItem2, sfmGameMenuItem3, sfmGameMenuItem4, sfmGameMenuItem5}; + +void sfmMenu() { + // create menu with title and 3 options to choose from + unsigned char mainMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsSFM, 3); + mainMenu = question_box(F("SF Memory"), menuOptions, 3, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + // Game menu + case 0: + sfmGameMenu(); + break; + // Flash menu + case 1: + mode = mode_SFM_Flash; + break; + // Reset + case 2: + resetArduino(); + break; + } +} + +void sfmGameMenu() { + // Switch to hirom all + if (send_SFM(0x04) == 0x2A) { + delay(300); + + // Fill arrays with data + getGames(); + + if (hasMenu) { + // Create submenu options + char menuOptionsSFMGames[8][20]; + for (int i = 0; i < (numGames); i++) { + strncpy(menuOptionsSFMGames[i], gameCode[i], 10); + } + + // 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(F("Select Game"), menuOptionsSFMGames, numGames, 0); + + // Switch to game + send_SFM(gameSubMenu + 0x80); + delay(200); + // Check for successfull switch + byte timeout = 0; + while (readBank_SFM(0, 0x2400) != 0x7D) { + delay(200); + // Try again + send_SFM(gameSubMenu + 0x80); + delay(200); + timeout++; + // Abort, something is wrong + if (timeout == 5) { + display_Clear(); + print_Msg(F("Game ")); + print_Msg(gameSubMenu + 0x80, HEX); + println_Msg(F(" Timeout")); + println_Msg(readBank_SFM(0, 0x2400), HEX); + println_Msg(F("")); + print_Error(F("Powercycle SFM cart"), true); + } + } + // Copy gameCode to romName in case of japanese chars in romName + strcpy(romName, gameCode[gameSubMenu + 1]); + + // Print info + getCartInfo_SFM(); + mode = mode_SFM_Game; + } + else { + // No menu so switch to only game + // Switch to game + send_SFM(0x80); + delay(200); + + // Copy gameCode to romName in case of japanese chars in romName + strcpy(romName, gameCode[0]); + + // Print info + getCartInfo_SFM(); + mode = mode_SFM_Game; + } + } + else { + print_Error(F("Switch to HiRom failed"), false); + } +} + +void sfmGameOptions() { + // create menu with title and 5 options to choose from + unsigned char gameSubMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsSFMGame, 5); + gameSubMenu = question_box(F("SFM Game Menu"), menuOptions, 5, 0); + + // wait for user choice to come back from the question box menu + switch (gameSubMenu) + { + // Read sram + case 0: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readSRAM(); + break; + + // Read rom + case 1: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readROM_SFM(); + compare_checksum(); + break; + + // Write sram + 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; + + // Switch game + case 3: + sfmGameMenu(); + break; + + // Reset + case 4: + resetArduino(); + break; + } + if (gameSubMenu != 3) { + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + } +} + +#ifdef enable_FLASH +void sfmFlashMenu() { + // create menu with title and 6 options to choose from + unsigned char flashSubMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsSFMFlash, 6); + flashSubMenu = question_box(F("SFM Flash Menu"), menuOptions, 6, 0); + + // wait for user choice to come back from the question box menu + switch (flashSubMenu) + { + // Read Flash + case 0: + // Clear screen + display_Clear(); + + // Reset to root directory + sd.chdir("/"); + + // Reset to HIROM ALL + romType = 1; + print_Msg(F("Switch to HiRom...")); + display_Update(); + if (send_SFM(0x04) == 0x2A) { + println_Msg(F("OK")); + display_Update(); + + // Reset flash + resetFlash_SFM(0xC0); + resetFlash_SFM(0xE0); + + flashSize = 4194304; + numBanks = 64; + + // Get name, add extension and convert to char array for sd lib + EEPROM_readAnything(0, foldern); + sprintf(fileName, "SFM%d", foldern); + strcat(fileName, ".bin"); + sd.mkdir("NP", true); + sd.chdir("NP"); + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(0, foldern); + + // Read flash + readFlash_SFM(); + } + else { + print_Error(F("Switch to HiRom failed"), false); + } + break; + + // Write Flash + case 1: + // Clear screen + display_Clear(); + + // Print warning + println_Msg(F("Attention")); + println_Msg(F("This will erase your")); + println_Msg(F("NP Cartridge.")); + println_Msg(""); + println_Msg(F("Press Button")); + println_Msg(F("to continue")); + display_Update(); + wait(); + + // Clear screen + display_Clear(); + + filePath[0] = '\0'; + sd.chdir("/"); + // Launch file browser + fileBrowser(F("Select 4MB file")); + display_Clear(); + sprintf(filePath, "%s/%s", filePath, fileName); + flashSize = 2097152; + numBanks = 32; + println_Msg(F("Writing 1st rom")); + display_Update(); + // Program 1st flashrom + write_SFM(0xC0, 0); + display_Clear(); + println_Msg(F("Writing 2nd rom")); + display_Update(); + // Program 2nd flashrom + write_SFM(0xE0, 2097152); + break; + + // Print mapping + case 2: + // Clear screen + display_Clear(); + + // Reset to root directory + sd.chdir("/"); + + // Reset to HIROM ALL + romType = 1; + print_Msg(F("Switch to HiRom...")); + display_Update(); + if (send_SFM(0x04) == 0x2A) { + println_Msg(F("OK")); + display_Update(); + idFlash_SFM(0xC0); + if (strcmp(flashid, "c2f3") == 0) { + idFlash_SFM(0xE0); + if (strcmp(flashid, "c2f3") == 0) { + // Reset flash + resetFlash_SFM(0xC0); + resetFlash_SFM(0xE0); + delay(100); + // Clear screen + display_Clear(); + printMapping(); + resetFlash_SFM(0xC0); + resetFlash_SFM(0xE0); + } + else { + print_Error(F("Error: Wrong Flash ID"), true); + } + } + else { + print_Error(F("Error: Wrong Flash ID"), true); + } + } + else { + print_Error(F("failed"), false); + } + break; + + // Read mapping + case 3: + // Clear screen + display_Clear(); + + // Reset to root directory + sd.chdir("/"); + + // Reset to HIROM ALL + romType = 1; + print_Msg(F("Switch to HiRom...")); + display_Update(); + if (send_SFM(0x04) == 0x2A) { + println_Msg(F("OK")); + display_Update(); + idFlash_SFM(0xC0); + if (strcmp(flashid, "c2f3") == 0) { + idFlash_SFM(0xE0); + if (strcmp(flashid, "c2f3") == 0) { + // Reset flash + resetFlash_SFM(0xC0); + resetFlash_SFM(0xE0); + delay(100); + readMapping(); + resetFlash_SFM(0xC0); + resetFlash_SFM(0xE0); + } + else { + print_Error(F("Error: Wrong Flash ID"), true); + } + } + else { + print_Error(F("Error: Wrong Flash ID"), true); + } + } + else { + print_Error(F("failed"), false); + } + break; + + // Write mapping + case 4: + // Clear screen + display_Clear(); + + // Print warning + println_Msg(F("Attention")); + println_Msg(F("This will erase your")); + println_Msg(F("NP Cartridge.")); + println_Msg(""); + println_Msg(F("Press Button")); + println_Msg(F("to continue")); + display_Update(); + wait(); + + // Clear screen + display_Clear(); + + // Reset to root directory + sd.chdir("/"); + + // Erase mapping + eraseMapping(0xD0); + eraseMapping(0xE0); + print_Msg(F("Blankcheck...")); + display_Update(); + if (blankcheckMapping_SFM()) { + println_Msg(F("OK")); + display_Update(); + } + else { + println_Msg(F("Nope")); + break; + } + + // Clear screen + display_Clear(); + + // Clear filepath + filePath[0] = '\0'; + + // Reset to root directory + sd.chdir("/"); + + // Launch file browser + fileBrowser(F("Select MAP file")); + display_Clear(); + sprintf(filePath, "%s/%s", filePath, fileName); + display_Update(); + + // Write mapping + writeMapping_SFM(0xD0, 0); + writeMapping_SFM(0xE0, 256); + break; + + // Go back + case 5: + mode = mode_SFM; + break; + } + if (flashSubMenu != 5) { + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + } +} +#endif + +// Read the games from the menu area +void getGames() { + // Set data pins to input + dataIn(); + // Set control pins to input + controlIn_SFM(); + + // Check if menu is present + byte menuString[] = {0x4D, 0x45, 0x4E, 0x55, 0x20, 0x50, 0x52, 0x4F, 0x47, 0x52, 0x41, 0x4D}; + for (int i = 0; i < 12; i++) { + if (menuString[i] != readBank_SFM(0xC0, 0x7FC0 + i)) { + hasMenu = false; + } + } + + if (hasMenu) { + // Count number of games + for (word i = 0x0000; i < 0xE000; i += 0x2000) { + if (readBank_SFM(0xC6, i) == numGames ) + numGames++; + } + + // Get game info + for (int i = 0; i < numGames; i++) { + // Read starting address and size + gameAddress[i] = 0xC0 + readBank_SFM(0xC6, i * 0x2000 + 0x01) * 0x8; + gameSize[i] = readBank_SFM(0xC6, i * 0x2000 + 0x03) * 128; + saveSize[i] = readBank_SFM(0xC6, i * 0x2000 + 0x05) / 8; + + //check if hirom + if (readBank_SFM(gameAddress[i], 0xFFD5) == 0x31) { + hirom[i] = true; + } + else if (readBank_SFM(gameAddress[i], 0xFFD5) == 0x21) { + hirom[i] = true; + } + else { + hirom[i] = false; + } + + if (hirom[i]) { + gameVersion[i] = readBank_SFM(gameAddress[i], 0xFFDB); + } else { + gameVersion[i] = readBank_SFM(gameAddress[i], 0x7FDB); + } + + // Read game code + byte myByte = 0; + byte myLength = 0; + for (int j = 0; j < 9; j++) { + myByte = readBank_SFM(0xC6, i * 0x2000 + 0x07 + j); + // Remove funny characters + if (((char(myByte) >= 44 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 9) { + gameCode[i][myLength] = char(myByte); + myLength++; + } + } + // End char array in case game code is less than 9 chars + gameCode[i][myLength] = '\0'; + } + } + else { + //check if hirom + if (readBank_SFM(0xC0, 0xFFD5) == 0x31) { + hirom[0] = true; + } + else { + hirom[0] = false; + } + + if (hirom[0]) { + gameVersion[0] = readBank_SFM(0xC0, 0xFFDB); + gameCode[0][0] = 'G'; + gameCode[0][1] = 'A'; + gameCode[0][2] = 'M'; + gameCode[0][3] = 'E'; + gameCode[0][4] = '-'; + gameCode[0][5] = char(readBank_SFM(0xC0, 0xFFB2)); + gameCode[0][6] = char(readBank_SFM(0xC0, 0xFFB3)); + gameCode[0][7] = char(readBank_SFM(0xC0, 0xFFB4)); + gameCode[0][8] = char(readBank_SFM(0xC0, 0xFFB5)); + gameCode[0][9] = '\0'; + + byte romSizeExp = readBank_SFM(0xC0, 0xFFD7) - 7; + gameSize[0] = 1; + while (romSizeExp--) + gameSize[0] *= 2; + } + else { + gameVersion[0] = readBank_SFM(0xC0, 0x7FDB); + gameCode[0][0] = 'G'; + gameCode[0][1] = 'A'; + gameCode[0][2] = 'M'; + gameCode[0][3] = 'E'; + gameCode[0][4] = '-'; + gameCode[0][5] = char(readBank_SFM(0xC0, 0x7FB2)); + gameCode[0][6] = char(readBank_SFM(0xC0, 0x7FB3)); + gameCode[0][7] = char(readBank_SFM(0xC0, 0x7FB4)); + gameCode[0][8] = char(readBank_SFM(0xC0, 0x7FB5)); + gameCode[0][9] = '\0'; + + byte romSizeExp = readBank_SFM(0xC0, 0x7FD7) - 7; + gameSize[0] = 1; + while (romSizeExp--) + gameSize[0] *= 2; + } + } +} + +/****************************************** + Setup + *****************************************/ +void setup_SFM() { + // Set cicrstPin(PG1) to Output + DDRG |= (1 << 1); + // Output a high signal to disable snesCIC + PORTG |= (1 << 1); + // Set cichstPin(PG0) to Input + DDRG &= ~(1 << 0); + + // Set Address Pins to Output + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //BA0-BA7 + DDRL = 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 IRQ(PH4) to Input + DDRH &= ~(1 << 4); + // Activate Internal Pullup Resistors + //PORTH |= (1 << 4); + + // Set Data Pins (D0-D7) to Input + DDRC = 0x00; + // Enable Internal Pullups + //PORTC = 0xFF; + + // Unused pins + // Set CPU Clock(PH1) to Output + DDRH |= (1 << 1); + //PORTH &= ~(1 << 1); + + // Adafruit Clock Generator + int32_t clock_offset = readClockOffset(); + if (clock_offset > INT32_MIN) { + clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, clock_offset); + } else { + clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, -16000); + } + clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); + clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); + clockgen.set_freq(2147727200ULL, SI5351_CLK0); + + // start outputting master clock + clockgen.output_enable(SI5351_CLK1, 0); + clockgen.output_enable(SI5351_CLK2, 0); + clockgen.output_enable(SI5351_CLK0, 1); + + // Wait until all is stable + delay(500); + + // Switch to HiRom All + byte timeout = 0; + send_SFM(0x04); + delay(200); + while (readBank_SFM(0, 0x2400) != 0x2A) { + delay(100); + // Try again + send_SFM(0x04); + delay(100); + timeout++; + // Abort, something is wrong + if (timeout == 5) { + println_Msg(F("Hirom All Timeout")); + println_Msg(F("")); + println_Msg(F("")); + print_Error(F("Powercycle SFM cart"), true); + } + } +} + +/****************************************** + I/O Functions + *****************************************/ +// Switch control pins to write +void controlOut_SFM() { + // 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_SFM() { + // 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_SFM(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_SFM(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_SFM() { + // Print start page + if (checkcart_SFM() == 0) { + // Checksum either corrupt or 0000 + errorLvl = 1; + rgb.setColor(255, 0, 0); + display_Clear(); + println_Msg(F("ERROR")); + println_Msg(F("Rom header corrupt")); + println_Msg(F("or missing")); + display_Update(); + wait(); + // Wait() clears errors but in this case we still have an error + errorLvl = 1; + } + + display_Clear(); + print_Msg(F("Name: ")); + println_Msg(romName); + println_Msg(F(" ")); + + print_Msg(F("Version: 1.")); + println_Msg(romVersion); + + print_Msg(F("Checksum: ")); + println_Msg(checksumStr); + + print_Msg(F("Size: ")); + print_Msg(romSize); + println_Msg(F("Mbit ")); + + print_Msg(F("Type: ")); + if (romType == 1) + println_Msg(F("HiROM")); + else if (romType == 0) + println_Msg(F("LoROM")); + else + println_Msg(romType); + + print_Msg(F("Banks: ")); + println_Msg(numBanks); + + print_Msg(F("Sram: ")); + print_Msg(sramSize); + println_Msg(F("Kbit")); + println_Msg(F("Press Button")); + display_Update(); + // Wait for user input + wait(); +} + +// Read header information +boolean checkcart_SFM() { + // set control to read + dataIn(); + + // Get Checksum as string + sprintf(checksumStr, "%02X%02X", readBank_SFM(0, 65503), readBank_SFM(0, 65502)); + + romType = readBank_SFM(0, 0xFFD5); + if ((romType >> 5) != 1) { // Detect invalid romType byte due to too long ROM name (22 chars) + romType = 0; // LoROM // Krusty's Super Fun House (U) 1.0 & Contra 3 (U) + } + else { + romType &= 1; // Must be LoROM or HiROM + } + + // Check RomSize + byte romSizeExp = readBank_SFM(0, 65495) - 7; + romSize = 1; + while (romSizeExp--) + romSize *= 2; + + numBanks = (long(romSize) * 1024 * 1024 / 8) / (32768 + (long(romType) * 32768)); + + //Check SD card for alt config + checkAltConf(); + + // Get name + byte myByte = 0; + byte myLength = 0; + for (unsigned int i = 65472; i < 65492; i++) { + myByte = readBank_SFM(0, i); + if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 15) { + romName[myLength] = char(myByte); + myLength++; + } + } + // If name consists out of all japanese characters use game code + if (myLength == 0) { + // Get rom code + romName[0] = 'S'; + romName[1] = 'H'; + romName[2] = 'V'; + romName[3] = 'C'; + romName[4] = '-'; + for (unsigned int i = 0; i < 4; i++) { + myByte = readBank_SFM(0, 0xFFB2 + i); + if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 4) { + romName[myLength + 5] = char(myByte); + myLength++; + } + } + if (myLength == 0) { + // Rom code unknown + romName[0] = 'U'; + romName[1] = 'N'; + romName[2] = 'K'; + romName[3] = 'N'; + romName[4] = 'O'; + romName[5] = 'W'; + romName[6] = 'N'; + } + } + + // Read sramSizeExp + byte sramSizeExp = readBank_SFM(0, 0xFFD8); + + // Calculate sramSize + if (sramSizeExp != 0) { + sramSizeExp = sramSizeExp + 3; + sramSize = 1; + while (sramSizeExp--) + sramSize *= 2; + } + else { + sramSize = 0; + } + + // ROM Version + romVersion = readBank_SFM(0, 65499); + + // Test if checksum is equal to reverse checksum + if (((word(readBank_SFM(0, 65500)) + (word(readBank_SFM(0, 65501)) * 256)) + (word(readBank_SFM(0, 65502)) + (word(readBank_SFM(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; + } +} + +// Read rom to SD card +void readROM_SFM() { + // Set control + dataIn(); + controlIn_SFM(); + + // 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, "NP/%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 == 0) { + 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_SFM(currBank, currByte + c); + } + myFile.write(sdBuffer, 512); + } + } + } + // Dump High-type ROM + else { + 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_SFM(currBank, currByte + c); + } + myFile.write(sdBuffer, 512); + } + } + } + // Close the file: + myFile.close(); + + // Signal end of process + print_Msg(F("Saved as ")); + println_Msg(fileName); +} + +/****************************************** + 29F1601 flashrom functions (NP) + *****************************************/ +// Reset the MX29F1601 flashrom, startbank is 0xC0 for first and 0xE0 for second flashrom +void resetFlash_SFM(int startBank) { + // Configure control pins + controlOut_SFM(); + // Set data pins to output + dataOut(); + + // Reset command sequence + if (romType) { + writeBank_SFM(startBank, 0x5555L * 2, 0xaa); + writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); + writeBank_SFM(startBank, 0x5555L * 2, 0xf0); + } + else { + writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0xaa); + writeBank_SFM(0, 0x8000 + 0x2AAAL * 2, 0x55); + writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0xf0); + } +} + +// Print flashrom manufacturer and device ID +void idFlash_SFM(int startBank) { + // Configure control pins + controlOut_SFM(); + // Set data pins to output + dataOut(); + + if (romType) { + // ID command sequence + writeBank_SFM(startBank, 0x5555L * 2, 0xaa); + writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); + writeBank_SFM(startBank, 0x5555L * 2, 0x90); + + // Set data pins to input again + dataIn(); + // Set control pins to input + controlIn_SFM(); + + // Read the two id bytes into a string + sprintf(flashid, "%x%x", readBank_SFM(startBank, 0x00), readBank_SFM(startBank, 0x02)); + } + else { + writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0xaa); + writeBank_SFM(0, 0x8000 + 0x2AAAL * 2, 0x55); + writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0x90); + + // Set data pins to input again + dataIn(); + // Set control pins to input + controlIn_SFM(); + + // Read the two id bytes into a string + sprintf(flashid, "%x%x", readBank_SFM(0, 0x8000), readBank_SFM(0, 0x8000 + 0x02)); + } +} + +// Write the flashroms by reading a file from the SD card, pos defines where in the file the reading/writing should start +void writeFlash_SFM(int startBank, uint32_t pos) { + display_Clear(); + print_Msg(F("Writing Bank 0x")); + print_Msg(startBank, HEX); + print_Msg(F("...")); + display_Update(); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Seek to a new position in the file + if (pos != 0) + myFile.seekCur(pos); + + // Configure control pins + controlOut_SFM(); + // Set data pins to output + dataOut(); + + if (romType) { + // Write hirom + for (int currBank = startBank; currBank < startBank + numBanks; currBank++) { + // Fill SDBuffer with 1 page at a time then write it repeat until all bytes are written + for (unsigned long currByte = 0; currByte < 0x10000; currByte += 128) { + myFile.read(sdBuffer, 128); + // Write command sequence + writeBank_SFM(startBank, 0x5555L * 2, 0xaa); + writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); + writeBank_SFM(startBank, 0x5555L * 2, 0xa0); + + for (byte c = 0; c < 128; c++) { + + // Write one byte of data + writeBank_SFM(currBank, currByte + c, sdBuffer[c]); + + if (c == 127) { + // Write the last byte twice or else it won't write at all + writeBank_SFM(currBank, currByte + c, sdBuffer[c]); + } + } + // Wait until write is finished + busyCheck_SFM(startBank); + } + } + } + else { + // Write lorom + for (int currBank = 0; currBank < numBanks; currBank++) { + for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte += 128) { + myFile.read(sdBuffer, 128); + // Write command sequence + writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0xaa); + writeBank_SFM(0, 0x8000 + 0x2AAAL * 2, 0x55); + writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0xa0); + + for (byte c = 0; c < 128; c++) { + // Write one byte of data + writeBank_SFM(currBank, currByte + c, sdBuffer[c]); + + if (c == 127) { + // Write the last byte twice or else it won't write at all + writeBank_SFM(currBank, currByte + c, sdBuffer[c]); + } + } + // Wait until write is finished + busyCheck_SFM(startBank); + } + } + } + // Close the file: + myFile.close(); + println_Msg(""); + } + else { + print_Error(F("Can't open file on SD"), true); + } +} + +// Delay between write operations based on status register +void busyCheck_SFM(byte startBank) { + // Set data pins to input + dataIn(); + // Set control pins to input and therefore pull CE low and latch status register content + controlIn_SFM(); + + // Read register + readBank_SFM(startBank, 0x0000); + + // Read D7 while D7 = 0 + //1 = B00000001, 1 << 7 = B10000000, PINC = B1XXXXXXX (X = don't care), & = bitwise and + while (!(PINC & (1 << 7))) { + // CE or OE must be toggled with each subsequent status read or the + // completion of a program or erase operation will not be evident. + // Switch RD(PH6) to HIGH + PORTH |= (1 << 6); + + // one nop ~62.5ns + __asm__("nop\n\t"); + + // Switch RD(PH6) to LOW + PORTH &= ~(1 << 6); + + // one nop ~62.5ns + __asm__("nop\n\t"); + } + + // Configure control pins + controlOut_SFM(); + // Set data pins to output + dataOut(); +} + +// Erase the flashrom to 0xFF +void eraseFlash_SFM(int startBank) { + + // Configure control pins + controlOut_SFM(); + // Set data pins to output + dataOut(); + + if (romType) { + // Erase command sequence + writeBank_SFM(startBank, 0x5555L * 2, 0xaa); + writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); + writeBank_SFM(startBank, 0x5555L * 2, 0x80); + writeBank_SFM(startBank, 0x5555L * 2, 0xaa); + writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); + writeBank_SFM(startBank, 0x5555L * 2, 0x10); + } + else { + writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0xaa); + writeBank_SFM(0, 0x8000 + 0x2AAAL * 2, 0x55); + writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0x80); + writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0xaa); + writeBank_SFM(0, 0x8000 + 0x2AAAL * 2, 0x55); + writeBank_SFM(1, 0x8000 + 0x1555L * 2, 0x10); + } + + // Wait for erase to complete + busyCheck_SFM(startBank); +} + +// Check if an erase succeeded, return 1 if blank and 0 if not +byte blankcheck_SFM(int startBank) { + + // Set data pins to input again + dataIn(); + // Set control pins to input + controlIn_SFM(); + + byte blank = 1; + if (romType) { + for (int currBank = startBank; currBank < startBank + numBanks; currBank++) { + for (unsigned long currByte = 0; currByte < 0x10000; currByte++) { + if (readBank_SFM(currBank, currByte) != 0xFF) { + currBank = startBank + numBanks; + blank = 0; + } + } + } + } + else { + for (int currBank = 0; currBank < numBanks; currBank++) { + for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte++) { + if (readBank_SFM(currBank, currByte) != 0xFF) { + currBank = numBanks; + blank = 0; + } + } + } + } + return blank; +} + +// Check if a write succeeded, returns 0 if all is ok and number of errors if not +unsigned long verifyFlash_SFM(int startBank, uint32_t pos) { + unsigned long verified = 0; + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Set file starting position + myFile.seekCur(pos); + + // Set data pins to input + dataIn(); + // Set control pins to input + controlIn_SFM(); + + if (romType) { + for (int currBank = startBank; currBank < startBank + numBanks; currBank++) { + for (unsigned long currByte = 0; currByte < 0x10000; currByte += 512) { + // Fill SDBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if (readBank_SFM(currBank, currByte + c) != sdBuffer[c]) { + verified++; + } + } + } + } + } + else { + for (int currBank = 0; currBank < numBanks; currBank++) { + for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte += 512) { + // Fill SDBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if (readBank_SFM(currBank, currByte + c) != sdBuffer[c]) { + verified++; + } + } + } + } + } + // Close the file: + myFile.close(); + } + else { + // SD Error + verified = 999999; + print_Error(F("Can't open file on SD"), false); + } + // Return 0 if verified ok, or number of errors + return verified; +} + +// Read flashroms and save them to the SD card +void readFlash_SFM() { + // Set data pins to input + dataIn(); + // Set control pins to input + controlIn_SFM(); + + print_Msg(F("Saving as NP/")); + print_Msg(fileName); + println_Msg(F("...")); + display_Update(); + + // Open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("Can't create file on SD"), true); + } + if (romType) { + for (int currBank = 0xC0; currBank < 0xC0 + numBanks; currBank++) { + for (unsigned long currByte = 0; currByte < 0x10000; currByte += 512) { + for (int c = 0; c < 512; c++) { + sdBuffer[c] = readBank_SFM(currBank, currByte + c); + } + myFile.write(sdBuffer, 512); + } + } + } + else { + for (int currBank = 0; currBank < numBanks; currBank++) { + for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte += 512) { + for (int c = 0; c < 512; c++) { + sdBuffer[c] = readBank_SFM(currBank, currByte + c); + } + myFile.write(sdBuffer, 512); + } + } + } + // Close the file: + myFile.close(); + println_Msg(""); + println_Msg(F("Finished reading")); + display_Update(); +} + +// Display protected sectors/banks as 0xc2 and unprotected as 0x00 +void readSectorProtection_SFM(byte startBank) { + + // Configure control pins + controlOut_SFM(); + // Set data pins to output + dataOut(); + + // Display Sector Protection Status + writeBank_SFM(startBank, 0x5555L * 2, 0xaa); + writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); + writeBank_SFM(startBank, 0x5555L * 2, 0x90); + + // Configure control pins + controlIn_SFM(); + // Set data pins to output + dataIn(); + display_Clear(); + for (int i = 0; i <= 0x1F; i++) { + print_Msg(F("Sector: 0x")); + print_Msg(startBank + i, HEX); + print_Msg(F(" Sector Protect: 0x")); + println_Msg(readBank_SFM(startBank + i, 0x04), HEX); + } + display_Update(); +} + +// Read the current mapping from the hidden "page buffer" and print it +void printMapping() { + // Switch to write + dataOut(); + controlOut_SFM(); + + // Reset to defaults + writeBank_SFM(0xC0, 0x0000, 0x38); + writeBank_SFM(0xC0, 0x0000, 0xd0); + // Read Extended Status Register (GSR and PSR) + writeBank_SFM(0xC0, 0x0000, 0x71); + // Page Buffer Swap + writeBank_SFM(0xC0, 0x0000, 0x72); + // Read Page Buffer + writeBank_SFM(0xC0, 0x0000, 0x75); + + // Switch to read + dataIn(); + controlIn_SFM(); + + // Read the mapping out of the first chip + char buffer[3]; + + for (int currByte = 0xFF00; currByte < 0xFF50; currByte += 10) { + for (int c = 0; c < 10; c++) { + itoa (readBank_SFM(0xC0, currByte + c), buffer, 16); + for (int i = 0; i < 2 - strlen(buffer); i++) { + print_Msg("0"); + } + // Now print the significant bits + print_Msg(buffer); + } + println_Msg(""); + } + display_Update(); + + // Switch to write + dataOut(); + controlOut_SFM(); + + // Reset Flash + writeBank_SFM(0xC0, 0x5555L * 2, 0xaa); + writeBank_SFM(0xC0, 0x2AAAL * 2, 0x55); + writeBank_SFM(0xC0, 0x5555L * 2, 0xf0); + + // Reset Flash + writeBank_SFM(0xE0, 0x5555L * 2, 0xaa); + writeBank_SFM(0xE0, 0x2AAAL * 2, 0x55); + writeBank_SFM(0xE0, 0x5555L * 2, 0xf0); + + // Switch to read + dataIn(); + controlIn_SFM(); +} + +// Read the current mapping from the hidden "page buffer" +void readMapping() { + + // Switch to write + dataOut(); + controlOut_SFM(); + + // Reset to defaults + writeBank_SFM(0xC0, 0x0000, 0x38); + writeBank_SFM(0xC0, 0x0000, 0xd0); + // Read Extended Status Register (GSR and PSR) + writeBank_SFM(0xC0, 0x0000, 0x71); + // Page Buffer Swap + writeBank_SFM(0xC0, 0x0000, 0x72); + // Read Page Buffer + writeBank_SFM(0xC0, 0x0000, 0x75); + + // Switch to read + dataIn(); + controlIn_SFM(); + + // Get name, add extension and convert to char array for sd lib + EEPROM_readAnything(0, foldern); + sprintf(fileName, "NP%d", foldern); + strcat(fileName, ".MAP"); + sd.mkdir("NP", true); + sd.chdir("NP"); + + // 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); + } + + // Read the mapping info out of the 1st chip + for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { + myFile.write(readBank_SFM(0xC0, currByte)); + } + + // Switch to write + dataOut(); + controlOut_SFM(); + + // Reset to defaults + writeBank_SFM(0xE0, 0x0000, 0x38); + writeBank_SFM(0xE0, 0x0000, 0xd0); + // Read Extended Status Register (GSR and PSR) + writeBank_SFM(0xE0, 0x0000, 0x71); + // Page Buffer Swap + writeBank_SFM(0xE0, 0x0000, 0x72); + // Read Page Buffer + writeBank_SFM(0xE0, 0x0000, 0x75); + + // Switch to read + dataIn(); + controlIn_SFM(); + + // Read the mapping info out of the 1st chip + for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { + myFile.write(readBank_SFM(0xE0, currByte)); + } + + // Close the file: + myFile.close(); + + // Switch to write + dataOut(); + controlOut_SFM(); + + // Reset Flash + writeBank_SFM(0xC0, 0x5555L * 2, 0xaa); + writeBank_SFM(0xC0, 0x2AAAL * 2, 0x55); + writeBank_SFM(0xC0, 0x5555L * 2, 0xf0); + + // Reset Flash + writeBank_SFM(0xE0, 0x5555L * 2, 0xaa); + writeBank_SFM(0xE0, 0x2AAAL * 2, 0x55); + writeBank_SFM(0xE0, 0x5555L * 2, 0xf0); + + // Switch to read + dataIn(); + controlIn_SFM(); + + // Signal end of process + print_Msg(F("Saved to NP/")); + println_Msg(fileName); + display_Update(); +} + +void eraseMapping(byte startBank) { + + if (unlockHirom()) { + // Get ID + idFlash_SFM(startBank); + if (strcmp(flashid, "c2f3") == 0) { + resetFlash_SFM(startBank); + + // Switch to write + dataOut(); + controlOut_SFM(); + + // Prepare to erase/write Page Buffer + writeBank_SFM(startBank, 0x5555L * 2, 0xaa); + writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); + writeBank_SFM(startBank, 0x5555L * 2, 0x77); + // Erase Page Buffer + writeBank_SFM(startBank, 0x5555L * 2, 0xaa); + writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); + writeBank_SFM(startBank, 0x5555L * 2, 0xe0); + + // Wait until complete + busyCheck_SFM(startBank); + + // Switch to read + dataIn(); + controlIn_SFM(); + } + else { + print_Error(F("Error: Wrong Flash ID"), true); + } + } + else { + print_Error(F("Unlock failed"), true); + } +} + +// Check if the current mapping is all 0xFF +byte blankcheckMapping_SFM() { + byte blank = 1; + + // Switch to write + dataOut(); + controlOut_SFM(); + + // Reset to defaults + writeBank_SFM(0xC0, 0x0000, 0x38); + writeBank_SFM(0xC0, 0x0000, 0xd0); + // Read Extended Status Register (GSR and PSR) + writeBank_SFM(0xC0, 0x0000, 0x71); + // Page Buffer Swap + writeBank_SFM(0xC0, 0x0000, 0x72); + // Read Page Buffer + writeBank_SFM(0xC0, 0x0000, 0x75); + + // Switch to read + dataIn(); + controlIn_SFM(); + + // Read the mapping info out of the 1st chip + for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { + if (readBank_SFM(0xC0, currByte) != 0xFF) { + blank = 0; + } + } + + // Switch to write + dataOut(); + controlOut_SFM(); + + // Reset to defaults + writeBank_SFM(0xE0, 0x0000, 0x38); + writeBank_SFM(0xE0, 0x0000, 0xd0); + // Read Extended Status Register (GSR and PSR) + writeBank_SFM(0xE0, 0x0000, 0x71); + // Page Buffer Swap + writeBank_SFM(0xE0, 0x0000, 0x72); + // Read Page Buffer + writeBank_SFM(0xE0, 0x0000, 0x75); + + // Switch to read + dataIn(); + controlIn_SFM(); + + // Read the mapping info out of the 1st chip + for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { + if (readBank_SFM(0xE0, currByte) != 0xFF) { + blank = 0; + } + } + + // Switch to write + dataOut(); + controlOut_SFM(); + + // Reset Flash + writeBank_SFM(0xC0, 0x5555L * 2, 0xaa); + writeBank_SFM(0xC0, 0x2AAAL * 2, 0x55); + writeBank_SFM(0xC0, 0x5555L * 2, 0xf0); + + // Reset Flash + writeBank_SFM(0xE0, 0x5555L * 2, 0xaa); + writeBank_SFM(0xE0, 0x2AAAL * 2, 0x55); + writeBank_SFM(0xE0, 0x5555L * 2, 0xf0); + + // Switch to read + dataIn(); + controlIn_SFM(); + + return blank; +} + +void writeMapping_SFM(byte startBank, uint32_t pos) { + + if (unlockHirom()) { + // Get ID + idFlash_SFM(startBank); + if (strcmp(flashid, "c2f3") == 0) { + resetFlash_SFM(startBank); + + // Switch to write + dataOut(); + controlOut_SFM(); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Seek to a new position in the file + if (pos != 0) + myFile.seekCur(pos); + + // Write to Page Buffer + for (unsigned long currByte = 0xFF00; currByte < 0xFFFF; currByte += 128) { + // Prepare to erase/write Page Buffer + writeBank_SFM(startBank, 0x5555L * 2, 0xaa); + writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); + writeBank_SFM(startBank, 0x5555L * 2, 0x77); + // Write Page Buffer Command + writeBank_SFM(startBank, 0x5555L * 2, 0xaa); + writeBank_SFM(startBank, 0x2AAAL * 2, 0x55); + writeBank_SFM(startBank, 0x5555L * 2, 0x99); + + myFile.read(sdBuffer, 128); + + for (byte c = 0; c < 128; c++) { + writeBank_SFM(startBank, currByte + c, sdBuffer[c]); + // Write last byte twice + if (c == 127) { + writeBank_SFM(startBank, currByte + c, sdBuffer[c]); + } + } + busyCheck_SFM(startBank); + } + + // Close the file: + myFile.close(); + println_Msg(""); + } + else { + print_Error(F("Can't open file on SD"), false); + } + + // Switch to read + dataIn(); + controlIn_SFM(); + } + else { + print_Error(F("Error: Wrong Flash ID"), true); + } + } + else { + print_Error(F("Unlock failed"), true); + } +} + +/****************************************** + SF Memory functions +*****************************************/ +// Switch to HiRom All and unlock Write Protection +boolean unlockHirom() { + romType = 1; + print_Msg(F("Switch to HiRom...")); + display_Update(); + if (send_SFM(0x04) == 0x2A) { + println_Msg(F("OK")); + display_Update(); + // Unlock Write Protection + print_Msg(F("Enable Write...")); + display_Update(); + send_SFM(0x02); + if (readBank_SFM(0, 0x2401) == 0x4) { + println_Msg(F("OK")); + display_Update(); + return 1; + } + else { + println_Msg(F("failed")); + display_Update(); + return 0; + } + } + else { + println_Msg(F("failed")); + display_Update(); + return 0; + } +} + +// Send a command to the MX15001 chip +byte send_SFM(byte command) { + // Switch to write + dataOut(); + controlOut_SFM(); + + // Write command + writeBank_SFM(0, 0x2400, 0x09); + + // Switch to read + dataIn(); + controlIn_SFM(); + + // Read status + sfmReady = readBank_SFM(0, 0x2400); + + // Switch to write + dataOut(); + controlOut_SFM(); + + writeBank_SFM(0, 0x2401, 0x28); + writeBank_SFM(0, 0x2401, 0x84); + + // NP_CMD_06h, send this only if above read has returned 7Dh, not if it's already returning 2Ah + if (sfmReady == 0x7D) { + writeBank_SFM(0, 0x2400, 0x06); + writeBank_SFM(0, 0x2400, 0x39); + } + + // Write the command + writeBank_SFM(0, 0x2400, command); + + // Switch to read + dataIn(); + controlIn_SFM(); + + // Read status + sfmReady = readBank_SFM(0, 0x2400); + return sfmReady; +} + +// This function will erase and program the NP cart from a 4MB file off the SD card +void write_SFM(int startBank, uint32_t pos) { + // Switch NP cart's mapping + if (unlockHirom()) { + // Get ID + idFlash_SFM(startBank); + if (strcmp(flashid, "c2f3") == 0) { + print_Msg(F("Flash ID: ")); + println_Msg(flashid); + display_Update(); + resetFlash_SFM(startBank); + delay(1000); + // Erase flash + print_Msg(F("Blankcheck...")); + display_Update(); + if (blankcheck_SFM(startBank)) { + println_Msg(F("OK")); + display_Update(); + } + else { + println_Msg(F("Nope")); + display_Clear(); + print_Msg(F("Erasing...")); + display_Update(); + eraseFlash_SFM(startBank); + resetFlash_SFM(startBank); + println_Msg(F("Done")); + print_Msg(F("Blankcheck...")); + display_Update(); + if (blankcheck_SFM(startBank)) { + println_Msg(F("OK")); + display_Update(); + } + else { + print_Error(F("Could not erase flash"), true); + } + } + // Write flash + writeFlash_SFM(startBank, pos); + + // Reset flash + resetFlash_SFM(startBank); + + // Checking for errors + print_Msg(F("Verifying...")); + display_Update(); + writeErrors = verifyFlash_SFM(startBank, pos); + if (writeErrors == 0) { + println_Msg(F("OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), true); + } + } + else { + print_Error(F("Error: Wrong Flash ID"), true); + } + } + else { + print_Error(F("Unlock failed"), true); + } +} + +#endif + +//****************************************** +// End of File +//****************************************** diff --git a/Cart_Reader/SNES.ino b/Cart_Reader/SNES.ino index 3b4b6ed..45e74e3 100644 --- a/Cart_Reader/SNES.ino +++ b/Cart_Reader/SNES.ino @@ -1,2110 +1,2116 @@ -//****************************************** -// SUPER NINTENDO MODULE -//****************************************** - -#include "options.h" -#ifdef enable_SNES - -/****************************************** - 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, 243 = CX4, 246 = ROM & DSP2 -byte romSizeExp = 0; // ROM-Size Exponent -boolean NP = false; -byte cx4Type = 0; -byte cx4Map = 0; -boolean altconf = 0; - -/****************************************** - Menu -*****************************************/ -// SNES/Nintendo Power SF Memory start menu -static const char snsMenuItem1[] PROGMEM = "Super Nintendo"; -static const char snsMenuItem2[] PROGMEM = "NPower SF Memory"; -static const char snsMenuItem3[] PROGMEM = "Satellaview BS-X"; -static const char snsMenuItem4[] PROGMEM = "HiROM repro"; -static const char snsMenuItem5[] PROGMEM = "LoROM repro"; -static const char snsMenuItem6[] PROGMEM = "Reset"; -static const char* const menuOptionsSNS[] PROGMEM = {snsMenuItem1, snsMenuItem2, snsMenuItem3, snsMenuItem4, snsMenuItem5, snsMenuItem6}; - -// SNES menu items -static const char SnesMenuItem1[] PROGMEM = "Read Rom"; -static const char SnesMenuItem2[] PROGMEM = "Read Save"; -static const char SnesMenuItem3[] PROGMEM = "Write Save"; -static const char SnesMenuItem4[] PROGMEM = "Test SRAM"; -static const char SnesMenuItem5[] PROGMEM = "Cycle cart"; -static const char SnesMenuItem6[] PROGMEM = "Force cart type"; -static const char SnesMenuItem7[] PROGMEM = "Reset"; -static const char* const menuOptionsSNES[] PROGMEM = {SnesMenuItem1, SnesMenuItem2, SnesMenuItem3, SnesMenuItem4, SnesMenuItem5, SnesMenuItem6, SnesMenuItem7}; - -// Manual config menu items -static const char confMenuItem1[] PROGMEM = "Use header info"; -static const char confMenuItem2[] PROGMEM = "4MB LoRom 256K Sram"; -static const char confMenuItem3[] PROGMEM = "4MB HiRom 64K Sram"; -static const char confMenuItem4[] PROGMEM = "6MB ExRom 256K Sram"; -static const char confMenuItem5[] PROGMEM = "Reset"; -static const char* const menuOptionsConfManual[] PROGMEM = {confMenuItem1, confMenuItem2, confMenuItem3, confMenuItem4, confMenuItem5}; - -// SNES start menu -void snsMenu() { - // create menu with title and 6 options to choose from - unsigned char snsCart; - // Copy menuOptions out of progmem - convertPgm(menuOptionsSNS, 6); - snsCart = question_box(F("Select Cart Type"), menuOptions, 6, 0); - - // wait for user choice to come back from the question box menu - switch (snsCart) - { - case 0: - display_Clear(); - display_Update(); - setup_Snes(); - mode = mode_SNES; - break; - -#ifdef enable_NP - case 1: - display_Clear(); - display_Update(); - setup_SFM(); - mode = mode_SFM; - break; -#endif - - case 2: - display_Clear(); - display_Update(); - setup_SV(); - mode = mode_SV; - break; - -#ifdef enable_FLASH - case 3: - display_Clear(); - display_Update(); - hiROM = 1; - setup_Flash8(); - id_Flash8(); - wait(); - mode = mode_FLASH8; - break; - - case 4: - display_Clear(); - display_Update(); - hiROM = 0; - setup_Flash8(); - id_Flash8(); - wait(); - mode = mode_FLASH8; - break; -#endif - - case 5: - resetArduino(); - break; - } -} - -// SNES Menu -void snesMenu() { - // create menu with title and 7 options to choose from - unsigned char mainMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsSNES, 7); - mainMenu = question_box(F("SNES Cart Reader"), menuOptions, 7, 0); - - // wait for user choice to come back from the question box menu - switch (mainMenu) - { - case 0: - { - if (numBanks > 0) { - display_Clear(); - // Change working dir to root - sd.chdir("/"); - // get current time - unsigned long startTime = millis(); - // start reading from cart - readROM_SNES(); - compare_checksum(); - // print elapsed time - print_Msg(F("Time elapsed: ")); - print_Msg((millis() - startTime) / 1000); - println_Msg(F("s")); - display_Update(); - } - else { - display_Clear(); - print_Error(F("Does not have ROM"), false); - } - } - break; - - case 1: - if (sramSize > 0) { - display_Clear(); - // Change working dir to root - sd.chdir("/"); - readSRAM(); - } - else { - display_Clear(); - print_Error(F("Does not have SRAM"), false); - } - break; - - case 2: - if (sramSize > 0) { - 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); - } - } - else { - display_Clear(); - print_Error(F("Does not have SRAM"), false); - } - break; - - case 3: - if (sramSize > 0) { - display_Clear(); - println_Msg(F("Warning:")); - println_Msg(F("This can erase")); - println_Msg(F("your save games")); - println_Msg(F("")); - println_Msg(F("")); - println_Msg(F("Press any button to")); - println_Msg(F("start sram testing")); - display_Update(); - wait(); - display_Clear(); - // Change working dir to root - sd.chdir("/"); - readSRAM(); - eraseSRAM(0x00); - eraseSRAM(0xFF); - writeSRAM(0); - unsigned long wrErrors = verifySRAM(); - if (wrErrors == 0) { - println_Msg(F("Restored OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(wrErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - } - else { - display_Clear(); - print_Error(F("Does not have SRAM"), 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); - - // Note: It is probably not intended to reset CIC or clocks here - // But if that's false, uncomment this: - // stopSnesClocks_resetCic_resetCart(); - - display_Clear(); - print_Msg(F("Resetting...")); - display_Update(); - delay(3000); // wait 3 secs to switch to next game - resetArduino(); - break; - - case 5: - confMenuManual(); - display_Clear(); - display_Update(); - break; - - case 6: - stopSnesClocks_resetCic_resetCart(); - resetArduino(); - break; - } - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); -} - -// Menu for manual configuration -void confMenuManual() { - // create menu with title and 5 options to choose from - unsigned char subMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsConfManual, 5); - subMenu = question_box(F("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 - stopSnesClocks_resetCic_resetCart(); - resetArduino(); - break; - } -} - -void stopSnesClocks_resetCic_resetCart() { - DDRG |= (1 << 1); // Set cicrstPin(PG1) to Output - PORTG |= (1 << 1); // pull high = reset CIC - DDRH |= (1 << 0); // Set RST(PH0) pin to Output - PORTH &= ~(1 << 0); // Switch RST(PH0) to LOW - clockgen.output_enable(SI5351_CLK1, 0); // CPU clock - clockgen.output_enable(SI5351_CLK2, 0); // CIC clock - clockgen.output_enable(SI5351_CLK0, 0); // master clock -} - -/****************************************** - 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); - - // 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 Input - 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); - - // Adafruit Clock Generator - // last number is the clock correction factor which is custom for each clock generator - clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, -16000); - - // Set clocks to 4Mhz/1Mhz for better SA-1 unlocking - clockgen.set_freq(100000000ULL, SI5351_CLK1); // CPU - clockgen.set_freq(100000000ULL, SI5351_CLK2); // CIC - clockgen.set_freq(400000000ULL, SI5351_CLK0); // EXT - - // Start outputting master clock, CIC clock - clockgen.output_enable(SI5351_CLK1, 0); // no CPU clock yet; seems to affect SA-1 success a lot - clockgen.output_enable(SI5351_CLK2, 1); // CIC clock (should go before master clock) - clockgen.output_enable(SI5351_CLK0, 1); // master clock - - // Wait for clock generator - clockgen.update_status(); - delay(500); - - // Start CIC by outputting a low signal to cicrstPin(PG1) - PORTG &= ~(1 << 1); - - // Wait for CIC reset - delay(500); - - // Print all the info - getCartInfo_SNES(); - - //Set clocks to standard or else SA-1 sram writing will fail - clockgen.set_freq(2147727200ULL, SI5351_CLK0); - clockgen.set_freq(357954500ULL, SI5351_CLK1); - clockgen.set_freq(307200000ULL, SI5351_CLK2); -} - -/****************************************** - 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; - - // Wait for the Byte to appear on the data bus - // Arduino running at 16Mhz -> one nop = 62.5ns - // slowRom is good for 200ns, fastRom is <= 120ns; S-CPU best case read speed: 3.57MHz / 280ns - // let's be conservative and use 6 x 62.5 = 375ns - NOP; NOP; NOP; NOP; NOP; NOP; - - // Read - byte tempByte = PINC; - return tempByte; -} - -void readLoRomBanks( unsigned int start, unsigned int total, SdFile *file) -{ - byte buffer[1024] = { 0 }; - - uint16_t c = 0; - uint16_t currByte = 32768; - - //Initialize progress bar - uint32_t processedProgressBar = 0; - uint32_t totalProgressBar = (uint32_t)(total - start) * 1024; - draw_progressbar(0, totalProgressBar); - - for (int currBank = start; currBank < total; currBank++) { - PORTL = currBank; - - // Blink led - PORTB ^= (1 << 4); - - currByte = 32768; - while (1) { - c = 0; - while (c < 1024) { - PORTF = (currByte & 0xFF); - PORTK = ((currByte >> 8) & 0xFF); - - // Wait for the Byte to appear on the data bus - // Arduino running at 16Mhz -> one nop = 62.5ns - // slowRom is good for 200ns, fastRom is <= 120ns; S-CPU best case read speed: 3.57MHz / 280ns - // let's be conservative and use 6 x 62.5 = 375ns - NOP; NOP; NOP; NOP; NOP; NOP; - - buffer[c] = PINC; - c++; - currByte++; - } - file->write(buffer, 1024); - - // exit while(1) loop once the uint16_t currByte overflows from 0xffff to 0 (current bank is done) - if (currByte == 0) break; - } - - // update progress bar - processedProgressBar += 1024; - draw_progressbar(processedProgressBar, totalProgressBar); - } -} - -void readHiRomBanks( unsigned int start, unsigned int total, SdFile *file) -{ - byte buffer[1024] = { 0 }; - - uint16_t c = 0; - uint16_t currByte = 0; - - //Initialize progress bar - uint32_t processedProgressBar = 0; - uint32_t totalProgressBar = (uint32_t)(total - start) * 1024; - draw_progressbar(0, totalProgressBar); - - for (int currBank = start; currBank < total; currBank++) { - PORTL = currBank; - - // Blink led - PORTB ^= (1 << 4); - - currByte = 0; - while (1) { - c = 0; - while (c < 1024) { - PORTF = (currByte & 0xFF); - PORTK = ((currByte >> 8) & 0xFF); - - // Wait for the Byte to appear on the data bus - // Arduino running at 16Mhz -> one nop = 62.5ns - // slowRom is good for 200ns, fastRom is <= 120ns; S-CPU best case read speed: 3.57MHz / 280ns - // let's be conservative and use 6 x 62.5 = 375ns - NOP; NOP; NOP; NOP; NOP; NOP; - - buffer[c] = PINC; - c++; - currByte++; - } - file->write(buffer, 1024); - - // exit while(1) loop once the uint16_t currByte overflows from 0xffff to 0 (current bank is done) - if (currByte == 0) break; - } - - // update progress bar - processedProgressBar += 1024; - draw_progressbar(processedProgressBar, totalProgressBar); - } -} - -/****************************************** - SNES ROM Functions -******************************************/ -void getCartInfo_SNES() { - boolean manualConfig = 0; - - //Prime SA1 cartridge - uint16_t c = 0; - uint16_t currByte = 0; - byte buffer[1024] = { 0 }; - PORTL = 192; - while (c < 1024) { - PORTF = (currByte & 0xFF); - PORTK = ((currByte >> 8) & 0xFF); - - // Wait for the Byte to appear on the data bus - // Arduino running at 16Mhz -> one nop = 62.5ns - // slowRom is good for 200ns, fastRom is <= 120ns; S-CPU best case read speed: 3.57MHz / 280ns - // let's be conservative and use 6 x 62.5 = 375ns - NOP; NOP; NOP; NOP; NOP; NOP; - - buffer[c] = PINC; - c++; - currByte++; - } - - // 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("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 == 243) - println_Msg(F("CX4")); - else if (romChips == 246) - println_Msg(F("DSP2")); - else if (romChips == 245) - println_Msg(F("SPC RAM BATT")); - else if (romChips == 249) - println_Msg(F("SPC RAM RTC")); - else - println_Msg(F("")); - - - if (altconf) - print_Msg(F("Rom Size: ")); - else - print_Msg(F("ROM Size: ")); - print_Msg(romSize); - println_Msg(F("Mbit")); - - print_Msg(F("Banks: ")); - print_Msg(numBanks); - print_Msg(F(" Chips: ")); - println_Msg(romChips); - - 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 -#ifdef enable_OLED - println_Msg(F(" ")); - println_Msg(F(" ")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); -#else - println_Msg(F(" ")); -#endif - - // Start manual config - if (manualConfig == 1) { - confMenuManual(); - } -} - -void checkAltConf() { - char tempStr1[2]; - char tempStr2[5]; - altconf = 0; - - 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 file size - romSize = (myFile.read() - 48) * 10 + (myFile.read() - 48); - - // Skip the , in the file - myFile.seekSet(myFile.curPosition() + 1); - - // Read number of banks - numBanks = (myFile.read() - 48) * 100 + (myFile.read() - 48) * 10 + (myFile.read() - 48); - - altconf = 1; - } - // If no match empty string advance by 9 and try again - else { - myFile.seekSet(myFile.curPosition() + 9); - } - } - } - // Close the file: - myFile.close(); -} - -// Read header -boolean checkcart_SNES() { - // set control to read - dataIn(); - - uint16_t c = 0; - uint16_t headerStart = 0xFFC0; - uint16_t currByte = headerStart; - byte snesHeader[64] = { 0 }; - PORTL = 0; - while (c < 64) { - PORTF = (currByte & 0xFF); - PORTK = ((currByte >> 8) & 0xFF); - - NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; - - snesHeader[c] = PINC; - c++; - currByte++; - } - - // Get Checksum as string - sprintf(checksumStr, "%02X%02X", snesHeader[0xFFDF - headerStart], snesHeader[0xFFDE - headerStart]); - - romType = snesHeader[0xFFD5 - headerStart]; - if ((romType >> 5) != 1) { // Detect invalid romType byte due to too long ROM name (22 chars) - romType = LO; // LoROM // Krusty's Super Fun House (U) 1.0 & Contra 3 (U) - } - else if (romType == 0x35) { - romType = EX; // Check if ExHiROM - } - else if (romType == 0x3A) { - romType = HI; // Check if SPC7110 - } - else { - romType &= 1; // Must be LoROM or HiROM - } - - // Check RomSpeed - romSpeed = (snesHeader[0xFFD5 - headerStart] >> 4); - - // Check RomChips - romChips = snesHeader[0xFFD6 - headerStart]; - - if (romChips == 69) { - romSize = 48; - numBanks = 96; - romType = HI; - } - else if (romChips == 243) { - cx4Type = snesHeader[0xFFC9 - headerStart] & 0xF; - if (cx4Type == 2) { // X2 - romSize = 12; - numBanks = 48; - } - else if (cx4Type == 3) { // X3 - romSize = 16; - numBanks = 64; - } - } - else if ((romChips == 245) && (romType == HI)) { - romSize = 24; - numBanks = 48; - } - else if ((romChips == 249) && (romType == HI)) { - romSize = 40; - numBanks = 80; - } - else { - // Check RomSize - byte romSizeExp = snesHeader[0xFFD7 - headerStart] - 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(); - - // Get name - byte myByte = 0; - byte myLength = 0; - for (unsigned int i = 0xFFC0; i < 0xFFD4; i++) { - myByte = snesHeader[i - headerStart]; - if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 15) { - romName[myLength] = char(myByte); - myLength++; - } - } - // If name consists out of all japanese characters use game code - if (myLength == 0) { - // Get rom code - romName[0] = 'S'; - romName[1] = 'H'; - romName[2] = 'V'; - romName[3] = 'C'; - romName[4] = '-'; - for (unsigned int i = 0; i < 4; i++) { - myByte = snesHeader[0xFFB2 + i - headerStart]; - if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 4) { - romName[myLength + 5] = char(myByte); - myLength++; - } - } - if (myLength == 0) { - // Rom code unknown - romName[0] = 'U'; - romName[1] = 'N'; - romName[2] = 'K'; - romName[3] = 'N'; - romName[4] = 'O'; - romName[5] = 'W'; - romName[6] = 'N'; - } - } - - // Read sramSizeExp - byte sramSizeExp; - if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { - // SuperFX - if (snesHeader[0x7FDA - headerStart] == 0x33) { - sramSizeExp = snesHeader[0x7FBD - headerStart]; - } - else { - if (strncmp(romName, "STARFOX2", 8) == 0) { - sramSizeExp = 6; - } - else { - sramSizeExp = 5; - } - } - } - else { - // No SuperFX - sramSizeExp = snesHeader[0xFFD8 - headerStart]; - } - - // Calculate sramSize - // Fail states usually have sramSizeExp at 255 (no cart inserted, SA-1 failure, etc) - if (sramSizeExp != 0 && sramSizeExp != 255) { - sramSizeExp = sramSizeExp + 3; - sramSize = 1; - while (sramSizeExp--) - sramSize *= 2; - } - else { - sramSize = 0; - } - - // Check Cart Country - //int cartCountry = snesHeader[0xFFD9 - headerStart]; - - // ROM Version - romVersion = snesHeader[0xFFDB - headerStart]; - - // Test if checksum is equal to reverse checksum - if (((word(snesHeader[0xFFDC - headerStart]) + (word(snesHeader[0xFFDD - headerStart]) * 256)) + (word(snesHeader[0xFFDE - headerStart]) + (word(snesHeader[0xFFDF - headerStart]) * 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 == 10) || (calcFilesize == 12) || (calcFilesize == 20) || (calcFilesize == 24)) { - unsigned long calcBase = 0; - unsigned long calcMirror = 0; - byte calcMirrorCount = 0; - if (calcFilesize > 16) - calcBase = 2097152; - else - calcBase = 1048576; - calcMirror = myFile.fileSize() - calcBase; - calcMirrorCount = calcBase / calcMirror; - - // Momotarou Dentetsu Happy Fix 3MB (24Mbit) - if ((calcFilesize == 24) && (romChips == 245)) { - 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 { - // Base 8/16 Mbit chunk - for (j = 0; j < (calcBase / 512); j++) { - myFile.read(sdBuffer, 512); - for (c = 0; c < 512; c++) { - calcChecksumChunk += sdBuffer[c]; - } - } - calcChecksum = calcChecksumChunk; - calcChecksumChunk = 0; - // Add the mirrored chunk - for (j = 0; j < (calcMirror / 512); j++) { - myFile.read(sdBuffer, 512); - for (c = 0; c < 512; c++) { - calcChecksumChunk += sdBuffer[c]; - } - } - calcChecksum += calcMirrorCount * 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 { - // Else show error - 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, "SNES/ROM/%s/%d", romName, foldern - 1); - - char calcsumStr[5]; - sprintf(calcsumStr, "%04X", calc_checksum(fileName, folder)); - - if (strcmp(calcsumStr, checksumStr) == 0) { - print_Msg(F("Checksum OK: ")); - println_Msg(calcsumStr); - display_Update(); - return 1; - } - else { - print_Msg(F("Checksum Error: ")); - println_Msg(calcsumStr); - print_Error(F(""), 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, "SNES/ROM/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - //clear the screen - display_Clear(); - print_Msg(F("Saving to ")); - print_Msg(folder); - println_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("Can't create file on SD"), true); - } - - //Dump Derby Stallion '96 (Japan) Actual Size is 24Mb - if ((romType == LO) && (numBanks == 128) && (strcmp("CC86", checksumStr) == 0)) { - // Read Banks 0x00-0x3F for the 1st/2nd MB - for (int currBank = 0; currBank < 64; 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); - } - } - //Read Bank 0x80-9F for the 3rd MB - for (int currBank = 128; currBank < 160; 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 Low-type ROM - else if (romType == LO) { - if (romChips == 243) { //0xF3 - cx4Map = readBank_SNES(0, 32594); //0x7F52 - if ((cx4Type == 2) && (cx4Map != 0)) { //X2 - dataOut(); - controlOut_SNES(); - writeBank_SNES(0, 32594, 0); // Set 0x7F52 to 0 - dataIn(); - controlIn_SNES(); - } - else if ((cx4Type == 3) && (cx4Map == 0)) { //X3 - dataOut(); - controlOut_SNES(); - writeBank_SNES(0, 32594, 1); // Set 0x7F52 to 1 - dataIn(); - controlIn_SNES(); - } - } - if (romSize > 24) { - // ROM > 96 banks (up to 128 banks) - readLoRomBanks( 0x80, numBanks + 0x80, &myFile ); - } else { - // Read up to 96 banks starting at bank 0×00. - readLoRomBanks( 0, numBanks, &myFile ); - } - if (romChips == 243) { //0xF3 - // Restore CX4 Mapping Register - dataOut(); - controlOut_SNES(); - writeBank_SNES(0, 32594, cx4Map); // 0x7F52 - dataIn(); - controlIn_SNES(); - } - } - - // 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(); - - readHiRomBanks( 240, 256, &myFile ); - if (currMemmap == 2) display_Clear(); // need more space for the progress bars - } - - dataOut(); - controlOut_SNES(); - - writeBank_SNES(0, 18439, initialSOMap); - - dataIn(); - controlIn_SNES(); - } - - // Dump SPC7110 High-type ROM - else if ((romType == HI) && ((romChips == 245) || (romChips == 249))) { - println_Msg(F("Dumping SPC7110 HiRom")); - display_Update(); - - // 0xC00000-0xDFFFFF - //print_Msg(F("Part 1")); - display_Update(); - readHiRomBanks( 192, 224, &myFile ); - - if (numBanks > 32) { - dataOut(); - controlOut_SNES(); - // Set 0x4834 to 0xFF - writeBank_SNES( 0, 0x4834, 0xFF ); - - dataIn(); - controlIn_SNES(); - - // 0xE00000-0xEFFFFF - //print_Msg(F(" 2")); - display_Update(); - readHiRomBanks( 224, 240, &myFile ); - - if (numBanks > 48) { - // 0xF00000-0xFFFFFF - //print_Msg(F(" 3")); - display_Update(); - readHiRomBanks( 240, 256, &myFile ); - - dataOut(); - controlOut_SNES(); - - // Set 0x4833 to 3 - writeBank_SNES( 0, 0x4833, 3 ); - - dataIn(); - controlIn_SNES(); - - // 0xF00000-0xFFFFFF - //print_Msg(F(" 4")); - display_Update(); - readHiRomBanks( 240, 256, &myFile ); - } - //println_Msg(F("")); - display_Clear(); // need more space due to the 4 progress bars - - // Return mapping registers to initial settings... - dataOut(); - controlOut_SNES(); - - writeBank_SNES( 0, 0x4833, 2 ); - writeBank_SNES( 0, 0x4834, 0 ); - - dataIn(); - controlIn_SNES(); - } - } - - // Dump standard High-type ROM - else if ((romType == HI) || (romType == SA) || (romType == EX)) { - println_Msg(F("Dumping HiRom...")); - display_Update(); - - readHiRomBanks( 192, numBanks + 192, &myFile ); - } - - // Close the file: - myFile.close(); -} - -/****************************************** - SNES SRAM Functions -*****************************************/ -// Write file to SRAM -void writeSRAM (boolean browseFile) { - if (browseFile) { - filePath[0] = '\0'; - sd.chdir("/"); - fileBrowser(F("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(); - - int sramBanks = 0; - // LoRom - if (romType == LO) { - // Sram size - long lastByte = (long(sramSize) * 128); - - if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX - if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) - sramBanks = lastByte / 0x10000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0x0000; currByte < 0x10000; currByte++) { - writeBank_SNES(currBank, currByte, myFile.read()); - } - } - } - else { // SuperFX SRAM - for (long currByte = 0; currByte < lastByte; currByte++) { - writeBank_SNES(0x70, currByte, myFile.read()); - } - } - } - else if (lastByte > 0x8000) { // Large SRAM Fix - sramBanks = lastByte / 0x8000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0x0000; currByte < 0x8000; currByte++) { - writeBank_SNES(currBank, currByte, myFile.read()); - } - } - } - else { - for (long currByte = 0; currByte < lastByte; currByte++) { - writeBank_SNES(0x70, currByte, myFile.read()); - } - } - } - // HiRom - else if (romType == HI) { - if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM - // Configure SPC7110 SRAM Register - // Set 0x4830 to 0x80 - writeBank_SNES(0, 0x4830, 0x80); - // Sram size - long lastByte = (long(sramSize) * 128) + 0x6000; - // Write to sram bank - for (long currByte = 0x6000; currByte < lastByte; currByte++) { - writeBank_SNES(0x30, currByte, myFile.read()); - } - // Reset SPC7110 SRAM Register - dataOut(); - // Reset 0x4830 to 0x0 - writeBank_SNES(0, 0x4830, 0); - dataIn(); - } - else { - // Writing SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128); - if (lastByte > 0x2000) { // Large SRAM Fix - sramBanks = lastByte / 0x2000; - for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { - for (long currByte = 0x6000; currByte < 0x8000; currByte++) { - writeBank_SNES(currBank, currByte, myFile.read()); - } - } - } - else { - lastByte += 0x6000; - // Write to sram bank - 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) * 128); - // Enable CPU Clock - 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 - byte lastBlock = 0; - lastBlock = lastByte / 0x2000; - - // Writing SRAM on SA1 needs CS(PH3) to be high - // PORTH |= (1 << 3); - - // Setup BW-RAM - // Set 0x2224 (SNES BMAPS) to map SRAM Block 0 to 0x6000-0x7FFF - writeBank_SNES(0, 0x2224, 0); - // Set 0x2226 (SNES SBWE) to 0x80 Write Enable - writeBank_SNES(0, 0x2226, 0x80); - // Set 0x2228 (SNES BWPA) to 0x00 BW-RAM Write-Protected Area - writeBank_SNES(0, 0x2228, 0); - delay(1000); - - // Use $2224 (SNES) to map BW-RAM block to 0x6000-0x7FFF - // Use $2226 (SNES) to write enable the BW-RAM - byte firstByte = 0; - for (byte currBlock = 0; currBlock < lastBlock; currBlock++) { - // Set 0x2224 (SNES BMAPS) to map SRAM Block to 0x6000-0x7FFF - writeBank_SNES(0, 0x2224, currBlock); - // Set 0x2226 (SNES SBWE) to 0x80 Write Enable - writeBank_SNES(0, 0x2226, 0x80); - for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { - myFile.read(sdBuffer, 512); - if ((currBlock == 0) && (currByte == 0x6000)) { - firstByte = sdBuffer[0]; - } - for (int c = 0; c < 512; c++) { - writeBank_SNES(0, currByte + c, sdBuffer[c]); - } - } - } - // Rewrite First Byte - writeBank_SNES(0, 0x2224, 0); - writeBank_SNES(0, 0x2226, 0x80); - writeBank_SNES(0, 0x6000, firstByte); - // 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, "SNES/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); - } - int sramBanks = 0; - if (romType == LO) { - // Sram size - long lastByte = (long(sramSize) * 128); - if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX - if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) - sramBanks = lastByte / 0x10000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0x0000; currByte < 0x10000; currByte++) { - myFile.write(readBank_SNES(currBank, currByte)); - } - } - } - else { // SuperFX SRAM - for (long currByte = 0; currByte < lastByte; currByte++) { - myFile.write(readBank_SNES(0x70, currByte)); - } - } - } - else if (lastByte > 0x8000) { // Large SRAM Fix - sramBanks = lastByte / 0x8000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0x0000; currByte < 0x8000; currByte++) { - myFile.write(readBank_SNES(currBank, currByte)); - } - } - } - else { - for (long currByte = 0; currByte < lastByte; currByte++) { - myFile.write(readBank_SNES(0x70, currByte)); - } - } - } - else if (romType == HI) { - if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM - // Configure SPC7110 SRAM Register - dataOut(); - // Set 0x4830 to 0x80 - writeBank_SNES(0, 0x4830, 0x80); - dataIn(); - // Sram size - long lastByte = (long(sramSize) * 128) + 0x6000; - for (long currByte = 0x6000; currByte < lastByte; currByte++) { - myFile.write(readBank_SNES(0x30, currByte)); - } - dataOut(); - // Reset 0x4830 to 0x0 - writeBank_SNES(0, 0x4830, 0); - dataIn(); - } - else { - // Dumping SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128); - if (lastByte > 0x2000) { // Large SRAM Fix - sramBanks = lastByte / 0x2000; - for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { - for (long currByte = 0x6000; currByte < 0x8000; currByte++) { - myFile.write(readBank_SNES(currBank, currByte)); - } - } - } - else { - lastByte += 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); - if (lastByte > 0x10000) { - sramBanks = lastByte / 0x10000; - for (int currBank = 0x40; currBank < sramBanks + 0x40; currBank++) { - for (long currByte = 0; currByte < 0x10000; currByte++) { - myFile.write(readBank_SNES(currBank, currByte)); - } - } - } - else { - for (long currByte = 0x0; currByte < lastByte; currByte++) { - myFile.write(readBank_SNES(0x40, currByte)); - } - } - } - - // Close the file: - myFile.close(); - - // Signal end of process - display_Clear(); - print_Msg(F("Saved to ")); - print_Msg(folder); - println_Msg(F("/...")); - 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(); - - int sramBanks = 0; - if (romType == LO) { - // Sram size - long lastByte = (long(sramSize) * 128); - if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX - if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) - sramBanks = lastByte / 0x10000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0; currByte < 0x10000; currByte += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { - writeErrors++; - } - } - } - } - } - else { // SuperFX SRAM - 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 (lastByte > 0x8000) { // Large SRAM Fix - sramBanks = lastByte / 0x8000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0; currByte < 0x8000; currByte += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { - writeErrors++; - } - } - } - } - } - else { - 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) { - if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM - // Configure SPC7110 SRAM Register - dataOut(); - // Set 0x4830 to 0x80 - writeBank_SNES(0, 0x4830, 0x80); - dataIn(); - // 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++; - } - } - } - dataOut(); - // Reset 0x4830 to 0x0 - writeBank_SNES(0, 0x4830, 0); - dataIn(); - } - else { - // Dumping SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128); - if (lastByte > 0x2000) { // Large SRAM Fix - sramBanks = lastByte / 0x2000; - for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { - for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { - writeErrors++; - } - } - } - } - } - else { - lastByte += 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); - - if (lastByte > 0x10000) { - sramBanks = lastByte / 0x10000; - for (int currBank = 0x40; currBank < sramBanks + 0x40; currBank++) { - for (long currByte = 0x0; currByte < 0x10000; currByte += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { - writeErrors++; - } - } - } - } - } - else { - 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++; - } - } - } - } - // Reset SA1 - // Set pins to input - dataIn(); - // Close the file: - myFile.close(); - if (writeErrors == 0) { - println_Msg(F("Verified OK")); - } - else { - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - display_Update(); - wait(); - - stopSnesClocks_resetCic_resetCart(); - - display_Clear(); - print_Msg(F("Resetting...")); - display_Update(); - delay(3000); // wait 3 secs - resetArduino(); - } - // 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(); - - int sramBanks = 0; - if (romType == LO) { - // Sram size - long lastByte = (long(sramSize) * 128); - - if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX - if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) - sramBanks = lastByte / 0x10000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0x0000; currByte < 0x10000; currByte++) { - writeBank_SNES(currBank, currByte, b); - } - } - } - else { // SuperFX SRAM - for (long currByte = 0; currByte < lastByte; currByte++) { - writeBank_SNES(0x70, currByte, b); - } - } - } - else if (lastByte > 0x8000) { // Large SRAM Fix - sramBanks = lastByte / 0x8000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0x0000; currByte < 0x8000; currByte++) { - writeBank_SNES(currBank, currByte, b); - } - } - } - else { - for (long currByte = 0; currByte < lastByte; currByte++) { - writeBank_SNES(0x70, currByte, b); - } - } - } - else if (romType == HI) { - if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM - // Configure SPC7110 SRAM Register - // Set 0x4830 to 0x80 - writeBank_SNES(0, 0x4830, 0x80); - // Sram size - long lastByte = (long(sramSize) * 128) + 0x6000; - // Write to sram bank - for (long currByte = 0x6000; currByte < lastByte; currByte++) { - writeBank_SNES(0x30, currByte, b); - } - // Reset SPC7110 SRAM Register - dataOut(); - // Reset 0x4830 to 0x0 - writeBank_SNES(0, 0x4830, 0); - dataIn(); - } - else { - // Writing SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128); - if (lastByte > 0x2000) { // Large SRAM Fix - sramBanks = lastByte / 0x2000; - for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { - for (long currByte = 0x6000; currByte < 0x8000; currByte++) { - writeBank_SNES(currBank, currByte, b); - } - } - } - else { - lastByte += 0x6000; - // Write to sram bank - 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); - } - } - // SA1 - else if (romType == SA) { - long lastByte = (long(sramSize) * 128); - // Enable CPU Clock - 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 $2224 to map BW-RAM block to 0x6000-0x7FFF - byte lastBlock = 0; - lastBlock = lastByte / 0x2000; - - // Writing SRAM on SA1 needs CS(PH3) to be high - // PORTH |= (1 << 3); - - // Setup BW-RAM - // Set 0x2224 (SNES BMAPS) to map SRAM Block 0 to 0x6000-0x7FFF - writeBank_SNES(0, 0x2224, 0); - // Set 0x2226 (SNES SBWE) to 0x80 Write Enable - writeBank_SNES(0, 0x2226, 0x80); - // Set 0x2228 (SNES BWPA) to 0x00 BW-RAM Write-Protected Area - writeBank_SNES(0, 0x2228, 0); - delay(1000); - - // Use $2224 (SNES) to map BW-RAM block to 0x6000-0x7FFF - // Use $2226 (SNES) to write enable the BW-RAM - for (byte currBlock = 0; currBlock < lastBlock; currBlock++) { - // Set 0x2224 (SNES BMAPS) to map SRAM Block to 0x6000-0x7FFF - writeBank_SNES(0, 0x2224, currBlock); - // Set 0x2226 (SNES SBWE) to 0x80 Write Enable - writeBank_SNES(0, 0x2226, 0x80); - for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { - for (int c = 0; c < 512; c++) { - writeBank_SNES(0, currByte + c, b); - } - } - } - // Rewrite First Byte - writeBank_SNES(0, 0x2224, 0); - writeBank_SNES(0, 0x2226, 0x80); - writeBank_SNES(0, 0x6000, b); - // Disable CPU clock - clockgen.output_enable(SI5351_CLK1, 0); - } - - dataIn(); - - // Variable for errors - writeErrors = 0; - - // Set control - controlIn_SNES(); - - sramBanks = 0; - if (romType == LO) { - // Sram size - long lastByte = (long(sramSize) * 128); - if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX - if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) - sramBanks = lastByte / 0x10000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0; currByte < 0x10000; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != b) { - writeErrors++; - } - } - } - } - } - else { // SuperFX SRAM - 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 (lastByte > 0x8000) { // Large SRAM Fix - sramBanks = lastByte / 0x8000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0; currByte < 0x8000; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != b) { - writeErrors++; - } - } - } - } - } - else { - 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) { - if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM - // Configure SPC7110 SRAM Register - dataOut(); - // Set 0x4830 to 0x80 - writeBank_SNES(0, 0x4830, 0x80); - dataIn(); - // 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++; - } - } - } - dataOut(); - // Reset 0x4830 to 0x0 - writeBank_SNES(0, 0x4830, 0); - dataIn(); - } - else { - // Dumping SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128); - if (lastByte > 0x2000) { // Large SRAM Fix - sramBanks = lastByte / 0x2000; - for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { - for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != b) { - writeErrors++; - } - } - } - } - } - else { - lastByte += 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++; - } - } - } - } - else if (romType == SA) { - // Dumping SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128); - if (lastByte > 0x10000) { - sramBanks = lastByte / 0x10000; - for (int currBank = 0x40; currBank < sramBanks + 0x40; currBank++) { - for (long currByte = 0x0; currByte < 0x10000; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != b) { - writeErrors++; - } - } - } - } - } - else { - for (long currByte = 0x0; currByte < lastByte; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(0x40, currByte + c)) != b) { - writeErrors++; - } - } - } - } - } - if (writeErrors == 0) { - println_Msg(F("OK")); - return 1; - } - else { - println_Msg(F("ERROR")); - return 0; - } - display_Update(); -} - -#endif - -//****************************************** -// End of File -//****************************************** +//****************************************** +// SUPER NINTENDO MODULE +//****************************************** + +#include "options.h" +#ifdef enable_SNES +#include "snes_clk.h" + +/****************************************** + 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, 243 = CX4, 246 = ROM & DSP2 +byte romSizeExp = 0; // ROM-Size Exponent +boolean NP = false; +byte cx4Type = 0; +byte cx4Map = 0; +boolean altconf = 0; + +/****************************************** + Menu +*****************************************/ +// SNES/Nintendo Power SF Memory start menu +static const char snsMenuItem1[] PROGMEM = "Super Nintendo"; +static const char snsMenuItem2[] PROGMEM = "NPower SF Memory"; +static const char snsMenuItem3[] PROGMEM = "Satellaview BS-X"; +static const char snsMenuItem4[] PROGMEM = "HiROM repro"; +static const char snsMenuItem5[] PROGMEM = "LoROM repro"; +static const char snsMenuItem6[] PROGMEM = "Reset"; +static const char* const menuOptionsSNS[] PROGMEM = {snsMenuItem1, snsMenuItem2, snsMenuItem3, snsMenuItem4, snsMenuItem5, snsMenuItem6}; + +// SNES menu items +static const char SnesMenuItem1[] PROGMEM = "Read Rom"; +static const char SnesMenuItem2[] PROGMEM = "Read Save"; +static const char SnesMenuItem3[] PROGMEM = "Write Save"; +static const char SnesMenuItem4[] PROGMEM = "Test SRAM"; +static const char SnesMenuItem5[] PROGMEM = "Cycle cart"; +static const char SnesMenuItem6[] PROGMEM = "Force cart type"; +static const char SnesMenuItem7[] PROGMEM = "Reset"; +static const char* const menuOptionsSNES[] PROGMEM = {SnesMenuItem1, SnesMenuItem2, SnesMenuItem3, SnesMenuItem4, SnesMenuItem5, SnesMenuItem6, SnesMenuItem7}; + +// Manual config menu items +static const char confMenuItem1[] PROGMEM = "Use header info"; +static const char confMenuItem2[] PROGMEM = "4MB LoRom 256K Sram"; +static const char confMenuItem3[] PROGMEM = "4MB HiRom 64K Sram"; +static const char confMenuItem4[] PROGMEM = "6MB ExRom 256K Sram"; +static const char confMenuItem5[] PROGMEM = "Reset"; +static const char* const menuOptionsConfManual[] PROGMEM = {confMenuItem1, confMenuItem2, confMenuItem3, confMenuItem4, confMenuItem5}; + +// SNES start menu +void snsMenu() { + // create menu with title and 6 options to choose from + unsigned char snsCart; + // Copy menuOptions out of progmem + convertPgm(menuOptionsSNS, 6); + snsCart = question_box(F("Select Cart Type"), menuOptions, 6, 0); + + // wait for user choice to come back from the question box menu + switch (snsCart) + { + case 0: + display_Clear(); + display_Update(); + setup_Snes(); + mode = mode_SNES; + break; + +#ifdef enable_NP + case 1: + display_Clear(); + display_Update(); + setup_SFM(); + mode = mode_SFM; + break; +#endif + + case 2: + display_Clear(); + display_Update(); + setup_SV(); + mode = mode_SV; + break; + +#ifdef enable_FLASH + case 3: + display_Clear(); + display_Update(); + hiROM = 1; + setup_Flash8(); + id_Flash8(); + wait(); + mode = mode_FLASH8; + break; + + case 4: + display_Clear(); + display_Update(); + hiROM = 0; + setup_Flash8(); + id_Flash8(); + wait(); + mode = mode_FLASH8; + break; +#endif + + case 5: + resetArduino(); + break; + } +} + +// SNES Menu +void snesMenu() { + // create menu with title and 7 options to choose from + unsigned char mainMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsSNES, 7); + mainMenu = question_box(F("SNES Cart Reader"), menuOptions, 7, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + case 0: + { + if (numBanks > 0) { + display_Clear(); + // Change working dir to root + sd.chdir("/"); + // get current time + unsigned long startTime = millis(); + // start reading from cart + readROM_SNES(); + compare_checksum(); + // print elapsed time + print_Msg(F("Time elapsed: ")); + print_Msg((millis() - startTime) / 1000); + println_Msg(F("s")); + display_Update(); + } + else { + display_Clear(); + print_Error(F("Does not have ROM"), false); + } + } + break; + + case 1: + if (sramSize > 0) { + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readSRAM(); + } + else { + display_Clear(); + print_Error(F("Does not have SRAM"), false); + } + break; + + case 2: + if (sramSize > 0) { + 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); + } + } + else { + display_Clear(); + print_Error(F("Does not have SRAM"), false); + } + break; + + case 3: + if (sramSize > 0) { + display_Clear(); + println_Msg(F("Warning:")); + println_Msg(F("This can erase")); + println_Msg(F("your save games")); + println_Msg(F("")); + println_Msg(F("")); + println_Msg(F("Press any button to")); + println_Msg(F("start sram testing")); + display_Update(); + wait(); + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readSRAM(); + eraseSRAM(0x00); + eraseSRAM(0xFF); + writeSRAM(0); + unsigned long wrErrors = verifySRAM(); + if (wrErrors == 0) { + println_Msg(F("Restored OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(wrErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + } + else { + display_Clear(); + print_Error(F("Does not have SRAM"), 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); + + // Note: It is probably not intended to reset CIC or clocks here + // But if that's false, uncomment this: + // stopSnesClocks_resetCic_resetCart(); + + display_Clear(); + print_Msg(F("Resetting...")); + display_Update(); + delay(3000); // wait 3 secs to switch to next game + resetArduino(); + break; + + case 5: + confMenuManual(); + display_Clear(); + display_Update(); + break; + + case 6: + stopSnesClocks_resetCic_resetCart(); + resetArduino(); + break; + } + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +// Menu for manual configuration +void confMenuManual() { + // create menu with title and 5 options to choose from + unsigned char subMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsConfManual, 5); + subMenu = question_box(F("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 + stopSnesClocks_resetCic_resetCart(); + resetArduino(); + break; + } +} + +void stopSnesClocks_resetCic_resetCart() { + DDRG |= (1 << 1); // Set cicrstPin(PG1) to Output + PORTG |= (1 << 1); // pull high = reset CIC + DDRH |= (1 << 0); // Set RST(PH0) pin to Output + PORTH &= ~(1 << 0); // Switch RST(PH0) to LOW + clockgen.output_enable(SI5351_CLK1, 0); // CPU clock + clockgen.output_enable(SI5351_CLK2, 0); // CIC clock + clockgen.output_enable(SI5351_CLK0, 0); // master clock +} + +/****************************************** + 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); + + // 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 Input + 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); + + // Adafruit Clock Generator + // last number is the clock correction factor which is custom for each clock generator + int32_t clock_offset = readClockOffset(); + if (clock_offset > INT32_MIN) { + clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, clock_offset); + } else { + clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, -16000); + } + + // Set clocks to 4Mhz/1Mhz for better SA-1 unlocking + clockgen.set_freq(100000000ULL, SI5351_CLK1); // CPU + clockgen.set_freq(100000000ULL, SI5351_CLK2); // CIC + clockgen.set_freq(400000000ULL, SI5351_CLK0); // EXT + + // Start outputting master clock, CIC clock + clockgen.output_enable(SI5351_CLK1, 0); // no CPU clock yet; seems to affect SA-1 success a lot + clockgen.output_enable(SI5351_CLK2, 1); // CIC clock (should go before master clock) + clockgen.output_enable(SI5351_CLK0, 1); // master clock + + // Wait for clock generator + clockgen.update_status(); + delay(500); + + // Start CIC by outputting a low signal to cicrstPin(PG1) + PORTG &= ~(1 << 1); + + // Wait for CIC reset + delay(500); + + // Print all the info + getCartInfo_SNES(); + + //Set clocks to standard or else SA-1 sram writing will fail + clockgen.set_freq(2147727200ULL, SI5351_CLK0); + clockgen.set_freq(357954500ULL, SI5351_CLK1); + clockgen.set_freq(307200000ULL, SI5351_CLK2); +} + +/****************************************** + 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; + + // Wait for the Byte to appear on the data bus + // Arduino running at 16Mhz -> one nop = 62.5ns + // slowRom is good for 200ns, fastRom is <= 120ns; S-CPU best case read speed: 3.57MHz / 280ns + // let's be conservative and use 6 x 62.5 = 375ns + NOP; NOP; NOP; NOP; NOP; NOP; + + // Read + byte tempByte = PINC; + return tempByte; +} + +void readLoRomBanks( unsigned int start, unsigned int total, SdFile *file) +{ + byte buffer[1024] = { 0 }; + + uint16_t c = 0; + uint16_t currByte = 32768; + + //Initialize progress bar + uint32_t processedProgressBar = 0; + uint32_t totalProgressBar = (uint32_t)(total - start) * 1024; + draw_progressbar(0, totalProgressBar); + + for (int currBank = start; currBank < total; currBank++) { + PORTL = currBank; + + // Blink led + PORTB ^= (1 << 4); + + currByte = 32768; + while (1) { + c = 0; + while (c < 1024) { + PORTF = (currByte & 0xFF); + PORTK = ((currByte >> 8) & 0xFF); + + // Wait for the Byte to appear on the data bus + // Arduino running at 16Mhz -> one nop = 62.5ns + // slowRom is good for 200ns, fastRom is <= 120ns; S-CPU best case read speed: 3.57MHz / 280ns + // let's be conservative and use 6 x 62.5 = 375ns + NOP; NOP; NOP; NOP; NOP; NOP; + + buffer[c] = PINC; + c++; + currByte++; + } + file->write(buffer, 1024); + + // exit while(1) loop once the uint16_t currByte overflows from 0xffff to 0 (current bank is done) + if (currByte == 0) break; + } + + // update progress bar + processedProgressBar += 1024; + draw_progressbar(processedProgressBar, totalProgressBar); + } +} + +void readHiRomBanks( unsigned int start, unsigned int total, SdFile *file) +{ + byte buffer[1024] = { 0 }; + + uint16_t c = 0; + uint16_t currByte = 0; + + //Initialize progress bar + uint32_t processedProgressBar = 0; + uint32_t totalProgressBar = (uint32_t)(total - start) * 1024; + draw_progressbar(0, totalProgressBar); + + for (int currBank = start; currBank < total; currBank++) { + PORTL = currBank; + + // Blink led + PORTB ^= (1 << 4); + + currByte = 0; + while (1) { + c = 0; + while (c < 1024) { + PORTF = (currByte & 0xFF); + PORTK = ((currByte >> 8) & 0xFF); + + // Wait for the Byte to appear on the data bus + // Arduino running at 16Mhz -> one nop = 62.5ns + // slowRom is good for 200ns, fastRom is <= 120ns; S-CPU best case read speed: 3.57MHz / 280ns + // let's be conservative and use 6 x 62.5 = 375ns + NOP; NOP; NOP; NOP; NOP; NOP; + + buffer[c] = PINC; + c++; + currByte++; + } + file->write(buffer, 1024); + + // exit while(1) loop once the uint16_t currByte overflows from 0xffff to 0 (current bank is done) + if (currByte == 0) break; + } + + // update progress bar + processedProgressBar += 1024; + draw_progressbar(processedProgressBar, totalProgressBar); + } +} + +/****************************************** + SNES ROM Functions +******************************************/ +void getCartInfo_SNES() { + boolean manualConfig = 0; + + //Prime SA1 cartridge + uint16_t c = 0; + uint16_t currByte = 0; + byte buffer[1024] = { 0 }; + PORTL = 192; + while (c < 1024) { + PORTF = (currByte & 0xFF); + PORTK = ((currByte >> 8) & 0xFF); + + // Wait for the Byte to appear on the data bus + // Arduino running at 16Mhz -> one nop = 62.5ns + // slowRom is good for 200ns, fastRom is <= 120ns; S-CPU best case read speed: 3.57MHz / 280ns + // let's be conservative and use 6 x 62.5 = 375ns + NOP; NOP; NOP; NOP; NOP; NOP; + + buffer[c] = PINC; + c++; + currByte++; + } + + // 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("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 == 243) + println_Msg(F("CX4")); + else if (romChips == 246) + println_Msg(F("DSP2")); + else if (romChips == 245) + println_Msg(F("SPC RAM BATT")); + else if (romChips == 249) + println_Msg(F("SPC RAM RTC")); + else + println_Msg(F("")); + + + if (altconf) + print_Msg(F("Rom Size: ")); + else + print_Msg(F("ROM Size: ")); + print_Msg(romSize); + println_Msg(F("Mbit")); + + print_Msg(F("Banks: ")); + print_Msg(numBanks); + print_Msg(F(" Chips: ")); + println_Msg(romChips); + + 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 +#ifdef enable_OLED + println_Msg(F(" ")); + println_Msg(F(" ")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +#else + println_Msg(F(" ")); +#endif + + // Start manual config + if (manualConfig == 1) { + confMenuManual(); + } +} + +void checkAltConf() { + char tempStr1[2]; + char tempStr2[5]; + altconf = 0; + + 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 file size + romSize = (myFile.read() - 48) * 10 + (myFile.read() - 48); + + // Skip the , in the file + myFile.seekSet(myFile.curPosition() + 1); + + // Read number of banks + numBanks = (myFile.read() - 48) * 100 + (myFile.read() - 48) * 10 + (myFile.read() - 48); + + altconf = 1; + } + // If no match empty string advance by 9 and try again + else { + myFile.seekSet(myFile.curPosition() + 9); + } + } + } + // Close the file: + myFile.close(); +} + +// Read header +boolean checkcart_SNES() { + // set control to read + dataIn(); + + uint16_t c = 0; + uint16_t headerStart = 0xFFC0; + uint16_t currByte = headerStart; + byte snesHeader[64] = { 0 }; + PORTL = 0; + while (c < 64) { + PORTF = (currByte & 0xFF); + PORTK = ((currByte >> 8) & 0xFF); + + NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; + + snesHeader[c] = PINC; + c++; + currByte++; + } + + // Get Checksum as string + sprintf(checksumStr, "%02X%02X", snesHeader[0xFFDF - headerStart], snesHeader[0xFFDE - headerStart]); + + romType = snesHeader[0xFFD5 - headerStart]; + if ((romType >> 5) != 1) { // Detect invalid romType byte due to too long ROM name (22 chars) + romType = LO; // LoROM // Krusty's Super Fun House (U) 1.0 & Contra 3 (U) + } + else if (romType == 0x35) { + romType = EX; // Check if ExHiROM + } + else if (romType == 0x3A) { + romType = HI; // Check if SPC7110 + } + else { + romType &= 1; // Must be LoROM or HiROM + } + + // Check RomSpeed + romSpeed = (snesHeader[0xFFD5 - headerStart] >> 4); + + // Check RomChips + romChips = snesHeader[0xFFD6 - headerStart]; + + if (romChips == 69) { + romSize = 48; + numBanks = 96; + romType = HI; + } + else if (romChips == 243) { + cx4Type = snesHeader[0xFFC9 - headerStart] & 0xF; + if (cx4Type == 2) { // X2 + romSize = 12; + numBanks = 48; + } + else if (cx4Type == 3) { // X3 + romSize = 16; + numBanks = 64; + } + } + else if ((romChips == 245) && (romType == HI)) { + romSize = 24; + numBanks = 48; + } + else if ((romChips == 249) && (romType == HI)) { + romSize = 40; + numBanks = 80; + } + else { + // Check RomSize + byte romSizeExp = snesHeader[0xFFD7 - headerStart] - 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(); + + // Get name + byte myByte = 0; + byte myLength = 0; + for (unsigned int i = 0xFFC0; i < 0xFFD4; i++) { + myByte = snesHeader[i - headerStart]; + if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 15) { + romName[myLength] = char(myByte); + myLength++; + } + } + // If name consists out of all japanese characters use game code + if (myLength == 0) { + // Get rom code + romName[0] = 'S'; + romName[1] = 'H'; + romName[2] = 'V'; + romName[3] = 'C'; + romName[4] = '-'; + for (unsigned int i = 0; i < 4; i++) { + myByte = snesHeader[0xFFB2 + i - headerStart]; + if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 4) { + romName[myLength + 5] = char(myByte); + myLength++; + } + } + if (myLength == 0) { + // Rom code unknown + romName[0] = 'U'; + romName[1] = 'N'; + romName[2] = 'K'; + romName[3] = 'N'; + romName[4] = 'O'; + romName[5] = 'W'; + romName[6] = 'N'; + } + } + + // Read sramSizeExp + byte sramSizeExp; + if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { + // SuperFX + if (snesHeader[0x7FDA - headerStart] == 0x33) { + sramSizeExp = snesHeader[0x7FBD - headerStart]; + } + else { + if (strncmp(romName, "STARFOX2", 8) == 0) { + sramSizeExp = 6; + } + else { + sramSizeExp = 5; + } + } + } + else { + // No SuperFX + sramSizeExp = snesHeader[0xFFD8 - headerStart]; + } + + // Calculate sramSize + // Fail states usually have sramSizeExp at 255 (no cart inserted, SA-1 failure, etc) + if (sramSizeExp != 0 && sramSizeExp != 255) { + sramSizeExp = sramSizeExp + 3; + sramSize = 1; + while (sramSizeExp--) + sramSize *= 2; + } + else { + sramSize = 0; + } + + // Check Cart Country + //int cartCountry = snesHeader[0xFFD9 - headerStart]; + + // ROM Version + romVersion = snesHeader[0xFFDB - headerStart]; + + // Test if checksum is equal to reverse checksum + if (((word(snesHeader[0xFFDC - headerStart]) + (word(snesHeader[0xFFDD - headerStart]) * 256)) + (word(snesHeader[0xFFDE - headerStart]) + (word(snesHeader[0xFFDF - headerStart]) * 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 == 10) || (calcFilesize == 12) || (calcFilesize == 20) || (calcFilesize == 24)) { + unsigned long calcBase = 0; + unsigned long calcMirror = 0; + byte calcMirrorCount = 0; + if (calcFilesize > 16) + calcBase = 2097152; + else + calcBase = 1048576; + calcMirror = myFile.fileSize() - calcBase; + calcMirrorCount = calcBase / calcMirror; + + // Momotarou Dentetsu Happy Fix 3MB (24Mbit) + if ((calcFilesize == 24) && (romChips == 245)) { + 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 { + // Base 8/16 Mbit chunk + for (j = 0; j < (calcBase / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + } + calcChecksum = calcChecksumChunk; + calcChecksumChunk = 0; + // Add the mirrored chunk + for (j = 0; j < (calcMirror / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + } + calcChecksum += calcMirrorCount * 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 { + // Else show error + 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, "SNES/ROM/%s/%d", romName, foldern - 1); + + char calcsumStr[5]; + sprintf(calcsumStr, "%04X", calc_checksum(fileName, folder)); + + if (strcmp(calcsumStr, checksumStr) == 0) { + print_Msg(F("Checksum OK: ")); + println_Msg(calcsumStr); + display_Update(); + return 1; + } + else { + print_Msg(F("Checksum Error: ")); + println_Msg(calcsumStr); + print_Error(F(""), 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, "SNES/ROM/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + //clear the screen + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_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("Can't create file on SD"), true); + } + + //Dump Derby Stallion '96 (Japan) Actual Size is 24Mb + if ((romType == LO) && (numBanks == 128) && (strcmp("CC86", checksumStr) == 0)) { + // Read Banks 0x00-0x3F for the 1st/2nd MB + for (int currBank = 0; currBank < 64; 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); + } + } + //Read Bank 0x80-9F for the 3rd MB + for (int currBank = 128; currBank < 160; 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 Low-type ROM + else if (romType == LO) { + if (romChips == 243) { //0xF3 + cx4Map = readBank_SNES(0, 32594); //0x7F52 + if ((cx4Type == 2) && (cx4Map != 0)) { //X2 + dataOut(); + controlOut_SNES(); + writeBank_SNES(0, 32594, 0); // Set 0x7F52 to 0 + dataIn(); + controlIn_SNES(); + } + else if ((cx4Type == 3) && (cx4Map == 0)) { //X3 + dataOut(); + controlOut_SNES(); + writeBank_SNES(0, 32594, 1); // Set 0x7F52 to 1 + dataIn(); + controlIn_SNES(); + } + } + if (romSize > 24) { + // ROM > 96 banks (up to 128 banks) + readLoRomBanks( 0x80, numBanks + 0x80, &myFile ); + } else { + // Read up to 96 banks starting at bank 0×00. + readLoRomBanks( 0, numBanks, &myFile ); + } + if (romChips == 243) { //0xF3 + // Restore CX4 Mapping Register + dataOut(); + controlOut_SNES(); + writeBank_SNES(0, 32594, cx4Map); // 0x7F52 + dataIn(); + controlIn_SNES(); + } + } + + // 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(); + + readHiRomBanks( 240, 256, &myFile ); + if (currMemmap == 2) display_Clear(); // need more space for the progress bars + } + + dataOut(); + controlOut_SNES(); + + writeBank_SNES(0, 18439, initialSOMap); + + dataIn(); + controlIn_SNES(); + } + + // Dump SPC7110 High-type ROM + else if ((romType == HI) && ((romChips == 245) || (romChips == 249))) { + println_Msg(F("Dumping SPC7110 HiRom")); + display_Update(); + + // 0xC00000-0xDFFFFF + //print_Msg(F("Part 1")); + display_Update(); + readHiRomBanks( 192, 224, &myFile ); + + if (numBanks > 32) { + dataOut(); + controlOut_SNES(); + // Set 0x4834 to 0xFF + writeBank_SNES( 0, 0x4834, 0xFF ); + + dataIn(); + controlIn_SNES(); + + // 0xE00000-0xEFFFFF + //print_Msg(F(" 2")); + display_Update(); + readHiRomBanks( 224, 240, &myFile ); + + if (numBanks > 48) { + // 0xF00000-0xFFFFFF + //print_Msg(F(" 3")); + display_Update(); + readHiRomBanks( 240, 256, &myFile ); + + dataOut(); + controlOut_SNES(); + + // Set 0x4833 to 3 + writeBank_SNES( 0, 0x4833, 3 ); + + dataIn(); + controlIn_SNES(); + + // 0xF00000-0xFFFFFF + //print_Msg(F(" 4")); + display_Update(); + readHiRomBanks( 240, 256, &myFile ); + } + //println_Msg(F("")); + display_Clear(); // need more space due to the 4 progress bars + + // Return mapping registers to initial settings... + dataOut(); + controlOut_SNES(); + + writeBank_SNES( 0, 0x4833, 2 ); + writeBank_SNES( 0, 0x4834, 0 ); + + dataIn(); + controlIn_SNES(); + } + } + + // Dump standard High-type ROM + else if ((romType == HI) || (romType == SA) || (romType == EX)) { + println_Msg(F("Dumping HiRom...")); + display_Update(); + + readHiRomBanks( 192, numBanks + 192, &myFile ); + } + + // Close the file: + myFile.close(); +} + +/****************************************** + SNES SRAM Functions +*****************************************/ +// Write file to SRAM +void writeSRAM (boolean browseFile) { + if (browseFile) { + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser(F("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(); + + int sramBanks = 0; + // LoRom + if (romType == LO) { + // Sram size + long lastByte = (long(sramSize) * 128); + + if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX + if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) + sramBanks = lastByte / 0x10000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0x0000; currByte < 0x10000; currByte++) { + writeBank_SNES(currBank, currByte, myFile.read()); + } + } + } + else { // SuperFX SRAM + for (long currByte = 0; currByte < lastByte; currByte++) { + writeBank_SNES(0x70, currByte, myFile.read()); + } + } + } + else if (lastByte > 0x8000) { // Large SRAM Fix + sramBanks = lastByte / 0x8000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0x0000; currByte < 0x8000; currByte++) { + writeBank_SNES(currBank, currByte, myFile.read()); + } + } + } + else { + for (long currByte = 0; currByte < lastByte; currByte++) { + writeBank_SNES(0x70, currByte, myFile.read()); + } + } + } + // HiRom + else if (romType == HI) { + if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM + // Configure SPC7110 SRAM Register + // Set 0x4830 to 0x80 + writeBank_SNES(0, 0x4830, 0x80); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + // Write to sram bank + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + writeBank_SNES(0x30, currByte, myFile.read()); + } + // Reset SPC7110 SRAM Register + dataOut(); + // Reset 0x4830 to 0x0 + writeBank_SNES(0, 0x4830, 0); + dataIn(); + } + else { + // Writing SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + if (lastByte > 0x2000) { // Large SRAM Fix + sramBanks = lastByte / 0x2000; + for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { + for (long currByte = 0x6000; currByte < 0x8000; currByte++) { + writeBank_SNES(currBank, currByte, myFile.read()); + } + } + } + else { + lastByte += 0x6000; + // Write to sram bank + 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) * 128); + // Enable CPU Clock + 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 + byte lastBlock = 0; + lastBlock = lastByte / 0x2000; + + // Writing SRAM on SA1 needs CS(PH3) to be high + // PORTH |= (1 << 3); + + // Setup BW-RAM + // Set 0x2224 (SNES BMAPS) to map SRAM Block 0 to 0x6000-0x7FFF + writeBank_SNES(0, 0x2224, 0); + // Set 0x2226 (SNES SBWE) to 0x80 Write Enable + writeBank_SNES(0, 0x2226, 0x80); + // Set 0x2228 (SNES BWPA) to 0x00 BW-RAM Write-Protected Area + writeBank_SNES(0, 0x2228, 0); + delay(1000); + + // Use $2224 (SNES) to map BW-RAM block to 0x6000-0x7FFF + // Use $2226 (SNES) to write enable the BW-RAM + byte firstByte = 0; + for (byte currBlock = 0; currBlock < lastBlock; currBlock++) { + // Set 0x2224 (SNES BMAPS) to map SRAM Block to 0x6000-0x7FFF + writeBank_SNES(0, 0x2224, currBlock); + // Set 0x2226 (SNES SBWE) to 0x80 Write Enable + writeBank_SNES(0, 0x2226, 0x80); + for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { + myFile.read(sdBuffer, 512); + if ((currBlock == 0) && (currByte == 0x6000)) { + firstByte = sdBuffer[0]; + } + for (int c = 0; c < 512; c++) { + writeBank_SNES(0, currByte + c, sdBuffer[c]); + } + } + } + // Rewrite First Byte + writeBank_SNES(0, 0x2224, 0); + writeBank_SNES(0, 0x2226, 0x80); + writeBank_SNES(0, 0x6000, firstByte); + // 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, "SNES/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); + } + int sramBanks = 0; + if (romType == LO) { + // Sram size + long lastByte = (long(sramSize) * 128); + if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX + if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) + sramBanks = lastByte / 0x10000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0x0000; currByte < 0x10000; currByte++) { + myFile.write(readBank_SNES(currBank, currByte)); + } + } + } + else { // SuperFX SRAM + for (long currByte = 0; currByte < lastByte; currByte++) { + myFile.write(readBank_SNES(0x70, currByte)); + } + } + } + else if (lastByte > 0x8000) { // Large SRAM Fix + sramBanks = lastByte / 0x8000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0x0000; currByte < 0x8000; currByte++) { + myFile.write(readBank_SNES(currBank, currByte)); + } + } + } + else { + for (long currByte = 0; currByte < lastByte; currByte++) { + myFile.write(readBank_SNES(0x70, currByte)); + } + } + } + else if (romType == HI) { + if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM + // Configure SPC7110 SRAM Register + dataOut(); + // Set 0x4830 to 0x80 + writeBank_SNES(0, 0x4830, 0x80); + dataIn(); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + myFile.write(readBank_SNES(0x30, currByte)); + } + dataOut(); + // Reset 0x4830 to 0x0 + writeBank_SNES(0, 0x4830, 0); + dataIn(); + } + else { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + if (lastByte > 0x2000) { // Large SRAM Fix + sramBanks = lastByte / 0x2000; + for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { + for (long currByte = 0x6000; currByte < 0x8000; currByte++) { + myFile.write(readBank_SNES(currBank, currByte)); + } + } + } + else { + lastByte += 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); + if (lastByte > 0x10000) { + sramBanks = lastByte / 0x10000; + for (int currBank = 0x40; currBank < sramBanks + 0x40; currBank++) { + for (long currByte = 0; currByte < 0x10000; currByte++) { + myFile.write(readBank_SNES(currBank, currByte)); + } + } + } + else { + for (long currByte = 0x0; currByte < lastByte; currByte++) { + myFile.write(readBank_SNES(0x40, currByte)); + } + } + } + + // Close the file: + myFile.close(); + + // Signal end of process + display_Clear(); + print_Msg(F("Saved to ")); + print_Msg(folder); + println_Msg(F("/...")); + 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(); + + int sramBanks = 0; + if (romType == LO) { + // Sram size + long lastByte = (long(sramSize) * 128); + if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX + if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) + sramBanks = lastByte / 0x10000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0; currByte < 0x10000; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + } + else { // SuperFX SRAM + 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 (lastByte > 0x8000) { // Large SRAM Fix + sramBanks = lastByte / 0x8000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0; currByte < 0x8000; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + } + else { + 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) { + if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM + // Configure SPC7110 SRAM Register + dataOut(); + // Set 0x4830 to 0x80 + writeBank_SNES(0, 0x4830, 0x80); + dataIn(); + // 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++; + } + } + } + dataOut(); + // Reset 0x4830 to 0x0 + writeBank_SNES(0, 0x4830, 0); + dataIn(); + } + else { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + if (lastByte > 0x2000) { // Large SRAM Fix + sramBanks = lastByte / 0x2000; + for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { + for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + } + else { + lastByte += 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); + + if (lastByte > 0x10000) { + sramBanks = lastByte / 0x10000; + for (int currBank = 0x40; currBank < sramBanks + 0x40; currBank++) { + for (long currByte = 0x0; currByte < 0x10000; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + } + else { + 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++; + } + } + } + } + // Reset SA1 + // Set pins to input + dataIn(); + // Close the file: + myFile.close(); + if (writeErrors == 0) { + println_Msg(F("Verified OK")); + } + else { + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + display_Update(); + wait(); + + stopSnesClocks_resetCic_resetCart(); + + display_Clear(); + print_Msg(F("Resetting...")); + display_Update(); + delay(3000); // wait 3 secs + resetArduino(); + } + // 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(); + + int sramBanks = 0; + if (romType == LO) { + // Sram size + long lastByte = (long(sramSize) * 128); + + if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX + if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) + sramBanks = lastByte / 0x10000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0x0000; currByte < 0x10000; currByte++) { + writeBank_SNES(currBank, currByte, b); + } + } + } + else { // SuperFX SRAM + for (long currByte = 0; currByte < lastByte; currByte++) { + writeBank_SNES(0x70, currByte, b); + } + } + } + else if (lastByte > 0x8000) { // Large SRAM Fix + sramBanks = lastByte / 0x8000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0x0000; currByte < 0x8000; currByte++) { + writeBank_SNES(currBank, currByte, b); + } + } + } + else { + for (long currByte = 0; currByte < lastByte; currByte++) { + writeBank_SNES(0x70, currByte, b); + } + } + } + else if (romType == HI) { + if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM + // Configure SPC7110 SRAM Register + // Set 0x4830 to 0x80 + writeBank_SNES(0, 0x4830, 0x80); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + // Write to sram bank + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + writeBank_SNES(0x30, currByte, b); + } + // Reset SPC7110 SRAM Register + dataOut(); + // Reset 0x4830 to 0x0 + writeBank_SNES(0, 0x4830, 0); + dataIn(); + } + else { + // Writing SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + if (lastByte > 0x2000) { // Large SRAM Fix + sramBanks = lastByte / 0x2000; + for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { + for (long currByte = 0x6000; currByte < 0x8000; currByte++) { + writeBank_SNES(currBank, currByte, b); + } + } + } + else { + lastByte += 0x6000; + // Write to sram bank + 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); + } + } + // SA1 + else if (romType == SA) { + long lastByte = (long(sramSize) * 128); + // Enable CPU Clock + 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 $2224 to map BW-RAM block to 0x6000-0x7FFF + byte lastBlock = 0; + lastBlock = lastByte / 0x2000; + + // Writing SRAM on SA1 needs CS(PH3) to be high + // PORTH |= (1 << 3); + + // Setup BW-RAM + // Set 0x2224 (SNES BMAPS) to map SRAM Block 0 to 0x6000-0x7FFF + writeBank_SNES(0, 0x2224, 0); + // Set 0x2226 (SNES SBWE) to 0x80 Write Enable + writeBank_SNES(0, 0x2226, 0x80); + // Set 0x2228 (SNES BWPA) to 0x00 BW-RAM Write-Protected Area + writeBank_SNES(0, 0x2228, 0); + delay(1000); + + // Use $2224 (SNES) to map BW-RAM block to 0x6000-0x7FFF + // Use $2226 (SNES) to write enable the BW-RAM + for (byte currBlock = 0; currBlock < lastBlock; currBlock++) { + // Set 0x2224 (SNES BMAPS) to map SRAM Block to 0x6000-0x7FFF + writeBank_SNES(0, 0x2224, currBlock); + // Set 0x2226 (SNES SBWE) to 0x80 Write Enable + writeBank_SNES(0, 0x2226, 0x80); + for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { + for (int c = 0; c < 512; c++) { + writeBank_SNES(0, currByte + c, b); + } + } + } + // Rewrite First Byte + writeBank_SNES(0, 0x2224, 0); + writeBank_SNES(0, 0x2226, 0x80); + writeBank_SNES(0, 0x6000, b); + // Disable CPU clock + clockgen.output_enable(SI5351_CLK1, 0); + } + + dataIn(); + + // Variable for errors + writeErrors = 0; + + // Set control + controlIn_SNES(); + + sramBanks = 0; + if (romType == LO) { + // Sram size + long lastByte = (long(sramSize) * 128); + if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX + if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) + sramBanks = lastByte / 0x10000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0; currByte < 0x10000; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != b) { + writeErrors++; + } + } + } + } + } + else { // SuperFX SRAM + 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 (lastByte > 0x8000) { // Large SRAM Fix + sramBanks = lastByte / 0x8000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0; currByte < 0x8000; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != b) { + writeErrors++; + } + } + } + } + } + else { + 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) { + if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM + // Configure SPC7110 SRAM Register + dataOut(); + // Set 0x4830 to 0x80 + writeBank_SNES(0, 0x4830, 0x80); + dataIn(); + // 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++; + } + } + } + dataOut(); + // Reset 0x4830 to 0x0 + writeBank_SNES(0, 0x4830, 0); + dataIn(); + } + else { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + if (lastByte > 0x2000) { // Large SRAM Fix + sramBanks = lastByte / 0x2000; + for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { + for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != b) { + writeErrors++; + } + } + } + } + } + else { + lastByte += 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++; + } + } + } + } + else if (romType == SA) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + if (lastByte > 0x10000) { + sramBanks = lastByte / 0x10000; + for (int currBank = 0x40; currBank < sramBanks + 0x40; currBank++) { + for (long currByte = 0x0; currByte < 0x10000; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != b) { + writeErrors++; + } + } + } + } + } + else { + for (long currByte = 0x0; currByte < lastByte; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x40, currByte + c)) != b) { + writeErrors++; + } + } + } + } + } + if (writeErrors == 0) { + println_Msg(F("OK")); + return 1; + } + else { + println_Msg(F("ERROR")); + return 0; + } + display_Update(); +} + +#endif + +//****************************************** +// End of File +//****************************************** diff --git a/Cart_Reader/snes_clk.cpp b/Cart_Reader/snes_clk.cpp new file mode 100644 index 0000000..8947f3f --- /dev/null +++ b/Cart_Reader/snes_clk.cpp @@ -0,0 +1,27 @@ +#include "snes_clk.h" +#include + +int32_t readClockOffset() { + File clock_file; + unsigned char* clock_buf; + int16_t i; + int32_t clock_offset; + if(!clock_file.open("/snes_clk.txt", FILE_READ)) { + return INT32_MIN; + } + + clock_buf = malloc(16 * sizeof(char)); + i = clock_file.read(clock_buf, 16); + clock_file.close(); + if(i == -1) { + free(clock_buf); + return INT32_MIN; + } else if(i < 16) { + clock_buf[i] = 0; + } + + clock_offset = (int32_t)atoi(clock_buf); + free(clock_buf); + + return clock_offset; +} diff --git a/Cart_Reader/snes_clk.h b/Cart_Reader/snes_clk.h new file mode 100644 index 0000000..c5e24a0 --- /dev/null +++ b/Cart_Reader/snes_clk.h @@ -0,0 +1,8 @@ +#ifndef _SNES_CLK_H +#define _SNES_CLK_H + +#include + +int32_t readClockOffset(); + +#endif