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
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,17 +354,7 @@ 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 mainMenu() {
// create menu with title and 6 options to choose from
unsigned char modeMenu;
// Copy menuOptions out of progmem
convertPgm(modeOptions, 7);
modeMenu = question_box("Cartridge Reader", menuOptions, 7, 0);
// wait for user choice to come back from the question box menu
switch (modeMenu)
{
case 0:
void n64Menu() {
// create menu with title and 3 options to choose from
unsigned char n64Dev;
// Copy menuOptions out of progmem
@ -399,16 +389,9 @@ void mainMenu() {
break;
}
break;
}
case 1:
display_Clear();
display_Update();
setup_Snes();
mode = mode_SNES;
break;
case 2:
void npMenu() {
// create menu with title and 2 options to choose from
unsigned char npCart;
// Copy menuOptions out of progmem
@ -432,9 +415,9 @@ void mainMenu() {
mode = mode_GBM;
break;
}
break;
}
case 3:
void gbxMenu() {
// create menu with title and 2 options to choose from
unsigned char gbType;
// Copy menuOptions out of progmem
@ -458,16 +441,9 @@ void mainMenu() {
mode = mode_GBA;
break;
}
break;
}
case 4:
display_Clear();
display_Update();
setup_MD();
mode = mode_MD;
break;
case 5:
void flashMenu() {
// create menu with title and 2 options to choose from
unsigned char flashSlot;
// Copy menuOptions out of progmem
@ -491,9 +467,9 @@ void mainMenu() {
mode = mode_FLASH16;
break;
}
break;
}
case 6:
void aboutScreen() {
display_Clear();
// Draw the Logo
display.drawBitmap(0, 0, sig, 128, 64, 1);
@ -541,6 +517,50 @@ void mainMenu() {
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;
// Copy menuOptions out of progmem
convertPgm(modeOptions, 7);
modeMenu = question_box("Cartridge Reader", menuOptions, 7, 0);
// wait for user choice to come back from the question box menu
switch (modeMenu)
{
case 0:
n64Menu();
break;
case 1:
display_Clear();
display_Update();
setup_Snes();
mode = mode_SNES;
break;
case 2:
npMenu();
break;
case 3:
gbxMenu();
break;
case 4:
display_Clear();
display_Update();
setup_MD();
mode = mode_MD;
break;
case 5:
flashMenu();
break;
case 6:
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("");

View File

@ -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;
}
//Test for Fujitsu MSP55LV100S (64MB)
else {
lastSector = cartSize * 1024 * 1024;
}
// Send flashrom ID command
setAddress_N64(romBase + (0x555 << 1));
writeWord_N64(0xAA);
writeWord_N64(0xAAAA);
setAddress_N64(romBase + (0x2AA << 1));
writeWord_N64(0x55);
writeWord_N64(0x5555);
setAddress_N64(romBase + (0x555 << 1));
writeWord_N64(0x90);
writeWord_N64(0x9090);
word tempWord;
setAddress_N64(romBase);
// Read 1 byte vendor ID
readWord_N64();
// Read 2 bytes flashrom ID
sprintf(cartID, "%04X", readWord_N64());
for (unsigned long currSector = 0x0; currSector < lastSector; currSector += 0x20000) {
setAddress_N64(romBase + currSector + 0x04);
tempWord = readWord_N64();
if (tempWord == 0x1) {
numLocked++;
if (strcmp(cartID, "7E7E") == 0) {
resetMSP55LV100_N64(romBase);
cartSize = 64;
strncpy(flashid , cartID, 5);
}
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
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);
for (unsigned long currSector = 0x2000000; currSector < 0x4000000; currSector += 0x20000) {
setAddress_N64(romBase + currSector + 0x04);
tempWord = readWord_N64();
if (tempWord == 0x1) {
numLocked++;
}
else if (tempWord == 0x0) {
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);
}
// 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) {