From c9a9e5872ed05f43b0b9abaa99721739d6268cef Mon Sep 17 00:00:00 2001 From: sanni Date: Thu, 16 Jun 2022 17:57:00 +0200 Subject: [PATCH] Add progressbar to SMS --- Cart_Reader/MD.ino | 6 +- Cart_Reader/SMS.ino | 1419 ++++++++++++++++++++++--------------------- 2 files changed, 718 insertions(+), 707 deletions(-) diff --git a/Cart_Reader/MD.ino b/Cart_Reader/MD.ino index e52d986..38623cd 100644 --- a/Cart_Reader/MD.ino +++ b/Cart_Reader/MD.ino @@ -410,7 +410,6 @@ void mdCartMenu() { resetArduino(); break; } - println_Msg(F("")); println_Msg(F("Press Button...")); display_Update(); wait(); @@ -1294,10 +1293,13 @@ void readROM_MD() { //println_Msg(F("s")); //display_Update(); + println_Msg(F("Calculating checksum...")); + display_Update(); + // Calculate and compare CRC32 with no-intro compareCRC("md.txt"); - // print Checksum + // Calculate internal checksum if (chksum == calcCKS) { println_Msg(F("Internal checksum OK")); display_Update(); diff --git a/Cart_Reader/SMS.ino b/Cart_Reader/SMS.ino index 9eec875..94d264a 100644 --- a/Cart_Reader/SMS.ino +++ b/Cart_Reader/SMS.ino @@ -1,705 +1,714 @@ -//******************************************************** -// SEGA MASTER SYSTEM/Mark III/SG-1000/GAME GEAR MODULE -//******************************************************** - -#include "options.h" -#ifdef enable_MD - -/****************************************** - Variables - *****************************************/ - -/****************************************** - Menu - *****************************************/ -// Adapter menu -static const char SMSAdapterItem1[] PROGMEM = "SMS/Mark 3 raphnet"; -static const char SMSAdapterItem2[] PROGMEM = "SG-1000 raphnet"; -static const char SMSAdapterItem3[] PROGMEM = "SMS Retrode"; -static const char SMSAdapterItem4[] PROGMEM = "GG Retrode"; -static const char* const menuAdapterSMS[] PROGMEM = {SMSAdapterItem1, SMSAdapterItem2, SMSAdapterItem3, SMSAdapterItem4}; - -// MD menu items -static const char SMSMenuItem1[] PROGMEM = "Read Rom"; -static const char SMSMenuItem2[] PROGMEM = "Read from SRAM"; -static const char SMSMenuItem3[] PROGMEM = "Write to SRAM"; -static const char SMSMenuItem4[] PROGMEM = "Reset"; -static const char SMSMenuItem5[] PROGMEM = "Change Retrode Mode"; -static const char* const menuOptionsSMS[] PROGMEM = {SMSMenuItem1, SMSMenuItem2, SMSMenuItem3, SMSMenuItem4, SMSMenuItem5}; - -// Rom Size menu -static const char SMSRomItem1[] PROGMEM = "8KB"; -static const char SMSRomItem2[] PROGMEM = "16KB"; -static const char SMSRomItem3[] PROGMEM = "24KB"; -static const char SMSRomItem4[] PROGMEM = "32KB"; -static const char SMSRomItem5[] PROGMEM = "40KB"; -static const char SMSRomItem6[] PROGMEM = "48KB"; -static const char SMSRomItem7[] PROGMEM = "512KB"; -static const char* const romOptionsSMS[] PROGMEM = {SMSRomItem1, SMSRomItem2, SMSRomItem3, SMSRomItem4, SMSRomItem5, SMSRomItem6, SMSRomItem7}; - -// Set retrode_mode to true when using a retrode SMS/GG adapter -static bool retrode_mode = false; -static bool retrode_mode_sms = false; // true: SMS/Mark3 false: GG -static bool raphnet_mode_sg1000 = false; // true: SG-1000 false: SMS/Mark3 - -void _smsMenu() { - // create menu with title and n options to choose from - unsigned char mainMenu; - - if (retrode_mode) { - // Copy menuOptions out of progmem - int noptions = sizeof(menuOptionsSMS) / sizeof(menuOptionsSMS[0]); - convertPgm(menuOptionsSMS, noptions); - mainMenu = question_box(retrode_mode ? (retrode_mode_sms ? F("Retrode:SMS") : F("Retrode:GG")) : F("SMS/GG Retrode:NO"), menuOptions, noptions, 0); - } - else { - // Copy menuOptions out of progmem - convertPgm(menuOptionsSMS, (raphnet_mode_sg1000 ? 1 : 4)); - mainMenu = question_box((raphnet_mode_sg1000 ? F("SG-1000") : F("SMS/Mark III") ), menuOptions, (raphnet_mode_sg1000 ? 1 : 4), 0); - } - - // wait for user choice to come back from the question box menu - switch (mainMenu) - { - case 0: - display_Clear(); - mode = mode_SMS; - setup_SMS(); - // Change working dir to root - sd.chdir("/"); - readROM_SMS(); - compareCRC("sms.txt"); -#ifdef global_log - save_log(); -#endif - break; - - case 1: - display_Clear(); - mode = mode_SMS; - setup_SMS(); - // Change working dir to root - sd.chdir("/"); - readSRAM_SMS(); - break; - - case 2: - display_Clear(); - mode = mode_SMS; - setup_SMS(); - // Change working dir to root - sd.chdir("/"); - writeSRAM_SMS(); - break; - - case 3: - // Reset - resetArduino(); - break; - - case 4: - if (retrode_mode && !retrode_mode_sms) { - // Change to SMS - retrode_mode_sms = true; - } else if (retrode_mode && retrode_mode_sms) { - // Change to GG - retrode_mode_sms = false; - } - break; - } - if (retrode_mode) { - println_Msg(retrode_mode ? (retrode_mode_sms ? F("Retrode Mode SMS") : F("Retrode Mode GG")) : F("Retrode Mode Off")); - println_Msg(F("Press Button...")); - } - else { - println_Msg(F("")); - println_Msg(F("Press Button...")); - } - display_Update(); - wait(); -} - -void smsMenu() { - // create main menu with title and 4 options to choose from - unsigned char SMSAdapterMenu; - // Copy menuOptions out of progmem - convertPgm(menuAdapterSMS, 4); - SMSAdapterMenu = question_box(F("Select System/Adapter"), menuOptions, 4, 0); - - // wait for user choice to come back from the question box menu - switch (SMSAdapterMenu) - { - case 0: - // raphnet SMS/Mark3 - retrode_mode = false; - retrode_mode_sms = false; - raphnet_mode_sg1000 = false; - break; - - case 1: - // raphnet SG-1000 - retrode_mode = false; - retrode_mode_sms = false; - raphnet_mode_sg1000 = true; - break; - - case 2: - // retrode SMS/Mark3 - retrode_mode = true; - retrode_mode_sms = true; - raphnet_mode_sg1000 = false; - break; - - case 3: - // retrode GG - retrode_mode = true; - retrode_mode_sms = false; - raphnet_mode_sg1000 = false; - break; - } - for (;;) _smsMenu(); -} - -/****************************************** - Setup - *****************************************/ -void setup_SMS() { - // Set Address Pins to Output - //A0-A7 - DDRF = 0xFF; - //A8-A14 - DDRK = 0xFF; - //A15 - DDRH |= (1 << 3); - - if (retrode_mode) { - // Revert changes from the other mode - PORTH &= ~((1 << 0) | (1 << 3) | (1 << 5)); - PORTL &= ~(1 << 1); - DDRH &= ~((1 << 0) | (1 << 5)); - DDRL &= ~((1 << 1)); - // Set Control Pins to Output OE(PH6) - DDRH |= (1 << 6); - // WR(PL5) and RD(PL6) - DDRL |= (1 << 5) | (1 << 6); - - // Setting OE(PH6) HIGH - PORTH |= (1 << 6); - // Setting WR(PL5) and RD(PL6) HIGH - PORTL |= (1 << 5) | (1 << 6); - } else { - // Revert changes from the other mode - PORTL &= ~((1 << 5) | (1 << 6)); - DDRL &= ~((1 << 5) | (1 << 6)); - // Set Control Pins to Output RST(PH0) WR(PH5) OE(PH6) - DDRH |= (1 << 0) | (1 << 5) | (1 << 6); - // CE(PL1) - DDRL |= (1 << 1); - - // Setting RST(PH0) WR(PH5) OE(PH6) HIGH - PORTH |= (1 << 0) | (1 << 5) | (1 << 6); - // CE(PL1) - PORTL |= (1 << 1); - } - - // ROM has 16KB banks which can be mapped to one of three slots via register writes - // Register Slot Address space - // $fffd 0 $0000-$3fff - // $fffe 1 $4000-$7fff - // $ffff 2 $8000-$bfff - // Disable sram - writeByte_SMS(0xFFFC, 0); - // Map first 3 banks so we can read-out the header info - writeByte_SMS(0xFFFD, 0); - writeByte_SMS(0xFFFE, 1); - writeByte_SMS(0xFFFF, 2); - - delay(400); - - // Print all the info - getCartInfo_SMS(); -} - -/****************************************** - Low level functions -*****************************************/ -void writeByte_SMS(word myAddress, byte myData) { - if (retrode_mode && !retrode_mode_sms) { - // Set Data Pins (D8-D15) to Output - DDRA = 0xFF; - } else { - // Set Data Pins (D0-D7) to Output - DDRC = 0xFF; - } - - // Set address - PORTF = myAddress & 0xFF; - PORTK = (myAddress >> 8) & 0xFF; - if (!retrode_mode) { - // CE(PH3) and OE(PH6) are connected - PORTH = (PORTH & 0b11110111) | ((myAddress >> 12) & 0b00001000); - } - - // Output data - if (retrode_mode && !retrode_mode_sms) { - PORTA = myData; - } else { - 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"); - - if (retrode_mode) { - // Switch WR(PL5) and OE/CE(PH6) to LOW - PORTL &= ~(1 << 5); - PORTH &= ~(1 << 6); - } else { - // Switch CE(PL1) and WR(PH5) to LOW - PORTL &= ~(1 << 1); - PORTH &= ~(1 << 5); - } - - // Leave WR low for at least 60ns - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - if (retrode_mode) { - // Switch WR(PL5) and OE/CE(PH6) to HIGH - PORTH |= (1 << 6); - PORTL |= (1 << 5); - } else { - // Switch CE(PL1) and WR(PH5) to HIGH - PORTH |= (1 << 5); - PORTL |= (1 << 1); - } - - // Leave WR high for at least 50ns - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - if (retrode_mode && !retrode_mode_sms) { - // Set Data Pins (D8-D15) to Input - DDRA = 0x00; - } else { - // Set Data Pins (D0-D7) to Input - DDRC = 0x00; - } -} - -byte readByte_SMS(word myAddress) { - if (retrode_mode && !retrode_mode_sms) { - // Set Data Pins (D8-D15) to Input - DDRA = 0x00; - } else { - // Set Data Pins (D0-D7) to Input - DDRC = 0x00; - } - - // Set Address - PORTF = myAddress & 0xFF; - PORTK = (myAddress >> 8) & 0xFF; - if (!retrode_mode) { - // CE(PH3) and OE(PH6) are connected - PORTH = (PORTH & 0b11110111) | ((myAddress >> 12) & 0b00001000); - } - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - if (retrode_mode) { - // Switch RD(PL6) and OE(PH6) to LOW - PORTL &= ~(1 << 6); - PORTH &= ~(1 << 6); - } else { - // Switch CE(PL1) and OE(PH6) to LOW - PORTL &= ~(1 << 1); - PORTH &= ~(1 << 6); - } - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Read - byte tempByte = (retrode_mode && !retrode_mode_sms) ? PINA : PINC; - - if (retrode_mode) { - // Switch RD(PL6) and OE(PH6) to HIGH - PORTH |= (1 << 6); - PORTL |= (1 << 6); - } else { - // Switch CE(PL1) and OE(PH6) to HIGH - PORTH |= (1 << 6); - PORTL |= (1 << 1); - } - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - return tempByte; -} - -//unsigned char hex2bcd (unsigned char x) { -// unsigned char y; -// y = (x / 10) << 4; -// y = y | (x % 10); -//return (y); -//} - -byte readNibble(byte data, byte number) { - return ((data >> (number * 4)) & 0xf); -} - -/****************************************** - MASTER SYSTEM functions -*****************************************/ -void getCartInfo_SMS() { - // Rom size - switch (readNibble(readByte_SMS(0x7fff), 0)) { - case 0xa: - // Adding UL gets rid of integer overflow compiler warning - cartSize = 8 * 1024UL; - break; - case 0xb: - cartSize = 16 * 1024UL; - break; - case 0xc: - cartSize = 32 * 1024UL; - break; - case 0xd: - cartSize = 48 * 1024UL; - break; - case 0xe: - cartSize = 64 * 1024UL; - break; - case 0xf: - cartSize = 128 * 1024UL; - break; - case 0x0: - cartSize = 256 * 1024UL; - break; - case 0x1: - cartSize = 512 * 1024UL; - break; - case 0x2: - cartSize = 512 * 1024UL; - break; - case 0x3: - // 0x3 is (only?) used in The Pro Yakyuu '91 (Game Gear) - cartSize = 128 * 1024UL; - break; - default: - cartSize = 48 * 1024UL; - // LED Error - setColor_RGB(0, 0, 255); - break; - - } - - // Read TMR SEGA string - for (byte i = 0; i < 8; i++) { - romName[i] = char(readByte_SMS(0x7ff0 + i)); - } - romName[8] = '\0'; - - // Attempt to detect cart size by checking if TMR SEGA is mirrored - unsigned long mirror_offset = cartSize; - char romName2[9]; - while (mirror_offset < 1024 * 1024UL) { - byte bank = 1 + (mirror_offset / (16 * 1024UL)); - writeByte_SMS(0xFFFE, bank); - for (byte i = 0; i < 8; i++) { - romName2[i] = char(readByte_SMS(0x7ff0 + i)); - } - romName2[8] = '\0'; - // print_Msg(F("Name2: ")); - // println_Msg(romName2); - // print_Msg(F("from bank ")); - // print_Msg(bank); - // print_Msg(F(" offset ")); - // print_Msg_PaddedHex32(mirror_offset + 0x7ff0); - // println_Msg(F("")); - if (strcmp(romName2, romName) == 0) { - break; - } - if (cartSize == 48 * 1024UL) { - cartSize = 64 * 1024UL; - } else { - cartSize *= 2; - } - mirror_offset = cartSize; - } - writeByte_SMS(0xFFFE, 1); - - // Fix for "Fantasy Zone (J) (V1.0)" that has not the normal header, but "COPYRIGHT SEGAPRG. BY T.ASAI". - char headerFZ[29]; - if (strcmp(romName, "G. BY T.A") != 0) { - for (byte i = 0; i < 28; i++) { - headerFZ[i] = char(readByte_SMS(0x7fe0 + i)); - } - headerFZ[28] = '\0'; - - if (strcmp(headerFZ, "COPYRIGHT SEGAPRG. BY T.ASAI") == 0) { - strcpy(romName, "TMR SEGA"); - cartSize = 128 * 1024UL; - } - } - - // SMS header not found - if (strcmp(romName, "TMR SEGA") != 0) { - // Set cartsize manually - unsigned char SMSRomMenu; - // Copy menuOptions out of progmem - convertPgm(romOptionsSMS, (raphnet_mode_sg1000 ? 4 : 7)); - SMSRomMenu = question_box(F("Select ROM size"), menuOptions, (raphnet_mode_sg1000 ? 4 : 7), 0); - - // wait for user choice to come back from the question box menu - switch (SMSRomMenu) - { - case 0: - // 8KB - cartSize = 8 * 1024UL; - break; - - case 1: - // 16KB - cartSize = 16 * 1024UL; - break; - - case 2: - // 24KB - cartSize = 24 * 1024UL; - break; - - case 3: - // 32KB - cartSize = 32 * 1024UL; - break; - - case 4: - // 40KB - cartSize = 40 * 1024UL; - break; - - case 5: - // 48KB - cartSize = 48 * 1024UL; - break; - - case 6: - // 512KB - cartSize = 512 * 1024UL; - break; - } - - display_Clear(); - println_Msg(F("SMS Header not found")); - println_Msg(F(" ")); - print_Msg(F("Name: ")); - println_Msg(romName); - print_Msg(F("Selected Size: ")); - print_Msg(cartSize / 1024); - println_Msg(F("KB")); - println_Msg(F(" ")); - sprintf(romName, "UNKNOWN"); - } - - // Header found - else { - display_Clear(); - println_Msg(F("SMS Header Info")); - println_Msg(F(" ")); - print_Msg(F("Name: ")); - println_Msg(romName); - print_Msg(F("Size: ")); - print_Msg(cartSize / 1024); - println_Msg(F("KB")); - println_Msg(F(" ")); - } - - // Wait for user input -#if (defined(enable_LCD) || defined(enable_OLED)) - println_Msg(F("Press Button...")); - display_Update(); - wait(); -#endif - // Turn off LED - setColor_RGB(0, 0, 0); -} - -// Read rom and save to the SD card -void readROM_SMS() { - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".SMS"); - - // create a new folder - EEPROM_readAnything(0, foldern); - sprintf(folder, "SMS/ROM/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - display_Clear(); - print_Msg(F("Saving to ")); - print_Msg(folder); - println_Msg(F("/...")); - display_Update(); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(0, foldern); - - // Open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("SD Error"), true); - } - - word bankSize = 16 * 1024UL; - - if (raphnet_mode_sg1000) { - // cart with no mapper - bankSize = cartSize; - } - - for (byte currBank = 0x0; currBank < (cartSize / bankSize); currBank++) { - // Write current 16KB bank to slot 2 register 0xFFFF - if (!raphnet_mode_sg1000) { - writeByte_SMS(0xFFFF, currBank); - } - - // Blink led - blinkLED(); - // Read 16KB from slot 2 which starts at 0x8000 - for (word currBuffer = 0; currBuffer < bankSize; currBuffer += 512) { - // Fill SD buffer - for (int currByte = 0; currByte < 512; currByte++) { - sdBuffer[currByte] = readByte_SMS((raphnet_mode_sg1000 ? 0 : 0x8000) + currBuffer + currByte); - } - // hexdump for debugging: - // if (currBank == 0 && currBuffer == 0) { - // for (word xi = 0; xi < 0x100; xi++) { - // if (xi%16==0) { - // print_Msg_PaddedHex16(xi); - // print_Msg(F(" ")); - // } - // print_Msg_PaddedHexByte(sdBuffer[xi]); - // if (xi>0&&((xi+1)%16)==0) { - // println_Msg(F("")); - // } else { - // print_Msg(F(" ")); - // } - // } - // } - myFile.write(sdBuffer, 512); - } - } - // Close the file: - myFile.close(); -} - -// Read SRAM and save to the SD card -void readSRAM_SMS() { - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".SAV"); - - // create a new folder - EEPROM_readAnything(0, foldern); - sprintf(folder, "SMS/SAVE/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - display_Clear(); - print_Msg(F("Saving to ")); - print_Msg(folder); - println_Msg(F("/...")); - display_Update(); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(0, foldern); - - // Open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("SD Error"), true); - } - // Write the whole 32KB - // When there is only 8KB of SRAM, the contents should be duplicated - word bankSize = 16 * 1024UL; - for (byte currBank = 0x0; currBank < 2; currBank++) { - writeByte_SMS(0xFFFC, 0x08 | (currBank << 2)); - - // Blink led - blinkLED(); - // Read 16KB from slot 2 which starts at 0x8000 - for (word currBuffer = 0; currBuffer < bankSize; currBuffer += 512) { - // Fill SD buffer - for (int currByte = 0; currByte < 512; currByte++) { - sdBuffer[currByte] = readByte_SMS(0x8000 + currBuffer + currByte); - } - myFile.write(sdBuffer, 512); - } - } - // Close the file: - myFile.close(); -} - -void writeSRAM_SMS() { - display_Clear(); - - if (false) { - print_Error(F("DISABLED"), false); - } - else { - fileBrowser(F("Select file")); - - sd.chdir(); - sprintf(filePath, "%s/%s", filePath, fileName); - - display_Clear(); - println_Msg(F("Restoring from ")); - println_Msg(filePath); - println_Msg(fileName); - display_Update(); - - if (myFile.open(filePath, O_READ)) { - // Get SRAM size from file, with a maximum of 32KB - uint32_t sramSize = myFile.fileSize(); - if (sramSize > ((uint32_t)32 * (uint32_t)1024)) { - sramSize = (uint32_t)32 * (uint32_t)1024; - } - print_Msg(F("sramSize: ")); - print_Msg(sramSize); - println_Msg(F("")); - word bankSize = 16 * 1024; - for (word address = 0x0; address < sramSize; address += 512) { - byte currBank = address >= bankSize ? 1 : 0; - word page_address = address - (currBank * bankSize); - writeByte_SMS(0xFFFC, 0x08 | (currBank << 2)); - // Blink led - blinkLED(); - myFile.read(sdBuffer, 512); - for (int x = 0; x < 512; x++) { - writeByte_SMS(0x8000 + page_address + x, sdBuffer[x]); - } - } - myFile.close(); - - // Blink led - blinkLED(); - - println_Msg(F("")); - println_Msg(F("DONE")); - display_Update(); - } - else { - print_Error(F("SD ERROR"), true); - } - } - - display_Clear(); - - sd.chdir(); // root - filePath[0] = '\0'; // Reset filePath -} -#endif - -//****************************************** -// End of File -//****************************************** +//******************************************************** +// SEGA MASTER SYSTEM/Mark III/SG-1000/GAME GEAR MODULE +//******************************************************** + +#include "options.h" +#ifdef enable_MD + +/****************************************** + Variables + *****************************************/ + +/****************************************** + Menu + *****************************************/ +// Adapter menu +static const char SMSAdapterItem1[] PROGMEM = "SMS/Mark 3 raphnet"; +static const char SMSAdapterItem2[] PROGMEM = "SG-1000 raphnet"; +static const char SMSAdapterItem3[] PROGMEM = "SMS Retrode"; +static const char SMSAdapterItem4[] PROGMEM = "GG Retrode"; +static const char* const menuAdapterSMS[] PROGMEM = {SMSAdapterItem1, SMSAdapterItem2, SMSAdapterItem3, SMSAdapterItem4}; + +// MD menu items +static const char SMSMenuItem1[] PROGMEM = "Read Rom"; +static const char SMSMenuItem2[] PROGMEM = "Read from SRAM"; +static const char SMSMenuItem3[] PROGMEM = "Write to SRAM"; +static const char SMSMenuItem4[] PROGMEM = "Reset"; +static const char SMSMenuItem5[] PROGMEM = "Change Retrode Mode"; +static const char* const menuOptionsSMS[] PROGMEM = {SMSMenuItem1, SMSMenuItem2, SMSMenuItem3, SMSMenuItem4, SMSMenuItem5}; + +// Rom Size menu +static const char SMSRomItem1[] PROGMEM = "8KB"; +static const char SMSRomItem2[] PROGMEM = "16KB"; +static const char SMSRomItem3[] PROGMEM = "24KB"; +static const char SMSRomItem4[] PROGMEM = "32KB"; +static const char SMSRomItem5[] PROGMEM = "40KB"; +static const char SMSRomItem6[] PROGMEM = "48KB"; +static const char SMSRomItem7[] PROGMEM = "512KB"; +static const char* const romOptionsSMS[] PROGMEM = {SMSRomItem1, SMSRomItem2, SMSRomItem3, SMSRomItem4, SMSRomItem5, SMSRomItem6, SMSRomItem7}; + +// Set retrode_mode to true when using a retrode SMS/GG adapter +static bool retrode_mode = false; +static bool retrode_mode_sms = false; // true: SMS/Mark3 false: GG +static bool raphnet_mode_sg1000 = false; // true: SG-1000 false: SMS/Mark3 + +void _smsMenu() { + // create menu with title and n options to choose from + unsigned char mainMenu; + + if (retrode_mode) { + // Copy menuOptions out of progmem + int noptions = sizeof(menuOptionsSMS) / sizeof(menuOptionsSMS[0]); + convertPgm(menuOptionsSMS, noptions); + mainMenu = question_box(retrode_mode ? (retrode_mode_sms ? F("Retrode:SMS") : F("Retrode:GG")) : F("SMS/GG Retrode:NO"), menuOptions, noptions, 0); + } + else { + // Copy menuOptions out of progmem + convertPgm(menuOptionsSMS, (raphnet_mode_sg1000 ? 1 : 4)); + mainMenu = question_box((raphnet_mode_sg1000 ? F("SG-1000") : F("SMS/Mark III") ), menuOptions, (raphnet_mode_sg1000 ? 1 : 4), 0); + } + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + case 0: + display_Clear(); + mode = mode_SMS; + setup_SMS(); + // Change working dir to root + sd.chdir("/"); + readROM_SMS(); + compareCRC("sms.txt"); +#ifdef global_log + save_log(); +#endif + break; + + case 1: + display_Clear(); + mode = mode_SMS; + setup_SMS(); + // Change working dir to root + sd.chdir("/"); + readSRAM_SMS(); + break; + + case 2: + display_Clear(); + mode = mode_SMS; + setup_SMS(); + // Change working dir to root + sd.chdir("/"); + writeSRAM_SMS(); + break; + + case 3: + // Reset + resetArduino(); + break; + + case 4: + if (retrode_mode && !retrode_mode_sms) { + // Change to SMS + retrode_mode_sms = true; + } else if (retrode_mode && retrode_mode_sms) { + // Change to GG + retrode_mode_sms = false; + } + break; + } + if (retrode_mode) { + println_Msg(retrode_mode ? (retrode_mode_sms ? F("Retrode Mode SMS") : F("Retrode Mode GG")) : F("Retrode Mode Off")); + println_Msg(F("Press Button...")); + } + else { + println_Msg(F("")); + println_Msg(F("Press Button...")); + } + display_Update(); + wait(); +} + +void smsMenu() { + // create main menu with title and 4 options to choose from + unsigned char SMSAdapterMenu; + // Copy menuOptions out of progmem + convertPgm(menuAdapterSMS, 4); + SMSAdapterMenu = question_box(F("Select System/Adapter"), menuOptions, 4, 0); + + // wait for user choice to come back from the question box menu + switch (SMSAdapterMenu) + { + case 0: + // raphnet SMS/Mark3 + retrode_mode = false; + retrode_mode_sms = false; + raphnet_mode_sg1000 = false; + break; + + case 1: + // raphnet SG-1000 + retrode_mode = false; + retrode_mode_sms = false; + raphnet_mode_sg1000 = true; + break; + + case 2: + // retrode SMS/Mark3 + retrode_mode = true; + retrode_mode_sms = true; + raphnet_mode_sg1000 = false; + break; + + case 3: + // retrode GG + retrode_mode = true; + retrode_mode_sms = false; + raphnet_mode_sg1000 = false; + break; + } + for (;;) _smsMenu(); +} + +/****************************************** + Setup + *****************************************/ +void setup_SMS() { + // Set Address Pins to Output + //A0-A7 + DDRF = 0xFF; + //A8-A14 + DDRK = 0xFF; + //A15 + DDRH |= (1 << 3); + + if (retrode_mode) { + // Revert changes from the other mode + PORTH &= ~((1 << 0) | (1 << 3) | (1 << 5)); + PORTL &= ~(1 << 1); + DDRH &= ~((1 << 0) | (1 << 5)); + DDRL &= ~((1 << 1)); + // Set Control Pins to Output OE(PH6) + DDRH |= (1 << 6); + // WR(PL5) and RD(PL6) + DDRL |= (1 << 5) | (1 << 6); + + // Setting OE(PH6) HIGH + PORTH |= (1 << 6); + // Setting WR(PL5) and RD(PL6) HIGH + PORTL |= (1 << 5) | (1 << 6); + } else { + // Revert changes from the other mode + PORTL &= ~((1 << 5) | (1 << 6)); + DDRL &= ~((1 << 5) | (1 << 6)); + // Set Control Pins to Output RST(PH0) WR(PH5) OE(PH6) + DDRH |= (1 << 0) | (1 << 5) | (1 << 6); + // CE(PL1) + DDRL |= (1 << 1); + + // Setting RST(PH0) WR(PH5) OE(PH6) HIGH + PORTH |= (1 << 0) | (1 << 5) | (1 << 6); + // CE(PL1) + PORTL |= (1 << 1); + } + + // ROM has 16KB banks which can be mapped to one of three slots via register writes + // Register Slot Address space + // $fffd 0 $0000-$3fff + // $fffe 1 $4000-$7fff + // $ffff 2 $8000-$bfff + // Disable sram + writeByte_SMS(0xFFFC, 0); + // Map first 3 banks so we can read-out the header info + writeByte_SMS(0xFFFD, 0); + writeByte_SMS(0xFFFE, 1); + writeByte_SMS(0xFFFF, 2); + + delay(400); + + // Print all the info + getCartInfo_SMS(); +} + +/****************************************** + Low level functions +*****************************************/ +void writeByte_SMS(word myAddress, byte myData) { + if (retrode_mode && !retrode_mode_sms) { + // Set Data Pins (D8-D15) to Output + DDRA = 0xFF; + } else { + // Set Data Pins (D0-D7) to Output + DDRC = 0xFF; + } + + // Set address + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + if (!retrode_mode) { + // CE(PH3) and OE(PH6) are connected + PORTH = (PORTH & 0b11110111) | ((myAddress >> 12) & 0b00001000); + } + + // Output data + if (retrode_mode && !retrode_mode_sms) { + PORTA = myData; + } else { + 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"); + + if (retrode_mode) { + // Switch WR(PL5) and OE/CE(PH6) to LOW + PORTL &= ~(1 << 5); + PORTH &= ~(1 << 6); + } else { + // Switch CE(PL1) and WR(PH5) to LOW + PORTL &= ~(1 << 1); + PORTH &= ~(1 << 5); + } + + // Leave WR low for at least 60ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + if (retrode_mode) { + // Switch WR(PL5) and OE/CE(PH6) to HIGH + PORTH |= (1 << 6); + PORTL |= (1 << 5); + } else { + // Switch CE(PL1) and WR(PH5) to HIGH + PORTH |= (1 << 5); + PORTL |= (1 << 1); + } + + // Leave WR high for at least 50ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + if (retrode_mode && !retrode_mode_sms) { + // Set Data Pins (D8-D15) to Input + DDRA = 0x00; + } else { + // Set Data Pins (D0-D7) to Input + DDRC = 0x00; + } +} + +byte readByte_SMS(word myAddress) { + if (retrode_mode && !retrode_mode_sms) { + // Set Data Pins (D8-D15) to Input + DDRA = 0x00; + } else { + // Set Data Pins (D0-D7) to Input + DDRC = 0x00; + } + + // Set Address + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + if (!retrode_mode) { + // CE(PH3) and OE(PH6) are connected + PORTH = (PORTH & 0b11110111) | ((myAddress >> 12) & 0b00001000); + } + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + if (retrode_mode) { + // Switch RD(PL6) and OE(PH6) to LOW + PORTL &= ~(1 << 6); + PORTH &= ~(1 << 6); + } else { + // Switch CE(PL1) and OE(PH6) to LOW + PORTL &= ~(1 << 1); + PORTH &= ~(1 << 6); + } + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Read + byte tempByte = (retrode_mode && !retrode_mode_sms) ? PINA : PINC; + + if (retrode_mode) { + // Switch RD(PL6) and OE(PH6) to HIGH + PORTH |= (1 << 6); + PORTL |= (1 << 6); + } else { + // Switch CE(PL1) and OE(PH6) to HIGH + PORTH |= (1 << 6); + PORTL |= (1 << 1); + } + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + return tempByte; +} + +//unsigned char hex2bcd (unsigned char x) { +// unsigned char y; +// y = (x / 10) << 4; +// y = y | (x % 10); +//return (y); +//} + +byte readNibble(byte data, byte number) { + return ((data >> (number * 4)) & 0xf); +} + +/****************************************** + MASTER SYSTEM functions +*****************************************/ +void getCartInfo_SMS() { + // Rom size + switch (readNibble(readByte_SMS(0x7fff), 0)) { + case 0xa: + // Adding UL gets rid of integer overflow compiler warning + cartSize = 8 * 1024UL; + break; + case 0xb: + cartSize = 16 * 1024UL; + break; + case 0xc: + cartSize = 32 * 1024UL; + break; + case 0xd: + cartSize = 48 * 1024UL; + break; + case 0xe: + cartSize = 64 * 1024UL; + break; + case 0xf: + cartSize = 128 * 1024UL; + break; + case 0x0: + cartSize = 256 * 1024UL; + break; + case 0x1: + cartSize = 512 * 1024UL; + break; + case 0x2: + cartSize = 512 * 1024UL; + break; + case 0x3: + // 0x3 is (only?) used in The Pro Yakyuu '91 (Game Gear) + cartSize = 128 * 1024UL; + break; + default: + cartSize = 48 * 1024UL; + // LED Error + setColor_RGB(0, 0, 255); + break; + + } + + // Read TMR SEGA string + for (byte i = 0; i < 8; i++) { + romName[i] = char(readByte_SMS(0x7ff0 + i)); + } + romName[8] = '\0'; + + // Attempt to detect cart size by checking if TMR SEGA is mirrored + unsigned long mirror_offset = cartSize; + char romName2[9]; + while (mirror_offset < 1024 * 1024UL) { + byte bank = 1 + (mirror_offset / (16 * 1024UL)); + writeByte_SMS(0xFFFE, bank); + for (byte i = 0; i < 8; i++) { + romName2[i] = char(readByte_SMS(0x7ff0 + i)); + } + romName2[8] = '\0'; + // print_Msg(F("Name2: ")); + // println_Msg(romName2); + // print_Msg(F("from bank ")); + // print_Msg(bank); + // print_Msg(F(" offset ")); + // print_Msg_PaddedHex32(mirror_offset + 0x7ff0); + // println_Msg(F("")); + if (strcmp(romName2, romName) == 0) { + break; + } + if (cartSize == 48 * 1024UL) { + cartSize = 64 * 1024UL; + } else { + cartSize *= 2; + } + mirror_offset = cartSize; + } + writeByte_SMS(0xFFFE, 1); + + // Fix for "Fantasy Zone (J) (V1.0)" that has not the normal header, but "COPYRIGHT SEGAPRG. BY T.ASAI". + char headerFZ[29]; + if (strcmp(romName, "G. BY T.A") != 0) { + for (byte i = 0; i < 28; i++) { + headerFZ[i] = char(readByte_SMS(0x7fe0 + i)); + } + headerFZ[28] = '\0'; + + if (strcmp(headerFZ, "COPYRIGHT SEGAPRG. BY T.ASAI") == 0) { + strcpy(romName, "TMR SEGA"); + cartSize = 128 * 1024UL; + } + } + + // SMS header not found + if (strcmp(romName, "TMR SEGA") != 0) { + // Set cartsize manually + unsigned char SMSRomMenu; + // Copy menuOptions out of progmem + convertPgm(romOptionsSMS, (raphnet_mode_sg1000 ? 4 : 7)); + SMSRomMenu = question_box(F("Select ROM size"), menuOptions, (raphnet_mode_sg1000 ? 4 : 7), 0); + + // wait for user choice to come back from the question box menu + switch (SMSRomMenu) + { + case 0: + // 8KB + cartSize = 8 * 1024UL; + break; + + case 1: + // 16KB + cartSize = 16 * 1024UL; + break; + + case 2: + // 24KB + cartSize = 24 * 1024UL; + break; + + case 3: + // 32KB + cartSize = 32 * 1024UL; + break; + + case 4: + // 40KB + cartSize = 40 * 1024UL; + break; + + case 5: + // 48KB + cartSize = 48 * 1024UL; + break; + + case 6: + // 512KB + cartSize = 512 * 1024UL; + break; + } + + display_Clear(); + println_Msg(F("SMS Header not found")); + println_Msg(F(" ")); + print_Msg(F("Name: ")); + println_Msg(romName); + print_Msg(F("Selected Size: ")); + print_Msg(cartSize / 1024); + println_Msg(F("KB")); + println_Msg(F(" ")); + sprintf(romName, "UNKNOWN"); + } + + // Header found + else { + display_Clear(); + println_Msg(F("SMS Header Info")); + println_Msg(F(" ")); + print_Msg(F("Name: ")); + println_Msg(romName); + print_Msg(F("Size: ")); + print_Msg(cartSize / 1024); + println_Msg(F("KB")); + println_Msg(F(" ")); + } + + // Wait for user input +#if (defined(enable_LCD) || defined(enable_OLED)) + println_Msg(F("Press Button...")); + display_Update(); + wait(); +#endif + // Turn off LED + setColor_RGB(0, 0, 0); +} + +// Read rom and save to the SD card +void readROM_SMS() { + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".SMS"); + + // create a new folder + EEPROM_readAnything(0, foldern); + sprintf(folder, "SMS/ROM/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(0, foldern); + + // Open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + + word bankSize = 16 * 1024UL; + + if (raphnet_mode_sg1000) { + // cart with no mapper + bankSize = cartSize; + } + + //Initialize progress bar + uint32_t processedProgressBar = 0; + uint32_t totalProgressBar = (uint32_t)(cartSize); + draw_progressbar(0, totalProgressBar); + + for (byte currBank = 0x0; currBank < (cartSize / bankSize); currBank++) { + // Write current 16KB bank to slot 2 register 0xFFFF + if (!raphnet_mode_sg1000) { + writeByte_SMS(0xFFFF, currBank); + } + + // Blink led + blinkLED(); + // Read 16KB from slot 2 which starts at 0x8000 + for (word currBuffer = 0; currBuffer < bankSize; currBuffer += 512) { + // Fill SD buffer + for (int currByte = 0; currByte < 512; currByte++) { + sdBuffer[currByte] = readByte_SMS((raphnet_mode_sg1000 ? 0 : 0x8000) + currBuffer + currByte); + } + // hexdump for debugging: + // if (currBank == 0 && currBuffer == 0) { + // for (word xi = 0; xi < 0x100; xi++) { + // if (xi%16==0) { + // print_Msg_PaddedHex16(xi); + // print_Msg(F(" ")); + // } + // print_Msg_PaddedHexByte(sdBuffer[xi]); + // if (xi>0&&((xi+1)%16)==0) { + // println_Msg(F("")); + // } else { + // print_Msg(F(" ")); + // } + // } + // } + myFile.write(sdBuffer, 512); + } + + // update progress bar + processedProgressBar += bankSize; + draw_progressbar(processedProgressBar, totalProgressBar); + } + // Close the file: + myFile.close(); +} + +// Read SRAM and save to the SD card +void readSRAM_SMS() { + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".SAV"); + + // create a new folder + EEPROM_readAnything(0, foldern); + sprintf(folder, "SMS/SAVE/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(0, foldern); + + // Open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + // Write the whole 32KB + // When there is only 8KB of SRAM, the contents should be duplicated + word bankSize = 16 * 1024UL; + for (byte currBank = 0x0; currBank < 2; currBank++) { + writeByte_SMS(0xFFFC, 0x08 | (currBank << 2)); + + // Blink led + blinkLED(); + // Read 16KB from slot 2 which starts at 0x8000 + for (word currBuffer = 0; currBuffer < bankSize; currBuffer += 512) { + // Fill SD buffer + for (int currByte = 0; currByte < 512; currByte++) { + sdBuffer[currByte] = readByte_SMS(0x8000 + currBuffer + currByte); + } + myFile.write(sdBuffer, 512); + } + } + // Close the file: + myFile.close(); +} + +void writeSRAM_SMS() { + display_Clear(); + + if (false) { + print_Error(F("DISABLED"), false); + } + else { + fileBrowser(F("Select file")); + + sd.chdir(); + sprintf(filePath, "%s/%s", filePath, fileName); + + display_Clear(); + println_Msg(F("Restoring from ")); + println_Msg(filePath); + println_Msg(fileName); + display_Update(); + + if (myFile.open(filePath, O_READ)) { + // Get SRAM size from file, with a maximum of 32KB + uint32_t sramSize = myFile.fileSize(); + if (sramSize > ((uint32_t)32 * (uint32_t)1024)) { + sramSize = (uint32_t)32 * (uint32_t)1024; + } + print_Msg(F("sramSize: ")); + print_Msg(sramSize); + println_Msg(F("")); + word bankSize = 16 * 1024; + for (word address = 0x0; address < sramSize; address += 512) { + byte currBank = address >= bankSize ? 1 : 0; + word page_address = address - (currBank * bankSize); + writeByte_SMS(0xFFFC, 0x08 | (currBank << 2)); + // Blink led + blinkLED(); + myFile.read(sdBuffer, 512); + for (int x = 0; x < 512; x++) { + writeByte_SMS(0x8000 + page_address + x, sdBuffer[x]); + } + } + myFile.close(); + + // Blink led + blinkLED(); + + println_Msg(F("")); + println_Msg(F("DONE")); + display_Update(); + } + else { + print_Error(F("SD ERROR"), true); + } + } + + display_Clear(); + + sd.chdir(); // root + filePath[0] = '\0'; // Reset filePath +} +#endif + +//****************************************** +// End of File +//******************************************