From 0e56a3451a03465232cfe1a765902f936124f5e1 Mon Sep 17 00:00:00 2001 From: sanni Date: Sat, 23 Jul 2022 11:04:17 +0200 Subject: [PATCH] V9.2 Alpha: Adds Coleco- and Intellivision (thx to skaman) --- Cart_Reader/COLV.ino | 615 ++++++++++ Cart_Reader/Cart_Reader.ino | 224 +++- Cart_Reader/{GBSmart.ino => GBS.ino} | 1620 +++++++++++++------------- Cart_Reader/INTV.ino | 1018 ++++++++++++++++ Cart_Reader/N64.ino | 3 + Cart_Reader/NES.ino | 20 +- Cart_Reader/NGP.ino | 2 +- Cart_Reader/{NP.ino => SFM.ino} | 4 +- Cart_Reader/SNES.ino | 2 +- 9 files changed, 2649 insertions(+), 859 deletions(-) create mode 100644 Cart_Reader/COLV.ino rename Cart_Reader/{GBSmart.ino => GBS.ino} (95%) create mode 100644 Cart_Reader/INTV.ino rename Cart_Reader/{NP.ino => SFM.ino} (99%) diff --git a/Cart_Reader/COLV.ino b/Cart_Reader/COLV.ino new file mode 100644 index 0000000..b8e9d85 --- /dev/null +++ b/Cart_Reader/COLV.ino @@ -0,0 +1,615 @@ +//****************************************** +// COLECOVISION MODULE +//****************************************** +#ifdef enable_COLV + +// Coleco Colecovision +// Cartridge Pinout +// 30P 2.54mm pitch connector +// +// FRONT SIDE BACK SIDE +// (EVEN) (ODD) +// +-------+ +// /C000 -| 2 1 |- D2 +// D3 -| 4 3 |- D1 +// D4 -| 6 5 |- D0 +// D5 -| 8 7 |- A0 +// D6 -| 10 9 |- A1 +// D7 -| 12 11 |- A2 +// A11 -| 14 13 |- GND (SHLD) +// A10 -| 16 15 |- A3 +// /8000 -| 18 17 |- A4 +// A14 -| 20 19 |- A13 +// /A000 -| 22 21 |- A5 +// A12 -| 24 23 |- A6 +// A9 -| 26 25 |- A7 +// A8 -| 28 27 |- /E000 +// VCC(+5V) -| 30 29 |- GND +// +-------+ + +// CONTROL PINS: +// CHIP SELECT PINS +// /8000(PH3) - CHIP 0 - SNES /CS +// /A000(PH4) - CHIP 1 - SNES /IRQ +// /C000(PH5) - CHIP 2 - SNES /WR +// /E000(PH6) - CHIP 3 - SNES /RD + +byte COL[] = {8, 12, 16, 20, 24, 32}; +byte collo = 0; // Lowest Entry +byte colhi = 5; // Highest Entry + +byte colsize; +byte newcolsize; + +// EEPROM MAPPING +// 08 ROM SIZE + +//****************************************** +// Menu +//****************************************** +// Base Menu +static const char colMenuItem1[] PROGMEM = "Select Cart"; +static const char colMenuItem2[] PROGMEM = "Read ROM"; +static const char colMenuItem3[] PROGMEM = "Set Size"; +static const char colMenuItem4[] PROGMEM = "Reset"; +static const char* const menuOptionsCOL[] PROGMEM = {colMenuItem1, colMenuItem2, colMenuItem3, colMenuItem4}; + +void setup_COL() +{ + // Set Address Pins to Output + // Colecovision uses A0-A14 [A15-A23 UNUSED] + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output + // ---(PH0) ---(PH1) /8000(PH3) /A000(PH4) /C000(PH5) /E000(PH6) + DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Set TIME(PJ0) to Output (UNUSED) + DDRJ |= (1 << 0); + + // Set Pins (D0-D7) to Input + DDRC = 0x00; + + // Setting Control Pins to HIGH + // ---(PH0) ---(PH1) /8000(PH3) /A000(PH4) /C000(PH5) /E000(PH6) + PORTH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Set Unused Data Pins (PA0-PA7) to Output + DDRA = 0xFF; + + // Set Unused Pins HIGH + PORTA = 0xFF; + PORTL = 0xFF; // A16-A23 + PORTJ |= (1 << 0); // TIME(PJ0) + + checkStatus_COL(); + strcpy(romName, "COLECO"); + + mode = mode_COL; +} + +void colMenu() +{ + convertPgm(menuOptionsCOL, 4); + uint8_t mainMenu = question_box(F("COLECOVISION MENU"), menuOptions, 4, 0); + + switch (mainMenu) + { + case 0: + // Select Cart + setCart_COL(); + wait(); + setup_COL(); + break; + + case 1: + // Read ROM + sd.chdir("/"); + readROM_COL(); + sd.chdir("/"); + break; + + case 2: + // Set Size + setROMSize_COL(); + break; + + case 3: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// READ CODE +//****************************************** +// CHIP SELECT CONTROL PINS +// /8000(PH3) - CHIP 0 +// /A000(PH4) - CHIP 1 +// /C000(PH5) - CHIP 2 +// /E000(PH6) - CHIP 3 + +uint8_t readData_COL(uint32_t addr) +{ + // SELECT ROM CHIP - PULL /CE LOW + uint8_t chipdecode = ((addr >> 13) & 0x3); + if (chipdecode == 3) // CHIP 3 + PORTH &= ~(1 << 6); // /E000 LOW (ENABLE) + else if (chipdecode == 2) // CHIP 2 + PORTH &= ~(1 << 5); // /C000 LOW (ENABLE) + else if (chipdecode == 1) // CHIP 1 + PORTH &= ~(1 << 4); // /A000 LOW (ENABLE) + else // CHIP 0 + PORTH &= ~(1 << 3); // /8000 LOW (ENABLE) + + PORTF = addr & 0xFF; // A0-A7 + PORTK = (addr >> 8) & 0xFF; // A8-A15 + + // LATCH ADDRESS - PULL /CE HIGH + PORTH |= (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); // ALL /CE HIGH (DISABLE) + + uint8_t ret = PINC; + return ret; +} + +void readSegment_COL(uint32_t startaddr, uint32_t endaddr) +{ + for (uint32_t addr = startaddr; addr < endaddr; addr += 512) { + for (int w = 0; w < 512; w++) { + uint8_t temp = readData_COL(addr + w); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + } +} + +void readROM_COL() +{ + strcpy(fileName, romName); + strcat(fileName, ".col"); + + // create a new folder for storing rom file + EEPROM_readAnything(0, foldern); + // sprintf(folder, "COL/ROM/%s/%d", romName, foldern); + sprintf(folder, "COL/ROM/%d", foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // open file on sdcard + if (!myFile.open(fileName, O_RDWR | O_CREAT)) + print_Error(F("Can't create file on SD"), true); + + // write new folder number back to EEPROM + foldern++; + EEPROM_writeAnything(0, foldern); + + // RESET ALL CS PINS HIGH (DISABLE) + PORTH |= (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + readSegment_COL(0x8000, 0xA000); // 8K + if (colsize > 0) { + readSegment_COL(0xA000, 0xB000); // +4K = 12K + if (colsize > 1) { + readSegment_COL(0xB000, 0xC000); // +4K = 16K + if (colsize > 2) { + readSegment_COL(0xC000, 0xD000); // +4K = 20K + if (colsize > 3) { + readSegment_COL(0xD000, 0xE000); // +4K = 24K + if (colsize > 4) { + readSegment_COL(0xE000, 0x10000); // +8K = 32K + } + } + } + } + } + myFile.close(); + + // RESET ALL CS PINS HIGH (DISABLE) + PORTH |= (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Compare CRC32 to database and rename ROM if found + compareCRC("colv.txt", 0, 0); + + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +void setROMSize_COL() +{ +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + if (collo == colhi) + newcolsize = collo; + else { + int b = 0; + int i = collo; + + display_Clear(); + print_Msg(F("ROM Size: ")); + println_Msg(COL[i]); + println_Msg(F("")); +#if defined(enable_OLED) + println_Msg(F("Press left to Change")); + println_Msg(F("and right to Select")); +#elif defined(enable_LCD) + println_Msg(F("Rotate to Change")); + println_Msg(F("Press to Select")); +#endif + display_Update(); + + while (1) { + b = checkButton(); + if (b == 2) { // Previous (doubleclick) + if (i == collo) + i = colhi; + else + i--; + + // Only update display after input because of slow LCD library + display_Clear(); + print_Msg(F("ROM Size: ")); + println_Msg(COL[i]); + println_Msg(F("")); +#if defined(enable_OLED) + println_Msg(F("Press left to Change")); + println_Msg(F("and right to Select")); +#elif defined(enable_LCD) + println_Msg(F("Rotate to Change")); + println_Msg(F("Press to Select")); +#endif + display_Update(); + } + if (b == 1) { // Next (press) + if (i == colhi) + i = collo; + else + i++; + + // Only update display after input because of slow LCD library + display_Clear(); + print_Msg(F("ROM Size: ")); + println_Msg(COL[i]); + println_Msg(F("")); +#if defined(enable_OLED) + println_Msg(F("Press left to Change")); + println_Msg(F("and right to Select")); +#elif defined(enable_LCD) + println_Msg(F("Rotate to Change")); + println_Msg(F("Press to Select")); +#endif + display_Update(); + } + if (b == 3) { // Long Press - Execute (hold) + newcolsize = i; + break; + } + } + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(F("ROM SIZE ")); + print_Msg(COL[newcolsize]); + println_Msg(F("K")); + display_Update(); + delay(1000); +#else + if (collo == colhi) + newcolsize = collo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (colhi - collo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(COL[i + collo]); + Serial.println(F("K")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newcolsize = sizeROM.toInt() + collo; + if (newcolsize > colhi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(F("")); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(COL[newcolsize]); + Serial.println(F("K")); +#endif + EEPROM_writeAnything(8, newcolsize); + colsize = newcolsize; +} + +void checkStatus_COL() +{ + EEPROM_readAnything(8, colsize); + if (colsize > 5) { + colsize = 0; + EEPROM_writeAnything(8, colsize); + } + +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("COLECOVISION READER")); + println_Msg(F("CURRENT SETTINGS")); + println_Msg(F("")); + print_Msg(F("ROM SIZE: ")); + print_Msg(COL[colsize]); + println_Msg(F("K")); + display_Update(); + wait(); +#else + Serial.print(F("CURRENT ROM SIZE: ")); + Serial.print(COL[colsize]); + Serial.println(F("K")); + Serial.println(F("")); +#endif +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +FsFile colcsvFile; +char colgame[36]; // title +char colrr[3]; // romsize +char colll[4]; // linelength (previous line) +unsigned long colcsvpos; // CSV File Position +char colcartCSV[] = "colcart.txt"; // CSV List +char colcsvEND[] = "EOF"; // CSV End Marker for scrolling + +bool readLine_COL(FsFile &f, char* line, size_t maxLen) +{ + for (size_t n = 0; n < maxLen; n++) { + int c = f.read(); + if ( c < 0 && n == 0) return false; // EOF + if (c < 0 || c == '\n') { + line[n] = 0; + return true; + } + line[n] = c; + } + return false; // line too long +} + +bool readVals_COL(char* colgame, char* colrr, char* colll) +{ + char line[42]; + colcsvpos = colcsvFile.position(); + if (!readLine_COL(colcsvFile, line, sizeof(line))) { + return false; // EOF or too long + } + char* comma = strtok(line, ","); + int x = 0; + while (comma != NULL) { + if (x == 0) + strcpy(colgame, comma); + else if (x == 1) + strcpy(colrr, comma); + else if (x == 2) + strcpy(colll, comma); + comma = strtok(NULL, ","); + x += 1; + } + return true; +} + +bool getCartListInfo_COL() +{ + bool buttonreleased = 0; + bool cartselected = 0; +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F(" HOLD TO FAST CYCLE")); + display_Update(); +#else + Serial.println(F("HOLD BUTTON TO FAST CYCLE")); +#endif + delay(2000); +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + boolean buttonVal1 = (PING & (1 << 2)); // PG2 +#endif + if (buttonVal1 == LOW) { // Button Held - Fast Cycle + while (1) { // Scroll Game List + while (readVals_COL(colgame, colrr, colll)) { + if (strcmp(colcsvEND, colgame) == 0) { + colcsvFile.seek(0); // Restart + } + else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART TITLE:")); + println_Msg(F("")); + println_Msg(colgame); + display_Update(); +#else + Serial.print(F("CART TITLE:")); + Serial.println(colgame); +#endif +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + boolean buttonVal1 = (PING & (1 << 2)); // PG2 +#endif + if (buttonVal1 == HIGH) { // Button Released + buttonreleased = 1; + break; + } + if (buttonreleased) { + buttonreleased = 0; // Reset Flag + break; + } + } + } +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + boolean buttonVal1 = (PING & (1 << 2)); // PG2 +#endif + if (buttonVal1 == HIGH) // Button Released + break; + } + } +#if (defined(enable_OLED) || defined(enable_LCD)) + display.setCursor(0, 56); + println_Msg(F("FAST CYCLE OFF")); + display_Update(); +#else + Serial.println(F("")); + Serial.println(F("FAST CYCLE OFF")); + Serial.println(F("PRESS BUTTON TO STEP FORWARD")); + Serial.println(F("DOUBLE CLICK TO STEP BACK")); + Serial.println(F("HOLD TO SELECT")); + Serial.println(F("")); +#endif + while (readVals_COL(colgame, colrr, colll)) { + if (strcmp(colcsvEND, colgame) == 0) { + colcsvFile.seek(0); // Restart + } + else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART TITLE:")); + println_Msg(F("")); + println_Msg(colgame); + display.setCursor(0, 48); +#if defined(enable_OLED) + println_Msg(F("Press left to Change")); + println_Msg(F("and right to Select")); +#elif defined(enable_LCD) + println_Msg(F("Rotate to Change")); + println_Msg(F("Press to Select")); +#endif + display_Update(); +#else + Serial.print(F("CART TITLE:")); + Serial.println(colgame); +#endif + while (1) { // Single Step + int b = checkButton(); + if (b == 1) { // Continue (press) + break; + } + if (b == 2) { // Reset to Start of List (doubleclick) + byte prevline = strtol(colll, NULL, 10); + colcsvpos -= prevline; + colcsvFile.seek(colcsvpos); + break; + } + if (b == 3) { // Long Press - Select Cart (hold) + newcolsize = strtol(colrr, NULL, 10); + EEPROM_writeAnything(8, newcolsize); + cartselected = 1; // SELECTION MADE +#if (defined(enable_OLED) || defined(enable_LCD)) + println_Msg(F("SELECTION MADE")); + display_Update(); +#else + Serial.println(F("SELECTION MADE")); +#endif + break; + } + } + if (cartselected) { + cartselected = 0; // Reset Flag + return true; + } + } + } +#if (defined(enable_OLED) || defined(enable_LCD)) + println_Msg(F("")); + println_Msg(F("END OF FILE")); + display_Update(); +#else + Serial.println(F("END OF FILE")); +#endif + + return false; +} + +void checkCSV_COL() +{ + if (getCartListInfo_COL()) { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART SELECTED")); + println_Msg(F("")); + println_Msg(colgame); + display_Update(); + // Display Settings + display.setCursor(0, 56); + print_Msg(F("CODE: R")); + println_Msg(newcolsize); + display_Update(); +#else + Serial.println(F("")); + Serial.println(F("CART SELECTED")); + Serial.println(colgame); + // Display Settings + Serial.print(F("CODE: R")); + Serial.println(newcolsize); + Serial.println(F("")); +#endif + } + else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display.setCursor(0, 56); + println_Msg(F("NO SELECTION")); + display_Update(); +#else + Serial.println(F("NO SELECTION")); +#endif + } +} + +void setCart_COL() +{ +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(colcartCSV); + display_Update(); +#endif + sd.chdir(); + sprintf(folder, "COL/CSV"); + sd.chdir(folder); // Switch Folder + colcsvFile = sd.open(colcartCSV, O_READ); + if (!colcsvFile) { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CSV FILE NOT FOUND!")); + display_Update(); +#else + Serial.println(F("CSV FILE NOT FOUND!")); +#endif + while (1) { + if (checkButton() != 0) + setup_COL(); + } + } + checkCSV_COL(); + + colcsvFile.close(); +} +#endif diff --git a/Cart_Reader/Cart_Reader.ino b/Cart_Reader/Cart_Reader.ino index f4b3332..99cd250 100644 --- a/Cart_Reader/Cart_Reader.ino +++ b/Cart_Reader/Cart_Reader.ino @@ -4,8 +4,8 @@ This project represents a community-driven effort to provide an easy to build and easy to modify cartridge dumper. - Date: 18.07.2022 - Version: 9.1 + Date: 23.07.2022 + Version: 9.2 Alpha SD lib: https://github.com/greiman/SdFat OLED lib: https://github.com/adafruit/Adafruit_SSD1306 @@ -25,7 +25,7 @@ MichlK - ROM Reader for Super Nintendo Jeff Saltzman - 4-Way Button Wayne and Layne - Video Game Shield menu - skaman - Cart ROM READER SNES ENHANCED & Famicom Cart Dumper + skaman - Cart ROM READER SNES ENHANCED, Famicom Cart Dumper, Coleco- and Intellivision modules Tamanegi_taro - PCE and Satellaview modules splash5 - GBSmart, Wonderswan and NGP modules hkz & themanbehindthecurtain - N64 flashram commands @@ -40,7 +40,8 @@ And a special Thank You to all coders and contributors on Github and the Arduino forum: jiyunomegami, splash5, Kreeblah, ramapcsx2, PsyK0p4T, Dakkaron, majorpbx, Pickle, sdhizumi, - Uzlopak, sakman55, Tombo89, scrap-a, Tombo89, borti4938, vogelfreiheit, CaitSith2, Modman, philenotfound + Uzlopak, sakman55, Tombo89, scrap-a, Tombo89, borti4938, vogelfreiheit, CaitSith2, Modman, + philenotfound, karimhadjsalem, nsx0r And to nocash for figuring out the secrets of the SFC Nintendo Power cartridge. @@ -59,7 +60,7 @@ **********************************************************************************/ -char ver[5] = "9.1"; +char ver[5] = "9.2A"; //****************************************** // !!! CHOOSE HARDWARE VERSION !!! @@ -81,7 +82,7 @@ char ver[5] = "9.1"; //****************************************** // remove // before #define to enable a module #define enable_SNES -#define enable_NP +#define enable_SFM #define enable_SV #define enable_MD #define enable_SMS @@ -93,6 +94,9 @@ char ver[5] = "9.1"; //#define enable_PCE //#define enable_WS //#define enable_NGP +//#define enable_INTV +//#define enable_COLV +//#define enable_VBOY //****************************************** // HW CONFIGS @@ -111,12 +115,14 @@ char ver[5] = "9.1"; #if (defined(HW2) || defined(HW3)) #define enable_OLED #define enable_Button2 -// #define clockgen_installed -// #define fastcrc +#define clockgen_installed +#define fastcrc #endif #if defined(HW1) #define enable_OLED +// #define clockgen_installed +// #define fastcrc #endif #if defined(SERIAL_MONITOR) @@ -296,6 +302,9 @@ bool i2c_found; #define mode_GB_GBSmart_Game 20 #define mode_WS 21 #define mode_NGP 22 +#define mode_INTV 23 +#define mode_COL 24 +#define mode_VBOY 25 // optimization-safe nop delay #define NOP __asm__ __volatile__ ("nop\n\t") @@ -686,13 +695,16 @@ static const char modeItem6[] PROGMEM = "SMS/GG/MIII/SG-1000"; static const char modeItem7[] PROGMEM = "PC Engine/TG16"; static const char modeItem8[] PROGMEM = "WonderSwan"; static const char modeItem9[] PROGMEM = "NeoGeo Pocket"; -static const char modeItem10[] PROGMEM = "Flashrom Programmer"; -static const char modeItem11[] PROGMEM = "About"; -static const char* const modeOptions[] PROGMEM = {modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7, modeItem8, modeItem9, modeItem10, modeItem11}; +static const char modeItem10[] PROGMEM = "Intellvision"; +static const char modeItem11[] PROGMEM = "Colecovision"; +static const char modeItem12[] PROGMEM = "Virtual Boy"; +static const char modeItem13[] PROGMEM = "Flashrom Programmer"; +static const char modeItem14[] PROGMEM = "About"; +static const char* const modeOptions[] PROGMEM = {modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7, modeItem8, modeItem9, modeItem10, modeItem11, modeItem12, modeItem13, modeItem14}; // All included slots void mainMenu() { - // create menu with title and 11 options to choose from + // create menu with title and 13 options to choose from unsigned char modeMenu; // Main menu spans across two pages @@ -708,8 +720,8 @@ void mainMenu() { } if (currPage == 2) { // Copy menuOptions out of progmem - convertPgm(modeOptions, 7, 4); - modeMenu = question_box(F("OPEN SOURCE CART READER"), menuOptions, 4, 0); + convertPgm(modeOptions, 7, 7); + modeMenu = question_box(F("OPEN SOURCE CART READER"), menuOptions, 7, 0); } if (numPages == 0) { // Execute choice @@ -793,8 +805,29 @@ void mainMenu() { break; #endif -#ifdef enable_FLASH +#ifdef enable_INTV case 9: + setup_INTV(); + intvMenu(); + break; +#endif + +#ifdef enable_COLV + case 10: + setup_COL(); + colMenu(); + break; +#endif + +#ifdef enable_VBOY + case 11: + setup_VBOY(); + vboyMenu(); + break; +#endif + +#ifdef enable_FLASH + case 12: #ifdef enable_FLASH16 flashMenu(); #else @@ -803,7 +836,7 @@ void mainMenu() { break; #endif - case 10: + case 13: aboutScreen(); break; @@ -830,14 +863,27 @@ static const char modeItem7[] PROGMEM = "Reset"; static const char* const modeOptions[] PROGMEM = {modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7}; // Add-ons submenu -static const char addonsItem1[] PROGMEM = "NES/Famicom"; -static const char addonsItem2[] PROGMEM = "Flashrom Programmer"; -static const char addonsItem3[] PROGMEM = "PC Engine/TG16"; -static const char addonsItem4[] PROGMEM = "SMS/GG/MIII/SG-1000"; -static const char addonsItem5[] PROGMEM = "WonderSwan"; -static const char addonsItem6[] PROGMEM = "NeoGeo Pocket"; -static const char addonsItem7[] PROGMEM = "Reset"; -static const char* const addonsOptions[] PROGMEM = {addonsItem1, addonsItem2, addonsItem3, addonsItem4, addonsItem5, addonsItem6, addonsItem7}; +static const char addonsItem1[] PROGMEM = "Consoles"; +static const char addonsItem2[] PROGMEM = "Handhelds"; +static const char addonsItem3[] PROGMEM = "Flashrom Programmer"; +static const char addonsItem4[] PROGMEM = "Reset"; +static const char* const addonsOptions[] PROGMEM = {addonsItem1, addonsItem2, addonsItem3, addonsItem4}; + +// Consoles submenu +static const char consolesItem1[] PROGMEM = "NES/Famicom"; +static const char consolesItem2[] PROGMEM = "PC Engine/TG16"; +static const char consolesItem3[] PROGMEM = "SMS/GG/MIII/SG-1000"; +static const char consolesItem4[] PROGMEM = "Intellivision"; +static const char consolesItem5[] PROGMEM = "Colecovision"; +static const char consolesItem6[] PROGMEM = "Reset"; +static const char* const consolesOptions[] PROGMEM = {consolesItem1, consolesItem2, consolesItem3, consolesItem4, consolesItem5, consolesItem6}; + +// Handhelds submenu +static const char handheldsItem1[] PROGMEM = "Virtual Boy"; +static const char handheldsItem2[] PROGMEM = "WonderSwan"; +static const char handheldsItem3[] PROGMEM = "NeoGeo Pocket"; +static const char handheldsItem4[] PROGMEM = "Reset"; +static const char* const handheldsOptions[] PROGMEM = {handheldsItem1, handheldsItem2, handheldsItem3, handheldsItem4}; // All included slots void mainMenu() { @@ -851,7 +897,7 @@ void mainMenu() { switch (modeMenu) { case 0: - addonsMenu(); + addonMenu(); break; #ifdef enable_SNES @@ -889,15 +935,54 @@ void mainMenu() { } // Everything that needs an adapter -void addonsMenu() { - // create menu with title and 7 options to choose from +void addonMenu() { + // create menu with title and 4 options to choose from unsigned char addonsMenu; // Copy menuOptions out of progmem - convertPgm(addonsOptions, 7); - addonsMenu = question_box(F("Choose Adapter"), menuOptions, 7, 0); + convertPgm(addonsOptions, 4); + addonsMenu = question_box(F("Type"), menuOptions, 4, 0); // wait for user choice to come back from the question box menu switch (addonsMenu) + { + // Consoles + case 0: + consoleMenu(); + break; + + // Handhelds + case 1: + handheldMenu(); + break; + +#ifdef enable_FLASH + case 2: + flashMenu(); + break; +#endif + + case 3: + resetArduino(); + break; + + default: + display_Clear(); + println_Msg(F("Please enable module")); + print_Error(F("in Cart_Reader.ino."), true); + break; + } +} + +// Everything that needs an adapter +void consoleMenu() { + // create menu with title and 6 options to choose from + unsigned char consolesMenu; + // Copy menuOptions out of progmem + convertPgm(consolesOptions, 6); + consolesMenu = question_box(F("Choose Adapter"), menuOptions, 6, 0); + + // wait for user choice to come back from the question box menu + switch (consolesMenu) { #ifdef enable_NES case 0: @@ -914,26 +999,66 @@ void addonsMenu() { break; #endif -#ifdef enable_FLASH - case 1: - flashMenu(); - break; -#endif - #ifdef enable_PCE - case 2: + case 1: pcsMenu(); break; #endif #ifdef enable_SMS - case 3: + case 2: smsMenu(); break; #endif -#ifdef enable_WS +#ifdef enable_INTV + case 3: + setup_INTV(); + intvMenu(); + break; +#endif + +#ifdef enable_COLV case 4: + setup_COL(); + colMenu(); + break; +#endif + + case 5: + resetArduino(); + break; + + default: + display_Clear(); + println_Msg(F("Please enable module")); + print_Error(F("in Cart_Reader.ino."), true); + break; + } +} + +// Everything that needs an adapter +void handheldMenu() { + // create menu with title and 4 options to choose from + unsigned char handheldsMenu; + // Copy menuOptions out of progmem + convertPgm(handheldsOptions, 4); + handheldsMenu = question_box(F("Choose Adapter"), menuOptions, 4, 0); + + // wait for user choice to come back from the question box menu + switch (handheldsMenu) + { +#ifdef enable_VBOY + case 0: + mode = mode_VBOY; + display_Clear(); + display_Update(); + setup_VBOY(); + break; +#endif + +#ifdef enable_WS + case 1: display_Clear(); display_Update(); setup_WS(); @@ -942,7 +1067,7 @@ void addonsMenu() { #endif #ifdef enable_NGP - case 5: + case 2: display_Clear(); display_Update(); setup_NGP(); @@ -950,7 +1075,7 @@ void addonsMenu() { break; #endif - case 6: + case 3: resetArduino(); break; @@ -2929,7 +3054,7 @@ void loop() { } #endif #endif -#ifdef enable_NP +#ifdef enable_SFM else if (mode == mode_SFM) { sfmMenu(); } @@ -2942,7 +3067,7 @@ void loop() { gbaMenu(); } #endif -#ifdef enable_NP +#ifdef enable_SFM #ifdef enable_FLASH else if (mode == mode_SFM_Flash) { sfmFlashMenu(); @@ -3007,6 +3132,21 @@ void loop() { else if (mode == mode_NGP) { ngpMenu(); } +#endif +#ifdef enable_INTV + else if (mode == mode_INTV) { + intvMenu(); + } +#endif +#ifdef enable_COLV + else if (mode == mode_COL) { + colMenu(); + } +#endif +#ifdef enable_VBOY + else if (mode == mode_VBOY) { + vboyMenu(); + } #endif else { display_Clear(); diff --git a/Cart_Reader/GBSmart.ino b/Cart_Reader/GBS.ino similarity index 95% rename from Cart_Reader/GBSmart.ino rename to Cart_Reader/GBS.ino index 9db7aac..ee957f1 100644 --- a/Cart_Reader/GBSmart.ino +++ b/Cart_Reader/GBS.ino @@ -1,810 +1,810 @@ -//****************************************** -// GB SMART MODULE -// Supports 32M cart with LH28F016SUT flash -//****************************************** -#ifdef enable_GBX -#define GB_SMART_GAMES_PER_PAGE 6 - -/****************************************** - Menu - *****************************************/ -// GB Smart menu items -static const char gbSmartMenuItem1[] PROGMEM = "Game Menu"; -static const char gbSmartMenuItem2[] PROGMEM = "Flash Menu"; -static const char gbSmartMenuItem3[] PROGMEM = "Reset"; -static const char* const menuOptionsGBSmart[] PROGMEM = {gbSmartMenuItem1, gbSmartMenuItem2, gbSmartMenuItem3}; - -static const char gbSmartFlashMenuItem1[] PROGMEM = "Read Flash"; -static const char gbSmartFlashMenuItem2[] PROGMEM = "Write Flash"; -static const char gbSmartFlashMenuItem3[] PROGMEM = "Back"; -static const char* const menuOptionsGBSmartFlash[] PROGMEM = {gbSmartFlashMenuItem1, gbSmartFlashMenuItem2, gbSmartFlashMenuItem3}; - -static const char gbSmartGameMenuItem1[] PROGMEM = "Read Game"; -static const char gbSmartGameMenuItem2[] PROGMEM = "Read SRAM"; -static const char gbSmartGameMenuItem3[] PROGMEM = "Write SRAM"; -static const char gbSmartGameMenuItem4[] PROGMEM = "Switch Game"; -static const char gbSmartGameMenuItem5[] PROGMEM = "Reset"; -static const char* const menuOptionsGBSmartGame[] PROGMEM = {gbSmartGameMenuItem1, gbSmartGameMenuItem2, gbSmartGameMenuItem3, gbSmartGameMenuItem4, gbSmartGameMenuItem5}; - -typedef struct -{ - uint8_t start_bank; - uint8_t rom_type; - uint8_t rom_size; - uint8_t sram_size; - char title[16]; -} GBSmartGameInfo; - -uint32_t gbSmartSize = 32 * 131072; -uint16_t gbSmartBanks = 256; - -uint8_t gbSmartBanksPerFlashChip = 128; -uint8_t gbSmartBanksPerFlashBlock = 4; -uint32_t gbSmartFlashBlockSize = (gbSmartBanksPerFlashBlock << 14); - -uint8_t gbSmartRomSizeGB = 0x07; -uint8_t gbSmartSramSizeGB = 0x04; -uint8_t gbSmartFlashSizeGB = 0x06; - -GBSmartGameInfo gbSmartGames[GB_SMART_GAMES_PER_PAGE]; - -byte signature[48]; -uint16_t gameMenuStartBank; - -#ifdef enable_NP -extern boolean hasMenu; -extern byte numGames; -#else -boolean hasMenu; -byte numGames; -#endif - -// Compare checksum -boolean compare_checksum_GBS() { - println_Msg(F("Calculating Checksum")); - display_Update(); - - strcpy(fileName, romName); - strcat(fileName, ".GB"); - - // last used rom folder - EEPROM_readAnything(0, foldern); - sprintf(folder, "GB/ROM/%s/%d", romName, foldern - 1); - - char calcsumStr[5]; - sprintf(calcsumStr, "%04X", calc_checksum_GB(fileName, folder)); - - if (strcmp(calcsumStr, checksumStr) == 0) { - print_Msg(F("Result: ")); - println_Msg(calcsumStr); - println_Msg(F("Checksum matches")); - display_Update(); - return 1; - } - else { - print_Msg(F("Result: ")); - println_Msg(calcsumStr); - print_Error(F("Checksum Error"), false); - return 0; - } -} - -byte readByte_GBS(word myAddress) { - PORTF = myAddress & 0xFF; - PORTK = (myAddress >> 8) & 0xFF; - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Switch CS(PH3) and RD(PH6) to LOW - PORTH &= ~((1 << 3) | (1 << 6)); - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Read - byte tempByte = PINC; - - // Switch CS(PH3) and RD(PH6) to HIGH - PORTH |= (1 << 3) | (1 << 6); - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - return tempByte; -} - -void setup_GBSmart() -{ - // take from setup_GB - // Set RST(PH0) to Input - DDRH &= ~(1 << 0); - // Activate Internal Pullup Resistors - PORTH |= (1 << 0); - - // Set Address Pins to Output - //A0-A7 - DDRF = 0xFF; - //A8-A15 - DDRK = 0xFF; - - // Set Control Pins to Output CS(PH3) WR(PH5) RD(PH6) AUDIOIN(PH4) RESET(PH0) - DDRH |= (1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); - // Output a high signal on all pins, pins are active low therefore everything is disabled now - PORTH |= (1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); - - // Set Data Pins (D0-D7) to Input - DDRC = 0x00; - - delay(400); - - gbSmartRemapStartBank(0x00, 0x00, 0x00); - - getCartInfo_GB(); - - for (byte i = 0; i < 0x30; i++) - signature[i] = readByte_GBS(0x0104 + i); - - gameMenuStartBank = 0x02; - hasMenu = true; - numGames = 0; - - display_Clear(); - display_Update(); -} - -void gbSmartMenu() -{ - uint8_t mainMenu; - - // Copy menuOptions out of progmem - convertPgm(menuOptionsGBSmart, 3); - mainMenu = question_box(F("GB Smart"), menuOptions, 3, 0); - - // wait for user choice to come back from the question box menu - switch (mainMenu) - { - case 0: - { - gbSmartGameMenu(); - break; - } - case 1: - { - mode = mode_GB_GBSmart_Flash; - break; - } - default: - { - asm volatile (" jmp 0"); - break; - } - } -} - -void gbSmartGameOptions() -{ - uint8_t gameSubMenu; - - convertPgm(menuOptionsGBSmartGame, 5); - gameSubMenu = question_box(F("GB Smart Game Menu"), menuOptions, 5, 0); - - switch (gameSubMenu) - { - case 0: // Read Game - { - display_Clear(); - sd.chdir("/"); - readROM_GB(); - compare_checksum_GBS(); - break; - } - case 1: // Read SRAM - { - display_Clear(); - sd.chdir("/"); - readSRAM_GB(); - break; - } - case 2: // Write SRAM - { - display_Clear(); - sd.chdir("/"); - writeSRAM_GB(); - uint32_t wrErrors = verifySRAM_GB(); - if (wrErrors == 0) - { - println_Msg(F("Verified OK")); - display_Update(); - } - else - { - print_Msg(F("Error: ")); - print_Msg(wrErrors); - println_Msg(F(" bytes")); - print_Error(F("did not verify."), false); - } - break; - } - case 3: // Switch Game - { - gameMenuStartBank = 0x02; - gbSmartGameMenu(); - break; - } - default: - { - asm volatile (" jmp 0"); - break; - } - } - - if (gameSubMenu != 3) - { - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - } -} - -void gbSmartGameMenu() -{ - uint8_t gameSubMenu = 0; -gb_smart_load_more_games: - if (gameMenuStartBank > 0xfe) - gameMenuStartBank = 0x02; - - gbSmartGetGames(); - - if (hasMenu) - { - char menuOptionsGBSmartGames[7][20]; - int i = 0; - for (; i < numGames; i++) - strncpy(menuOptionsGBSmartGames[i], gbSmartGames[i].title, 16); - - strncpy(menuOptionsGBSmartGames[i], "...", 16); - gameSubMenu = question_box(F("Select Game"), menuOptionsGBSmartGames, i + 1, 0); - - if (gameSubMenu >= i) - goto gb_smart_load_more_games; - } - else - { - gameSubMenu = 0; - } - - // copy romname - strcpy(romName, gbSmartGames[gameSubMenu].title); - - // select a game - gbSmartRemapStartBank(gbSmartGames[gameSubMenu].start_bank, gbSmartGames[gameSubMenu].rom_size, gbSmartGames[gameSubMenu].sram_size); - getCartInfo_GB(); - showCartInfo_GB(); - - mode = mode_GB_GBSmart_Game; -} - -void gbSmartFlashMenu() -{ - uint8_t flashSubMenu; - - convertPgm(menuOptionsGBSmartFlash, 3); - flashSubMenu = question_box(F("GB Smart Flash Menu"), menuOptions, 3, 0); - - switch (flashSubMenu) - { - case 0: - { - // read flash - display_Clear(); - sd.chdir("/"); - - EEPROM_readAnything(0, foldern); - sprintf(fileName, "GBS%d.bin", foldern); - sd.mkdir("GB/GBS", true); - sd.chdir("GB/GBS"); - foldern = foldern + 1; - EEPROM_writeAnything(0, foldern); - - gbSmartReadFlash(); - break; - } - case 1: - { - // write flash - display_Clear(); - - println_Msg(F("Attention")); - println_Msg(F("This will erase your")); - println_Msg(F("GB Smart Cartridge.")); - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - - display_Clear(); - filePath[0] = '\0'; - sd.chdir("/"); - fileBrowser(F("Select 4MB file")); - - sprintf(filePath, "%s/%s", filePath, fileName); - gbSmartWriteFlash(); - break; - } - default: - { - mode = mode_GB_GBSmart; - return; - } - } - - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); -} - -void gbSmartGetGames() -{ - static const byte menu_title[] = {0x47, 0x42, 0x31, 0x36, 0x4d}; - - // reset remap setting - gbSmartRemapStartBank(0x00, gbSmartRomSizeGB, gbSmartSramSizeGB); - - uint16_t i; - uint8_t myByte, myLength; - - // check if contain menu - hasMenu = true; - dataIn(); - for (i = 0; i < 5; i++) - { - if (readByte_GBS(0x0134 + i) != menu_title[i]) - { - hasMenu = false; - break; - } - } - - if (hasMenu) - { - for (i = gameMenuStartBank, numGames = 0; i < gbSmartBanks && numGames < GB_SMART_GAMES_PER_PAGE; ) - { - myLength = 0; - - // switch bank - dataOut(); - writeByte_GB(0x2100, i); - - dataIn(); - // read signature - for (uint8_t j = 0x00; j < 0x30; j++) - { - if (readByte_GBS(0x4104 + j) != signature[j]) - { - i += 0x02; - goto gb_smart_get_game_loop_end; - } - } - - for (uint8_t j = 0; j < 15; j++) - { - myByte = readByte_GBS(0x4134 + j); - - if (((char(myByte) >= 0x30 && char(myByte) <= 0x39) || - (char(myByte) >= 0x41 && char(myByte) <= 0x7a))) - gbSmartGames[numGames].title[myLength++] = char(myByte); - } - - gbSmartGames[numGames].title[myLength] = 0x00; - gbSmartGames[numGames].start_bank = i; - gbSmartGames[numGames].rom_type = readByte_GBS(0x4147); - gbSmartGames[numGames].rom_size = readByte_GBS(0x4148); - gbSmartGames[numGames].sram_size = readByte_GBS(0x4149); - - myByte = (2 << gbSmartGames[numGames].rom_size); - i += myByte; - numGames++; -gb_smart_get_game_loop_end:; - } - - gameMenuStartBank = i; - } - else - { - dataIn(); - for (uint8_t j = 0; j < 15; j++) - { - myByte = readByte_GBS(0x0134 + j); - - if (((char(myByte) >= 0x30 && char(myByte) <= 0x39) || - (char(myByte) >= 0x41 && char(myByte) <= 0x7a))) - gbSmartGames[0].title[myLength++] = char(myByte); - } - - gbSmartGames[0].title[myLength] = 0x00; - gbSmartGames[0].start_bank = 0x00; - gbSmartGames[0].rom_type = readByte_GBS(0x0147); - gbSmartGames[0].rom_size = readByte_GBS(0x0148); - gbSmartGames[0].sram_size = readByte_GBS(0x0149); - - numGames = 1; - gameMenuStartBank = 0xfe; - } -} - -void gbSmartReadFlash() -{ - print_Msg(F("Saving as GB/GBS/")); - print_Msg(fileName); - println_Msg(F("...")); - display_Update(); - - if (!myFile.open(fileName, O_RDWR | O_CREAT)) - print_Error(F("Can't create file on SD"), true); - - // reset flash to read array state - for (int i = 0x00; i < gbSmartBanks; i += gbSmartBanksPerFlashChip) - gbSmartResetFlash(i); - - // remaps mmc to full access - gbSmartRemapStartBank(0x00, gbSmartRomSizeGB, gbSmartSramSizeGB); - - // dump fixed bank 0x00 - dataIn(); - for (uint16_t addr = 0x0000; addr <= 0x3fff; addr += 512) - { - for (uint16_t c = 0; c < 512; c++) - sdBuffer[c] = readByte_GBS(addr + c); - - myFile.write(sdBuffer, 512); - } - - // read rest banks - for (uint16_t bank = 0x01; bank < gbSmartBanks; bank++) - { - dataOut(); - writeByte_GB(0x2100, bank); - - dataIn(); - for (uint16_t addr = 0x4000; addr <= 0x7fff; addr += 512) - { - for (uint16_t c = 0; c < 512; c++) - sdBuffer[c] = readByte_GBS(addr + c); - - myFile.write(sdBuffer, 512); - } - } - - // back to initial state - writeByte_GB(0x2100, 0x01); - - myFile.close(); - println_Msg(""); - println_Msg(F("Finished reading")); - display_Update(); -} - -void gbSmartWriteFlash() -{ - for (int bank = 0x00; bank < gbSmartBanks; bank += gbSmartBanksPerFlashChip) - { - display_Clear(); - - print_Msg(F("Erasing...")); - display_Update(); - - gbSmartEraseFlash(bank); - gbSmartResetFlash(bank); - - println_Msg(F("Done")); - print_Msg(F("Blankcheck...")); - display_Update(); - - if (!gbSmartBlankCheckingFlash(bank)) - print_Error(F("Could not erase flash"), true); - - println_Msg(F("Passed")); - display_Update(); - - // write full chip - gbSmartWriteFlash(bank); - - // reset chip - gbSmartWriteFlashByte(0x0000, 0xff); - } - - print_Msg(F("Verifying...")); - display_Update(); - - writeErrors = gbSmartVerifyFlash(); - if (writeErrors == 0) - { - println_Msg(F("OK")); - display_Update(); - } - else - { - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), true); - } -} - -void gbSmartWriteFlash(uint32_t start_bank) -{ - if (!myFile.open(filePath, O_READ)) - print_Error(F("Can't open file on SD"), true); - - // switch to flash base bank - gbSmartRemapStartBank(start_bank, gbSmartFlashSizeGB, gbSmartSramSizeGB); - - myFile.seekCur((start_bank << 14)); - - print_Msg(F("Writing Bank 0x")); - print_Msg(start_bank, HEX); - print_Msg(F("...")); - display_Update(); - - // handle bank 0x00 on 0x0000 - gbSmartWriteFlashFromMyFile(0x0000); - - // handle rest banks on 0x4000 - for (uint8_t bank = 0x01; bank < gbSmartBanksPerFlashChip; bank++) - { - dataOut(); - writeByte_GB(0x2100, bank); - - gbSmartWriteFlashFromMyFile(0x4000); - } - - myFile.close(); - println_Msg(""); -} - -void gbSmartWriteFlashFromMyFile(uint32_t addr) -{ - for (uint16_t i = 0; i < 16384; i += 256) - { - myFile.read(sdBuffer, 256); - - // sequence load to page - dataOut(); - gbSmartWriteFlashByte(addr, 0xe0); - gbSmartWriteFlashByte(addr, 0xff); - gbSmartWriteFlashByte(addr, 0x00); // BCH should be 0x00 - - // fill page buffer - for (int d = 0; d < 256; d++) - gbSmartWriteFlashByte(d, sdBuffer[d]); - - // start flashing page - gbSmartWriteFlashByte(addr, 0x0c); - gbSmartWriteFlashByte(addr, 0xff); - gbSmartWriteFlashByte(addr + i, 0x00); // BCH should be 0x00 - - // waiting for finishing - dataIn(); - while ((readByte_GBS(addr + i) & 0x80) == 0x00); - } - - // blink LED - blinkLED(); -} - -uint32_t gbSmartVerifyFlash() -{ - uint32_t verified = 0; - - if (!myFile.open(filePath, O_READ)) - { - verified = 0xffffffff; - print_Error(F("Can't open file on SD"), false); - } - else - { - // remaps mmc to full access - gbSmartRemapStartBank(0x00, gbSmartRomSizeGB, gbSmartSramSizeGB); - - // verify bank 0x00 - dataIn(); - for (uint16_t addr = 0x0000; addr <= 0x3fff; addr += 512) - { - myFile.read(sdBuffer, 512); - - for (uint16_t c = 0; c < 512; c++) - { - if (readByte_GBS(addr + c) != sdBuffer[c]) - verified++; - } - } - - // verify rest banks - for (uint16_t bank = 0x01; bank < gbSmartBanks; bank++) - { - dataOut(); - writeByte_GB(0x2100, bank); - - dataIn(); - for (uint16_t addr = 0x4000; addr <= 0x7fff; addr += 512) - { - myFile.read(sdBuffer, 512); - - for (uint16_t c = 0; c < 512; c++) - { - if (readByte_GBS(addr + c) != sdBuffer[c]) - verified++; - } - } - } - - // back to initial state - writeByte_GB(0x2100, 0x01); - - myFile.close(); - } - - return verified; -} - -byte gbSmartBlankCheckingFlash(uint8_t flash_start_bank) -{ - gbSmartRemapStartBank(flash_start_bank, gbSmartFlashSizeGB, gbSmartSramSizeGB); - - // check first bank - dataIn(); - for (uint16_t addr = 0x0000; addr <= 0x3fff; addr++) - { - if (readByte_GBS(addr) != 0xff) - return 0; - } - - // check rest banks - for (uint16_t bank = 0x01; bank < gbSmartBanksPerFlashChip; bank++) - { - dataOut(); - writeByte_GB(0x2100, bank); - - dataIn(); - for (uint16_t addr = 0x4000; addr <= 0x7fff; addr++) - { - if (readByte_GBS(addr) != 0xff) - return 0; - } - } - - return 1; -} - -void gbSmartResetFlash(uint8_t flash_start_bank) -{ - gbSmartRemapStartBank(flash_start_bank, gbSmartFlashSizeGB, gbSmartSramSizeGB); - - dataOut(); - gbSmartWriteFlashByte(0x0, 0xff); -} - -void gbSmartEraseFlash(uint8_t flash_start_bank) -{ - gbSmartRemapStartBank(flash_start_bank, gbSmartFlashSizeGB, gbSmartSramSizeGB); - - // handling first flash block - dataOut(); - gbSmartWriteFlashByte(0x0000, 0x20); - gbSmartWriteFlashByte(0x0000, 0xd0); - - dataIn(); - while ((readByte_GBS(0x0000) & 0x80) == 0x00); - - // blink LED - blinkLED(); - - // rest of flash block - for (uint32_t ba = gbSmartBanksPerFlashBlock; ba < gbSmartBanksPerFlashChip; ba += gbSmartBanksPerFlashBlock) - { - dataOut(); - writeByte_GB(0x2100, ba); - - gbSmartWriteFlashByte(0x4000, 0x20); - gbSmartWriteFlashByte(0x4000, 0xd0); - - dataIn(); - while ((readByte_GBS(0x4000) & 0x80) == 0x00); - - // blink LED - blinkLED(); - } -} - -void gbSmartWriteFlashByte(uint32_t myAddress, uint8_t myData) -{ - PORTF = myAddress & 0xff; - PORTK = (myAddress >> 8) & 0xff; - PORTC = myData; - - // wait for 62.5 x 4 = 250ns - __asm__("nop\n\tnop\n\tnop\n\tnop\n\t"); - - // Pull FLASH_WE (PH4) low - PORTH &= ~(1 << 4); - - // pull low for another 250ns - __asm__("nop\n\tnop\n\tnop\n\tnop\n\t"); - - // Pull FLASH_WE (PH4) high - PORTH |= (1 << 4); - - // pull high for another 250ns - __asm__("nop\n\tnop\n\tnop\n\tnop\n\t"); -} - -// rom_start_bank = 0x00 means back to original state -void gbSmartRemapStartBank(uint8_t rom_start_bank, uint8_t rom_size, uint8_t sram_size) -{ - rom_start_bank &= 0xfe; - - dataOut(); - - // clear base bank setting - writeByte_GB(0x1000, 0xa5); - writeByte_GB(0x7000, 0x00); - writeByte_GB(0x1000, 0x98); - writeByte_GB(0x2000, rom_start_bank); - - if (rom_start_bank > 1) - { - // start set new base bank - writeByte_GB(0x1000, 0xa5); - - dataIn(); - rom_start_bank = gbSmartGetResizeParam(rom_size, sram_size); - - dataOut(); - writeByte_GB(0x7000, rom_start_bank); - writeByte_GB(0x1000, 0x98); - - writeByte_GB(0x2100, 0x01); - } - - dataIn(); -} - -// Get magic number for 0x7000 register. -// Use for setting correct rom and sram size -// Code logic is take from SmartCard32M V1.3 menu code, -// see 0x2db2 to 0x2e51 (0xa0 bytes) -uint8_t gbSmartGetResizeParam(uint8_t rom_size, uint8_t sram_size) -{ - if (rom_size < 0x0f) - { - rom_size &= 0x07; - rom_size ^= 0x07; - } - else - { - rom_size = 0x01; - } - - if (sram_size > 0) - { - if (sram_size > 1) - { - sram_size--; - sram_size ^= 0x03; - sram_size <<= 4; - sram_size &= 0x30; - } - else - { - sram_size = 0x20; // 2KiB treat as 8KiB - } - } - else - { - sram_size = 0x30; // no sram - } - - return (sram_size | rom_size); -} - -#endif +//****************************************** +// GB SMART MODULE +// Supports 32M cart with LH28F016SUT flash +//****************************************** +#ifdef enable_GBX +#define GB_SMART_GAMES_PER_PAGE 6 + +/****************************************** + Menu + *****************************************/ +// GB Smart menu items +static const char gbSmartMenuItem1[] PROGMEM = "Game Menu"; +static const char gbSmartMenuItem2[] PROGMEM = "Flash Menu"; +static const char gbSmartMenuItem3[] PROGMEM = "Reset"; +static const char* const menuOptionsGBSmart[] PROGMEM = {gbSmartMenuItem1, gbSmartMenuItem2, gbSmartMenuItem3}; + +static const char gbSmartFlashMenuItem1[] PROGMEM = "Read Flash"; +static const char gbSmartFlashMenuItem2[] PROGMEM = "Write Flash"; +static const char gbSmartFlashMenuItem3[] PROGMEM = "Back"; +static const char* const menuOptionsGBSmartFlash[] PROGMEM = {gbSmartFlashMenuItem1, gbSmartFlashMenuItem2, gbSmartFlashMenuItem3}; + +static const char gbSmartGameMenuItem1[] PROGMEM = "Read Game"; +static const char gbSmartGameMenuItem2[] PROGMEM = "Read SRAM"; +static const char gbSmartGameMenuItem3[] PROGMEM = "Write SRAM"; +static const char gbSmartGameMenuItem4[] PROGMEM = "Switch Game"; +static const char gbSmartGameMenuItem5[] PROGMEM = "Reset"; +static const char* const menuOptionsGBSmartGame[] PROGMEM = {gbSmartGameMenuItem1, gbSmartGameMenuItem2, gbSmartGameMenuItem3, gbSmartGameMenuItem4, gbSmartGameMenuItem5}; + +typedef struct +{ + uint8_t start_bank; + uint8_t rom_type; + uint8_t rom_size; + uint8_t sram_size; + char title[16]; +} GBSmartGameInfo; + +uint32_t gbSmartSize = 32 * 131072; +uint16_t gbSmartBanks = 256; + +uint8_t gbSmartBanksPerFlashChip = 128; +uint8_t gbSmartBanksPerFlashBlock = 4; +uint32_t gbSmartFlashBlockSize = (gbSmartBanksPerFlashBlock << 14); + +uint8_t gbSmartRomSizeGB = 0x07; +uint8_t gbSmartSramSizeGB = 0x04; +uint8_t gbSmartFlashSizeGB = 0x06; + +GBSmartGameInfo gbSmartGames[GB_SMART_GAMES_PER_PAGE]; + +byte signature[48]; +uint16_t gameMenuStartBank; + +#ifdef enable_SFM +extern boolean hasMenu; +extern byte numGames; +#else +boolean hasMenu; +byte numGames; +#endif + +// Compare checksum +boolean compare_checksum_GBS() { + println_Msg(F("Calculating Checksum")); + display_Update(); + + strcpy(fileName, romName); + strcat(fileName, ".GB"); + + // last used rom folder + EEPROM_readAnything(0, foldern); + sprintf(folder, "GB/ROM/%s/%d", romName, foldern - 1); + + char calcsumStr[5]; + sprintf(calcsumStr, "%04X", calc_checksum_GB(fileName, folder)); + + if (strcmp(calcsumStr, checksumStr) == 0) { + print_Msg(F("Result: ")); + println_Msg(calcsumStr); + println_Msg(F("Checksum matches")); + display_Update(); + return 1; + } + else { + print_Msg(F("Result: ")); + println_Msg(calcsumStr); + print_Error(F("Checksum Error"), false); + return 0; + } +} + +byte readByte_GBS(word myAddress) { + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Switch CS(PH3) and RD(PH6) to LOW + PORTH &= ~((1 << 3) | (1 << 6)); + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Read + byte tempByte = PINC; + + // Switch CS(PH3) and RD(PH6) to HIGH + PORTH |= (1 << 3) | (1 << 6); + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + return tempByte; +} + +void setup_GBSmart() +{ + // take from setup_GB + // Set RST(PH0) to Input + DDRH &= ~(1 << 0); + // Activate Internal Pullup Resistors + PORTH |= (1 << 0); + + // Set Address Pins to Output + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + + // Set Control Pins to Output CS(PH3) WR(PH5) RD(PH6) AUDIOIN(PH4) RESET(PH0) + DDRH |= (1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + // Output a high signal on all pins, pins are active low therefore everything is disabled now + PORTH |= (1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Set Data Pins (D0-D7) to Input + DDRC = 0x00; + + delay(400); + + gbSmartRemapStartBank(0x00, 0x00, 0x00); + + getCartInfo_GB(); + + for (byte i = 0; i < 0x30; i++) + signature[i] = readByte_GBS(0x0104 + i); + + gameMenuStartBank = 0x02; + hasMenu = true; + numGames = 0; + + display_Clear(); + display_Update(); +} + +void gbSmartMenu() +{ + uint8_t mainMenu; + + // Copy menuOptions out of progmem + convertPgm(menuOptionsGBSmart, 3); + mainMenu = question_box(F("GB Smart"), menuOptions, 3, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + case 0: + { + gbSmartGameMenu(); + break; + } + case 1: + { + mode = mode_GB_GBSmart_Flash; + break; + } + default: + { + asm volatile (" jmp 0"); + break; + } + } +} + +void gbSmartGameOptions() +{ + uint8_t gameSubMenu; + + convertPgm(menuOptionsGBSmartGame, 5); + gameSubMenu = question_box(F("GB Smart Game Menu"), menuOptions, 5, 0); + + switch (gameSubMenu) + { + case 0: // Read Game + { + display_Clear(); + sd.chdir("/"); + readROM_GB(); + compare_checksum_GBS(); + break; + } + case 1: // Read SRAM + { + display_Clear(); + sd.chdir("/"); + readSRAM_GB(); + break; + } + case 2: // Write SRAM + { + display_Clear(); + sd.chdir("/"); + writeSRAM_GB(); + uint32_t wrErrors = verifySRAM_GB(); + if (wrErrors == 0) + { + println_Msg(F("Verified OK")); + display_Update(); + } + else + { + print_Msg(F("Error: ")); + print_Msg(wrErrors); + println_Msg(F(" bytes")); + print_Error(F("did not verify."), false); + } + break; + } + case 3: // Switch Game + { + gameMenuStartBank = 0x02; + gbSmartGameMenu(); + break; + } + default: + { + asm volatile (" jmp 0"); + break; + } + } + + if (gameSubMenu != 3) + { + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + } +} + +void gbSmartGameMenu() +{ + uint8_t gameSubMenu = 0; +gb_smart_load_more_games: + if (gameMenuStartBank > 0xfe) + gameMenuStartBank = 0x02; + + gbSmartGetGames(); + + if (hasMenu) + { + char menuOptionsGBSmartGames[7][20]; + int i = 0; + for (; i < numGames; i++) + strncpy(menuOptionsGBSmartGames[i], gbSmartGames[i].title, 16); + + strncpy(menuOptionsGBSmartGames[i], "...", 16); + gameSubMenu = question_box(F("Select Game"), menuOptionsGBSmartGames, i + 1, 0); + + if (gameSubMenu >= i) + goto gb_smart_load_more_games; + } + else + { + gameSubMenu = 0; + } + + // copy romname + strcpy(romName, gbSmartGames[gameSubMenu].title); + + // select a game + gbSmartRemapStartBank(gbSmartGames[gameSubMenu].start_bank, gbSmartGames[gameSubMenu].rom_size, gbSmartGames[gameSubMenu].sram_size); + getCartInfo_GB(); + showCartInfo_GB(); + + mode = mode_GB_GBSmart_Game; +} + +void gbSmartFlashMenu() +{ + uint8_t flashSubMenu; + + convertPgm(menuOptionsGBSmartFlash, 3); + flashSubMenu = question_box(F("GB Smart Flash Menu"), menuOptions, 3, 0); + + switch (flashSubMenu) + { + case 0: + { + // read flash + display_Clear(); + sd.chdir("/"); + + EEPROM_readAnything(0, foldern); + sprintf(fileName, "GBS%d.bin", foldern); + sd.mkdir("GB/GBS", true); + sd.chdir("GB/GBS"); + foldern = foldern + 1; + EEPROM_writeAnything(0, foldern); + + gbSmartReadFlash(); + break; + } + case 1: + { + // write flash + display_Clear(); + + println_Msg(F("Attention")); + println_Msg(F("This will erase your")); + println_Msg(F("GB Smart Cartridge.")); + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + + display_Clear(); + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser(F("Select 4MB file")); + + sprintf(filePath, "%s/%s", filePath, fileName); + gbSmartWriteFlash(); + break; + } + default: + { + mode = mode_GB_GBSmart; + return; + } + } + + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +void gbSmartGetGames() +{ + static const byte menu_title[] = {0x47, 0x42, 0x31, 0x36, 0x4d}; + + // reset remap setting + gbSmartRemapStartBank(0x00, gbSmartRomSizeGB, gbSmartSramSizeGB); + + uint16_t i; + uint8_t myByte, myLength; + + // check if contain menu + hasMenu = true; + dataIn(); + for (i = 0; i < 5; i++) + { + if (readByte_GBS(0x0134 + i) != menu_title[i]) + { + hasMenu = false; + break; + } + } + + if (hasMenu) + { + for (i = gameMenuStartBank, numGames = 0; i < gbSmartBanks && numGames < GB_SMART_GAMES_PER_PAGE; ) + { + myLength = 0; + + // switch bank + dataOut(); + writeByte_GB(0x2100, i); + + dataIn(); + // read signature + for (uint8_t j = 0x00; j < 0x30; j++) + { + if (readByte_GBS(0x4104 + j) != signature[j]) + { + i += 0x02; + goto gb_smart_get_game_loop_end; + } + } + + for (uint8_t j = 0; j < 15; j++) + { + myByte = readByte_GBS(0x4134 + j); + + if (((char(myByte) >= 0x30 && char(myByte) <= 0x39) || + (char(myByte) >= 0x41 && char(myByte) <= 0x7a))) + gbSmartGames[numGames].title[myLength++] = char(myByte); + } + + gbSmartGames[numGames].title[myLength] = 0x00; + gbSmartGames[numGames].start_bank = i; + gbSmartGames[numGames].rom_type = readByte_GBS(0x4147); + gbSmartGames[numGames].rom_size = readByte_GBS(0x4148); + gbSmartGames[numGames].sram_size = readByte_GBS(0x4149); + + myByte = (2 << gbSmartGames[numGames].rom_size); + i += myByte; + numGames++; +gb_smart_get_game_loop_end:; + } + + gameMenuStartBank = i; + } + else + { + dataIn(); + for (uint8_t j = 0; j < 15; j++) + { + myByte = readByte_GBS(0x0134 + j); + + if (((char(myByte) >= 0x30 && char(myByte) <= 0x39) || + (char(myByte) >= 0x41 && char(myByte) <= 0x7a))) + gbSmartGames[0].title[myLength++] = char(myByte); + } + + gbSmartGames[0].title[myLength] = 0x00; + gbSmartGames[0].start_bank = 0x00; + gbSmartGames[0].rom_type = readByte_GBS(0x0147); + gbSmartGames[0].rom_size = readByte_GBS(0x0148); + gbSmartGames[0].sram_size = readByte_GBS(0x0149); + + numGames = 1; + gameMenuStartBank = 0xfe; + } +} + +void gbSmartReadFlash() +{ + print_Msg(F("Saving as GB/GBS/")); + print_Msg(fileName); + println_Msg(F("...")); + display_Update(); + + if (!myFile.open(fileName, O_RDWR | O_CREAT)) + print_Error(F("Can't create file on SD"), true); + + // reset flash to read array state + for (int i = 0x00; i < gbSmartBanks; i += gbSmartBanksPerFlashChip) + gbSmartResetFlash(i); + + // remaps mmc to full access + gbSmartRemapStartBank(0x00, gbSmartRomSizeGB, gbSmartSramSizeGB); + + // dump fixed bank 0x00 + dataIn(); + for (uint16_t addr = 0x0000; addr <= 0x3fff; addr += 512) + { + for (uint16_t c = 0; c < 512; c++) + sdBuffer[c] = readByte_GBS(addr + c); + + myFile.write(sdBuffer, 512); + } + + // read rest banks + for (uint16_t bank = 0x01; bank < gbSmartBanks; bank++) + { + dataOut(); + writeByte_GB(0x2100, bank); + + dataIn(); + for (uint16_t addr = 0x4000; addr <= 0x7fff; addr += 512) + { + for (uint16_t c = 0; c < 512; c++) + sdBuffer[c] = readByte_GBS(addr + c); + + myFile.write(sdBuffer, 512); + } + } + + // back to initial state + writeByte_GB(0x2100, 0x01); + + myFile.close(); + println_Msg(""); + println_Msg(F("Finished reading")); + display_Update(); +} + +void gbSmartWriteFlash() +{ + for (int bank = 0x00; bank < gbSmartBanks; bank += gbSmartBanksPerFlashChip) + { + display_Clear(); + + print_Msg(F("Erasing...")); + display_Update(); + + gbSmartEraseFlash(bank); + gbSmartResetFlash(bank); + + println_Msg(F("Done")); + print_Msg(F("Blankcheck...")); + display_Update(); + + if (!gbSmartBlankCheckingFlash(bank)) + print_Error(F("Could not erase flash"), true); + + println_Msg(F("Passed")); + display_Update(); + + // write full chip + gbSmartWriteFlash(bank); + + // reset chip + gbSmartWriteFlashByte(0x0000, 0xff); + } + + print_Msg(F("Verifying...")); + display_Update(); + + writeErrors = gbSmartVerifyFlash(); + if (writeErrors == 0) + { + println_Msg(F("OK")); + display_Update(); + } + else + { + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), true); + } +} + +void gbSmartWriteFlash(uint32_t start_bank) +{ + if (!myFile.open(filePath, O_READ)) + print_Error(F("Can't open file on SD"), true); + + // switch to flash base bank + gbSmartRemapStartBank(start_bank, gbSmartFlashSizeGB, gbSmartSramSizeGB); + + myFile.seekCur((start_bank << 14)); + + print_Msg(F("Writing Bank 0x")); + print_Msg(start_bank, HEX); + print_Msg(F("...")); + display_Update(); + + // handle bank 0x00 on 0x0000 + gbSmartWriteFlashFromMyFile(0x0000); + + // handle rest banks on 0x4000 + for (uint8_t bank = 0x01; bank < gbSmartBanksPerFlashChip; bank++) + { + dataOut(); + writeByte_GB(0x2100, bank); + + gbSmartWriteFlashFromMyFile(0x4000); + } + + myFile.close(); + println_Msg(""); +} + +void gbSmartWriteFlashFromMyFile(uint32_t addr) +{ + for (uint16_t i = 0; i < 16384; i += 256) + { + myFile.read(sdBuffer, 256); + + // sequence load to page + dataOut(); + gbSmartWriteFlashByte(addr, 0xe0); + gbSmartWriteFlashByte(addr, 0xff); + gbSmartWriteFlashByte(addr, 0x00); // BCH should be 0x00 + + // fill page buffer + for (int d = 0; d < 256; d++) + gbSmartWriteFlashByte(d, sdBuffer[d]); + + // start flashing page + gbSmartWriteFlashByte(addr, 0x0c); + gbSmartWriteFlashByte(addr, 0xff); + gbSmartWriteFlashByte(addr + i, 0x00); // BCH should be 0x00 + + // waiting for finishing + dataIn(); + while ((readByte_GBS(addr + i) & 0x80) == 0x00); + } + + // blink LED + blinkLED(); +} + +uint32_t gbSmartVerifyFlash() +{ + uint32_t verified = 0; + + if (!myFile.open(filePath, O_READ)) + { + verified = 0xffffffff; + print_Error(F("Can't open file on SD"), false); + } + else + { + // remaps mmc to full access + gbSmartRemapStartBank(0x00, gbSmartRomSizeGB, gbSmartSramSizeGB); + + // verify bank 0x00 + dataIn(); + for (uint16_t addr = 0x0000; addr <= 0x3fff; addr += 512) + { + myFile.read(sdBuffer, 512); + + for (uint16_t c = 0; c < 512; c++) + { + if (readByte_GBS(addr + c) != sdBuffer[c]) + verified++; + } + } + + // verify rest banks + for (uint16_t bank = 0x01; bank < gbSmartBanks; bank++) + { + dataOut(); + writeByte_GB(0x2100, bank); + + dataIn(); + for (uint16_t addr = 0x4000; addr <= 0x7fff; addr += 512) + { + myFile.read(sdBuffer, 512); + + for (uint16_t c = 0; c < 512; c++) + { + if (readByte_GBS(addr + c) != sdBuffer[c]) + verified++; + } + } + } + + // back to initial state + writeByte_GB(0x2100, 0x01); + + myFile.close(); + } + + return verified; +} + +byte gbSmartBlankCheckingFlash(uint8_t flash_start_bank) +{ + gbSmartRemapStartBank(flash_start_bank, gbSmartFlashSizeGB, gbSmartSramSizeGB); + + // check first bank + dataIn(); + for (uint16_t addr = 0x0000; addr <= 0x3fff; addr++) + { + if (readByte_GBS(addr) != 0xff) + return 0; + } + + // check rest banks + for (uint16_t bank = 0x01; bank < gbSmartBanksPerFlashChip; bank++) + { + dataOut(); + writeByte_GB(0x2100, bank); + + dataIn(); + for (uint16_t addr = 0x4000; addr <= 0x7fff; addr++) + { + if (readByte_GBS(addr) != 0xff) + return 0; + } + } + + return 1; +} + +void gbSmartResetFlash(uint8_t flash_start_bank) +{ + gbSmartRemapStartBank(flash_start_bank, gbSmartFlashSizeGB, gbSmartSramSizeGB); + + dataOut(); + gbSmartWriteFlashByte(0x0, 0xff); +} + +void gbSmartEraseFlash(uint8_t flash_start_bank) +{ + gbSmartRemapStartBank(flash_start_bank, gbSmartFlashSizeGB, gbSmartSramSizeGB); + + // handling first flash block + dataOut(); + gbSmartWriteFlashByte(0x0000, 0x20); + gbSmartWriteFlashByte(0x0000, 0xd0); + + dataIn(); + while ((readByte_GBS(0x0000) & 0x80) == 0x00); + + // blink LED + blinkLED(); + + // rest of flash block + for (uint32_t ba = gbSmartBanksPerFlashBlock; ba < gbSmartBanksPerFlashChip; ba += gbSmartBanksPerFlashBlock) + { + dataOut(); + writeByte_GB(0x2100, ba); + + gbSmartWriteFlashByte(0x4000, 0x20); + gbSmartWriteFlashByte(0x4000, 0xd0); + + dataIn(); + while ((readByte_GBS(0x4000) & 0x80) == 0x00); + + // blink LED + blinkLED(); + } +} + +void gbSmartWriteFlashByte(uint32_t myAddress, uint8_t myData) +{ + PORTF = myAddress & 0xff; + PORTK = (myAddress >> 8) & 0xff; + PORTC = myData; + + // wait for 62.5 x 4 = 250ns + __asm__("nop\n\tnop\n\tnop\n\tnop\n\t"); + + // Pull FLASH_WE (PH4) low + PORTH &= ~(1 << 4); + + // pull low for another 250ns + __asm__("nop\n\tnop\n\tnop\n\tnop\n\t"); + + // Pull FLASH_WE (PH4) high + PORTH |= (1 << 4); + + // pull high for another 250ns + __asm__("nop\n\tnop\n\tnop\n\tnop\n\t"); +} + +// rom_start_bank = 0x00 means back to original state +void gbSmartRemapStartBank(uint8_t rom_start_bank, uint8_t rom_size, uint8_t sram_size) +{ + rom_start_bank &= 0xfe; + + dataOut(); + + // clear base bank setting + writeByte_GB(0x1000, 0xa5); + writeByte_GB(0x7000, 0x00); + writeByte_GB(0x1000, 0x98); + writeByte_GB(0x2000, rom_start_bank); + + if (rom_start_bank > 1) + { + // start set new base bank + writeByte_GB(0x1000, 0xa5); + + dataIn(); + rom_start_bank = gbSmartGetResizeParam(rom_size, sram_size); + + dataOut(); + writeByte_GB(0x7000, rom_start_bank); + writeByte_GB(0x1000, 0x98); + + writeByte_GB(0x2100, 0x01); + } + + dataIn(); +} + +// Get magic number for 0x7000 register. +// Use for setting correct rom and sram size +// Code logic is take from SmartCard32M V1.3 menu code, +// see 0x2db2 to 0x2e51 (0xa0 bytes) +uint8_t gbSmartGetResizeParam(uint8_t rom_size, uint8_t sram_size) +{ + if (rom_size < 0x0f) + { + rom_size &= 0x07; + rom_size ^= 0x07; + } + else + { + rom_size = 0x01; + } + + if (sram_size > 0) + { + if (sram_size > 1) + { + sram_size--; + sram_size ^= 0x03; + sram_size <<= 4; + sram_size &= 0x30; + } + else + { + sram_size = 0x20; // 2KiB treat as 8KiB + } + } + else + { + sram_size = 0x30; // no sram + } + + return (sram_size | rom_size); +} + +#endif diff --git a/Cart_Reader/INTV.ino b/Cart_Reader/INTV.ino new file mode 100644 index 0000000..3a31eb3 --- /dev/null +++ b/Cart_Reader/INTV.ino @@ -0,0 +1,1018 @@ +//****************************************** +// INTELLIVISION MODULE +//****************************************** +#ifdef enable_INTV + +// Mattel Intellivision +// Cartridge Pinout +// 44P 2.54mm pitch connector +// +// TOP SIDE BOTTOM SIDE +// (EVEN) (ODD) +// +-------+ +// GND -| 2 1 |- GND +// CBLNK -| 4 3 |- /MSYNC +// EXT AUD -| 6 5 |- DB7 +// EXT VID -| 8 7 |- DB8 +// MCLK -| 10 9 |- DB6 +// RESET -| 12 11 |- DB9 +// SR1 -| 14 13 |- DB5 +// VOICE -| 16 15 |- DB10 +// VOICE -| 18 17 |- DB4 +// GND -| 20 19 |- DB11 +// GND -| 22 21 |- DB3 +// GND -| 24 23 |- DB12 +// GND -| 26 25 |- DB13 +// GND -| 28 27 |- DB2 +// /BUSAK -| 30 29 |- DB14 +// BC1 -| 32 31 |- DB1 +// BC2 -| 34 33 |- DB0 +// BDIR -| 36 35 |- DB15 +// BDIR -| 38 37 |- BDIR +// BC2 -| 40 39 |- BC2 +// BC1 -| 42 41 |- BC1 +// GND -| 44 43 |- VCC(+5V) +// +-------+ + +// CONTROL PINS: +// /MSYNC(PH3)- PIN 3 [ROM PIN 14] +// BC1(PH4) - PIN 41 [ROM PIN 28] +// BC2(PH5) - PIN 39 [ROM PIN 27] +// BDIR(PH6) - PIN 37 [ROM PIN 26] +// NOTE: BDIR/BC1/BC2 ONLY CONNECTED TO BOTTOM (ODD) PINS + +// NOT CONNECTED: +// RESET(PH0) - N/C - GND IN CART +// MCLK(PH1) - N/C - GND IN CART + +// BUS PROTOCOL COMMANDS - BDIR/BC1/BC2 +// NO ACTION (NACT): 0/0/0 +// ADDR TO REG (ADAR): 0/1/0 +// INT ADDR TO BUS (IAB): 0/0/1 +// DATA TO BUS (DTB): 0/1/1 +// BUS TO ADDR (BAR): 1/0/0 +// DATA WRITE (DW): 1/1/0 +// DATA WRITE STROBE (DWS): 1/0/1 +// INTERRUPT ACK (INTAK): 1/1/1 + +// Cart Configurations +// Format = {mapper,romlo,romhi,ramsize} +static const byte PROGMEM intvmapsize [] = { + 0, 0, 2, 0, // default mattel up to 32K (8K/16K/24K/32K) + 1, 1, 3, 0, // demo cart 16K, championship tennis 32K, wsml baseball 48K + 2, 1, 3, 0, // up to 48K (16K/32K/48K) + 3, 4, 4, 0, // tower of doom 48K + 4, 0, 1, 1, // uscf chess 16K + RAM 1K + 5, 2, 3, 0, // congo bongo/defender/pac-man 24K, dig dug 32K + 6, 1, 1, 0, // centipede 16K + 7, 1, 1, 0, // imagic carts 16K + 8, 1, 1, 0, // mte-201 test cart 16K + 9, 3, 3, 2, // triple challenge 32K + RAM 2K +}; + +byte intvmapcount = 10; // (sizeof(mapsize)/sizeof(mapsize[0])) / 4; +boolean intvmapfound = false; +byte intvmapselect; +int intvindex; + +byte INTV[] = {8, 16, 24, 32, 48}; +byte intvlo = 0; // Lowest Entry +byte intvhi = 4; // Highest Entry + +byte intvmapper; +byte newintvmapper; +byte intvsize; +byte newintvsize; + +// EEPROM MAPPING +// 07 MAPPER +// 08 ROM SIZE + +//****************************************** +// Menu +//****************************************** +// Base Menu +static const char intvMenuItem1[] PROGMEM = "Select Cart"; +static const char intvMenuItem2[] PROGMEM = "Read ROM"; +static const char intvMenuItem3[] PROGMEM = "Set Mapper + Size"; +static const char intvMenuItem4[] PROGMEM = "Reset"; +static const char* const menuOptionsINTV[] PROGMEM = {intvMenuItem1, intvMenuItem2, intvMenuItem3, intvMenuItem4}; + +void setup_INTV() +{ + // Set Address Pins to Output (UNUSED) + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output + // ---(PH0) ---(PH1) /MSYNC(PH3) BC1(PH4) BC2(PH5) BDIR(PH6) + DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Set TIME(PJ0) to Output (UNUSED) + DDRJ |= (1 << 0); + + // Set Pins (DB0-DB15) to Input + DDRC = 0x00; + DDRA = 0x00; + + // Setting Control Pins to HIGH + // ---(PH0) ---(PH1) /MSYNC(PH3) BC1(PH4) BC2(PH5) BDIR(PH6) + PORTH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Set Unused Pins HIGH + PORTF = 0xFF; // A0-A7 + PORTK = 0xFF; // A8-A15 + PORTL = 0xFF; // A16-A23 + PORTJ |= (1 << 0); // TIME(PJ0) + + checkStatus_INTV(); + strcpy(romName, "INTV"); + + mode = mode_INTV; +} + +void intvMenu() +{ + convertPgm(menuOptionsINTV, 4); + uint8_t mainMenu = question_box(F("INTELLIVISION MENU"), menuOptions, 4, 0); + + switch (mainMenu) + { + case 0: + // Select Cart + setCart_INTV(); + wait(); + setup_INTV(); + break; + + case 1: + // Read ROM + sd.chdir("/"); + readROM_INTV(); + sd.chdir("/"); + break; + + case 2: + // Set Mapper + setMapper_INTV(); + checkMapperSize_INTV(); + setROMSize_INTV(); + break; + + case 3: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// INTELLIVISION BUS PROTOCOL COMMANDS +//****************************************** +// CONTROL PINS - BDIR/BC1/BC2 +// NO ACTION (NACT): 0/0/0 +// ADDR TO REG (ADAR): 0/1/0 [NOTE: ORDER BDIR/BC1/BC2] +// DATA TO BUS (DTB): 0/1/1 +// BUS TO ADDR (BAR): 1/0/0 +// DATA WRITE (DW): 1/1/0 [NOTE: ORDER BDIR/BC1/BC2] +// DATA WRITE STROBE (DWS): 1/0/1 [NOTE: ORDER BDIR/BC1/BC2] + +// IMMEDIATE MODE DATA READ SEQUENCE: BAR-NACT-DTB-NACT +// IMMEDIATE MODE DATA WRITE SEQUENCE: BAR-NACT-DW-DWS-NACT +// DIRECT ADDRESSING MODE READ SEQUENCE: BAR-NACT-ADAR-NACT-DTB-NACT + +// NO ACTION (NACT) - 0/0/0 +void NACT_INT() +{ + // Switch BC1(PH4) + BC2(PH5) + BDIR(PH6) to LOW + PORTH &= ~(1 << 4) & ~(1 << 5) & ~(1 << 6); + // DB0..DB15 INPUT + DDRC = 0x00; + DDRA = 0x00; +} + +// SET ADDRESS - BUS TO ADDR (BAR) - 1/0/0 +void BAR_INT() +{ + // Switch BDIR(PH6) to HIGH + PORTH |= (1 << 6); + // Switch BC1(PH4) + BC2(PH5) to LOW + PORTH &= ~(1 << 4) & ~(1 << 5); + // Address OUT + DDRC = 0xFF; + DDRA = 0xFF; +} + +// READ DATA - DATA TO BUS (DTB) - 0/1/1 +void DTB_INT() +{ + // Switch BDIR(PH6) to LOW + PORTH &= ~(1 << 6); + // Switch BC1(PH4) + BC2(PH5) to HIGH + PORTH |= (1 << 4) | (1 << 5); + // Data IN + DDRC = 0x00; + DDRA = 0x00; +} + +// ADDRESS DATA TO ADDRESS REGISTER (ADAR) - 0/1/0 +void ADAR_INT() +{ + // Switch BC2(PH5) + BDIR(PH6) to LOW + PORTH &= ~(1 << 5) & ~(1 << 6); + // Switch BC1(PH4) to HIGH + PORTH |= (1 << 4); +} + +// DATA WRITE PAIRED COMMAND - DW + DWS +// DATA SHOULD BE STABLE ACROSS BOTH + +// DATA WRITE (DW) - 1/1/0 +void DW_INT() +{ + // Switch BC1(PH4) + BDIR(PH6) to HIGH + PORTH |= (1 << 4) | (1 << 6); + // Switch BC2(PH5) to LOW + PORTH &= ~(1 << 5); +} + +// DATA WRITE STROBE (DWS) - 1/0/1 +void DWS_INT() +{ + // Switch BC2(PH5) + BDIR(PH6) to HIGH + PORTH |= (1 << 5) | (1 << 6); + // Switch BC1(PH4) to LOW + PORTH &= ~(1 << 4); +} + +//****************************************** +// READ CODE +//****************************************** + +uint16_t readData_INTV(uint32_t addr) +{ + PORTC = addr & 0xFF; + PORTA = (addr >> 8) & 0xFF; + + BAR_INT(); + // Wait for bus + // 5 x 62.5ns = 312.5ns + NOP; NOP; NOP; NOP; NOP; + + NACT_INT(); + NOP; NOP; NOP; NOP; NOP; + + DTB_INT(); + NOP; NOP; NOP; NOP; NOP; + uint16_t ret = (((PINA & 0xFF) << 8) | (PINC & 0xFF)); + + NACT_INT(); + NOP; NOP; NOP; NOP; NOP; + + return ret; +} + +// MAPPER ROM ADDRESSES +// 0: 0x50-0x70,0xD0-0xE0,0xF0-0x100, // default mattel up to 32K (8K/16K/24K/32K) +// 1: 0x50-0x70,0xD0-0x100, // demo cart 16K, championship tennis 32K, wsml baseball 48K +// 2: 0x50-0x70,0x90-0xC0,0xD0-0xE0, // up to 48K (16K/32K/48K) +// 3: 0x50-0x70,0x90-0xB0,0xD0-0xE0,0xF0-0x100, // tower of doom 48K +// 4: 0x50-0x70, // uscf chess 16K + RAM 1K +// 5: 0x50-0x80,0x90-0xC0, // congo bongo/defender/pac-man 24K, dig dug 32K +// 6: 0x60-0x80, // centipede 16K +// 7: 0x48-0x68, // imagic carts 16K +// 8: 0x50-0x60,0x70-0x80, // mte-201 test cart 16K +// 9: 0x50-0x70,0x90-0xB0,[0xC0-0xC8,0xD0-0xD8] // triple challenge 32K + RAM 2K [0xC0 + 0xD0 segments are not needed] + +void readSegment_INTV(uint32_t startaddr, uint32_t endaddr) +{ + for (uint32_t addr = startaddr; addr < endaddr; addr += 256) { + for (uint16_t w = 0; w < 256; w++) { + uint16_t temp = readData_INTV(addr + w); + sdBuffer[w * 2] = (temp >> 8) & 0xFF; + sdBuffer[(w * 2) + 1] = temp & 0xFF; + } + myFile.write(sdBuffer, 512); + } +} + +// MODIFIED READ ROUTINE FOR ALL 10 MAPPERS +void readROM_INTV() +{ + strcpy(fileName, romName); + strcat(fileName, ".int"); + + // create a new folder for storing rom file + EEPROM_readAnything(0, foldern); + sprintf(folder, "INTV/ROM/%d", foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // open file on sdcard + if (!myFile.open(fileName, O_RDWR | O_CREAT)) + print_Error(F("Can't create file on SD"), true); + + // write new folder number back to EEPROM + foldern++; + EEPROM_writeAnything(0, foldern); + + switch (intvmapper) { + case 0: //default mattel up to 32K (8K/16K/24K/32K) + readSegment_INTV(0x5000, 0x6000); // 8K + if (intvsize > 0) { + readSegment_INTV(0x6000, 0x7000); // +8K = 16K + if (intvsize > 1) { + readSegment_INTV(0xD000, 0xE000); // +8K = 24K + if (intvsize > 2) + readSegment_INTV(0xF000, 0x10000); // +8K = 32K + } + } + break; + + case 1: // demo cart/championship tennis/wsml baseball + readSegment_INTV(0x5000, 0x7000); // 16K Demo Cart + if (intvsize > 1) { + readSegment_INTV(0xD000, 0xE000); // +8K = 24K [NONE] + if (intvsize > 2) { + readSegment_INTV(0xE000, 0xF000); // +8K = 32K Championship Tennis + if (intvsize > 3) { + readSegment_INTV(0xF000, 0x10000); // +8K = 40K WSML Baseball [MISSING 8K ECS BANK] + // ecs bank switch + ecsBank(0xFFFF, 0x1); // switch ecs page 1 to 0xF000 + readSegment_INTV(0xF000, 0x10000); // + 8K = 48K WSML Baseball + ecsBank(0xFFFF, 0x0); // reset ecs page 0 to 0xF000 + } + } + } + break; + + case 2: // up to 48K (16K/32K/48K) + readSegment_INTV(0x5000, 0x7000); // 16K + if (intvsize > 1) { + readSegment_INTV(0x9000, 0xA000); // +8K = 24K [NONE] + if (intvsize > 2) { + readSegment_INTV(0xA000, 0xB000); // +8K = 32K + if (intvsize > 3) { + readSegment_INTV(0xB000, 0xC000); // +8K = 40K + readSegment_INTV(0xD000, 0xE000); // +8K = 48K + } + } + } + break; + + case 3: // tower of doom 48K + readSegment_INTV(0x5000, 0x7000); // 16K + readSegment_INTV(0x9000, 0xB000); // +16K = 32K + readSegment_INTV(0xD000, 0xE000); // +8K = 40K + readSegment_INTV(0xF000, 0x10000); // +8K = 48K + break; + + case 4: // chess 16K + PORTH &= ~(1 << 3); // /MSYNC to LOW + readSegment_INTV(0x5000, 0x6000); // 8K + PORTH |= (1 << 3); // /MSYNC to HIGH + readSegment_INTV(0x6000, 0x7000); // 8K + break; + + case 5:// congo bongo/defender/pac-man/dig dug + readSegment_INTV(0x5000, 0x7000); // 16K + readSegment_INTV(0x7000, 0x8000); // +8K = 24K Congo Bongo/Defender/Pac-Man + if (intvsize > 2) { + readSegment_INTV(0x9000, 0xA000); // +8K = 32K Dig Dug + //readSegment_INTV(0xA000,0xC000); // +16K = 48K [UNUSED] + } + break; + + case 6: // centipede 16K + readSegment_INTV(0x6000, 0x8000); // 16K + break; + + case 7: // imagic carts 16K + readSegment_INTV(0x4800, 0x6800); // 16K + break; + + case 8: //mte-201 test cart 16K + readSegment_INTV(0x5000, 0x6000); // 8K + readSegment_INTV(0x7000, 0x8000); // +8K = 16K + break; + + case 9: // triple challenge 32K [KNOWN ROM 44K BAD!] + readSegment_INTV(0x5000, 0x7000); // 16K + readSegment_INTV(0x9000, 0xB000); // +16K = 32K + // 0xC000 + 0xD000 SEGMENTS ARE NOT NEEDED (PER INTVNUT POST) + // readSegment_INTV(0xC000,0xC800); // +4K = 36K + // readSegment_INTV(0xD000,0xE000); // +8K = 44K + break; + } + myFile.close(); + + // Compare CRC32 to database and rename ROM if found + compareCRC("intv.txt", 0, 0); + + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +// ECS BANKSWITCH FOR WSML BASEBALL +// write $xA5y to $xFFF +// x = rom location ($x000 - $xFFF) +// y = page (up to 16 - WSML Baseball only uses 0/1) +void ecsBank(uint32_t addr, uint8_t bank) { + uint16_t ecsdata = (addr & 0xF000) + 0x0A50 + bank; // $xA5y + + // Data OUT + DDRA = 0xFF; + DDRC = 0xFF; + + // Set Address + PORTA = (addr >> 8) & 0xFF; + PORTC = addr & 0xFF; + + BAR_INT(); + NOP; NOP; NOP; NOP; NOP; + NACT_INT(); + NOP; + + // Data OUT + DDRA = 0xFF; + DDRC = 0xFF; + PORTA = (ecsdata >> 8) & 0xFF; // $xA + PORTC = ecsdata & 0xFF; // $5y + + DW_INT(); + NOP; NOP; NOP; NOP; NOP; + DWS_INT(); + NOP; NOP; NOP; NOP; NOP; + NACT_INT(); + NOP; NOP; NOP; NOP; NOP; +} + +//****************************************** +// MAPPER CODE +//****************************************** + +void setMapper_INTV() +{ +#if (defined(enable_OLED) || defined(enable_LCD)) + int b = 0; + int i = 0; + // Check Button Status +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + boolean buttonVal1 = (PING & (1 << 2)); // PG2 +#endif + if (buttonVal1 == LOW) { // Button Pressed + while (1) { // Scroll Mapper List +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + boolean buttonVal1 = (PING & (1 << 2)); // PG2 +#endif + if (buttonVal1 == HIGH) { // Button Released + // Correct Overshoot + if (i == 0) + i = intvmapcount - 1; + else + i--; + break; + } + display_Clear(); + print_Msg(F("Mapper: ")); + intvindex = i * 4; + intvmapselect = pgm_read_byte(intvmapsize + intvindex); + println_Msg(intvmapselect); + display_Update(); + if (i == (intvmapcount - 1)) + i = 0; + else + i++; + delay(250); + } + } + + display_Clear(); + print_Msg(F("Mapper: ")); + intvindex = i * 4; + intvmapselect = pgm_read_byte(intvmapsize + intvindex); + println_Msg(intvmapselect); + println_Msg(F("")); +#if defined(enable_OLED) + println_Msg(F("Press left to Change")); + println_Msg(F("and right to Select")); +#elif defined(enable_LCD) + println_Msg(F("Rotate to Change")); + println_Msg(F("Press to Select")); +#endif + display_Update(); + + while (1) { + b = checkButton(); + + if (b == 2) { // Previous Mapper (doubleclick) + if (i == 0) + i = intvmapcount - 1; + else + i--; + + // Only update display after input because of slow LCD library + display_Clear(); + print_Msg(F("Mapper: ")); + intvindex = i * 4; + intvmapselect = pgm_read_byte(intvmapsize + intvindex); + println_Msg(intvmapselect); + println_Msg(F("")); +#if defined(enable_OLED) + println_Msg(F("Press left to Change")); + println_Msg(F("and right to Select")); +#elif defined(enable_LCD) + println_Msg(F("Rotate to Change")); + println_Msg(F("Press to Select")); +#endif + display_Update(); + } + if (b == 1) { // Next Mapper (press) + if (i == (intvmapcount - 1)) + i = 0; + else + i++; + + // Only update display after input because of slow LCD library + display_Clear(); + print_Msg(F("Mapper: ")); + intvindex = i * 4; + intvmapselect = pgm_read_byte(intvmapsize + intvindex); + println_Msg(intvmapselect); + println_Msg(F("")); +#if defined(enable_OLED) + println_Msg(F("Press left to Change")); + println_Msg(F("and right to Select")); +#elif defined(enable_LCD) + println_Msg(F("Rotate to Change")); + println_Msg(F("Press to Select")); +#endif + display_Update(); + } + if (b == 3) { // Long Press - Execute (hold) + newintvmapper = intvmapselect; + break; + } + } + display.setCursor(0, 56); + print_Msg(F("MAPPER ")); + print_Msg(newintvmapper); + println_Msg(F(" SELECTED")); + display_Update(); + delay(1000); +#else +setmapper: + String newmap; + intvmapfound = false; + Serial.print(F("Enter Mapper [0-9]: ")); + while (Serial.available() == 0) {} + newmap = Serial.readStringUntil('\n'); + Serial.println(newmap); + newintvmapper = newmap.toInt(); + for (int i = 0; i < intvmapcount; i++) { + intvindex = i * 4; + intvmapselect = pgm_read_byte(intvmapsize + intvindex); + if (newintvmapper == intvmapselect) + intvmapfound = true; + } + if (intvmapfound == false) { + Serial.println(F("MAPPER NOT SUPPORTED!")); + Serial.println(F("")); + newintvmapper = 0; + goto setmapper; + } +#endif + EEPROM_writeAnything(7, newintvmapper); + intvmapper = newintvmapper; +} + +void checkMapperSize_INTV() { + for (int i = 0; i < intvmapcount; i++) { + intvindex = i * 4; + byte mapcheck = pgm_read_byte(intvmapsize + intvindex); + if (mapcheck == intvmapper) { + intvlo = pgm_read_byte(intvmapsize + intvindex + 1); + intvhi = pgm_read_byte(intvmapsize + intvindex + 2); + break; + } + } +} + +void setROMSize_INTV() +{ +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + if (intvlo == intvhi) + newintvsize = intvlo; + else { + int b = 0; + int i = intvlo; + + // Only update display after input because of slow LCD library + display_Clear(); + print_Msg(F("ROM Size: ")); + println_Msg(INTV[i]); + println_Msg(F("")); +#if defined(enable_OLED) + println_Msg(F("Press left to Change")); + println_Msg(F("and right to Select")); +#elif defined(enable_LCD) + println_Msg(F("Rotate to Change")); + println_Msg(F("Press to Select")); +#endif + display_Update(); + + while (1) { + b = checkButton(); + if (b == 2) { // Previous (doubleclick) + if (i == intvlo) + i = intvhi; + else + i--; + + // Only update display after input because of slow LCD library + display_Clear(); + print_Msg(F("ROM Size: ")); + println_Msg(INTV[i]); + println_Msg(F("")); +#if defined(enable_OLED) + println_Msg(F("Press left to Change")); + println_Msg(F("and right to Select")); +#elif defined(enable_LCD) + println_Msg(F("Rotate to Change")); + println_Msg(F("Press to Select")); +#endif + display_Update(); + } + if (b == 1) { // Next (press) + if (i == intvhi) + i = intvlo; + else + i++; + + display_Clear(); + print_Msg(F("ROM Size: ")); + println_Msg(INTV[i]); + println_Msg(F("")); +#if defined(enable_OLED) + println_Msg(F("Press left to Change")); + println_Msg(F("and right to Select")); +#elif defined(enable_LCD) + println_Msg(F("Rotate to Change")); + println_Msg(F("Press to Select")); +#endif + display_Update(); + } + if (b == 3) { // Long Press - Execute (hold) + newintvsize = i; + break; + } + } + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(F("ROM SIZE ")); + print_Msg(INTV[newintvsize]); + println_Msg(F("K")); + display_Update(); + delay(1000); +#else + if (intvlo == intvhi) + newintvsize = intvlo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (intvhi - intvlo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(INTV[i + intvlo]); + Serial.println(F("K")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newintvsize = sizeROM.toInt() + intvlo; + if (newintvsize > intvhi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(F("")); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(INTV[newintvsize]); + Serial.println(F("K")); +#endif + EEPROM_writeAnything(8, newintvsize); + intvsize = newintvsize; +} + +void checkStatus_INTV() +{ + EEPROM_readAnything(7, intvmapper); + EEPROM_readAnything(8, intvsize); + if (intvmapper > 9) { + intvmapper = 0; + EEPROM_writeAnything(7, intvmapper); + } + if (intvsize > 4) { + intvsize = 0; + EEPROM_writeAnything(8, intvsize); + } + +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("INTELLIVISION READER")); + println_Msg(F("CURRENT SETTINGS")); + println_Msg(F("")); + print_Msg(F("MAPPER: ")); + println_Msg(intvmapper); + print_Msg(F("ROM SIZE: ")); + print_Msg(INTV[intvsize]); + println_Msg(F("K")); + display_Update(); + wait(); +#else + Serial.print(F("CURRENT MAPPER: ")); + Serial.println(intvmapper); + Serial.print(F("CURRENT ROM SIZE: ")); + Serial.print(INTV[intvsize]); + Serial.println(F("K")); + Serial.println(F("")); +#endif +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +FsFile intvcsvFile; +char intvgame[40]; // title +char intvmm[3]; // mapper +char intvrr[3]; // romsize +char intvss[3]; // ramsize +char intvll[4]; // linelength (previous line) +unsigned long intvcsvpos; // CSV File Position +char intvcartCSV[] = "intvcart.txt"; // CSV List +char intvcsvEND[] = "EOF"; // CSV End Marker for scrolling + +bool readLine_INTV(FsFile &f, char* line, size_t maxLen) +{ + for (size_t n = 0; n < maxLen; n++) { + int c = f.read(); + if ( c < 0 && n == 0) return false; // EOF + if (c < 0 || c == '\n') { + line[n] = 0; + return true; + } + line[n] = c; + } + return false; // line too long +} + +bool readVals_INTV(char* intvgame, char* intvmm, char* intvrr, char* intvss, char* intvll) +{ + char line[52]; + intvcsvpos = intvcsvFile.position(); + if (!readLine_INTV(intvcsvFile, line, sizeof(line))) { + return false; // EOF or too long + } + char* comma = strtok(line, ","); + int x = 0; + while (comma != NULL) { + if (x == 0) + strcpy(intvgame, comma); + else if (x == 1) + strcpy(intvmm, comma); + else if (x == 2) + strcpy(intvrr, comma); + else if (x == 3) + strcpy(intvss, comma); + else if (x == 4) + strcpy(intvll, comma); + comma = strtok(NULL, ","); + x += 1; + } + return true; +} + +bool getCartListInfo_INTV() +{ + bool buttonreleased = 0; + bool cartselected = 0; +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F(" HOLD TO FAST CYCLE")); + display_Update(); +#else + Serial.println(F("HOLD BUTTON TO FAST CYCLE")); +#endif + delay(2000); +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + boolean buttonVal1 = (PING & (1 << 2)); // PG2 +#endif + if (buttonVal1 == LOW) { // Button Held - Fast Cycle + while (1) { // Scroll Game List + while (readVals_INTV(intvgame, intvmm, intvrr, intvss, intvll)) { + if (strcmp(intvcsvEND, intvgame) == 0) { + intvcsvFile.seek(0); // Restart + } + else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART TITLE:")); + println_Msg(F("")); + println_Msg(intvgame); + display_Update(); +#else + Serial.print(F("CART TITLE:")); + Serial.println(intvgame); +#endif +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + boolean buttonVal1 = (PING & (1 << 2)); // PG2 +#endif + if (buttonVal1 == HIGH) { // Button Released + buttonreleased = 1; + break; + } + if (buttonreleased) { + buttonreleased = 0; // Reset Flag + break; + } + } + } +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + boolean buttonVal1 = (PING & (1 << 2)); // PG2 +#endif + if (buttonVal1 == HIGH) // Button Released + break; + } + } +#if (defined(enable_OLED) || defined(enable_LCD)) + display.setCursor(0, 56); + println_Msg(F("FAST CYCLE OFF")); + display_Update(); +#else + Serial.println(F("")); + Serial.println(F("FAST CYCLE OFF")); + Serial.println(F("PRESS BUTTON TO STEP FORWARD")); + Serial.println(F("DOUBLE CLICK TO STEP BACK")); + Serial.println(F("HOLD TO SELECT")); + Serial.println(F("")); +#endif + while (readVals_INTV(intvgame, intvmm, intvrr, intvss, intvll)) { + if (strcmp(intvcsvEND, intvgame) == 0) { + intvcsvFile.seek(0); // Restart + } + else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART TITLE:")); + println_Msg(F("")); + println_Msg(intvgame); + display.setCursor(0, 48); +#if defined(enable_OLED) + println_Msg(F("Press left to Change")); + println_Msg(F("and right to Select")); +#elif defined(enable_LCD) + println_Msg(F("Rotate to Change")); + println_Msg(F("Press to Select")); +#endif + display_Update(); +#else + Serial.print(F("CART TITLE:")); + Serial.println(intvgame); +#endif + while (1) { // Single Step + int b = checkButton(); + if (b == 1) { // Continue (press) + break; + } + if (b == 2) { // Reset to Start of List (doubleclick) + byte prevline = strtol(intvll, NULL, 10); + intvcsvpos -= prevline; + intvcsvFile.seek(intvcsvpos); + break; + } + if (b == 3) { // Long Press - Select Cart (hold) + newintvmapper = strtol(intvmm, NULL, 10); + newintvsize = strtol(intvrr, NULL, 10); + EEPROM_writeAnything(7, newintvmapper); + EEPROM_writeAnything(8, newintvsize); + cartselected = 1; // SELECTION MADE +#if (defined(enable_OLED) || defined(enable_LCD)) + println_Msg(F("SELECTION MADE")); + display_Update(); +#else + Serial.println(F("SELECTION MADE")); +#endif + break; + } + } + if (cartselected) { + cartselected = 0; // Reset Flag + return true; + } + } + } +#if (defined(enable_OLED) || defined(enable_LCD)) + println_Msg(F("")); + println_Msg(F("END OF FILE")); + display_Update(); +#else + Serial.println(F("END OF FILE")); +#endif + + return false; +} + +void checkCSV_INTV() +{ + if (getCartListInfo_INTV()) { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART SELECTED")); + println_Msg(F("")); + println_Msg(intvgame); + display_Update(); + // Display Settings + display.setCursor(0, 56); + print_Msg(F("CODE: M")); + print_Msg(newintvmapper); + print_Msg(F("/R")); + println_Msg(newintvsize); + display_Update(); +#else + Serial.println(F("")); + Serial.println(F("CART SELECTED")); + Serial.println(intvgame); + // Display Settings + Serial.print(F("CODE: M")); + Serial.print(newintvmapper); + Serial.print(F("/R")); + Serial.println(newintvsize); + Serial.println(F("")); +#endif + } + else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display.setCursor(0, 56); + println_Msg(F("NO SELECTION")); + display_Update(); +#else + Serial.println(F("NO SELECTION")); +#endif + } +} + +void setCart_INTV() +{ +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(intvcartCSV); + display_Update(); +#endif + sd.chdir(); + sprintf(folder, "INTV/CSV"); + sd.chdir(folder); // Switch Folder + intvcsvFile = sd.open(intvcartCSV, O_READ); + if (!intvcsvFile) { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CSV FILE NOT FOUND!")); + display_Update(); +#else + Serial.println(F("CSV FILE NOT FOUND!")); +#endif + while (1) { + if (checkButton() != 0) + setup_INTV(); + } + } + checkCSV_INTV(); + + intvcsvFile.close(); +} +#endif diff --git a/Cart_Reader/N64.ino b/Cart_Reader/N64.ino index b8cdad8..e8616a3 100644 --- a/Cart_Reader/N64.ino +++ b/Cart_Reader/N64.ino @@ -4759,7 +4759,10 @@ void flashGameshark_N64() { if (writeErrors == 0) { println_Msg(F("OK")); + println_Msg(F("")); + println_Msg(F("Turn Cart Reader off now")); display_Update(); + while (1); } else { print_Msg(writeErrors); diff --git a/Cart_Reader/NES.ino b/Cart_Reader/NES.ino index 7a6b8d0..cd6e373 100644 --- a/Cart_Reader/NES.ino +++ b/Cart_Reader/NES.ino @@ -55,6 +55,7 @@ static const byte PROGMEM mapsize [] = { 37, 4, 4, 6, 6, 0, 0, // (super mario bros + tetris + world cup) 47, 4, 4, 6, 6, 0, 0, // (super spike vball + world cup) 48, 3, 4, 6, 6, 0, 0, // taito tc0690 + 64, 2, 3, 4, 5, 0, 0, // tengen rambo-1 65, 3, 4, 5, 6, 0, 0, // irem h-3001 66, 2, 3, 2, 3, 0, 0, // gxrom/mhrom 67, 3, 3, 5, 5, 0, 0, // sunsoft 3 @@ -89,6 +90,7 @@ static const byte PROGMEM mapsize [] = { 153, 5, 5, 0, 0, 1, 1, // (famicom jump ii) [sram r/w] 154, 3, 3, 5, 5, 0, 0, // namcot-3453 (devil man) 155, 3, 3, 3, 5, 0, 1, // mmc1 variant [sram r/w] + 158, 3, 3, 5, 5, 0, 0, // tengen rambo-1 variant (alien syndrome (u)) 159, 3, 4, 5, 6, 1, 1, // bandai x24c01 [eep r/w] 180, 3, 3, 0, 0, 0, 0, // unrom variant (crazy climber) 184, 1, 1, 2, 3, 0, 0, // sunsoft 1 @@ -2366,8 +2368,10 @@ void readPRG(boolean readrom) { case 4: case 47: + case 64: case 118: case 119: + case 158: banks = ((int_pow(2, prgsize) * 2)) - 2; // Set Number of Banks if (mapper == 47) write_prg_byte(0xA001, 0x80); // Block Register - PRG RAM Chip Enable, Writable @@ -2386,6 +2390,10 @@ void readPRG(boolean readrom) { dumpPRG(base, address); } } + if ((mapper == 64) || (mapper == 158)) { + write_prg_byte(0x8000, 15); // PRG Bank 2 ($C000-$DFFF) + write_prg_byte(0x8001, banks); + } for (word address = 0x4000; address < 0x8000; address += 512) { // Final 2 Banks ($C000-$FFFF) dumpPRG(base, address); } @@ -2870,8 +2878,10 @@ void readCHR(boolean readrom) { case 4: case 47: + case 64: case 118: case 119: + case 158: banks = int_pow(2, chrsize) * 4; if (mapper == 47) write_prg_byte(0xA001, 0x80); // Block Register - PRG RAM Chip Enable, Writable @@ -4012,7 +4022,7 @@ void NESmaker_ResetFlash() { // Reset Flash write_prg_byte(0xC000, 0x00); write_prg_byte(0xAAAA, 0x55); write_prg_byte(0xC000, 0x01); - write_prg_byte(0x9555, 0xF0); // Reset + write_prg_byte(0x9555, 0xFF); // Reset } // SST 39SF040 Software ID @@ -4027,7 +4037,12 @@ void NESmaker_ID() { // Read Flash ID unsigned char ID1 = read_prg_byte(0x8000); unsigned char ID2 = read_prg_byte(0x8001); sprintf(flashid, "%02X%02X", ID1, ID2); - NESmaker_ResetFlash(); // Software ID Exit + write_prg_byte(0xC000, 0x01); + write_prg_byte(0x9555, 0xAA); + write_prg_byte(0xC000, 0x00); + write_prg_byte(0xAAAA, 0x55); + write_prg_byte(0xC000, 0x01); + write_prg_byte(0x9555, 0xF0); // Software ID Exit if (strcmp(flashid, "BFB7") == 0) // SST 39SF040 flashfound = 1; } @@ -4105,7 +4120,6 @@ void writeFLASH() { //open file on sd card if (myFile.open(filePath, O_READ)) { - myFile.seekSet(16); banks = int_pow(2, prgsize); // 256K/512K for (int i = 0; i < banks; i++) { // 16K Banks for (word sector = 0; sector < 0x4000; sector += 0x1000) { // 4K Sectors ($8000/$9000/$A000/$B000) diff --git a/Cart_Reader/NGP.ino b/Cart_Reader/NGP.ino index 5cfd212..fe9288b 100644 --- a/Cart_Reader/NGP.ino +++ b/Cart_Reader/NGP.ino @@ -241,7 +241,7 @@ void readROM_NGP(char *outPathBuf, size_t bufferSize) { progress += 512; draw_progressbar(progress, cartSize); } - + myFile.close(); } diff --git a/Cart_Reader/NP.ino b/Cart_Reader/SFM.ino similarity index 99% rename from Cart_Reader/NP.ino rename to Cart_Reader/SFM.ino index 4502089..3423163 100644 --- a/Cart_Reader/NP.ino +++ b/Cart_Reader/SFM.ino @@ -1,7 +1,7 @@ //****************************************** -// NINTENDO POWER SF MEMORY MODULE +// SF MEMORY MODULE //****************************************** -#ifdef enable_NP +#ifdef enable_SFM /****************************************** SF Memory Clock Source diff --git a/Cart_Reader/SNES.ino b/Cart_Reader/SNES.ino index 887583f..9864121 100644 --- a/Cart_Reader/SNES.ino +++ b/Cart_Reader/SNES.ino @@ -154,7 +154,7 @@ void snsMenu() { mode = mode_SNES; break; -#ifdef enable_NP +#ifdef enable_SFM case 1: display_Clear(); display_Update();