From bc62414c59f927a10e7c96242ff15a19fc8a9718 Mon Sep 17 00:00:00 2001 From: Richard Weick <55462393+RWeick@users.noreply.github.com> Date: Sun, 10 Sep 2023 21:49:11 -0500 Subject: [PATCH] Update N64.ino This adds full support for the Xplorer 64, including backup and writing of new firmware to the device. Firmware must be unscrambled (not dumped directly from the chips in a chip reader) in order to work with this program. Firmware dumped via the Sanni Cart Reader with this function work perfectly when written back. --- Cart_Reader/N64.ino | 301 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 298 insertions(+), 3 deletions(-) diff --git a/Cart_Reader/N64.ino b/Cart_Reader/N64.ino index d3e13103..8627793b 100644 --- a/Cart_Reader/N64.ino +++ b/Cart_Reader/N64.ino @@ -55,7 +55,7 @@ static const char n64MenuItem1[] PROGMEM = "Game Cartridge"; static const char n64MenuItem2[] PROGMEM = "Controller"; static const char n64MenuItem3[] PROGMEM = "Flash Repro"; static const char n64MenuItem4[] PROGMEM = "Flash Gameshark"; -static const char n64MenuItem5[] PROGMEM = "Backup Xplorer 64"; +static const char n64MenuItem5[] PROGMEM = "Flash Xplorer 64"; //static const char n64MenuItem6[] PROGMEM = "Reset"; (stored in common strings array) static const char* const menuOptionsN64[] PROGMEM = { n64MenuItem1, n64MenuItem2, n64MenuItem3, n64MenuItem4, n64MenuItem5, string_reset2 }; @@ -160,7 +160,7 @@ void n64Menu() { display_Clear(); display_Update(); setup_N64_Cart(); - backupXplorer_N64(); + flashXplorer_N64(); mode = mode_N64_Cart; print_STR(press_button_STR, 1); display_Update(); @@ -4476,11 +4476,139 @@ unsigned long verifyGameshark_N64() { /****************************************** XPLORER 64 Functions *****************************************/ +void flashXplorer_N64() { + // Check flashrom ID's + idXplorer_N64(); + + if (flashid == 0x0808) { + backupXplorer_N64(); + println_Msg(""); + println_Msg(F("This will erase your")); + println_Msg(F("Xplorer64 cartridge")); + println_Msg(F("Attention: Use 3.3V!")); + println_Msg(F("Power OFF if Unsure!")); + // Prints string out of the common strings array either with or without newline + print_STR(press_button_STR, 1); + display_Update(); + wait(); + + // Launch file browser + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser(F("Select XP64 rom file")); + display_Update(); + + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + // Get rom size from file + fileSize = myFile.fileSize(); + display_Clear(); + print_Msg(F("File size: ")); + print_Msg(fileSize / 1024); + println_Msg(F(" KB")); + display_Update(); + + // Compare file size to flashrom size + if (fileSize > 262144) { + print_FatalError(file_too_big_STR); + } + + // SST 29LE010, chip erase not needed as this eeprom automaticly erases during the write cycle + eraseXplorer_N64(); + blankCheck_XP64(); + + // Write flashrom + display_Clear(); + print_Msg(F("Writing ")); + println_Msg(filePath); + display_Update(); + writeXplorer_N64(); + + // Close the file: + myFile.close(); + + // Verify + print_STR(verifying_STR, 0); + display_Update(); + writeErrors = verifyXplorer_N64(); + + if (writeErrors == 0) { + display_Clear(); + display_Update(); + println_Msg(F("Verfied OK")); + println_Msg(F("")); + println_Msg(F("Turn Cart Reader off now")); + display_Update(); + while (1) + ; + } else { + display_Clear(); + display_Update(); + println_Msg(F("Verification Failed")); + println_Msg(writeErrors); + print_Msg(F(" bytes ")); + print_Error(did_not_verify_STR); + } + } else { + print_Error(F("Can't open file")); + } + } + + // Prints string out of the common strings array either with or without newline + print_STR(press_button_STR, 1); + display_Update(); + wait(); + display_Clear(); + display_Update(); +} + +//Test for SST 29LE010 +void idXplorer_N64() { + flashid = 0x0; + //Send flashrom ID command + oddXPaddrWrite(0x1040AAAA, 0xAAAA); + evenXPaddrWrite(0x10405555, 0x5555); + oddXPaddrWrite(0x1040AAAA, 0x9090); + + setAddress_N64(0x10760000); + readWord_N64(); + setAddress_N64(0x10400D88); + flashid = readWord_N64(); + setAddress_N64(0x10740000); + readWord_N64(); + + if (flashid == 0x0808) { + flashSize = 262144; + } else { + println_Msg(F("Check cart connection")); + println_Msg(F("Unknown Flash ID")); + sprintf(flashid_str, "%04X", flashid); + print_STR(press_button_STR, 1); + display_Update(); + wait(); + mainMenu(); + } + sprintf(flashid_str, "%04X", flashid); + // Reset flashrom + resetXplorer_N64(); +} + +void resetXplorer_N64() { + // Send reset command for SST 29LE010 + oddXPaddrWrite(0x1040AAAA, 0xAAAA); + evenXPaddrWrite(0x10405555, 0x5555); + oddXPaddrWrite(0x1040AAAA, 0xF0F0); + delay(100); +} + // Read rom and save to the SD card void backupXplorer_N64() { // create a new folder EEPROM_readAnything(0, foldern); - sprintf(fileName, "XP64%d", foldern); + sprintf(fileName, "XP64-%d", foldern); strcat(fileName, ".z64"); sd.mkdir("N64/ROM/XPLORER64", true); sd.chdir("N64/ROM/XPLORER64"); @@ -4525,6 +4653,173 @@ void backupXplorer_N64() { println_Msg(F("Done.")); } +unsigned long unscramble(unsigned long addr) { + unsigned long result = (((addr >> 4) & 0x001) | ((addr >> 8) & 0x002) | + ((~addr >> 9) & 0x004) | ((addr >> 3) & 0x008) | + ((addr >> 6) & 0x010) | ((addr >> 2) & 0x020) | + ((~addr << 5) & 0x0C0) | ((~addr << 8) & 0x100) | + ((~addr << 6) & 0x200) | ((~addr << 2) & 0x400) | + ((addr << 6) & 0x800) | (addr & 0x1F000)); + + return result; +} + + +unsigned long scramble(unsigned long addr) { + unsigned long result = (((~addr >> 8) & 0x001) | ((~addr >> 5) & 0x006) | + ((~addr >> 6) & 0x008) | ((addr << 4) & 0x010) | + ((addr >> 6) & 0x020) | ((addr << 3) & 0x040) | + ((addr << 2) & 0x080) | ((~addr >> 2) & 0x100) | + ((addr << 8) & 0x200) | ((addr << 6) & 0x400) | + ((~addr << 9) & 0x800) | (addr & 0x1F000)); + + return result; +} + + +void oddXPaddrWrite(unsigned long addr, word data) { + unsigned long oddAddr = (0x10400000 + ((unscramble((addr & 0xFFFFF) / 2) - 1) * 2)); + setAddress_N64(0x10770000); + readWord_N64(); + readWord_N64(); + setAddress_N64(oddAddr); + writeWord_N64(data); + writeWord_N64(data); + setAddress_N64(0x10740000); + readWord_N64(); + readWord_N64(); + +} + +void evenXPaddrWrite(unsigned long addr, word data) { + unsigned long evenAddr = (0x10400000 + (unscramble((addr & 0xFFFFF) / 2) * 2)); + setAddress_N64(0x10760000); + readWord_N64(); + readWord_N64(); + setAddress_N64(evenAddr); + writeWord_N64(data); + writeWord_N64(data); + setAddress_N64(0x10740000); + readWord_N64(); + readWord_N64(); +} + +void eraseXplorer_N64() { + println_Msg(F("Erasing...")); + display_Update(); + + // Send chip erase to SST 29LE010 / AMTEL AT29LV010A / SST 29EE010 + oddXPaddrWrite(0x1040AAAA, 0xAAAA); + evenXPaddrWrite(0x10405555, 0x5555); + oddXPaddrWrite(0x1040AAAA, 0x8080); + oddXPaddrWrite(0x1040AAAA, 0xAAAA); + evenXPaddrWrite(0x10405555, 0x5555); + oddXPaddrWrite(0x1040AAAA,0x1010); + + delay(20); +} + +void blankCheck_XP64() { + // Blankcheck + println_Msg(F("Blankcheck...")); + display_Update(); + + for (unsigned long currSector = 0; currSector < 262144; currSector += 131072) { + // Blink led + blinkLED(); + for (unsigned long currSdBuffer = 0; currSdBuffer < 131072; currSdBuffer += 512) { + for (int currByte = 0; currByte < 512; currByte += 2) { + // Read flash + setAddress_N64(0x10400000 + currSector + currSdBuffer + currByte); + // Compare both + if (readWord_N64() != 0xFFFF) { + println_Msg(F("Not empty")); + print_FatalError(F("Erase failed")); + } + } + } + } +} + +void writeXplorer_N64() { + // Write Xplorer64 with 2x SST 29LE010 + // Each 29LE010 has 1024 pages, each 128 bytes in size + //Initialize progress bar + uint32_t processedProgressBar = 0; + uint32_t totalProgressBar = (uint32_t)(fileSize); + draw_progressbar(0, totalProgressBar); + for (unsigned long currPage = 0; currPage < fileSize / 2; currPage += 128) { + + // Fill SD buffer with data in the order it will be expected by the CPLD + for (unsigned long i = 0; i < 256; i += 2) { + unsigned long unscrambled_address = (unscramble(((currPage*2) + i) / 2) * 2); + myFile.seek(unscrambled_address); + myFile.read(&sdBuffer[i], 1); + myFile.seek(unscrambled_address + 1); + myFile.read(&sdBuffer[i + 1], 1); + } + + //Send page write command to both flashroms + oddXPaddrWrite(0x1040AAAA, 0xAAAA); + evenXPaddrWrite(0x10405555, 0x5555); + oddXPaddrWrite(0x1040AAAA, 0xA0A0); + + // Write 1 page each, one flashrom gets the low byte, the other the high byte. + for (unsigned long currByte = 0; currByte < 256; currByte += 2) { + // Join two bytes into one word + word currWord = ((sdBuffer[currByte] & 0xFF) << 8) | (sdBuffer[currByte + 1] & 0xFF); + // Set address + if ((((currByte/2) >> 4) & 0x1) == 0) { + evenXPaddrWrite(0x10400000 + (currPage*2) + currByte, currWord); + } else { + oddXPaddrWrite(0x10400000 + (currPage*2) + currByte, currWord); + } + } + processedProgressBar += 256; + draw_progressbar(processedProgressBar, totalProgressBar); + blinkLED(); + } +} + +unsigned long verifyXplorer_N64() { + uint32_t processedProgressBar = 0; + uint32_t totalProgressBar = (uint32_t)(262144); + println_Msg(F("")); + draw_progressbar(0, totalProgressBar); + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + myFile.seek(0); + writeErrors = 0; + + for (unsigned long currSector = 0; currSector < 262144; currSector += 131072) { + for (unsigned long currSdBuffer = 0; currSdBuffer < 131072; currSdBuffer += 512) { + // Fill SD buffer + myFile.read(sdBuffer, 512); + for (int currByte = 0; currByte < 512; currByte += 2) { + // Join two bytes into one word + word currWord = ((sdBuffer[currByte] & 0xFF) << 8) | (sdBuffer[currByte + 1] & 0xFF); + // Read flash + setAddress_N64(romBase + 0x400000 + currSector + currSdBuffer + currByte); + // Compare both + if (readWord_N64() != currWord) { + writeErrors++; + } + } + processedProgressBar += 512; + draw_progressbar(processedProgressBar, totalProgressBar); + blinkLED(); + } + } + // Close the file: + myFile.close(); + return writeErrors; + } else { + print_STR(open_file_STR, 1); + display_Update(); + return 9999; + } +} + #endif //******************************************