From d74ad957d824e6a4ad16d9d8c0dfc763e948b6d1 Mon Sep 17 00:00:00 2001 From: Roger Braunstein Date: Sun, 2 Jul 2023 16:08:09 -0700 Subject: [PATCH] Stubs out Loopy menu item --- Cart_Reader/Cart_Reader.ino | 33 ++- Cart_Reader/Config.h | 27 +- Cart_Reader/LOOPY.ino | 576 ++++++++++++++++++++++++++++++++++++ 3 files changed, 617 insertions(+), 19 deletions(-) create mode 100644 Cart_Reader/LOOPY.ino diff --git a/Cart_Reader/Cart_Reader.ino b/Cart_Reader/Cart_Reader.ino index 3968ccc..65d08c6 100644 --- a/Cart_Reader/Cart_Reader.ino +++ b/Cart_Reader/Cart_Reader.ino @@ -250,6 +250,7 @@ void print_STR(byte string_number, boolean newline) { #define mode_SUPRACAN 32 #define mode_MSX 33 #define mode_POKE 34 +#define mode_LOOPY 35 // optimization-safe nop delay #define NOP __asm__ __volatile__("nop\n\t") @@ -840,11 +841,12 @@ static const char modeItem18[] PROGMEM = "Fairchild Channel F"; static const char modeItem19[] PROGMEM = "Super A'can"; static const char modeItem20[] PROGMEM = "MSX"; static const char modeItem21[] PROGMEM = "Pokemon Mini (3V)"; -static const char modeItem22[] PROGMEM = "Flashrom Programmer"; -static const char modeItem23[] PROGMEM = "Self Test (3V)"; -static const char modeItem24[] PROGMEM = "About"; +static const char modeItem22[] PROGMEM = "Casio Loopy"; +static const char modeItem23[] PROGMEM = "Flashrom Programmer"; +static const char modeItem24[] PROGMEM = "Self Test (3V)"; +static const char modeItem25[] PROGMEM = "About"; //static const char modeItem25[] PROGMEM = "Reset"; (stored in common strings array) -static const char* const modeOptions[] PROGMEM = { modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7, modeItem8, modeItem9, modeItem10, modeItem11, modeItem12, modeItem13, modeItem14, modeItem15, modeItem16, modeItem17, modeItem18, modeItem19, modeItem20, modeItem21, modeItem22, modeItem23, modeItem24, string_reset2 }; +static const char* const modeOptions[] PROGMEM = { modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7, modeItem8, modeItem9, modeItem10, modeItem11, modeItem12, modeItem13, modeItem14, modeItem15, modeItem16, modeItem17, modeItem18, modeItem19, modeItem20, modeItem21, modeItem22, modeItem23, modeItem24, modeItem25, string_reset2 }; // All included slots void mainMenu() { @@ -1036,8 +1038,14 @@ void mainMenu() { break; #endif -#ifdef enable_FLASH +#ifdef enable_LOOPY case 21: + loopyMenu(); + break; +#endif + +#ifdef enable_FLASH + case 22: #ifdef ENABLE_VSELECT setup_FlashVoltage(); #endif @@ -1046,16 +1054,16 @@ void mainMenu() { #endif #ifdef enable_selftest - case 22: + case 23: selfTest(); break; #endif - case 23: + case 24: aboutScreen(); break; - case 24: + case 25: resetArduino(); break; @@ -1112,7 +1120,8 @@ static const char* const consoles80Options[] PROGMEM = { consoles80Item1, consol // 90s Consoles submenu static const char consoles90Item1[] PROGMEM = "Super A'can"; -static const char* const consoles90Options[] PROGMEM = { consoles90Item1, string_reset2 }; +static const char consoles90Item2[] PROGMEM = "Casio Loopy"; +static const char* const consoles90Options[] PROGMEM = { consoles90Item1, consoles90Item2, string_reset2 }; // Handhelds submenu static const char handheldsItem1[] PROGMEM = "Virtual Boy"; @@ -1353,7 +1362,13 @@ void consoles90Menu() { break; #endif +#ifdef enable_LOOPY case 1: + loopyMenu(); + break; +#endif + + case 2: resetArduino(); break; diff --git a/Cart_Reader/Config.h b/Cart_Reader/Config.h index c1271b5..3004e26 100644 --- a/Cart_Reader/Config.h +++ b/Cart_Reader/Config.h @@ -29,7 +29,7 @@ * Choose your hardware version: */ -//#define HW5 +#define HW5 //#define HW4 //#define HW3 //#define HW2 @@ -104,7 +104,7 @@ /* [ Flashrom Programmer for SNES repros -------------------------- ] */ -#define enable_FLASH +//#define enable_FLASH //#define enable_FLASH16 /****/ @@ -133,14 +133,14 @@ /* [ Nintendo 64 -------------------------------------------------- ] */ -#define enable_N64 +//#define enable_N64 /****/ /* [ Nintendo Entertainment System/Family Computer ---------------- ] */ -#define enable_NES +//#define enable_NES /****/ @@ -175,35 +175,35 @@ /* [ Sega Master System/Mark III/Game Gear/SG-1000 ---------------- ] */ -#define enable_SMS +//#define enable_SMS /****/ /* [ Sega Mega Drive/Genesis -------------------------------------- ] */ -#define enable_MD +//#define enable_MD /****/ /* [ Super Famicom SF Memory Cassette ----------------------------- ] */ -#define enable_SFM +//#define enable_SFM /****/ /* [ Super Famicom Satellaview ------------------------------------ ] */ -#define enable_SV +//#define enable_SV /****/ /* [ Super Nintendo ----------------------------------------------- ] */ -#define enable_SNES +//#define enable_SNES /****/ @@ -235,6 +235,13 @@ /****/ +/* [ Casio Loopy -------------------------------------------------- ] +*/ + +#define enable_LOOPY + +/****/ + /*==== FIRMWARE OPTIONS ===========================================*/ /* [ LCD: Background Color ---------------------------------------- ] @@ -244,7 +251,7 @@ * Green, Red, Blue */ -#define background_color 100, 0, 0 +#define background_color 20, 100, 60 /****/ diff --git a/Cart_Reader/LOOPY.ino b/Cart_Reader/LOOPY.ino new file mode 100644 index 0000000..4691f7f --- /dev/null +++ b/Cart_Reader/LOOPY.ino @@ -0,0 +1,576 @@ +//****************************************** +// CASIO LOOPY MODULE +//****************************************** +#ifdef enable_LOOPY +// TODO EVERYTHING + +// Nintendo VirtualBoy +// Cartridge Pinout +// 60P 2.00mm pitch connector +// +// TOP SIDE BOTTOM SIDE +// +-------+ +// GND -| 1 2 |- GND +// /WE0 (SRAM WRITE ENABLE) -| 3 4 |- nc +// nc -| 5 6 |- /CS1 (SRAM ENABLE) +// CS2 (SRAM) -| 7 8 |- VCC(+5V) +// nc -| 9 10 |- A23 +// A19 -| 11 12 |- A22 +// A18 -| 13 14 |- A21 +// A8 -| 15 16 |- A20 +// A7 -| 17 18 |- A9 +// A6 -| 19 20 |- A10 +// A5 -| 21 22 |- A11 +// A4 -| 23 24 |- A12 +// A3 -| 25 26 |- A13 +// A2 -| 27 28 |- A14 +// A1 -| 29 30 |- A15 +// /CE (ROM) -| 31 32 |- A16 +// GND -| 33 34 |- A17 +// /OE -| 35 36 |- VCC(+5V) +// D8 -| 37 38 |- D7 +// D0 -| 39 40 |- D15 +// D9 -| 41 42 |- D6 +// D1 -| 43 44 |- D14 +// D10 -| 45 46 |- D5 +// D2 -| 47 48 |- D13 +// D11 -| 49 50 |- D4 +// D3 -| 51 52 |- D12 +// VCC(+5V) -| 53 54 |- VCC(+5V) +// nc -| 55 56 |- nc +// nc -| 57 58 |- nc +// GND -| 59 60 |- GND +// +-------+ +// + +// CONTROL PINS: +// CS2(SRAM) - (PH0) - VBOY PIN 7 - SNES RST +// /CE(ROM) - (PH3) - VBOY PIN 31 - SNES /CS +// /CS1(SRAM ENABLE) - (PH4) - VBOY PIN 6 - SNES /IRQ +// /WE0(SRAM WRITE ENABLE) - (PH5) - VBOY PIN 3 - SNES /WR +// /OE - (PH6) - VBOY PIN 35 - SNES /RD + +// NOT CONNECTED: +// CLK(PH1) - N/C + +//****************************************** +// SETUP +//****************************************** + +void setup_LOOPY() { + // Request 5V + setVoltage(VOLTS_SET_5V); + + // Set Address Pins to Output + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output + // CS2(PH0) ---(PH1) /CE(PH3) /CS1(PH4) /WE0(PH5) /OE(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-D15) to Input + DDRC = 0x00; + DDRA = 0x00; + + // Setting Control Pins to HIGH + // ---(PH1) /CE(PH3) /CS1(PH4) /WE0(PH5) /OE(PH6) + PORTH |= (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + // Set CS2(PH0) to LOW + PORTH &= ~(1 << 0); + + // Set Unused Pins HIGH + PORTJ |= (1 << 0); // TIME(PJ0) + + getCartInfo_LOOPY(); + + mode = mode_LOOPY; +} + +//****************************************** +// MENU +//****************************************** + +// Base Menu +static const char loopyMenuItem1[] PROGMEM = "Read ROM"; +static const char loopyMenuItem2[] PROGMEM = "Read SRAM"; +static const char loopyMenuItem3[] PROGMEM = "Write SRAM"; +//static const char loopyMenuItem4[] PROGMEM = "Reset"; (stored in common strings array) +static const char* const menuOptionsLOOPY[] PROGMEM = { loopyMenuItem1, loopyMenuItem2, loopyMenuItem3, string_reset2 }; + +void loopyMenu() { + convertPgm(menuOptionsLOOPY, 4); + uint8_t mainMenu = question_box(F("CASIO LOOPY MENU"), menuOptions, 4, 0); + + switch (mainMenu) { + case 0: + // Read ROM + sd.chdir("/"); + readROM_LOOPY(); + sd.chdir("/"); + break; + + case 1: + // Read SRAM + if (sramSize) { + sd.chdir("/"); + display_Clear(); + println_Msg(F("Reading SRAM...")); + display_Update(); + readSRAM_LOOPY(); + sd.chdir("/"); + } else { + print_Error(F("Cart has no SRAM")); + } +#if (defined(enable_OLED) || defined(enable_LCD)) + // Wait for user input + // Prints string out of the common strings array either with or without newline + print_STR(press_button_STR, 1); + display_Update(); + wait(); +#endif + break; + + case 2: + // Write SRAM + if (sramSize) { + // Change working dir to root + sd.chdir("/"); + fileBrowser(F("Select SAV file")); + display_Clear(); + writeSRAM_LOOPY(); + writeErrors = verifySRAM_LOOPY(); + if (writeErrors == 0) { + println_Msg(F("SRAM verified OK")); + display_Update(); + } else { + print_STR(error_STR, 0); + print_Msg(writeErrors); + print_STR(_bytes_STR, 1); + print_Error(did_not_verify_STR); + } + } else { + print_Error(F("Cart has no SRAM")); + } +#if (defined(enable_OLED) || defined(enable_LCD)) + // Wait for user input + // Prints string out of the common strings array either with or without newline + print_STR(press_button_STR, 1); + display_Update(); + wait(); +#endif + break; + + case 3: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// LOW LEVEL FUNCTIONS +//****************************************** + +void writeByte_LOOPY(unsigned long myAddress, byte myData) { + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + PORTL = (myAddress >> 16) & 0xFF; + + __asm__("nop\n\t"); + + PORTA = myData; + + // Set CS2(PH0), /CE(PH3), /OE(PH6) to HIGH + PORTH |= (1 << 0) | (1 << 3) | (1 << 6); + // Set /CS1(PH4), /WE0(PH5) to LOW + PORTH &= ~(1 << 4) & ~(1 << 5); + + __asm__("nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t"); + + // Set CS2(PH0), /CS1(PH4), /WE0(PH5) to HIGH + PORTH |= (1 << 0) | (1 << 4) | (1 << 5); + + __asm__("nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t"); +} + +word readWord_LOOPY(unsigned long myAddress) { + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + PORTL = (myAddress >> 16) & 0xFF; + + __asm__("nop\n\t"); + + // Set CS2(PH0), /CS1(PH4), /WE0(PH5) to HIGH + PORTH |= (1 << 0) | (1 << 4) | (1 << 5); + // Set /CE(PH3), /OE(PH6) to LOW + PORTH &= ~(1 << 3) & ~(1 << 6); + + __asm__("nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t"); + + word tempWord = ((PINA & 0xFF) << 8) | (PINC & 0xFF); + + // Set /CE(PH3), /OE(PH6) to HIGH + PORTH |= (1 << 3) | (1 << 6); + // Setting CS2(PH0) LOW + PORTH &= ~(1 << 0); + + __asm__("nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t"); + + return tempWord; +} + +byte readByte_LOOPY(unsigned long myAddress) { // SRAM BYTE + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + PORTL = (myAddress >> 16) & 0xFF; + + __asm__("nop\n\t"); + + // Set CS2(PH0), /CE(PH3), /WE0(PH5) to HIGH + PORTH |= (1 << 0) | (1 << 3) | (1 << 5); + // Set /CS1(PH4), /OE(PH6) to LOW + PORTH &= ~(1 << 4) & ~(1 << 6); + + __asm__("nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t"); + + byte tempByte = PINA; + + __asm__("nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t"); + + // Set /CS1(PH4), /OE(PH6) to HIGH + PORTH |= (1 << 3) | (1 << 6); + // Setting CS2(PH0) LOW + PORTH &= ~(1 << 0); + + __asm__("nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t"); + + return tempByte; +} + +// Switch data pins to write +void dataOut_LOOPY() { + DDRC = 0xFF; + DDRA = 0xFF; +} + +// Switch data pins to read +void dataIn_LOOPY() { + DDRC = 0x00; + DDRA = 0x00; +} + +//****************************************** +// CART INFO +//****************************************** + +void getCartInfo_LOOPY() { + // Set control + dataIn_LOOPY(); + + cartSize = 0; + for (unsigned long address = 0x80000; address <= 0x400000; address *= 2) { + // Get Serial + word vbSerial = readWord_LOOPY((address - 0x204) / 2); // Cart Serial + + switch (vbSerial) { + case 0x4D54: // MT = Mario's Tennis + case 0x4832: // H2 = Panic Bomber/Tobidase! Panibomb + case 0x5350: // SP = Space Invaders + case 0x5353: // SS = Space Squash + case 0x5452: // TR = V-Tetris + cartSize = 0x80000; // 512KB + break; + + case 0x494D: // IM = Insmouse no Yakata + case 0x4A42: // JB = Jack Bros. + case 0x4D43: // MC = Mario Clash + case 0x5245: // RE = Red Alarm + case 0x4833: // H3 = Vertical Force + case 0x5642: // VB = Virtual Bowling + case 0x4A56: // JV = Virtual Lab + case 0x5650: // VP = Virtual League Baseball/Virtual Pro Yakyuu '95 + cartSize = 0x100000; // 1MB + break; + + case 0x5042: // PB = 3-D Tetris + case 0x4750: // GP = Galactic Pinball + case 0x5344: // SD = SD Gundam Dimension War + case 0x5442: // TB = Teleroboxer + case 0x5646: // VF = Virtual Fishing + cartSize = 0x100000; // 1MB + sramSize = 0x2000; // 8KB + break; + + case 0x5647: // VG = Golf/T&E Virtual Golf + case 0x4E46: // NF = Nester's Funky Bowling + case 0x5745: // WE = Waterworld + cartSize = 0x200000; // 2MB + break; + + case 0x5743: // WC = Virtual Boy Wario Land + cartSize = 0x200000; // 2MB + sramSize = 0x2000; // 8KB + break; + + case 0x4644: // FD = Hyper Fighting + cartSize = 0x400000; // 4MB + sramSize = 0x2000; // 8KB + break; + } + + if (cartSize) + break; + } + + // Get name + for (byte c = 0; c < 20; c += 2) { + // split word + word myWord = readWord_LOOPY((cartSize - 0x220 + c) / 2); + byte loByte = myWord & 0xFF; + byte hiByte = myWord >> 8; + + // write to buffer + sdBuffer[c] = hiByte; + sdBuffer[c + 1] = loByte; + } + byte myLength = 0; + for (unsigned int i = 0; i < 20; i++) { + if (((char(sdBuffer[i]) >= 48 && char(sdBuffer[i]) <= 57) || (char(sdBuffer[i]) >= 65 && char(sdBuffer[i]) <= 122)) && myLength < 15) { + romName[myLength] = char(sdBuffer[i]); + myLength++; + } + } + + display_Clear(); + println_Msg(F("Cart Info")); + println_Msg(F(" ")); + print_Msg(F("Name: ")); + println_Msg(romName); + print_Msg(F("Size: ")); + print_Msg(cartSize * 8 / 1024 / 1024); + println_Msg(F(" MBit")); + print_Msg(F("Sram: ")); + if (sramSize > 0) { + print_Msg(sramSize * 8 / 1024); + println_Msg(F(" KBit")); + } else + println_Msg(F("None")); + println_Msg(F(" ")); + +#if (defined(enable_OLED) || defined(enable_LCD)) + // Wait for user input + // Prints string out of the common strings array either with or without newline + print_STR(press_button_STR, 1); + display_Update(); + wait(); +#endif +} + +//****************************************** +// READ CODE +//****************************************** + +void readROM_LOOPY() { + dataIn_LOOPY(); + + strcpy(fileName, romName); + strcat(fileName, ".vb"); + + EEPROM_readAnything(0, foldern); + sprintf(folder, "LOOPY/ROM/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + display_Clear(); + print_STR(saving_to_STR, 0); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + foldern = foldern + 1; + EEPROM_writeAnything(0, foldern); + + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_FatalError(sd_error_STR); + } + + word d = 0; + uint32_t progress = 0; + draw_progressbar(0, cartSize); + // HYPER FIGHTING FIX + // VIRTUAL BOY ADDRESSING IS TOP DOWN + // ONLY FOR HYPER FIGHTING PLUGIN WITH ALL ADDRESS LINES CONNECTED + // NORMAL PLUGIN DOES NOT HAVE THE UPPER ADDRESS LINES CONNECTED + if (cartSize == 0x400000) { + unsigned long romData = 0x1000000 - (cartSize / 2); + for (unsigned long currBuffer = romData; currBuffer < 0x1000000; currBuffer += 256) { + for (int currWord = 0; currWord < 256; currWord++) { + word myWord = readWord_LOOPY(currBuffer + currWord); + // Split word into two bytes + sdBuffer[d] = ((myWord >> 8) & 0xFF); + sdBuffer[d + 1] = (myWord & 0xFF); + d += 2; + } + myFile.write(sdBuffer, 512); + d = 0; + progress += 512; + draw_progressbar(progress, cartSize); + } + } else { + for (unsigned long currBuffer = 0; currBuffer < cartSize / 2; currBuffer += 256) { + for (int currWord = 0; currWord < 256; currWord++) { + word myWord = readWord_LOOPY(currBuffer + currWord); + // Split word into two bytes + sdBuffer[d] = ((myWord >> 8) & 0xFF); + sdBuffer[d + 1] = (myWord & 0xFF); + d += 2; + } + myFile.write(sdBuffer, 512); + d = 0; + progress += 512; + draw_progressbar(progress, cartSize); + } + } + myFile.close(); + + // Compare CRC32 to database and rename ROM if found + // Arguments: database name, precalculated crc string or 0 to calculate, rename rom or not, starting offset + compareCRC("vb.txt", 0, 1, 0); + +#if (defined(enable_OLED) || defined(enable_LCD)) + // Wait for user input + println_Msg(F("")); + // Prints string out of the common strings array either with or without newline + print_STR(press_button_STR, 1); + display_Update(); + wait(); +#endif +} + +//****************************************** +// SRAM +//****************************************** + +void writeSRAM_LOOPY() { + dataOut_LOOPY(); + + sprintf(filePath, "%s/%s", filePath, fileName); + println_Msg(F("Writing...")); + println_Msg(filePath); + display_Update(); + + if (myFile.open(filePath, O_READ)) { + for (unsigned long currByte = 0; currByte < sramSize; currByte++) { + // writeWord_LOOPY(currByte, ((myFile.read() << 8 ) & 0xFF)); + writeByte_LOOPY(currByte, (myFile.read())); + } + myFile.close(); + print_STR(done_STR, 1); + display_Update(); + } else { + print_FatalError(sd_error_STR); + } + dataIn_LOOPY(); +} + +void readSRAM_LOOPY() { + dataIn_LOOPY(); + + strcpy(fileName, romName); + strcat(fileName, ".sav"); + + EEPROM_readAnything(0, foldern); + sprintf(folder, "LOOPY/SAVE/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + foldern = foldern + 1; + EEPROM_writeAnything(0, foldern); + + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_FatalError(sd_error_STR); + } + for (unsigned long currBuffer = 0; currBuffer < sramSize; currBuffer += 512) { + for (int currByte = 0; currByte < 512; currByte++) { + byte myByte = readByte_LOOPY(currBuffer + currByte); + sdBuffer[currByte] = myByte; + } + myFile.write(sdBuffer, 512); + } + myFile.close(); + print_Msg(F("Saved to ")); + print_Msg(folder); + println_Msg(F("/")); + display_Update(); +} + +unsigned long verifySRAM_LOOPY() { + dataIn_LOOPY(); + writeErrors = 0; + + if (myFile.open(filePath, O_READ)) { + for (unsigned long currBuffer = 0; currBuffer < sramSize; currBuffer += 512) { + for (int currByte = 0; currByte < 512; currByte++) { + byte myByte = readByte_LOOPY(currBuffer + currByte); + sdBuffer[currByte] = myByte; + } + for (int i = 0; i < 512; i++) { + if (myFile.read() != sdBuffer[i]) { + writeErrors++; + } + } + } + myFile.close(); + } else { + print_FatalError(sd_error_STR); + } + + return writeErrors; +} +#endif +//****************************************** +// End of File +//****************************************** \ No newline at end of file