Skip to content

Commit

Permalink
Update N64.ino
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
RWeick authored Sep 11, 2023
1 parent 996d8f7 commit bc62414
Showing 1 changed file with 298 additions and 3 deletions.
301 changes: 298 additions & 3 deletions Cart_Reader/N64.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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 };

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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

//******************************************
Expand Down

0 comments on commit bc62414

Please sign in to comment.