From 27b63819e5ad579527782c3df0419022eaedd6d7 Mon Sep 17 00:00:00 2001 From: sanni Date: Sun, 22 Oct 2017 13:32:14 +0200 Subject: [PATCH] V30: Add support for Fujitsu MSP55LV100S N64 repros and fix bug in Serial Monitor filebrowser - The Fujitsu MSP55LV100S consists out of two Fujitsu MSP55LV512 which themselves are just Spansion S29GL256N flashroms. One flashrom is used for the high byte the other for the low byte. - You can now browse pages with the Serial Monitor's file browser just like you can with the OLED. --- Cart_Reader/Cart_Reader.ino | 348 ++++++++++++++++++++---------------- Cart_Reader/N64.ino | 249 +++++++++++++++----------- 2 files changed, 337 insertions(+), 260 deletions(-) diff --git a/Cart_Reader/Cart_Reader.ino b/Cart_Reader/Cart_Reader.ino index 7d6d12f..3a7d83b 100644 --- a/Cart_Reader/Cart_Reader.ino +++ b/Cart_Reader/Cart_Reader.ino @@ -2,8 +2,8 @@ Cartridge Reader for Arduino Mega2560 Author: sanni - Date: 2017-10-13 - Version: V29D + Date: 2017-10-22 + Version: V30 SD lib: https://github.com/greiman/SdFat LCD lib: https://github.com/adafruit/Adafruit_SSD1306 @@ -35,7 +35,7 @@ infinest - help with GB Memory cart **********************************************************************************/ -char ver[5] = "V29D"; +char ver[5] = "V30"; /****************************************** Define Output @@ -354,6 +354,171 @@ static const char gbxMenuItem1[] PROGMEM = "Game Boy (Color)"; static const char gbxMenuItem2[] PROGMEM = "Game Boy Advance"; static const char* const menuOptionsGBx[] PROGMEM = {gbxMenuItem1, gbxMenuItem2}; +void n64Menu() { + // create menu with title and 3 options to choose from + unsigned char n64Dev; + // Copy menuOptions out of progmem + convertPgm(menuOptionsN64, 3); + n64Dev = question_box("Select N64 device", menuOptions, 3, 0); + + // wait for user choice to come back from the question box menu + switch (n64Dev) + { + case 0: + display_Clear(); + display_Update(); + setup_N64_Cart(); + printCartInfo_N64(); + mode = mode_N64_Cart; + break; + + case 1: + display_Clear(); + display_Update(); + setup_N64_Controller(); + mode = mode_N64_Controller; + break; + + case 2: + display_Clear(); + display_Update(); + setup_N64_Cart(); + flashRepro_N64(); + printCartInfo_N64(); + mode = mode_N64_Cart; + break; + + } +} + +void npMenu() { + // create menu with title and 2 options to choose from + unsigned char npCart; + // Copy menuOptions out of progmem + convertPgm(menuOptionsNP, 2); + npCart = question_box("Select NP Cart", menuOptions, 2, 0); + + // wait for user choice to come back from the question box menu + switch (npCart) + { + case 0: + display_Clear(); + display_Update(); + setup_SFM(); + mode = mode_SFM; + break; + + case 1: + display_Clear(); + display_Update(); + setup_GBM(); + mode = mode_GBM; + break; + } +} + +void gbxMenu() { + // create menu with title and 2 options to choose from + unsigned char gbType; + // Copy menuOptions out of progmem + convertPgm(menuOptionsGBx, 2); + gbType = question_box("Select Game Boy", menuOptions, 2, 0); + + // wait for user choice to come back from the question box menu + switch (gbType) + { + case 0: + display_Clear(); + display_Update(); + setup_GB(); + mode = mode_GB; + break; + + case 1: + display_Clear(); + display_Update(); + setup_GBA(); + mode = mode_GBA; + break; + } +} + +void flashMenu() { + // create menu with title and 2 options to choose from + unsigned char flashSlot; + // Copy menuOptions out of progmem + convertPgm(menuOptionsFlash, 2); + flashSlot = question_box("Select flashrom slot", menuOptions, 2, 0); + + // wait for user choice to come back from the question box menu + switch (flashSlot) + { + case 0: + display_Clear(); + display_Update(); + setup_Flash8(); + mode = mode_FLASH8; + break; + + case 1: + display_Clear(); + display_Update(); + setup_Flash16(); + mode = mode_FLASH16; + break; + } +} + +void aboutScreen() { + display_Clear(); + // Draw the Logo + display.drawBitmap(0, 0, sig, 128, 64, 1); + println_Msg(F("Cartridge Reader")); + println_Msg(F("github.com/sanni")); + print_Msg(F("2017 ")); + println_Msg(ver); + println_Msg(F("")); + println_Msg(F("")); + println_Msg(F("")); + println_Msg(F("")); + println_Msg(F("Press Button")); + display_Update(); + + while (1) { + if (enable_OLED) { + // get input button + int b = checkButton(); + + // if the cart readers input button is pressed shortly + if (b == 1) { + asm volatile (" jmp 0"); + } + + // if the cart readers input button is pressed long + if (b == 3) { + asm volatile (" jmp 0"); + } + + // if the button is pressed super long + if (b == 4) { + display_Clear(); + println_Msg(F("Resetting folder...")); + display_Update(); + delay(2000); + foldern = 0; + EEPROM_writeAnything(10, foldern); + asm volatile (" jmp 0"); + } + } + if (enable_Serial) { + wait_serial(); + asm volatile (" jmp 0"); + } + rgb.setColor(random(0, 255), random(0, 255), random(0, 255)); + delay(random(50, 100)); + } +} + void mainMenu() { // create menu with title and 6 options to choose from unsigned char modeMenu; @@ -365,40 +530,7 @@ void mainMenu() { switch (modeMenu) { case 0: - // create menu with title and 3 options to choose from - unsigned char n64Dev; - // Copy menuOptions out of progmem - convertPgm(menuOptionsN64, 3); - n64Dev = question_box("Select N64 device", menuOptions, 3, 0); - - // wait for user choice to come back from the question box menu - switch (n64Dev) - { - case 0: - display_Clear(); - display_Update(); - setup_N64_Cart(); - printCartInfo_N64(); - mode = mode_N64_Cart; - break; - - case 1: - display_Clear(); - display_Update(); - setup_N64_Controller(); - mode = mode_N64_Controller; - break; - - case 2: - display_Clear(); - display_Update(); - setup_N64_Cart(); - flashRepro_N64(); - printCartInfo_N64(); - mode = mode_N64_Cart; - break; - - } + n64Menu(); break; case 1: @@ -409,55 +541,11 @@ void mainMenu() { break; case 2: - // create menu with title and 2 options to choose from - unsigned char npCart; - // Copy menuOptions out of progmem - convertPgm(menuOptionsNP, 2); - npCart = question_box("Select NP Cart", menuOptions, 2, 0); - - // wait for user choice to come back from the question box menu - switch (npCart) - { - case 0: - display_Clear(); - display_Update(); - setup_SFM(); - mode = mode_SFM; - break; - - case 1: - display_Clear(); - display_Update(); - setup_GBM(); - mode = mode_GBM; - break; - } + npMenu(); break; case 3: - // create menu with title and 2 options to choose from - unsigned char gbType; - // Copy menuOptions out of progmem - convertPgm(menuOptionsGBx, 2); - gbType = question_box("Select Game Boy", menuOptions, 2, 0); - - // wait for user choice to come back from the question box menu - switch (gbType) - { - case 0: - display_Clear(); - display_Update(); - setup_GB(); - mode = mode_GB; - break; - - case 1: - display_Clear(); - display_Update(); - setup_GBA(); - mode = mode_GBA; - break; - } + gbxMenu(); break; case 4: @@ -468,79 +556,11 @@ void mainMenu() { break; case 5: - // create menu with title and 2 options to choose from - unsigned char flashSlot; - // Copy menuOptions out of progmem - convertPgm(menuOptionsFlash, 2); - flashSlot = question_box("Select flashrom slot", menuOptions, 2, 0); - - // wait for user choice to come back from the question box menu - switch (flashSlot) - { - case 0: - display_Clear(); - display_Update(); - setup_Flash8(); - mode = mode_FLASH8; - break; - - case 1: - display_Clear(); - display_Update(); - setup_Flash16(); - mode = mode_FLASH16; - break; - } + flashMenu(); break; case 6: - display_Clear(); - // Draw the Logo - display.drawBitmap(0, 0, sig, 128, 64, 1); - println_Msg(F("Cartridge Reader")); - println_Msg(F("github.com/sanni")); - print_Msg(F("2017 ")); - println_Msg(ver); - println_Msg(F("")); - println_Msg(F("")); - println_Msg(F("")); - println_Msg(F("")); - println_Msg(F("Press Button")); - display_Update(); - - while (1) { - if (enable_OLED) { - // get input button - int b = checkButton(); - - // if the cart readers input button is pressed shortly - if (b == 1) { - asm volatile (" jmp 0"); - } - - // if the cart readers input button is pressed long - if (b == 3) { - asm volatile (" jmp 0"); - } - - // if the button is pressed super long - if (b == 4) { - display_Clear(); - println_Msg(F("Resetting folder...")); - display_Update(); - delay(2000); - foldern = 0; - EEPROM_writeAnything(10, foldern); - asm volatile (" jmp 0"); - } - } - if (enable_Serial) { - wait_serial(); - asm volatile (" jmp 0"); - } - rgb.setColor(random(0, 255), random(0, 255), random(0, 255)); - delay(random(50, 100)); - } + aboutScreen(); break; } } @@ -776,13 +796,33 @@ byte questionBox_Serial(const char* question, char answers[7][20], int num_answe } // Wait for user input Serial.println(""); - Serial.print(F("Please enter a single number: _ ")); + Serial.println(F("Please browse pages with 'u'(up) and 'd'(down)")); + Serial.print(F("and enter a selection by typing a number(0-6): _ ")); while (Serial.available() == 0) { } // Read the incoming byte: incomingByte = Serial.read() - 48; + if (incomingByte == 69) { + if (filebrowse == 1) { + if (currPage > 1) { + lastPage = currPage; + currPage--; + } + else { + root = 1; + } + } + + } + else if (incomingByte == 52) { + if ((numPages > currPage) && (filebrowse == 1)) { + lastPage = currPage; + currPage++; + } + } + // Print the received byte for validation e.g. in case of a different keyboard mapping Serial.println(incomingByte); Serial.println(""); diff --git a/Cart_Reader/N64.ino b/Cart_Reader/N64.ino index 17d4bfd..ab7aeee 100644 --- a/Cart_Reader/N64.ino +++ b/Cart_Reader/N64.ino @@ -983,7 +983,8 @@ void printCartInfo_N64() { // Set cartSize to 64MB for test dumps cartSize = 64; strcpy(romName, "GPERROR"); - print_Error(F("Cartridge unknown"), true); + print_Error(F("Cartridge unknown"), false); + wait(); } } @@ -2073,7 +2074,7 @@ void flashRepro_N64() { } else if (strcmp(flashid, "8816") == 0) println_Msg(F("Intel 4400L0ZDQ0")); - else if (strcmp(flashid, "127E") == 0) + else if (strcmp(flashid, "7E7E") == 0) println_Msg(F("Fujitsu MSP55LV100S")); else if ((strcmp(flashid, "227E") == 0) && (strcmp(cartID, "2301") == 0)) println_Msg(F("Fujitsu MSP55LV512")); @@ -2093,12 +2094,6 @@ void flashRepro_N64() { display_Update(); wait(); - /* Check if sectors are protected - if ((strcmp(flashid, "227E") == 0) || (strcmp(flashid, "127E") == 0)) { - sectorCheck_N64(); - } - */ - // Launch file browser filePath[0] = '\0'; sd.chdir("/"); @@ -2124,10 +2119,14 @@ void flashRepro_N64() { } // Erase needed sectors - if ((strcmp(flashid, "227E") == 0) || (strcmp(flashid, "127E") == 0)) { - // Spansion S29GL256N, Fujitsu MSP55LV512 or Fujitsu MSP55LV100S with 0x20000 sector size and 32 byte buffer + if (strcmp(flashid, "227E") == 0) { + // Spansion S29GL256N or Fujitsu MSP55LV512 with 0x20000 sector size and 32 byte buffer eraseFlashrom_N64(0x20000); } + else if (strcmp(flashid, "7E7E") == 0) { + // Fujitsu MSP55LV100S + eraseMSP55LV100_N64(); + } else if ((strcmp(flashid, "8813") == 0) || (strcmp(flashid, "8816") == 0)) { // Intel 4400L0ZDQ0 eraseIntel4400_N64(); @@ -2146,10 +2145,14 @@ void flashRepro_N64() { println_Msg(filePath); display_Update(); - if ((strcmp(flashid, "227E") == 0) || (strcmp(flashid, "127E") == 0)) { - // Spansion S29GL256N or Fujitsu MSP55LV512 or Fujitsu MSP55LV100S with 0x20000 sector size and 32 byte buffer + if (strcmp(flashid, "227E") == 0) { + // Spansion S29GL256N or Fujitsu MSP55LV512 with 0x20000 sector size and 32 byte buffer writeFlashBuffer_N64(0x20000, 32); } + else if (strcmp(flashid, "7E7E") == 0) { + //Fujitsu MSP55LV100S + writeMSP55LV100_N64(0x20000); + } else if ((strcmp(flashid, "22C9") == 0) || (strcmp(flashid, "22CB") == 0)) { // Macronix MX29LV640 without buffer writeFlashrom_N64(); @@ -2178,6 +2181,8 @@ void flashRepro_N64() { } } else { + // Close the file + myFile.close(); print_Error(F("failed"), false); } } @@ -2207,6 +2212,14 @@ void resetIntel4400_N64() { } } +// Reset Fujitsu MSP55LV100S +void resetMSP55LV100_N64(unsigned long flashBase) { + // Send reset Command + setAddress_N64(flashBase); + writeWord_N64(0xF0F0); + delay(100); +} + // Common reset command void resetFlashrom_N64(unsigned long flashBase) { // Send reset Command @@ -2320,108 +2333,32 @@ void idFlashrom_N64() { // Empty cartID string cartID[0] = '\0'; } - //Fujitsu MSP55LV100S (128MB) - else if (strcmp(flashid, "127E") == 0) { - resetFlashrom_N64(romBase); - cartSize = 64; - } //Fujitsu MSP55LV512/Spansion S29GL512N (64MB) else if ((strcmp(cartID, "2301") == 0) && (strcmp(flashid, "227E") == 0)) { resetFlashrom_N64(romBase); cartSize = 64; } -} - -// Check for sector protection -void sectorCheck_N64() { - word numLocked = 0; - word numUnlocked = 0; - word numError = 0; - unsigned long lastSector; - - // Spansion S29GL256N(32MB/64MB) with two flashrom chips - if ((cartSize == 64) && (strcmp(cartID, "2201") == 0)) { - lastSector = 0x2000000; - } - else { - lastSector = cartSize * 1024 * 1024; - } - - // Send flashrom ID command - setAddress_N64(romBase + (0x555 << 1)); - writeWord_N64(0xAA); - setAddress_N64(romBase + (0x2AA << 1)); - writeWord_N64(0x55); - setAddress_N64(romBase + (0x555 << 1)); - writeWord_N64(0x90); - - word tempWord; - - for (unsigned long currSector = 0x0; currSector < lastSector; currSector += 0x20000) { - setAddress_N64(romBase + currSector + 0x04); - tempWord = readWord_N64(); - - if (tempWord == 0x1) { - numLocked++; - } - else if (tempWord == 0x0) { - numUnlocked++; - } - else { - numError++; - } - } - resetFlashrom_N64(romBase); - - // Spansion S29GL256N(32MB/64MB) with two flashrom chips - if ((cartSize == 64) && (strcmp(cartID, "2201") == 0)) { + //Test for Fujitsu MSP55LV100S (64MB) + else { // Send flashrom ID command - setAddress_N64(romBase + 0x2000000 + (0x555 << 1)); - writeWord_N64(0xAA); - setAddress_N64(romBase + 0x2000000 + (0x2AA << 1)); - writeWord_N64(0x55); - setAddress_N64(romBase + 0x2000000 + (0x555 << 1)); - writeWord_N64(0x90); + setAddress_N64(romBase + (0x555 << 1)); + writeWord_N64(0xAAAA); + setAddress_N64(romBase + (0x2AA << 1)); + writeWord_N64(0x5555); + setAddress_N64(romBase + (0x555 << 1)); + writeWord_N64(0x9090); - for (unsigned long currSector = 0x2000000; currSector < 0x4000000; currSector += 0x20000) { - setAddress_N64(romBase + currSector + 0x04); - tempWord = readWord_N64(); + setAddress_N64(romBase); + // Read 1 byte vendor ID + readWord_N64(); + // Read 2 bytes flashrom ID + sprintf(cartID, "%04X", readWord_N64()); - if (tempWord == 0x1) { - numLocked++; - } - else if (tempWord == 0x0) { - numUnlocked++; - } - else { - numError++; - } + if (strcmp(cartID, "7E7E") == 0) { + resetMSP55LV100_N64(romBase); + cartSize = 64; + strncpy(flashid , cartID, 5); } - resetFlashrom_N64(romBase + 0x2000000); - } - - display_Clear(); - println_Msg(F("Sector Check")); - print_Msg(F("Unlocked: ")); - println_Msg(numUnlocked); - print_Msg(F("Locked: ")); - println_Msg(numLocked); - print_Msg(F("Error: ")); - println_Msg(numError); - println_Msg(F("")); - - if ((numLocked != 0) || (numError != 0)) { - println_Msg(F("Error:")); - println_Msg(F("Not all Sectors")); - print_Error(F("are unlocked"), true); - } - - else { - println_Msg(F("")); - println_Msg(F("Press button")); - display_Update(); - wait(); - display_Clear(); } } @@ -2484,7 +2421,7 @@ void eraseIntel4400_N64() { PORTB ^= (1 << 4); } - // Check if we should erase the seconds chip too + // Check if we should erase the second chip too if ((cartSize = 64) && (fileSize > 0x2000000)) { // Switch base address to second chip flashBase = romBase + 0x2000000; @@ -2538,6 +2475,50 @@ void eraseIntel4400_N64() { } } +// Erase Fujutsu MSP55LV100S +void eraseMSP55LV100_N64() { + unsigned long flashBase = romBase; + unsigned long sectorSize = 0x20000; + + print_Msg(F("Erasing...")); + display_Update(); + + for (unsigned long currSector = 0; currSector < fileSize; currSector += sectorSize) { + // Blink led + PORTB ^= (1 << 4); + + // Send Erase Command to first chip + setAddress_N64(flashBase + (0x555 << 1)); + writeWord_N64(0xAAAA); + setAddress_N64(flashBase + (0x2AA << 1)); + writeWord_N64(0x5555); + setAddress_N64(flashBase + (0x555 << 1)); + writeWord_N64(0x8080); + setAddress_N64(flashBase + (0x555 << 1)); + writeWord_N64(0xAAAA); + setAddress_N64(flashBase + (0x2AA << 1)); + writeWord_N64(0x5555); + setAddress_N64(romBase + currSector); + writeWord_N64(0x3030); + + // Read the status register + setAddress_N64(romBase + currSector); + word statusReg = readWord_N64(); + while ((statusReg | 0xFF7F) != 0xFFFF) { + setAddress_N64(romBase + currSector); + statusReg = readWord_N64(); + } + + // Read the status register + setAddress_N64(romBase + currSector); + statusReg = readWord_N64(); + while ((statusReg | 0x7FFF) != 0xFFFF) { + setAddress_N64(romBase + currSector); + statusReg = readWord_N64(); + } + } +} + // Common sector erase command void eraseFlashrom_N64(unsigned long sectorSize) { unsigned long flashBase = romBase; @@ -2653,6 +2634,62 @@ void writeIntel4400_N64() { } } } +// Write Fujitsu MSP55LV100S flashrom consisting out of two MSP55LV512 flashroms one used for the high byte the other for the low byte +void writeMSP55LV100_N64(unsigned long sectorSize) { + unsigned long flashBase = romBase; + + for (unsigned long currSector = 0; currSector < fileSize; currSector += sectorSize) { + // Blink led + PORTB ^= (1 << 4); + + // Write to flashrom + for (unsigned long currSdBuffer = 0; currSdBuffer < sectorSize; currSdBuffer += 512) { + // Fill SD buffer + myFile.read(sdBuffer, 512); + + // Write 32 bytes at a time + for (int currWriteBuffer = 0; currWriteBuffer < 512; currWriteBuffer += 32) { + + // 2 unlock commands + setAddress_N64(flashBase + (0x555 << 1)); + writeWord_N64(0xAAAA); + setAddress_N64(flashBase + (0x2AA << 1)); + writeWord_N64(0x5555); + + // Write buffer load command at sector address + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer); + writeWord_N64(0x2525); + // Write word count (minus 1) at sector address + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer); + writeWord_N64(0x0F0F); + + // Define variable before loop so we can use it later when reading the status register + word currWord; + + for (byte currByte = 0; currByte < 32; currByte += 2) { + // Join two bytes into one word + currWord = ( ( sdBuffer[currWriteBuffer + currByte] & 0xFF ) << 8 ) | ( sdBuffer[currWriteBuffer + currByte + 1] & 0xFF ); + + // Load Buffer Words + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + currByte); + writeWord_N64(currWord); + } + + // Write Buffer to Flash + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + 30); + writeWord_N64(0x2929); + + // Read the status register at last written address + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + 30); + word statusReg = readWord_N64(); + while ((statusReg | 0x7F7F) != (currWord | 0x7F7F)) { + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + 30); + statusReg = readWord_N64(); + } + } + } + } +} // Write Spansion S29GL256N flashrom using the 32 byte write buffer void writeFlashBuffer_N64(unsigned long sectorSize, byte bufferSize) {