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.
This commit is contained in:
sanni 2017-10-22 13:32:14 +02:00 committed by GitHub
parent 49fa28ee46
commit 27b63819e5
2 changed files with 337 additions and 260 deletions

View File

@ -2,8 +2,8 @@
Cartridge Reader for Arduino Mega2560 Cartridge Reader for Arduino Mega2560
Author: sanni Author: sanni
Date: 2017-10-13 Date: 2017-10-22
Version: V29D Version: V30
SD lib: https://github.com/greiman/SdFat SD lib: https://github.com/greiman/SdFat
LCD lib: https://github.com/adafruit/Adafruit_SSD1306 LCD lib: https://github.com/adafruit/Adafruit_SSD1306
@ -35,7 +35,7 @@
infinest - help with GB Memory cart infinest - help with GB Memory cart
**********************************************************************************/ **********************************************************************************/
char ver[5] = "V29D"; char ver[5] = "V30";
/****************************************** /******************************************
Define Output 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 gbxMenuItem2[] PROGMEM = "Game Boy Advance";
static const char* const menuOptionsGBx[] PROGMEM = {gbxMenuItem1, gbxMenuItem2}; 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() { void mainMenu() {
// create menu with title and 6 options to choose from // create menu with title and 6 options to choose from
unsigned char modeMenu; unsigned char modeMenu;
@ -365,40 +530,7 @@ void mainMenu() {
switch (modeMenu) switch (modeMenu)
{ {
case 0: case 0:
// create menu with title and 3 options to choose from n64Menu();
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;
}
break; break;
case 1: case 1:
@ -409,55 +541,11 @@ void mainMenu() {
break; break;
case 2: case 2:
// create menu with title and 2 options to choose from npMenu();
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;
}
break; break;
case 3: case 3:
// create menu with title and 2 options to choose from gbxMenu();
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;
}
break; break;
case 4: case 4:
@ -468,79 +556,11 @@ void mainMenu() {
break; break;
case 5: case 5:
// create menu with title and 2 options to choose from flashMenu();
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;
}
break; break;
case 6: case 6:
display_Clear(); aboutScreen();
// 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));
}
break; break;
} }
} }
@ -776,13 +796,33 @@ byte questionBox_Serial(const char* question, char answers[7][20], int num_answe
} }
// Wait for user input // Wait for user input
Serial.println(""); 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) { while (Serial.available() == 0) {
} }
// Read the incoming byte: // Read the incoming byte:
incomingByte = Serial.read() - 48; 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 // Print the received byte for validation e.g. in case of a different keyboard mapping
Serial.println(incomingByte); Serial.println(incomingByte);
Serial.println(""); Serial.println("");

View File

@ -983,7 +983,8 @@ void printCartInfo_N64() {
// Set cartSize to 64MB for test dumps // Set cartSize to 64MB for test dumps
cartSize = 64; cartSize = 64;
strcpy(romName, "GPERROR"); 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) else if (strcmp(flashid, "8816") == 0)
println_Msg(F("Intel 4400L0ZDQ0")); println_Msg(F("Intel 4400L0ZDQ0"));
else if (strcmp(flashid, "127E") == 0) else if (strcmp(flashid, "7E7E") == 0)
println_Msg(F("Fujitsu MSP55LV100S")); println_Msg(F("Fujitsu MSP55LV100S"));
else if ((strcmp(flashid, "227E") == 0) && (strcmp(cartID, "2301") == 0)) else if ((strcmp(flashid, "227E") == 0) && (strcmp(cartID, "2301") == 0))
println_Msg(F("Fujitsu MSP55LV512")); println_Msg(F("Fujitsu MSP55LV512"));
@ -2093,12 +2094,6 @@ void flashRepro_N64() {
display_Update(); display_Update();
wait(); wait();
/* Check if sectors are protected
if ((strcmp(flashid, "227E") == 0) || (strcmp(flashid, "127E") == 0)) {
sectorCheck_N64();
}
*/
// Launch file browser // Launch file browser
filePath[0] = '\0'; filePath[0] = '\0';
sd.chdir("/"); sd.chdir("/");
@ -2124,10 +2119,14 @@ void flashRepro_N64() {
} }
// Erase needed sectors // Erase needed sectors
if ((strcmp(flashid, "227E") == 0) || (strcmp(flashid, "127E") == 0)) { if (strcmp(flashid, "227E") == 0) {
// Spansion S29GL256N, Fujitsu MSP55LV512 or Fujitsu MSP55LV100S with 0x20000 sector size and 32 byte buffer // Spansion S29GL256N or Fujitsu MSP55LV512 with 0x20000 sector size and 32 byte buffer
eraseFlashrom_N64(0x20000); eraseFlashrom_N64(0x20000);
} }
else if (strcmp(flashid, "7E7E") == 0) {
// Fujitsu MSP55LV100S
eraseMSP55LV100_N64();
}
else if ((strcmp(flashid, "8813") == 0) || (strcmp(flashid, "8816") == 0)) { else if ((strcmp(flashid, "8813") == 0) || (strcmp(flashid, "8816") == 0)) {
// Intel 4400L0ZDQ0 // Intel 4400L0ZDQ0
eraseIntel4400_N64(); eraseIntel4400_N64();
@ -2146,10 +2145,14 @@ void flashRepro_N64() {
println_Msg(filePath); println_Msg(filePath);
display_Update(); display_Update();
if ((strcmp(flashid, "227E") == 0) || (strcmp(flashid, "127E") == 0)) { if (strcmp(flashid, "227E") == 0) {
// Spansion S29GL256N or Fujitsu MSP55LV512 or Fujitsu MSP55LV100S with 0x20000 sector size and 32 byte buffer // Spansion S29GL256N or Fujitsu MSP55LV512 with 0x20000 sector size and 32 byte buffer
writeFlashBuffer_N64(0x20000, 32); 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)) { else if ((strcmp(flashid, "22C9") == 0) || (strcmp(flashid, "22CB") == 0)) {
// Macronix MX29LV640 without buffer // Macronix MX29LV640 without buffer
writeFlashrom_N64(); writeFlashrom_N64();
@ -2178,6 +2181,8 @@ void flashRepro_N64() {
} }
} }
else { else {
// Close the file
myFile.close();
print_Error(F("failed"), false); 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 // Common reset command
void resetFlashrom_N64(unsigned long flashBase) { void resetFlashrom_N64(unsigned long flashBase) {
// Send reset Command // Send reset Command
@ -2320,108 +2333,32 @@ void idFlashrom_N64() {
// Empty cartID string // Empty cartID string
cartID[0] = '\0'; cartID[0] = '\0';
} }
//Fujitsu MSP55LV100S (128MB)
else if (strcmp(flashid, "127E") == 0) {
resetFlashrom_N64(romBase);
cartSize = 64;
}
//Fujitsu MSP55LV512/Spansion S29GL512N (64MB) //Fujitsu MSP55LV512/Spansion S29GL512N (64MB)
else if ((strcmp(cartID, "2301") == 0) && (strcmp(flashid, "227E") == 0)) { else if ((strcmp(cartID, "2301") == 0) && (strcmp(flashid, "227E") == 0)) {
resetFlashrom_N64(romBase); resetFlashrom_N64(romBase);
cartSize = 64; cartSize = 64;
} }
} //Test for Fujitsu MSP55LV100S (64MB)
else {
// 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)) {
// Send flashrom ID command // Send flashrom ID command
setAddress_N64(romBase + 0x2000000 + (0x555 << 1)); setAddress_N64(romBase + (0x555 << 1));
writeWord_N64(0xAA); writeWord_N64(0xAAAA);
setAddress_N64(romBase + 0x2000000 + (0x2AA << 1)); setAddress_N64(romBase + (0x2AA << 1));
writeWord_N64(0x55); writeWord_N64(0x5555);
setAddress_N64(romBase + 0x2000000 + (0x555 << 1)); setAddress_N64(romBase + (0x555 << 1));
writeWord_N64(0x90); writeWord_N64(0x9090);
for (unsigned long currSector = 0x2000000; currSector < 0x4000000; currSector += 0x20000) { setAddress_N64(romBase);
setAddress_N64(romBase + currSector + 0x04); // Read 1 byte vendor ID
tempWord = readWord_N64(); readWord_N64();
// Read 2 bytes flashrom ID
sprintf(cartID, "%04X", readWord_N64());
if (tempWord == 0x1) { if (strcmp(cartID, "7E7E") == 0) {
numLocked++; resetMSP55LV100_N64(romBase);
} cartSize = 64;
else if (tempWord == 0x0) { strncpy(flashid , cartID, 5);
numUnlocked++;
}
else {
numError++;
}
} }
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); 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)) { if ((cartSize = 64) && (fileSize > 0x2000000)) {
// Switch base address to second chip // Switch base address to second chip
flashBase = romBase + 0x2000000; 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 // Common sector erase command
void eraseFlashrom_N64(unsigned long sectorSize) { void eraseFlashrom_N64(unsigned long sectorSize) {
unsigned long flashBase = romBase; 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 // Write Spansion S29GL256N flashrom using the 32 byte write buffer
void writeFlashBuffer_N64(unsigned long sectorSize, byte bufferSize) { void writeFlashBuffer_N64(unsigned long sectorSize, byte bufferSize) {