From 27a213a3c1a276b0914a5ac52bcb841be7fd94fa Mon Sep 17 00:00:00 2001 From: sanni Date: Thu, 13 Jun 2024 16:43:20 +0200 Subject: [PATCH] Add CFI option to SNES repro menu Only tested with 29LV640 on Chinese LoROM repro. --- Cart_Reader/Config.h | 978 ++++----- Cart_Reader/FLASH.ino | 190 ++ Cart_Reader/SNES.ino | 4506 +++++++++++++++++++++-------------------- 3 files changed, 2950 insertions(+), 2724 deletions(-) diff --git a/Cart_Reader/Config.h b/Cart_Reader/Config.h index c261b49..474c10b 100644 --- a/Cart_Reader/Config.h +++ b/Cart_Reader/Config.h @@ -1,489 +1,489 @@ -/******************************************************************** - Open Source Cartridge Reader -********************************************************************/ - -#ifndef CONFIG_H_ -#define CONFIG_H_ - -/***** FIRMWARE CONFIGURATION ************************************** - - Add or remove the "//" in front of items to toggle them. - - Disabled: - //#define HW5 - - Enabled: - #define HW5 - - Things in ** blocks like this are comments. Changing them doesn't - affect the firmware that is flashed to your OSCR. - - If you only get a blank screen or "Press Button" message after - flashing you have enabled too many modules. - -********************************************************************/ - -/*==== HARDWARE VERSION ===========================================*/ - -/* - Choose your hardware version: -*/ - -//#define HW5 -//#define HW4 -//#define HW3 -//#define HW2 -//#define HW1 -//#define SERIAL_MONITOR - -/****/ - -/*==== HARDWARE MODULES ===========================================*/ - -/* [ Automatic Voltage Selection ---------------------------------- ] - Enable this if you have the VSELECT module. -*/ - -//#define ENABLE_VSELECT - -/****/ - -/* [ Clock Generator ---------------------------------------------- ] - Enable this if you have the clock generator module. This will - automatically be enabled if you selected HW2 or newer above. -*/ - -//#define ENABLE_CLOCKGEN - -/****/ - -/* [ Real Time Clock ---------------------------------------------- ] - Enable this if you have the RTC module. You can configure the - type later in this file. -*/ - -//#define ENABLE_RTC - -/****/ - -/*==== GAME SYSTEM CORES ==========================================*/ - -/* [ Atari 2600 --------------------------------------------------- ] -*/ - -//#define ENABLE_2600 - -/****/ - -/* [ Atari 5200 --------------------------------------------------- ] -*/ - -//#define ENABLE_5200 - -/****/ - -/* [ Atari 7800 --------------------------------------------------- ] -*/ - -//#define ENABLE_7800 - -/****/ - -/* [ Benesse Pocket Challenge W ----------------------------------- ] -*/ - -//#define ENABLE_PCW - -/****/ - -/* [ C64 --------------------------------------------------- ] -*/ - -//#define ENABLE_C64 - -/****/ - -/* [ ColecoVision ------------------------------------------------- ] -*/ - -//#define ENABLE_COLV - -/****/ - -/* [ Emerson Arcadia 2001 ----------------------------------------- ] -*/ - -//#define ENABLE_ARC - -/****/ - -/* [ Fairchild Channel F ------------------------------------------ ] -*/ - -//#define ENABLE_FAIRCHILD - -/****/ - -/* [ Flashrom Programmer for SNES repros -------------------------- ] -*/ - -//#define ENABLE_FLASH -//#define ENABLE_FLASH16 - -/****/ - -/* [ Game Boy (Color) and Advance --------------------------------- ] -*/ - -#define ENABLE_GBX - -/****/ - -/* [ Intellivision ------------------------------------------------ ] -*/ - -//#define ENABLE_INTV - -/****/ - -/* [ Neo Geo Pocket ----------------------------------------------- ] -*/ - -//#define ENABLE_NGP - -/****/ - -/* [ Nintendo 64 -------------------------------------------------- ] -*/ - -#define ENABLE_N64 - -/****/ - -/* [ Nintendo Entertainment System/Family Computer ---------------- ] -*/ - -#define ENABLE_NES - -/****/ - -/* [ Magnavox Odyssey 2 ------------------------------------------- ] -*/ - -//#define ENABLE_ODY2 - -/****/ - -/* [ MSX ------------------------------------------- ] -*/ - -//#define ENABLE_MSX - -/****/ - -/* [ PC Engine/TurboGrafx 16 -------------------------------------- ] -*/ - -//#define ENABLE_PCE - -/****/ - -/* [ Pokemon Mini -------------------------------------- ] -*/ - -//#define ENABLE_POKE - -/****/ - -/* [ Sega Master System/Mark III/Game Gear/SG-1000 ---------------- ] -*/ - -#define ENABLE_SMS - -/****/ - -/* [ Sega Mega Drive/Genesis -------------------------------------- ] -*/ - -#define ENABLE_MD - -/****/ - -/* [ Super Famicom SF Memory Cassette ----------------------------- ] -*/ - -//#define ENABLE_SFM - -/****/ - -/* [ Super Famicom Satellaview ------------------------------------ ] -*/ - -//#define ENABLE_SV - -/****/ - -/* [ Super Famicom Sufami Turbo ----------------------------------- ] -*/ - -//#define ENABLE_ST - -/****/ - -/* [ Super Famicom Game Processor RAM Cassette -------------------- ] -*/ - -//#define ENABLE_GPC - -/****/ - -/* [ Super Nintendo ----------------------------------------------- ] -*/ - -#define ENABLE_SNES - -/****/ - -/* [ Vectrex --------------------------------------------------- ] -*/ - -//#define ENABLE_VECTREX - -/****/ - -/* [ Virtual Boy -------------------------------------------------- ] -*/ - -//#define ENABLE_VBOY - -/****/ - -/* [ Watara Supervision ------------------------------------------- ] -*/ - -//#define ENABLE_WSV - -/****/ - -/* [ WonderSwan and Benesse Pocket Challenge v2 ------------------- ] -*/ - -//#define ENABLE_WS - -/****/ - -/* [ Super A'can -------------------------------------------------- ] -*/ - -//#define ENABLE_SUPRACAN - -/****/ - -/* [ Casio Loopy -------------------------------------------------- ] -*/ - -//#define ENABLE_LOOPY - -/****/ - -/*==== FIRMWARE OPTIONS ===========================================*/ - -/* [ Config File -------------------------------------------------- ] - Allow changing some configuration values via a config file. You - generally can only use the config to set options or disable - certain featuress. It cannot be used to toggle firmware options - on, only off. - - Note For Developers: See OSCR.* for info. - - Filename: config.txt -*/ - -//#define ENABLE_CONFIG - -/****/ - -/* [ LCD: Background Color ---------------------------------------- ] - Set the backlight color of the LCD if you have one. - - Can be set using config: - lcd.confColor=1 - lcd.red=0 - lcd.green=0 - lcd.blue=0 - - PARAMETERS: - Green, Red, Blue -*/ - -#define OPTION_LCD_BG_COLOR 100, 0, 0 - -/****/ - -/* [ 3.3V Stability Fix (3V3FIX) ---------------------------------- ] - Enable this if you are having stability issues when using 3.3V, - works best with VSELECT. - - If not using VSELECT, always turn the cart reader on with the - voltage switch set to 5V and switch to 5V before selecting a - cartridge from the menu. -*/ - -//#define ENABLE_3V3FIX - -/****/ - -/* [ Updater ------------------------------------------------------ ] - Disable this if you don't plan to/want to use the firmware - updater utility. This setting is ignored on hardware versions - other than HW5 and HW3. -*/ - -#define ENABLE_UPDATER - -/****/ - -/* [ Self Test ---------------------------------------------------- ] - Tests for shorts and other issues in your OSCR build. -*/ - -#define ENABLE_SELFTEST - -/****/ - -/* [ Logging ------------------------------------------------------ ] - Write all info to OSCR_LOG.txt in root dir - - Can be toggled off using config: - oscr.logging=0 -*/ - -#define ENABLE_GLOBAL_LOG - -/****/ - -/* [ RTC: IC Type ------------------------------------------------- ] - When the RTC module is installed, choose the type here. This - setting is ignored if the RTC option is not enabled. -*/ - -#define DS3231 -//#define DS1307 - -/****/ - -/* [ SNES Core/CLOCKGEN: Read Clock Generator Calibration Data ---- ] - Toggle clock calibration menu and whether or not to use calibration data from snes_clk.txt -*/ - -//#define OPTION_CLOCKGEN_CALIBRATION -//#define OPTION_CLOCKGEN_USE_CALIBRATION - -/****/ - -/* [ MegaDrive/Genesis Core: Compatibility Settings --------------- ] - Allows you to create a text file on the SD card called - "mdconf.txt" which you should place the following into: - - [segaSram16bit] N - - Where N is: - 0: Output each byte once. Not supported by emulators. (default) - 1: Duplicate each byte. Usable by Kega Fusion. - 2: Same as 1 + pad with 0xFF so that the file size is 64KB. - - ** - ** DEPRECATED: Use the config file instead. See below. - ** -*/ - -//#define use_md_conf - -/* - Configure how the MD core saves are formatted. - - Can be set using config: - md.saveType=0 - - If config is enabled, this option does nothing -- use the config. - - Options: - 0: Output each byte once. Not supported by emulators. (default) - 1: Duplicate each byte. Usable by Kega Fusion. - 2: Same as 1 + pad with 0xFF so that the file size is 64KB. -*/ - -//#define OPTION_MD_DEFAULT_SAVE_TYPE 0 - -/****/ - -/* [ N64 Core: Fast CRC ------------------------------------------- ] - Toggle so the CRC for N64 Roms will be calculated during dumping - from memory instead of after dumping from SD card, not compatible - with all Cart Readers -*/ - -//#define OPTION_N64_FASTCRC - -/****/ - -/* [ N64 Core: Log Summary ---------------------------------------- ] - Enable to save a n64log.txt file with rom info in /N64/ROM -*/ - -//#define OPTION_N64_SAVESUMMARY - -/****/ - -/*==== PROCESSING =================================================*/ - -/* - You probably shouldn't change this stuff! -*/ - -#if defined(ENABLE_CONFIG) -# define CONFIG_FILE "config.txt" -// Define the max length of the key=value pairs -// Do your best not to have to increase these. -# define CONFIG_KEY_MAX 32 -# define CONFIG_VALUE_MAX 32 -#endif - -#if (defined(HW4) || defined(HW5)) -# define ENABLE_LCD -# define ENABLE_NEOPIXEL -# define ENABLE_ROTARY -//# define rotate_counter_clockwise -# define ENABLE_CLOCKGEN -# define OPTION_N64_FASTCRC -# define OPTION_WS_ADAPTER_V2 -#endif - -#if (defined(HW2) || defined(HW3)) -# define ENABLE_OLED -# define ENABLE_BUTTON2 -# define ENABLE_CLOCKGEN -# define ENABLE_CA_LED -# define OPTION_N64_FASTCRC -#endif - -#if defined(HW1) -# define ENABLE_OLED -//#define ENABLE_CLOCKGEN -//#define OPTION_N64_FASTCRC -#endif - -#if defined(SERIAL_MONITOR) -# define ENABLE_SERIAL -//#define ENABLE_CLOCKGEN -//#define OPTION_N64_FASTCRC -#endif - -/* Firmware updater only works with HW3 and HW5 */ -#if !(defined(HW5) || defined(HW3)) -# undef ENABLE_UPDATER -#endif - -/* End of settings */ - -#endif /* CONFIG_H_ */ +/******************************************************************** + Open Source Cartridge Reader +********************************************************************/ + +#ifndef CONFIG_H_ +#define CONFIG_H_ + +/***** FIRMWARE CONFIGURATION ************************************** + + Add or remove the "//" in front of items to toggle them. + + Disabled: + //#define HW5 + + Enabled: + #define HW5 + + Things in ** blocks like this are comments. Changing them doesn't + affect the firmware that is flashed to your OSCR. + + If you only get a blank screen or "Press Button" message after + flashing you have enabled too many modules. + +********************************************************************/ + +/*==== HARDWARE VERSION ===========================================*/ + +/* + Choose your hardware version: +*/ + +//#define HW5 +//#define HW4 +//#define HW3 +//#define HW2 +//#define HW1 +//#define SERIAL_MONITOR + +/****/ + +/*==== HARDWARE MODULES ===========================================*/ + +/* [ Automatic Voltage Selection ---------------------------------- ] + Enable this if you have the VSELECT module. +*/ + +//#define ENABLE_VSELECT + +/****/ + +/* [ Clock Generator ---------------------------------------------- ] + Enable this if you have the clock generator module. This will + automatically be enabled if you selected HW2 or newer above. +*/ + +//#define ENABLE_CLOCKGEN + +/****/ + +/* [ Real Time Clock ---------------------------------------------- ] + Enable this if you have the RTC module. You can configure the + type later in this file. +*/ + +//#define ENABLE_RTC + +/****/ + +/*==== GAME SYSTEM CORES ==========================================*/ + +/* [ Atari 2600 --------------------------------------------------- ] +*/ + +//#define ENABLE_2600 + +/****/ + +/* [ Atari 5200 --------------------------------------------------- ] +*/ + +//#define ENABLE_5200 + +/****/ + +/* [ Atari 7800 --------------------------------------------------- ] +*/ + +//#define ENABLE_7800 + +/****/ + +/* [ Benesse Pocket Challenge W ----------------------------------- ] +*/ + +//#define ENABLE_PCW + +/****/ + +/* [ C64 --------------------------------------------------- ] +*/ + +//#define ENABLE_C64 + +/****/ + +/* [ ColecoVision ------------------------------------------------- ] +*/ + +//#define ENABLE_COLV + +/****/ + +/* [ Emerson Arcadia 2001 ----------------------------------------- ] +*/ + +//#define ENABLE_ARC + +/****/ + +/* [ Fairchild Channel F ------------------------------------------ ] +*/ + +//#define ENABLE_FAIRCHILD + +/****/ + +/* [ Flashrom Programmer for SNES repros -------------------------- ] +*/ + +#define ENABLE_FLASH +//#define ENABLE_FLASH16 + +/****/ + +/* [ Game Boy (Color) and Advance --------------------------------- ] +*/ + +#define ENABLE_GBX + +/****/ + +/* [ Intellivision ------------------------------------------------ ] +*/ + +//#define ENABLE_INTV + +/****/ + +/* [ Neo Geo Pocket ----------------------------------------------- ] +*/ + +//#define ENABLE_NGP + +/****/ + +/* [ Nintendo 64 -------------------------------------------------- ] +*/ + +#define ENABLE_N64 + +/****/ + +/* [ Nintendo Entertainment System/Family Computer ---------------- ] +*/ + +#define ENABLE_NES + +/****/ + +/* [ Magnavox Odyssey 2 ------------------------------------------- ] +*/ + +//#define ENABLE_ODY2 + +/****/ + +/* [ MSX ------------------------------------------- ] +*/ + +//#define ENABLE_MSX + +/****/ + +/* [ PC Engine/TurboGrafx 16 -------------------------------------- ] +*/ + +//#define ENABLE_PCE + +/****/ + +/* [ Pokemon Mini -------------------------------------- ] +*/ + +//#define ENABLE_POKE + +/****/ + +/* [ Sega Master System/Mark III/Game Gear/SG-1000 ---------------- ] +*/ + +#define ENABLE_SMS + +/****/ + +/* [ Sega Mega Drive/Genesis -------------------------------------- ] +*/ + +#define ENABLE_MD + +/****/ + +/* [ Super Famicom SF Memory Cassette ----------------------------- ] +*/ + +#define ENABLE_SFM + +/****/ + +/* [ Super Famicom Satellaview ------------------------------------ ] +*/ + +//#define ENABLE_SV + +/****/ + +/* [ Super Famicom Sufami Turbo ----------------------------------- ] +*/ + +//#define ENABLE_ST + +/****/ + +/* [ Super Famicom Game Processor RAM Cassette -------------------- ] +*/ + +//#define ENABLE_GPC + +/****/ + +/* [ Super Nintendo ----------------------------------------------- ] +*/ + +#define ENABLE_SNES + +/****/ + +/* [ Vectrex --------------------------------------------------- ] +*/ + +//#define ENABLE_VECTREX + +/****/ + +/* [ Virtual Boy -------------------------------------------------- ] +*/ + +//#define ENABLE_VBOY + +/****/ + +/* [ Watara Supervision ------------------------------------------- ] +*/ + +//#define ENABLE_WSV + +/****/ + +/* [ WonderSwan and Benesse Pocket Challenge v2 ------------------- ] +*/ + +//#define ENABLE_WS + +/****/ + +/* [ Super A'can -------------------------------------------------- ] +*/ + +//#define ENABLE_SUPRACAN + +/****/ + +/* [ Casio Loopy -------------------------------------------------- ] +*/ + +//#define ENABLE_LOOPY + +/****/ + +/*==== FIRMWARE OPTIONS ===========================================*/ + +/* [ Config File -------------------------------------------------- ] + Allow changing some configuration values via a config file. You + generally can only use the config to set options or disable + certain featuress. It cannot be used to toggle firmware options + on, only off. + + Note For Developers: See OSCR.* for info. + + Filename: config.txt +*/ + +//#define ENABLE_CONFIG + +/****/ + +/* [ LCD: Background Color ---------------------------------------- ] + Set the backlight color of the LCD if you have one. + + Can be set using config: + lcd.confColor=1 + lcd.red=0 + lcd.green=0 + lcd.blue=0 + + PARAMETERS: + Green, Red, Blue +*/ + +#define OPTION_LCD_BG_COLOR 100, 0, 0 + +/****/ + +/* [ 3.3V Stability Fix (3V3FIX) ---------------------------------- ] + Enable this if you are having stability issues when using 3.3V, + works best with VSELECT. + + If not using VSELECT, always turn the cart reader on with the + voltage switch set to 5V and switch to 5V before selecting a + cartridge from the menu. +*/ + +//#define ENABLE_3V3FIX + +/****/ + +/* [ Updater ------------------------------------------------------ ] + Disable this if you don't plan to/want to use the firmware + updater utility. This setting is ignored on hardware versions + other than HW5 and HW3. +*/ + +#define ENABLE_UPDATER + +/****/ + +/* [ Self Test ---------------------------------------------------- ] + Tests for shorts and other issues in your OSCR build. +*/ + +#define ENABLE_SELFTEST + +/****/ + +/* [ Logging ------------------------------------------------------ ] + Write all info to OSCR_LOG.txt in root dir + + Can be toggled off using config: + oscr.logging=0 +*/ + +#define ENABLE_GLOBAL_LOG + +/****/ + +/* [ RTC: IC Type ------------------------------------------------- ] + When the RTC module is installed, choose the type here. This + setting is ignored if the RTC option is not enabled. +*/ + +#define DS3231 +//#define DS1307 + +/****/ + +/* [ SNES Core/CLOCKGEN: Read Clock Generator Calibration Data ---- ] + Toggle clock calibration menu and whether or not to use calibration data from snes_clk.txt +*/ + +//#define OPTION_CLOCKGEN_CALIBRATION +//#define OPTION_CLOCKGEN_USE_CALIBRATION + +/****/ + +/* [ MegaDrive/Genesis Core: Compatibility Settings --------------- ] + Allows you to create a text file on the SD card called + "mdconf.txt" which you should place the following into: + + [segaSram16bit] N + + Where N is: + 0: Output each byte once. Not supported by emulators. (default) + 1: Duplicate each byte. Usable by Kega Fusion. + 2: Same as 1 + pad with 0xFF so that the file size is 64KB. + + ** + ** DEPRECATED: Use the config file instead. See below. + ** +*/ + +//#define use_md_conf + +/* + Configure how the MD core saves are formatted. + + Can be set using config: + md.saveType=0 + + If config is enabled, this option does nothing -- use the config. + + Options: + 0: Output each byte once. Not supported by emulators. (default) + 1: Duplicate each byte. Usable by Kega Fusion. + 2: Same as 1 + pad with 0xFF so that the file size is 64KB. +*/ + +//#define OPTION_MD_DEFAULT_SAVE_TYPE 0 + +/****/ + +/* [ N64 Core: Fast CRC ------------------------------------------- ] + Toggle so the CRC for N64 Roms will be calculated during dumping + from memory instead of after dumping from SD card, not compatible + with all Cart Readers +*/ + +//#define OPTION_N64_FASTCRC + +/****/ + +/* [ N64 Core: Log Summary ---------------------------------------- ] + Enable to save a n64log.txt file with rom info in /N64/ROM +*/ + +//#define OPTION_N64_SAVESUMMARY + +/****/ + +/*==== PROCESSING =================================================*/ + +/* + You probably shouldn't change this stuff! +*/ + +#if defined(ENABLE_CONFIG) +# define CONFIG_FILE "config.txt" +// Define the max length of the key=value pairs +// Do your best not to have to increase these. +# define CONFIG_KEY_MAX 32 +# define CONFIG_VALUE_MAX 32 +#endif + +#if (defined(HW4) || defined(HW5)) +# define ENABLE_LCD +# define ENABLE_NEOPIXEL +# define ENABLE_ROTARY +//# define rotate_counter_clockwise +# define ENABLE_CLOCKGEN +# define OPTION_N64_FASTCRC +# define OPTION_WS_ADAPTER_V2 +#endif + +#if (defined(HW2) || defined(HW3)) +# define ENABLE_OLED +# define ENABLE_BUTTON2 +# define ENABLE_CLOCKGEN +# define ENABLE_CA_LED +# define OPTION_N64_FASTCRC +#endif + +#if defined(HW1) +# define ENABLE_OLED +//#define ENABLE_CLOCKGEN +//#define OPTION_N64_FASTCRC +#endif + +#if defined(SERIAL_MONITOR) +# define ENABLE_SERIAL +//#define ENABLE_CLOCKGEN +//#define OPTION_N64_FASTCRC +#endif + +/* Firmware updater only works with HW3 and HW5 */ +#if !(defined(HW5) || defined(HW3)) +# undef ENABLE_UPDATER +#endif + +/* End of settings */ + +#endif /* CONFIG_H_ */ diff --git a/Cart_Reader/FLASH.ino b/Cart_Reader/FLASH.ino index 3e9113c..e8f25d7 100644 --- a/Cart_Reader/FLASH.ino +++ b/Cart_Reader/FLASH.ino @@ -2363,7 +2363,197 @@ void print_Eprom(int numBytes) { } display_Update(); } + #endif + +/****************************************** +CFI flashrom functions (copy&paste from GB.ino) +*****************************************/ +void sendCFICommand_Flash(byte cmd) { + writeByteCompensated_Flash(0xAAA, 0xaa); + writeByteCompensated_Flash(0x555, 0x55); + writeByteCompensated_Flash(0xAAA, cmd); +} + +byte readByteCompensated_Flash(int address) { + byte data = readByte_Flash(address >> (flashX16Mode ? 1 : 0)); + if (flashSwitchLastBits) { + return (data & 0b11111100) | ((data << 1) & 0b10) | ((data >> 1) & 0b01); + } + return data; +} + +void writeByteCompensated_Flash(int address, byte data) { + if (flashSwitchLastBits) { + data = (data & 0b11111100) | ((data << 1) & 0b10) | ((data >> 1) & 0b01); + } + writeByte_Flash(address >> (flashX16Mode ? 1 : 0), data); +} + +void startCFIMode_Flash(boolean x16Mode) { + if (x16Mode) { + writeByte_Flash(0x555, 0xf0); //x16 mode reset command + delay(500); + writeByte_Flash(0x555, 0xf0); //Double reset to get out of possible Autoselect + CFI mode + delay(500); + writeByte_Flash(0x55, 0x98); //x16 CFI Query command + } else { + writeByte_Flash(0xAAA, 0xf0); //x8 mode reset command + delay(100); + writeByte_Flash(0xAAA, 0xf0); //Double reset to get out of possible Autoselect + CFI mode + delay(100); + writeByte_Flash(0xAA, 0x98); //x8 CFI Query command + } +} + +void identifyCFI_Flash() { + display_Clear(); + + // Reset flash + dataOut(); + writeByteCompensated_Flash(0xAAA, 0xf0); + delay(100); + + // Trying x8 mode first + startCFIMode_Flash(false); + dataIn8(); + + char cfiQRYx8[7]; + char cfiQRYx16[7]; + sprintf(cfiQRYx8, "%02X%02X%02X", readByte_Flash(0x20), readByte_Flash(0x22), readByte_Flash(0x24)); + sprintf(cfiQRYx16, "%02X%02X%02X", readByte_Flash(0x10), readByte_Flash(0x11), readByte_Flash(0x12)); + + if (strcmp(cfiQRYx8, "515259") == 0) { + println_Msg(F("Normal CFI x8 Mode")); + flashX16Mode = false; + flashSwitchLastBits = false; + } else if (strcmp(cfiQRYx8, "52515A") == 0) { // QRY in x8 mode with switched last bit + println_Msg(F("Switched CFI x8 Mode")); + flashX16Mode = false; + flashSwitchLastBits = true; + } else if (strcmp(cfiQRYx16, "515259") == 0) { // QRY in x16 mode + println_Msg(F("Normal CFI x16 Mode")); + flashX16Mode = true; + flashSwitchLastBits = false; + } else if (strcmp(cfiQRYx16, "52515A") == 0) { // QRY in x16 mode with switched last bit + println_Msg(F("Switched CFI x16 Mode")); + flashX16Mode = true; + flashSwitchLastBits = true; + } else { + // Try x16 mode next + startCFIMode(true); + sprintf(cfiQRYx16, "%02X%02X%02X", readByte_Flash(0x10), readByte_Flash(0x11), readByte_Flash(0x12)); + if (strcmp(cfiQRYx16, "515259") == 0) { // QRY in x16 mode + println_Msg(F("Normal CFI x16 Mode")); + flashX16Mode = true; + flashSwitchLastBits = false; + } else if (strcmp(cfiQRYx16, "52515A") == 0) { // QRY in x16 mode with switched last bit + println_Msg(F("Switched CFI x16 Mode")); + flashX16Mode = true; + flashSwitchLastBits = true; + } else { + println_Msg(F("CFI Query failed!")); + display_Update(); + wait(); + return; + } + } + flashBanks = 1 << (readByteCompensated_Flash(0x4E) - 14); // - flashX16Mode); + + // Reset flash + dataOut(); + writeByteCompensated_Flash(0xAAA, 0xf0); + dataIn8(); + delay(100); + display_Update(); +} + +// Write flashrom +void writeCFI_Flash() { + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser(F("Select file")); + display_Clear(); + + if (openFlashFile()) { + + // Reset flash + dataOut(); + writeByteCompensated_Flash(0xAAA, 0xf0); + dataIn8(); + delay(100); + + // Reset flash + dataOut(); + writeByte_Flash(0x555, 0xf0); + dataIn8(); + + delay(100); + println_Msg(F("Erasing...")); + display_Update(); + + // Erase flash + dataOut(); + sendCFICommand_Flash(0x80); + sendCFICommand_Flash(0x10); + dataIn8(); + + // Read the status register + byte statusReg = readByte_Flash(0); + + // After a completed erase D7 will output 1 + while ((statusReg & 0x80) != 0x80) { + // Blink led + blinkLED(); + delay(100); + // Update Status + statusReg = readByte_Flash(0); + } + + println_Msg(F("Writing flash")); + display_Update(); + + //Initialize progress bar + uint32_t processedProgressBar = 0; + uint32_t totalProgressBar = (uint32_t)fileSize; + draw_progressbar(0, totalProgressBar); + + for (unsigned long currAddr = 0; currAddr < fileSize; currAddr += 512) { + myFile.read(sdBuffer, 512); + + // Blink led + if (currAddr % 4096 == 0) + blinkLED(); + + for (int currByte = 0; currByte < 512; currByte++) { + // Write command sequence + dataOut(); + sendCFICommand_Flash(0xa0); + + // Write current byte + writeByte_Flash(currAddr + currByte, sdBuffer[currByte]); + + dataIn8(); + + // Read the status register + byte statusReg = readByte_Flash(currAddr + currByte); + while ((statusReg & 0x80) != (sdBuffer[currByte] & 0x80)) { + statusReg = readByte_Flash(currAddr + currByte); + } + } + // update progress bar + processedProgressBar += 512; + draw_progressbar(processedProgressBar, totalProgressBar); + } + // Close the file: + myFile.close(); + } + // Reset flash + dataOut(); + writeByteCompensated_Flash(0xAAA, 0xf0); + delay(100); + dataIn8(); +} #endif //****************************************** diff --git a/Cart_Reader/SNES.ino b/Cart_Reader/SNES.ino index 884c402..72007b3 100644 --- a/Cart_Reader/SNES.ino +++ b/Cart_Reader/SNES.ino @@ -1,2235 +1,2271 @@ -//****************************************** -// SUPER NINTENDO MODULE -//****************************************** -#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 = "SNES/SFC cartridge"; -static const char snsMenuItem2[] PROGMEM = "SF Memory Cassette"; -static const char snsMenuItem3[] PROGMEM = "Satellaview BS-X"; -static const char snsMenuItem4[] PROGMEM = "Sufami Turbo"; -static const char snsMenuItem5[] PROGMEM = "Game Processor RAM"; -static const char snsMenuItem6[] PROGMEM = "Flash repro"; -#ifdef OPTION_CLOCKGEN_CALIBRATION -static const char snsMenuItem7[] PROGMEM = "Calibrate Clock"; -static const char* const menuOptionsSNS[] PROGMEM = { snsMenuItem1, snsMenuItem2, snsMenuItem3, snsMenuItem4, snsMenuItem5, snsMenuItem6, snsMenuItem7, FSTRING_RESET }; -#else -static const char* const menuOptionsSNS[] PROGMEM = { snsMenuItem1, snsMenuItem2, snsMenuItem3, snsMenuItem4, snsMenuItem5, snsMenuItem6, FSTRING_RESET }; -#endif - -// SNES menu items -static const char SnesMenuItem4[] PROGMEM = "Test SRAM"; -static const char SnesMenuItem6[] PROGMEM = "Force cart type"; -static const char* const menuOptionsSNES[] PROGMEM = { FSTRING_READ_ROM, FSTRING_READ_SAVE, FSTRING_WRITE_SAVE, SnesMenuItem4, FSTRING_REFRESH_CART, SnesMenuItem6, FSTRING_RESET }; - -// 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* const menuOptionsConfManual[] PROGMEM = { confMenuItem1, confMenuItem2, confMenuItem3, confMenuItem4, FSTRING_RESET }; - -// Repro menu items -static const char reproMenuItem1[] PROGMEM = "LoROM (P0)"; -static const char reproMenuItem2[] PROGMEM = "HiROM (P0)"; -static const char reproMenuItem3[] PROGMEM = "ExLoROM (P1)"; -static const char reproMenuItem4[] PROGMEM = "ExHiROM (P1)"; -static const char* const menuOptionsRepro[] PROGMEM = { reproMenuItem1, reproMenuItem2, reproMenuItem3, reproMenuItem4, FSTRING_RESET }; - -// SNES repro menu -void reproMenu() { - // create menu with title and 6 options to choose from - unsigned char snsRepro; - // Copy menuOptions out of progmem - convertPgm(menuOptionsRepro, 5); - snsRepro = question_box(F("Select Repro Type"), menuOptions, 5, 0); - - // wait for user choice to come back from the question box menu - switch (snsRepro) { -#ifdef ENABLE_FLASH - case 0: - // LoRom - display_Clear(); - display_Update(); - mapping = 0; - setup_Flash8(); - id_Flash8(); - wait(); - mode = CORE_FLASH8; - break; - - case 1: - // HiRom - display_Clear(); - display_Update(); - mapping = 1; - setup_Flash8(); - id_Flash8(); - wait(); - mode = CORE_FLASH8; - break; - - case 2: - // ExLoRom - display_Clear(); - display_Update(); - mapping = 2; - setup_Flash8(); - id_Flash8(); - wait(); - mode = CORE_FLASH8; - break; - - case 3: - // ExHiRom - display_Clear(); - display_Update(); - mapping = 3; - setup_Flash8(); - id_Flash8(); - wait(); - mode = CORE_FLASH8; - break; -#endif - - case 4: - resetArduino(); - break; - } -} - -// SNES start menu -void snsMenu() { - // create menu with title and 7 options to choose from - unsigned char snsCart; - // Copy menuOptions out of progmem -#ifdef OPTION_CLOCKGEN_CALIBRATION - convertPgm(menuOptionsSNS, 8); - snsCart = question_box(FS(FSTRING_SELECT_CART_TYPE), menuOptions, 8, 0); -#else - convertPgm(menuOptionsSNS, 7); - snsCart = question_box(FS(FSTRING_SELECT_CART_TYPE), menuOptions, 7, 0); -#endif - - // wait for user choice to come back from the question box menu - switch (snsCart) { - case 0: - display_Clear(); - display_Update(); - setup_Snes(); - mode = CORE_SNES; - break; - -#ifdef ENABLE_SFM - case 1: - display_Clear(); - display_Update(); - setup_SFM(); - mode = CORE_SFM; - break; -#endif - -#ifdef ENABLE_SV - case 2: - display_Clear(); - display_Update(); - setup_SV(); - mode = CORE_SV; - break; -#endif - -#ifdef ENABLE_ST - case 3: - display_Clear(); - display_Update(); - setup_ST(); - mode = CORE_ST; - break; -#endif - -#ifdef ENABLE_GPC - case 4: - display_Clear(); - display_Update(); - setup_GPC(); - mode = CORE_GPC; - break; -#endif - -#ifdef ENABLE_FLASH - case 5: - setup_FlashVoltage(); - reproMenu(); - break; -#endif - - case 6: -#ifdef OPTION_CLOCKGEN_CALIBRATION - clkcal(); - break; - - case 7: -#endif - resetArduino(); - break; - - default: - print_MissingModule(); // does not return - } -} - -// 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("/"); - // start reading from cart - readROM_SNES(); - // Internal Checksum - compare_checksum(); - // CRC32 - compareCRC("snes.txt", 0, 1, 0); -#ifdef ENABLE_GLOBAL_LOG - save_log(); -#endif - display_Update(); - } else { - display_Clear(); - print_Error(F("Does not have ROM")); - } - } - 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")); - } - 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_STR(error_STR, 0); - print_Msg(wrErrors); - print_STR(_bytes_STR, 1); - print_Error(did_not_verify_STR); - } - } else { - display_Clear(); - print_Error(F("Does not have SRAM")); - } - 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(FS(FSTRING_EMPTY)); - println_Msg(FS(FSTRING_EMPTY)); - 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_STR(error_STR, 0); - print_Msg(wrErrors); - print_STR(_bytes_STR, 1); - print_Error(did_not_verify_STR); - } - } else { - display_Clear(); - print_Error(F("Does not have SRAM")); - } - 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(FS(FSTRING_EMPTY)); - // Prints string out of the common strings array either with or without newline - print_STR(press_button_STR, 1); - 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 - if (i2c_found) { - 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() { - // Request 5V - setVoltage(VOLTS_SET_5V); - - // 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 - initializeClockOffset(); - - if (i2c_found) { - // 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); - } -#ifdef ENABLE_CLOCKGEN - else { - display_Clear(); - print_FatalError(F("Clock Generator not found")); - } -#endif - - // 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(); - - if (i2c_found) { - //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, FsFile* 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 (word currBank = start; currBank < total; currBank++) { - PORTL = currBank; - - // Blink led - blinkLED(); - - 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, FsFile* 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 (word currBank = start; currBank < total; currBank++) { - PORTL = currBank; - - // Blink led - blinkLED(); - - 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 - PORTL = 192; - for (uint16_t currByte = 0; currByte < 1024; currByte++) { - PORTF = currByte & 0xFF; - PORTK = currByte >> 8; - - // 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; - } - - // Print start page - if (checkcart_SNES() == 0) { - // Checksum either corrupt or 0000 - manualConfig = 1; - errorLvl = 1; - rgbLed(red_color); - - display_Clear(); - println_Msg(F("ERROR")); - println_Msg(F("Rom header corrupt")); - println_Msg(F("or missing")); - println_Msg(FS(FSTRING_EMPTY)); - println_Msg(FS(FSTRING_EMPTY)); - 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(FS(FSTRING_NAME)); - println_Msg(romName); - - print_Msg(F("Revision: ")); - println_Msg(romVersion); - - 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(FS(FSTRING_SPACE)); - 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 == 67) { - println_Msg(F("SDD1")); - } else if (romChips == 69) { - println_Msg(F("SDD1 BATT")); - } else if (romChips == 85) - println_Msg(F("SRTC RAM 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(FS(FSTRING_EMPTY)); - - - if (altconf) - print_Msg(F("Rom Size: ")); - else - print_Msg(F("ROM Size: ")); - if ((romSize >> 3) < 1) { - print_Msg(1024 * romSize >> 3); - print_Msg(F(" KB")); - } else { - print_Msg(romSize >> 3); - print_Msg(F(" MB")); - } - print_Msg(F(" (")); - print_Msg(numBanks); - println_Msg(F(" banks)")); - - //print_Msg(F("Chips: ")); - //println_Msg(romChips); - - print_Msg(F("Save Size: ")); - print_Msg(sramSize >> 3); - println_Msg(F(" KB")); - - print_Msg(FS(FSTRING_CHECKSUM)); - println_Msg(checksumStr); - display_Update(); - - // Wait for user input -#if (defined(ENABLE_LCD) || defined(ENABLE_OLED)) - // Prints string out of the common strings array either with or without newline - print_STR(press_button_STR, 1); - display_Update(); - wait(); -#endif -#ifdef ENABLE_SERIAL - println_Msg(FS(FSTRING_SPACE)); -#endif - - // Start manual config - if (manualConfig == 1) { - confMenuManual(); - } -} - -void checkAltConf(char crcStr[9]) { - char tempStr2[5]; - char tempStr3[9]; - altconf = 0; - - if (myFile.open("snes.txt", O_READ)) { - // Get cart info - display_Clear(); - println_Msg(F("Searching database...")); - print_Msg(FS(FSTRING_CHECKSUM)); - println_Msg(checksumStr); - display_Update(); - - while (myFile.available()) { - // Skip first line with name - skip_line(&myFile); - - // Skip over the CRC checksum - myFile.seekCur(9); - - // Get internal ROM checksum as string - for (byte j = 0; j < 4; j++) { - tempStr2[j] = char(myFile.read()); - } - tempStr2[4] = '\0'; - - // Check if checksum string is a match else go to next entry in database - if (strcmp(tempStr2, checksumStr) == 0) { - // Skip the , in the file - myFile.seekCur(1); - - // Read the CRC32 of the SNES header out of database - for (byte k = 0; k < 8; k++) { - tempStr3[k] = char(myFile.read()); - } - tempStr3[8] = '\0'; - - print_Msg(F("Header CRC32: ")); - println_Msg(tempStr3); - display_Update(); - - // Skip the , in the file - myFile.seekCur(1); - - // Read file size - byte romSize2 = (myFile.read() - 48) * 10 + (myFile.read() - 48); - - // Skip the , in the file - myFile.seekCur(1); - - // Read number of banks - byte numBanks2 = (myFile.read() - 48) * 100 + (myFile.read() - 48) * 10 + (myFile.read() - 48); - - // skip CRLF - myFile.seekCur(2); - // skip third empty line - skip_line(&myFile); - - // Some games have the same checksum, so compare CRC32 of header area with database too - if (strcmp(tempStr3, crcStr) == 0) { - println_Msg(F("Found")); - display_Update(); - // Game found, check if ROM sizes differ but only change ROM size if non- standard size found in database, else trust the header to be right and the database to be wrong - if (((romSize != romSize2) || (numBanks != numBanks2)) && ((romSize2 == 10) || (romSize2 == 12) || (romSize2 == 20) || (romSize2 == 24) || (romSize2 == 40) || (romSize2 == 48))) { - // Correct size - println_Msg(F("Correcting size")); - print_Msg(FS(FSTRING_SIZE)); - print_Msg(romSize); - print_Msg(F(" -> ")); - print_Msg(romSize2); - println_Msg(F("Mbit")); - print_Msg(F("Banks: ")); - print_Msg(numBanks); - print_Msg(F(" -> ")); - println_Msg(numBanks2); - display_Update(); - delay(1000); - romSize = romSize2; - numBanks = numBanks2; - altconf = 1; - } - break; - } - } - // If no match go to next entry - else { - // skip rest of line - myFile.seekCur(18); - // skip third empty line - skip_line(&myFile); - } - } - // Close the file: - myFile.close(); - } -} - -// Read header -boolean checkcart_SNES() { - // set control to read - dataIn(); - - uint16_t headerStart = 0xFFB0; - byte snesHeader[80]; - PORTL = 0; - for (uint16_t c = 0, currByte = headerStart; c < 80; c++, currByte++) { - PORTF = (currByte & 0xFF); - PORTK = ((currByte >> 8) & 0xFF); - - NOP; - NOP; - NOP; - NOP; - NOP; - NOP; - NOP; - NOP; - NOP; - NOP; - NOP; - NOP; - - snesHeader[c] = PINC; - } - - // Calculate CRC32 of header - char crcStr[9]; - sprintf(crcStr, "%08lX", calculateCRC(snesHeader, 80)); - - // 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 if (strcmp("3BB0", checksumStr) == 0) { // invalid romType due to too long ROM name (Yuyu no Quiz de GO!GO!) - romType = LO; - } 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 == 67) { - romSize = 32; - numBanks = 64; - 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, pass CRC32 of snesHeader but filter out 0000 and FFFF checksums - if (!(strcmp(checksumStr, "0000") == 0) && !(strcmp(checksumStr, "FFFF") == 0)) { - checkAltConf(crcStr); - } - - // Get name - byte myLength = buildRomName(romName, &snesHeader[0xFFC0 - headerStart], 21); - - // 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++) { - byte myByte; - myByte = snesHeader[0xFFB2 + i - headerStart]; - if (((myByte >= '0' && myByte <= '9') || (myByte >= 'A' && myByte <= 'z')) && myLength < 4) { - romName[myLength + 5] = 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[0xFFDA - headerStart] == 0x33) { - sramSizeExp = snesHeader[0xFFBD - 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")); - return 0; - } -} - -boolean compare_checksum() { - print_Msg(F("Checksum... ")); - display_Update(); - - char calcsumStr[5]; - sprintf(calcsumStr, "%04X", calc_checksum(fileName, folder)); - print_Msg(calcsumStr); - - if (strcmp(calcsumStr, checksumStr) == 0) { - println_Msg(F(" -> OK")); - display_Update(); - return 1; - } else { - print_Msg(F(" != ")); - println_Msg(checksumStr); - print_Error(F("Invalid Checksum")); - 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 - createFolderAndOpenFile("SNES", "ROM", romName, "sfc"); - - //Dump Derby Stallion '96 (Japan) and Sound Novel Tsukuru (Japan) - Actual Size is 24Mb - if ((romType == LO) && (numBanks == 96) && ((strcmp("CC86", checksumStr) == 0) || (strcmp("A77B", 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 || romChips == 67)) { - println_Msg(F("Dumping SDD1 HiRom")); - display_Update(); - controlIn_SNES(); - byte initialSOMap = readBank_SNES(0, 18439); - - for (word 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(FS(FSTRING_EMPTY)); - 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(); - - if (romChips == 85) { - // Daikaijuu Monogatari 2, keeps out S-RTC register area - readHiRomBanks(192, 192 + 64, &myFile); - readHiRomBanks(64, numBanks, &myFile); // (64 + (numBanks - 64)) - } else { - 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); - if (i2c_found) { - // 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); - if (i2c_found) { - // 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(FS(FSTRING_FILE_DOESNT_EXIST)); - } -} - -void readSRAM() { - // set control - controlIn_SNES(); - - // Get name, add extension and convert to char array for sd lib - createFolder("SNES", "SAVE", romName, "srm"); - - // 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_FatalError(sd_error_STR); - } - 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_STR(error_STR, 0); - print_Msg(writeErrors); - print_STR(_bytes_STR, 1); - print_Error(did_not_verify_STR); - } - 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")); - return 1; - } -} - -// 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); - if (i2c_found) { - // 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); - - if (i2c_found) { - // 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(FS(FSTRING_OK)); - return 1; - } else { - println_Msg(F("ERROR")); - return 0; - } - display_Update(); -} - -#endif - -//****************************************** -// End of File -//****************************************** +//****************************************** +// SUPER NINTENDO MODULE +//****************************************** +#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 = "SNES/SFC cartridge"; +static const char snsMenuItem2[] PROGMEM = "SF Memory Cassette"; +static const char snsMenuItem3[] PROGMEM = "Satellaview BS-X"; +static const char snsMenuItem4[] PROGMEM = "Sufami Turbo"; +static const char snsMenuItem5[] PROGMEM = "Game Processor RAM"; +static const char snsMenuItem6[] PROGMEM = "Flash repro"; +#ifdef OPTION_CLOCKGEN_CALIBRATION +static const char snsMenuItem7[] PROGMEM = "Calibrate Clock"; +static const char* const menuOptionsSNS[] PROGMEM = { snsMenuItem1, snsMenuItem2, snsMenuItem3, snsMenuItem4, snsMenuItem5, snsMenuItem6, snsMenuItem7, FSTRING_RESET }; +#else +static const char* const menuOptionsSNS[] PROGMEM = { snsMenuItem1, snsMenuItem2, snsMenuItem3, snsMenuItem4, snsMenuItem5, snsMenuItem6, FSTRING_RESET }; +#endif + +// SNES menu items +static const char SnesMenuItem4[] PROGMEM = "Test SRAM"; +static const char SnesMenuItem6[] PROGMEM = "Force cart type"; +static const char* const menuOptionsSNES[] PROGMEM = { FSTRING_READ_ROM, FSTRING_READ_SAVE, FSTRING_WRITE_SAVE, SnesMenuItem4, FSTRING_REFRESH_CART, SnesMenuItem6, FSTRING_RESET }; + +// 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* const menuOptionsConfManual[] PROGMEM = { confMenuItem1, confMenuItem2, confMenuItem3, confMenuItem4, FSTRING_RESET }; + +// Repro menu items +static const char reproMenuItem1[] PROGMEM = "CFI LoROM"; +static const char reproMenuItem2[] PROGMEM = "CFI HiROM"; +static const char reproMenuItem3[] PROGMEM = "LoROM (P0)"; +static const char reproMenuItem4[] PROGMEM = "HiROM (P0)"; +static const char reproMenuItem5[] PROGMEM = "ExLoROM (P1)"; +static const char reproMenuItem6[] PROGMEM = "ExHiROM (P1)"; +static const char* const menuOptionsRepro[] PROGMEM = { reproMenuItem1, reproMenuItem2, reproMenuItem3, reproMenuItem4, reproMenuItem5, reproMenuItem6, FSTRING_RESET }; + +// SNES repro menu +void reproMenu() { + // create menu with title and 7 options to choose from + unsigned char snsRepro; + // Copy menuOptions out of progmem + convertPgm(menuOptionsRepro, 7); + snsRepro = question_box(F("Select Repro Type"), menuOptions, 7, 0); + + // wait for user choice to come back from the question box menu + switch (snsRepro) { +#ifdef ENABLE_FLASH + case 0: + // CFI LoROM + display_Clear(); + display_Update(); + mapping = 0; + flashSize = 4194304; + setup_Flash8(); + identifyCFI_Flash(); + writeCFI_Flash(); + verifyFlash(); + // Prints string out of the common strings array either with or without newline + print_STR(press_button_STR, 0); + display_Update(); + wait(); + resetArduino(); + break; + + case 1: + // CFI HiROM + display_Clear(); + display_Update(); + mapping = 1; + flashSize = 4194304; + setup_Flash8(); + identifyCFI_Flash(); + writeCFI_Flash(); + verifyFlash(); + // Prints string out of the common strings array either with or without newline + print_STR(press_button_STR, 0); + display_Update(); + wait(); + resetArduino(); + break; + + case 2: + // LoRom + display_Clear(); + display_Update(); + mapping = 0; + setup_Flash8(); + id_Flash8(); + wait(); + mode = CORE_FLASH8; + break; + + case 3: + // HiRom + display_Clear(); + display_Update(); + mapping = 1; + setup_Flash8(); + id_Flash8(); + wait(); + mode = CORE_FLASH8; + break; + + case 4: + // ExLoRom + display_Clear(); + display_Update(); + mapping = 2; + setup_Flash8(); + id_Flash8(); + wait(); + mode = CORE_FLASH8; + break; + + case 5: + // ExHiRom + display_Clear(); + display_Update(); + mapping = 3; + setup_Flash8(); + id_Flash8(); + wait(); + mode = CORE_FLASH8; + break; +#endif + + case 6: + resetArduino(); + break; + } +} + +// SNES start menu +void snsMenu() { + // create menu with title and 7 options to choose from + unsigned char snsCart; + // Copy menuOptions out of progmem +#ifdef OPTION_CLOCKGEN_CALIBRATION + convertPgm(menuOptionsSNS, 8); + snsCart = question_box(FS(FSTRING_SELECT_CART_TYPE), menuOptions, 8, 0); +#else + convertPgm(menuOptionsSNS, 7); + snsCart = question_box(FS(FSTRING_SELECT_CART_TYPE), menuOptions, 7, 0); +#endif + + // wait for user choice to come back from the question box menu + switch (snsCart) { + case 0: + display_Clear(); + display_Update(); + setup_Snes(); + mode = CORE_SNES; + break; + +#ifdef ENABLE_SFM + case 1: + display_Clear(); + display_Update(); + setup_SFM(); + mode = CORE_SFM; + break; +#endif + +#ifdef ENABLE_SV + case 2: + display_Clear(); + display_Update(); + setup_SV(); + mode = CORE_SV; + break; +#endif + +#ifdef ENABLE_ST + case 3: + display_Clear(); + display_Update(); + setup_ST(); + mode = CORE_ST; + break; +#endif + +#ifdef ENABLE_GPC + case 4: + display_Clear(); + display_Update(); + setup_GPC(); + mode = CORE_GPC; + break; +#endif + +#ifdef ENABLE_FLASH + case 5: + setup_FlashVoltage(); + reproMenu(); + break; +#endif + + case 6: +#ifdef OPTION_CLOCKGEN_CALIBRATION + clkcal(); + break; + + case 7: +#endif + resetArduino(); + break; + + default: + print_MissingModule(); // does not return + } +} + +// 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("/"); + // start reading from cart + readROM_SNES(); + // Internal Checksum + compare_checksum(); + // CRC32 + compareCRC("snes.txt", 0, 1, 0); +#ifdef ENABLE_GLOBAL_LOG + save_log(); +#endif + display_Update(); + } else { + display_Clear(); + print_Error(F("Does not have ROM")); + } + } + 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")); + } + 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_STR(error_STR, 0); + print_Msg(wrErrors); + print_STR(_bytes_STR, 1); + print_Error(did_not_verify_STR); + } + } else { + display_Clear(); + print_Error(F("Does not have SRAM")); + } + 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(FS(FSTRING_EMPTY)); + println_Msg(FS(FSTRING_EMPTY)); + 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_STR(error_STR, 0); + print_Msg(wrErrors); + print_STR(_bytes_STR, 1); + print_Error(did_not_verify_STR); + } + } else { + display_Clear(); + print_Error(F("Does not have SRAM")); + } + 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(FS(FSTRING_EMPTY)); + // Prints string out of the common strings array either with or without newline + print_STR(press_button_STR, 1); + 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 + if (i2c_found) { + 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() { + // Request 5V + setVoltage(VOLTS_SET_5V); + + // 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 + initializeClockOffset(); + + if (i2c_found) { + // 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); + } +#ifdef ENABLE_CLOCKGEN + else { + display_Clear(); + print_FatalError(F("Clock Generator not found")); + } +#endif + + // 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(); + + if (i2c_found) { + //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, FsFile* 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 (word currBank = start; currBank < total; currBank++) { + PORTL = currBank; + + // Blink led + blinkLED(); + + 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, FsFile* 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 (word currBank = start; currBank < total; currBank++) { + PORTL = currBank; + + // Blink led + blinkLED(); + + 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 + PORTL = 192; + for (uint16_t currByte = 0; currByte < 1024; currByte++) { + PORTF = currByte & 0xFF; + PORTK = currByte >> 8; + + // 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; + } + + // Print start page + if (checkcart_SNES() == 0) { + // Checksum either corrupt or 0000 + manualConfig = 1; + errorLvl = 1; + rgbLed(red_color); + + display_Clear(); + println_Msg(F("ERROR")); + println_Msg(F("Rom header corrupt")); + println_Msg(F("or missing")); + println_Msg(FS(FSTRING_EMPTY)); + println_Msg(FS(FSTRING_EMPTY)); + 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(FS(FSTRING_NAME)); + println_Msg(romName); + + print_Msg(F("Revision: ")); + println_Msg(romVersion); + + 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(FS(FSTRING_SPACE)); + 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 == 67) { + println_Msg(F("SDD1")); + } else if (romChips == 69) { + println_Msg(F("SDD1 BATT")); + } else if (romChips == 85) + println_Msg(F("SRTC RAM 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(FS(FSTRING_EMPTY)); + + + if (altconf) + print_Msg(F("Rom Size: ")); + else + print_Msg(F("ROM Size: ")); + if ((romSize >> 3) < 1) { + print_Msg(1024 * romSize >> 3); + print_Msg(F(" KB")); + } else { + print_Msg(romSize >> 3); + print_Msg(F(" MB")); + } + print_Msg(F(" (")); + print_Msg(numBanks); + println_Msg(F(" banks)")); + + //print_Msg(F("Chips: ")); + //println_Msg(romChips); + + print_Msg(F("Save Size: ")); + print_Msg(sramSize >> 3); + println_Msg(F(" KB")); + + print_Msg(FS(FSTRING_CHECKSUM)); + println_Msg(checksumStr); + display_Update(); + + // Wait for user input +#if (defined(ENABLE_LCD) || defined(ENABLE_OLED)) + // Prints string out of the common strings array either with or without newline + print_STR(press_button_STR, 1); + display_Update(); + wait(); +#endif +#ifdef ENABLE_SERIAL + println_Msg(FS(FSTRING_SPACE)); +#endif + + // Start manual config + if (manualConfig == 1) { + confMenuManual(); + } +} + +void checkAltConf(char crcStr[9]) { + char tempStr2[5]; + char tempStr3[9]; + altconf = 0; + + if (myFile.open("snes.txt", O_READ)) { + // Get cart info + display_Clear(); + println_Msg(F("Searching database...")); + print_Msg(FS(FSTRING_CHECKSUM)); + println_Msg(checksumStr); + display_Update(); + + while (myFile.available()) { + // Skip first line with name + skip_line(&myFile); + + // Skip over the CRC checksum + myFile.seekCur(9); + + // Get internal ROM checksum as string + for (byte j = 0; j < 4; j++) { + tempStr2[j] = char(myFile.read()); + } + tempStr2[4] = '\0'; + + // Check if checksum string is a match else go to next entry in database + if (strcmp(tempStr2, checksumStr) == 0) { + // Skip the , in the file + myFile.seekCur(1); + + // Read the CRC32 of the SNES header out of database + for (byte k = 0; k < 8; k++) { + tempStr3[k] = char(myFile.read()); + } + tempStr3[8] = '\0'; + + print_Msg(F("Header CRC32: ")); + println_Msg(tempStr3); + display_Update(); + + // Skip the , in the file + myFile.seekCur(1); + + // Read file size + byte romSize2 = (myFile.read() - 48) * 10 + (myFile.read() - 48); + + // Skip the , in the file + myFile.seekCur(1); + + // Read number of banks + byte numBanks2 = (myFile.read() - 48) * 100 + (myFile.read() - 48) * 10 + (myFile.read() - 48); + + // skip CRLF + myFile.seekCur(2); + // skip third empty line + skip_line(&myFile); + + // Some games have the same checksum, so compare CRC32 of header area with database too + if (strcmp(tempStr3, crcStr) == 0) { + println_Msg(F("Found")); + display_Update(); + // Game found, check if ROM sizes differ but only change ROM size if non- standard size found in database, else trust the header to be right and the database to be wrong + if (((romSize != romSize2) || (numBanks != numBanks2)) && ((romSize2 == 10) || (romSize2 == 12) || (romSize2 == 20) || (romSize2 == 24) || (romSize2 == 40) || (romSize2 == 48))) { + // Correct size + println_Msg(F("Correcting size")); + print_Msg(FS(FSTRING_SIZE)); + print_Msg(romSize); + print_Msg(F(" -> ")); + print_Msg(romSize2); + println_Msg(F("Mbit")); + print_Msg(F("Banks: ")); + print_Msg(numBanks); + print_Msg(F(" -> ")); + println_Msg(numBanks2); + display_Update(); + delay(1000); + romSize = romSize2; + numBanks = numBanks2; + altconf = 1; + } + break; + } + } + // If no match go to next entry + else { + // skip rest of line + myFile.seekCur(18); + // skip third empty line + skip_line(&myFile); + } + } + // Close the file: + myFile.close(); + } +} + +// Read header +boolean checkcart_SNES() { + // set control to read + dataIn(); + + uint16_t headerStart = 0xFFB0; + byte snesHeader[80]; + PORTL = 0; + for (uint16_t c = 0, currByte = headerStart; c < 80; c++, currByte++) { + PORTF = (currByte & 0xFF); + PORTK = ((currByte >> 8) & 0xFF); + + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + + snesHeader[c] = PINC; + } + + // Calculate CRC32 of header + char crcStr[9]; + sprintf(crcStr, "%08lX", calculateCRC(snesHeader, 80)); + + // 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 if (strcmp("3BB0", checksumStr) == 0) { // invalid romType due to too long ROM name (Yuyu no Quiz de GO!GO!) + romType = LO; + } 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 == 67) { + romSize = 32; + numBanks = 64; + 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, pass CRC32 of snesHeader but filter out 0000 and FFFF checksums + if (!(strcmp(checksumStr, "0000") == 0) && !(strcmp(checksumStr, "FFFF") == 0)) { + checkAltConf(crcStr); + } + + // Get name + byte myLength = buildRomName(romName, &snesHeader[0xFFC0 - headerStart], 21); + + // 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++) { + byte myByte; + myByte = snesHeader[0xFFB2 + i - headerStart]; + if (((myByte >= '0' && myByte <= '9') || (myByte >= 'A' && myByte <= 'z')) && myLength < 4) { + romName[myLength + 5] = 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[0xFFDA - headerStart] == 0x33) { + sramSizeExp = snesHeader[0xFFBD - 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")); + return 0; + } +} + +boolean compare_checksum() { + print_Msg(F("Checksum... ")); + display_Update(); + + char calcsumStr[5]; + sprintf(calcsumStr, "%04X", calc_checksum(fileName, folder)); + print_Msg(calcsumStr); + + if (strcmp(calcsumStr, checksumStr) == 0) { + println_Msg(F(" -> OK")); + display_Update(); + return 1; + } else { + print_Msg(F(" != ")); + println_Msg(checksumStr); + print_Error(F("Invalid Checksum")); + 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 + createFolderAndOpenFile("SNES", "ROM", romName, "sfc"); + + //Dump Derby Stallion '96 (Japan) and Sound Novel Tsukuru (Japan) - Actual Size is 24Mb + if ((romType == LO) && (numBanks == 96) && ((strcmp("CC86", checksumStr) == 0) || (strcmp("A77B", 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 || romChips == 67)) { + println_Msg(F("Dumping SDD1 HiRom")); + display_Update(); + controlIn_SNES(); + byte initialSOMap = readBank_SNES(0, 18439); + + for (word 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(FS(FSTRING_EMPTY)); + 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(); + + if (romChips == 85) { + // Daikaijuu Monogatari 2, keeps out S-RTC register area + readHiRomBanks(192, 192 + 64, &myFile); + readHiRomBanks(64, numBanks, &myFile); // (64 + (numBanks - 64)) + } else { + 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); + if (i2c_found) { + // 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); + if (i2c_found) { + // 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(FS(FSTRING_FILE_DOESNT_EXIST)); + } +} + +void readSRAM() { + // set control + controlIn_SNES(); + + // Get name, add extension and convert to char array for sd lib + createFolder("SNES", "SAVE", romName, "srm"); + + // 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_FatalError(sd_error_STR); + } + 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_STR(error_STR, 0); + print_Msg(writeErrors); + print_STR(_bytes_STR, 1); + print_Error(did_not_verify_STR); + } + 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")); + return 1; + } +} + +// 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); + if (i2c_found) { + // 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); + + if (i2c_found) { + // 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(FS(FSTRING_OK)); + return 1; + } else { + println_Msg(F("ERROR")); + return 0; + } + display_Update(); +} + +#endif + +//****************************************** +// End of File +//******************************************