From 463147561948d6ff090e92ecf9b7b652736c3dec Mon Sep 17 00:00:00 2001 From: sanni Date: Sun, 14 Oct 2018 10:09:25 +0200 Subject: [PATCH] Add files via upload --- Cart_Reader/EEPROMAnything.h | 38 +- Cart_Reader/GB.ino | 2070 ++++++------ Cart_Reader/GBA.ino | 5266 ++++++++++++++--------------- Cart_Reader/MD.ino | 996 +++--- Cart_Reader/N64.ino | 6044 +++++++++++++++++----------------- Cart_Reader/NP.ino | 4672 +++++++++++++------------- Cart_Reader/PCE.ino | 1416 ++++---- Cart_Reader/SNES.ino | 3852 +++++++++++----------- 8 files changed, 12177 insertions(+), 12177 deletions(-) diff --git a/Cart_Reader/EEPROMAnything.h b/Cart_Reader/EEPROMAnything.h index e5b4900..bae3580 100644 --- a/Cart_Reader/EEPROMAnything.h +++ b/Cart_Reader/EEPROMAnything.h @@ -1,20 +1,20 @@ -#include -#include // for type definitions - -template int EEPROM_writeAnything(int ee, const T& value) -{ - const byte* p = (const byte*)(const void*)&value; - unsigned int i; - for (i = 0; i < sizeof(value); i++) - EEPROM.write(ee++, *p++); - return i; -} - -template int EEPROM_readAnything(int ee, T& value) -{ - byte* p = (byte*)(void*)&value; - unsigned int i; - for (i = 0; i < sizeof(value); i++) - *p++ = EEPROM.read(ee++); - return i; +#include +#include // for type definitions + +template int EEPROM_writeAnything(int ee, const T& value) +{ + const byte* p = (const byte*)(const void*)&value; + unsigned int i; + for (i = 0; i < sizeof(value); i++) + EEPROM.write(ee++, *p++); + return i; +} + +template int EEPROM_readAnything(int ee, T& value) +{ + byte* p = (byte*)(void*)&value; + unsigned int i; + for (i = 0; i < sizeof(value); i++) + *p++ = EEPROM.read(ee++); + return i; } diff --git a/Cart_Reader/GB.ino b/Cart_Reader/GB.ino index 604defb..3c4705d 100644 --- a/Cart_Reader/GB.ino +++ b/Cart_Reader/GB.ino @@ -1,1036 +1,1036 @@ -//****************************************** -// GAME BOY MODULE -//****************************************** - -/****************************************** - Variables - *****************************************/ -// Game Boy -int sramBanks; -int romBanks; -uint16_t sramEndAddress = 0; - -/****************************************** - Menu - *****************************************/ -// GBx start menu -static const char gbxMenuItem1[] PROGMEM = "Game Boy (Color)"; -static const char gbxMenuItem2[] PROGMEM = "Game Boy Advance"; -static const char gbxMenuItem3[] PROGMEM = "NPower GB Memory"; -static const char* const menuOptionsGBx[] PROGMEM = {gbxMenuItem1, gbxMenuItem2, gbxMenuItem3}; - -// GB menu items -static const char GBMenuItem1[] PROGMEM = "Read Rom"; -static const char GBMenuItem2[] PROGMEM = "Read Save"; -static const char GBMenuItem3[] PROGMEM = "Write Save"; -static const char GBMenuItem4[] PROGMEM = "Flash MBC3 cart"; -static const char GBMenuItem5[] PROGMEM = "Flash MBC5 cart"; -static const char GBMenuItem6[] PROGMEM = "Reset"; -static const char* const menuOptionsGB[] PROGMEM = {GBMenuItem1, GBMenuItem2, GBMenuItem3, GBMenuItem4, GBMenuItem5, GBMenuItem6}; - -// Start menu for both GB and GBA -void gbxMenu() { - // create menu with title and 3 options to choose from - unsigned char gbType; - // Copy menuOptions out of progmem - convertPgm(menuOptionsGBx, 3); - gbType = question_box("Select Game Boy", menuOptions, 3, 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; - - case 2: - display_Clear(); - display_Update(); - setup_GBM(); - mode = mode_GBM; - break; - } -} - -void gbMenu() { - // create menu with title and 6 options to choose from - unsigned char mainMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsGB, 6); - mainMenu = question_box("GB Cart Reader", menuOptions, 6, 0); - - // wait for user choice to come back from the question box menu - switch (mainMenu) - { - case 0: - display_Clear(); - // Change working dir to root - sd.chdir("/"); - readROM_GB(); - compare_checksum_GB(); - break; - - case 1: - display_Clear(); - // Does cartridge have SRAM - if (sramEndAddress > 0) { - // Change working dir to root - sd.chdir("/"); - readSRAM_GB(); - } - else { - print_Error(F("Cart has no Sram"), false); - } - break; - - case 2: - display_Clear(); - // Does cartridge have SRAM - if (sramEndAddress > 0) { - // Change working dir to root - sd.chdir("/"); - writeSRAM_GB(); - unsigned long wrErrors; - wrErrors = verifySRAM_GB(); - if (wrErrors == 0) { - println_Msg(F("Verified OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(wrErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - } - else { - print_Error(F("Cart has no Sram"), false); - } - break; - - case 3: - // Change working dir to root - sd.chdir("/"); - //MBC3 - writeFlash_GB(3); - // Reset - wait(); - asm volatile (" jmp 0"); - break; - - case 4: - // Change working dir to root - sd.chdir("/"); - //MBC5 - writeFlash_GB(5); - // Reset - wait(); - asm volatile (" jmp 0"); - break; - - case 5: - asm volatile (" jmp 0"); - break; - } - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); -} - -/****************************************** - Setup - *****************************************/ -void setup_GB() { - // Set RST(PH0) to Input - DDRH &= ~(1 << 0); - // Activate Internal Pullup Resistors - PORTH |= (1 << 0); - - // Set Address Pins to Output - //A0-A7 - DDRF = 0xFF; - //A8-A15 - DDRK = 0xFF; - - // Set Control Pins to Output CS(PH3) WR(PH5) RD(PH6) - DDRH |= (1 << 3) | (1 << 5) | (1 << 6); - // Output a high signal on all pins, pins are active low therefore everything is disabled now - PORTH |= (1 << 3) | (1 << 5) | (1 << 6); - - // Set Data Pins (D0-D7) to Input - DDRC = 0x00; - // Disable Internal Pullups - //PORTC = 0x00; - - delay(400); - - // Print start page - getCartInfo_GB(); - display_Clear(); - if (strcmp(checksumStr, "00") != 0) { - println_Msg(F("GB Cart Info")); - print_Msg(F("Name: ")); - println_Msg(romName); - print_Msg(F("Rom Type: ")); - switch (romType) { - case 0: print_Msg(F("ROM ONLY")); break; - case 1: print_Msg(F("MBC1")); break; - case 2: print_Msg(F("MBC1+RAM")); break; - case 3: print_Msg(F("MBC1+RAM")); break; - case 5: print_Msg(F("MBC2")); break; - case 6: print_Msg(F("MBC2")); break; - case 8: print_Msg(F("ROM+RAM")); break; - case 9: print_Msg(F("ROM ONLY")); break; - case 11: print_Msg(F("MMM01")); break; - case 12: print_Msg(F("MMM01+RAM")); break; - case 13: print_Msg(F("MMM01+RAM")); break; - case 15: print_Msg(F("MBC3+TIMER")); break; - case 16: print_Msg(F("MBC3+TIMER+RAM")); break; - case 17: print_Msg(F("MBC3")); break; - case 18: print_Msg(F("MBC3+RAM")); break; - case 19: print_Msg(F("MBC3+RAM")); break; - case 21: print_Msg(F("MBC4")); break; - case 22: print_Msg(F("MBC4+RAM")); break; - case 23: print_Msg(F("MBC4+RAM")); break; - case 25: print_Msg(F("MBC5")); break; - case 26: print_Msg(F("MBC5+RAM")); break; - case 27: print_Msg(F("MBC5+RAM")); break; - case 28: print_Msg(F("MBC5+RUMBLE")); break; - case 29: print_Msg(F("MBC5+RUMBLE+RAM")); break; - case 30: print_Msg(F("MBC5+RUMBLE+RAM")); break; - case 252: print_Msg(F("Gameboy Camera")); break; - default: print_Msg(F("Not found")); - } - println_Msg(F(" ")); - print_Msg(F("Rom Size: ")); - switch (romSize) { - case 0: print_Msg(F("32KB")); break; - case 1: print_Msg(F("64KB")); break; - case 2: print_Msg(F("128KB")); break; - case 3: print_Msg(F("256KB")); break; - case 4: print_Msg(F("512KB")); break; - case 5: - if (romType == 1 || romType == 2 || romType == 3) { - print_Msg(F("1MB")); - } - else { - print_Msg(F("1MB")); - } - break; - case 6: - if (romType == 1 || romType == 2 || romType == 3) { - print_Msg(F("2MB")); - } - else { - print_Msg(F("2MB")); - } - break; - case 7: print_Msg(F("4MB")); break; - case 82: print_Msg(F("1.1MB")); break; - case 83: print_Msg(F("1.2MB")); break; - case 84: print_Msg(F("1.5MB)")); break; - default: print_Msg(F("Not found")); - } - println_Msg(F("")); - print_Msg(F("Banks: ")); - println_Msg(romBanks); - - print_Msg(F("Sram Size: ")); - switch (sramSize) { - case 0: - if (romType == 6) { - print_Msg(F("512B")); - } - else { - print_Msg(F("None")); - } - break; - case 1: print_Msg(F("2KB")); break; - case 2: print_Msg(F("8KB")); break; - case 3: print_Msg(F("32KB")); break; - case 4: print_Msg(F("128KB")); break; - default: print_Msg(F("Not found")); - } - println_Msg(F("")); - print_Msg(F("Checksum: ")); - println_Msg(checksumStr); - display_Update(); - - // Wait for user input - println_Msg(F("Press Button...")); - display_Update(); - wait(); - } - else { - print_Error(F("GAMEPAK ERROR"), true); - } -} - -/****************************************** - I/O Functions - *****************************************/ - -/****************************************** - Low level functions -*****************************************/ -// Switch data pins to read -void dataIn_GB() { - // Set to Input - DDRC = 0x00; -} - -byte readByte_GB(word myAddress) { - PORTF = myAddress & 0xFF; - PORTK = (myAddress >> 8) & 0xFF; - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Switch CS(PH3) and RD(PH6) to LOW - PORTH &= ~((1 << 3) | (1 << 6)); - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Read - byte tempByte = PINC; - - // Switch CS(PH3) and RD(PH6) to HIGH - PORTH |= (1 << 3) | (1 << 6); - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - return tempByte; -} - -void writeByte_GB(int myAddress, uint8_t myData) { - PORTF = myAddress & 0xFF; - PORTK = (myAddress >> 8) & 0xFF; - PORTC = myData; - - // Arduino running at 16Mhz -> one nop = 62.5ns - // Wait till output is stable - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Pull WR(PH5) low - PORTH &= ~(1 << 5); - - // Leave WE low for at least 60ns - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Pull WR(PH5) HIGH - PORTH |= (1 << 5); - - // Leave WE high for at least 50ns - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); -} - -/****************************************** - Game Boy functions -*****************************************/ -// Read Cartridge Header -void getCartInfo_GB() { - romType = readByte_GB(0x0147); - romSize = readByte_GB(0x0148); - sramSize = readByte_GB(0x0149); - - // ROM banks - romBanks = 2; // Default 32K - if (romSize >= 1) { // Calculate rom size - romBanks = 2 << romSize; - } - - // RAM banks - sramBanks = 0; // Default 0K RAM - if (romType == 6) { - sramBanks = 1; - } - if (sramSize == 2) { - sramBanks = 1; - } - if (sramSize == 3) { - sramBanks = 4; - } - if (sramSize == 4) { - sramBanks = 16; - } - if (sramSize == 5) { - sramBanks = 8; - } - - // RAM end address - if (romType == 6) { - sramEndAddress = 0xA1FF; // MBC2 512bytes (nibbles) - } - if (sramSize == 1) { - sramEndAddress = 0xA7FF; // 2K RAM - } - if (sramSize > 1) { - sramEndAddress = 0xBFFF; // 8K RAM - } - - // Get Checksum as string - sprintf(checksumStr, "%02X%02X", readByte_GB(0x014E), readByte_GB(0x014F)); - - // Get name - byte myByte = 0; - byte myLength = 0; - - for (int addr = 0x0134; addr <= 0x13C; addr++) { - myByte = readByte_GB(addr); - if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 15) { - romName[myLength] = char(myByte); - myLength++; - } - } -} - -// Dump ROM -void readROM_GB() { - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".GB"); - - // create a new folder for the rom file - EEPROM_readAnything(10, foldern); - sprintf(folder, "GB/ROM/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - display_Clear(); - print_Msg(F("Saving to ")); - print_Msg(folder); - println_Msg(F("/...")); - display_Update(); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - //open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("Can't create file on SD"), true); - } - - uint16_t romAddress = 0; - - // Read number of banks and switch banks - for (uint16_t bank = 1; bank < romBanks; bank++) { - // Switch data pins to output - dataOut(); - - if (romType >= 5) { // MBC2 and above - writeByte_GB(0x2100, bank); // Set ROM bank - } - else { // MBC1 - writeByte_GB(0x6000, 0); // Set ROM Mode - writeByte_GB(0x4000, bank >> 5); // Set bits 5 & 6 (01100000) of ROM bank - writeByte_GB(0x2000, bank & 0x1F); // Set bits 0 & 4 (00011111) of ROM bank - } - - // Switch data pins to intput - dataIn_GB(); - - if (bank > 1) { - romAddress = 0x4000; - } - - // Read up to 7FFF per bank - while (romAddress <= 0x7FFF) { - uint8_t readData[512]; - for (int i = 0; i < 512; i++) { - readData[i] = readByte_GB(romAddress + i); - } - myFile.write(readData, 512); - romAddress += 512; - } - } - - // Close the file: - myFile.close(); -} - -unsigned int calc_checksum_GB (char* fileName, char* folder) { - unsigned int calcChecksum = 0; - int calcFilesize = 0; - unsigned long i = 0; - int c = 0; - - if (strcmp(folder, "root") != 0) - sd.chdir(folder); - - // If file exists - if (myFile.open(fileName, O_READ)) { - calcFilesize = myFile.fileSize() * 8 / 1024 / 1024; - for (i = 0; i < (myFile.fileSize() / 512); i++) { - myFile.read(sdBuffer, 512); - for (c = 0; c < 512; c++) { - calcChecksum += sdBuffer[c]; - } - } - myFile.close(); - sd.chdir(); - // Subtract checksum bytes - calcChecksum -= readByte_GB(0x014E); - calcChecksum -= readByte_GB(0x014F); - - // Return result - return (calcChecksum); - } - // Else show error - else { - print_Error(F("DUMP ROM 1ST"), false); - return 0; - } -} - -boolean compare_checksum_GB() { - - println_Msg(F("Calculating Checksum")); - display_Update(); - - strcpy(fileName, romName); - strcat(fileName, ".GB"); - - // last used rom folder - EEPROM_readAnything(10, foldern); - sprintf(folder, "GB/ROM/%s/%d", romName, foldern - 1); - - char calcsumStr[5]; - sprintf(calcsumStr, "%04X", calc_checksum_GB(fileName, folder)); - - if (strcmp(calcsumStr, checksumStr) == 0) { - print_Msg(F("Result: ")); - println_Msg(calcsumStr); - println_Msg(F("Checksum matches")); - display_Update(); - return 1; - } - else { - print_Msg(F("Result: ")); - println_Msg(calcsumStr); - print_Error(F("Checksum Error"), false); - return 0; - } -} - -// Read RAM -void readSRAM_GB() { - // Does cartridge have RAM - if (sramEndAddress > 0) { - - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".sav"); - - // create a new folder for the save file - EEPROM_readAnything(10, foldern); - sprintf(folder, "GB/SAVE/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - //open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("SD Error"), true); - } - - dataIn_GB(); - - // MBC2 Fix (unknown why this fixes it, maybe has to read ROM before RAM?) - readByte_GB(0x0134); - - dataOut(); - if (romType <= 4) { // MBC1 - writeByte_GB(0x6000, 1); // Set RAM Mode - } - - // Initialise MBC - writeByte_GB(0x0000, 0x0A); - - // Switch SRAM banks - for (uint8_t bank = 0; bank < sramBanks; bank++) { - dataOut(); - writeByte_GB(0x4000, bank); - - // Read SRAM - dataIn_GB(); - for (uint16_t sramAddress = 0xA000; sramAddress <= sramEndAddress; sramAddress += 64) { - uint8_t readData[64]; - for (uint8_t i = 0; i < 64; i++) { - readData[i] = readByte_GB(sramAddress + i); - } - myFile.write(readData, 64); - } - } - - // Disable SRAM - dataOut(); - writeByte_GB(0x0000, 0x00); - dataIn_GB(); - - // Close the file: - myFile.close(); - - // Signal end of process - print_Msg(F("Saved to ")); - print_Msg(folder); - println_Msg(F("/")); - display_Update(); - } - else { - print_Error(F("Cart has no SRAM"), false); - } -} - -// Write RAM -void writeSRAM_GB() { - // Does cartridge have SRAM - if (sramEndAddress > 0) { - - filePath[0] = '\0'; - sd.chdir("/"); - fileBrowser("Select sav file"); - // Create filepath - sprintf(filePath, "%s/%s", filePath, fileName); - - //open file on sd card - if (myFile.open(filePath, O_READ)) { - // Set pins to input - dataIn_GB(); - - // MBC2 Fix (unknown why this fixes it, maybe has to read ROM before RAM?) - readByte_GB(0x0134); - - dataOut(); - - if (romType <= 4) { // MBC1 - writeByte_GB(0x6000, 1); // Set RAM Mode - } - - // Initialise MBC - writeByte_GB(0x0000, 0x0A); - - // Switch RAM banks - for (uint8_t bank = 0; bank < sramBanks; bank++) { - writeByte_GB(0x4000, bank); - - // Write RAM - for (uint16_t sramAddress = 0xA000; sramAddress <= sramEndAddress; sramAddress++) { - // Pull CS(PH3) LOW - PORTH &= ~(1 << 3); - // Write to RAM - writeByte_GB(sramAddress, myFile.read()); - asm volatile("nop"); - asm volatile("nop"); - asm volatile("nop"); - // Pull CS(PH3) HIGH - PORTH |= (1 << 3) ; - } - } - // Disable RAM - writeByte_GB(0x0000, 0x00); - - // Set pins to input - dataIn_GB(); - - // Close the file: - myFile.close(); - display_Clear(); - println_Msg(F("SRAM writing finished")); - display_Update(); - - } - else { - print_Error(F("File doesnt exist"), false); - } - } - else { - print_Error(F("Cart has no SRAM"), false); - } -} - -// Check if the SRAM was written without any error -unsigned long verifySRAM_GB() { - - //open file on sd card - if (myFile.open(filePath, O_READ)) { - - // Variable for errors - writeErrors = 0; - - dataIn_GB(); - - // MBC2 Fix (unknown why this fixes it, maybe has to read ROM before RAM?) - readByte_GB(0x0134); - - // Does cartridge have RAM - if (sramEndAddress > 0) { - dataOut(); - if (romType <= 4) { // MBC1 - writeByte_GB(0x6000, 1); // Set RAM Mode - } - - // Initialise MBC - writeByte_GB(0x0000, 0x0A); - - // Switch SRAM banks - for (uint8_t bank = 0; bank < sramBanks; bank++) { - dataOut(); - writeByte_GB(0x4000, bank); - - // Read SRAM - dataIn_GB(); - for (uint16_t sramAddress = 0xA000; sramAddress <= sramEndAddress; sramAddress += 64) { - //fill sdBuffer - myFile.read(sdBuffer, 64); - for (int c = 0; c < 64; c++) { - if (readByte_GB(sramAddress + c) != sdBuffer[c]) { - writeErrors++; - } - } - } - } - dataOut(); - // Disable RAM - writeByte_GB(0x0000, 0x00); - dataIn_GB(); - } - // Close the file: - myFile.close(); - return writeErrors; - } - else { - print_Error(F("Can't open file"), true); - } -} - -// Write 29F032 flashrom -// A0-A13 directly connected to cart edge -> 16384(0x0-0x3FFF) bytes per bank -> 256(0x0-0xFF) banks -// A14-A21 connected to MBC5 -void writeFlash_GB(byte MBC) { - // Launch filebrowser - filePath[0] = '\0'; - sd.chdir("/"); - fileBrowser("Select file"); - display_Clear(); - - // Create filepath - sprintf(filePath, "%s/%s", filePath, fileName); - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - // Get rom size from file - myFile.seekCur(0x147); - romType = myFile.read(); - romSize = myFile.read(); - // Go back to file beginning - myFile.seekSet(0); - - // ROM banks - romBanks = 2; // Default 32K - if (romSize >= 1) { // Calculate rom size - romBanks = 2 << romSize; - } - - // Set data pins to output - dataOut(); - - // Set ROM bank hi 0 - writeByte_GB(0x3000, 0); - // Set ROM bank low 0 - writeByte_GB(0x2000, 0); - delay(100); - - // Reset flash - writeByte_GB(0x555, 0xf0); - delay(100); - - // ID command sequence - writeByte_GB(0x555, 0xaa); - writeByte_GB(0x2aa, 0x55); - writeByte_GB(0x555, 0x90); - - dataIn_GB(); - - // Read the two id bytes into a string - sprintf(flashid, "%02X%02X", readByte_GB(0), readByte_GB(1)); - - if (strcmp(flashid, "04D4") == 0) { - println_Msg(F("MBM29F033C")); - print_Msg(F("Banks: ")); - print_Msg(romBanks); - println_Msg(F("/256")); - display_Update(); - } - else if (strcmp(flashid, "0141") == 0) { - println_Msg(F("AM29F032B")); - print_Msg(F("Banks: ")); - print_Msg(romBanks); - println_Msg(F("/256")); - display_Update(); - } - else if (strcmp(flashid, "01AD") == 0) { - println_Msg(F("AM29F016B")); - print_Msg(F("Banks: ")); - print_Msg(romBanks); - println_Msg(F("/128")); - display_Update(); - } - else if (strcmp(flashid, "01D5") == 0) { - println_Msg(F("AM29F080B")); - print_Msg(F("Banks: ")); - print_Msg(romBanks); - println_Msg(F("/64")); - display_Update(); - } - else { - print_Msg(F("Flash ID: ")); - println_Msg(flashid); - display_Update(); - print_Error(F("Unknown flashrom"), true); - } - dataOut(); - - // Reset flash - writeByte_GB(0x555, 0xf0); - - delay(100); - println_Msg(F("Erasing flash")); - display_Update(); - - // Erase flash - writeByte_GB(0x555, 0xaa); - writeByte_GB(0x2aa, 0x55); - writeByte_GB(0x555, 0x80); - writeByte_GB(0x555, 0xaa); - writeByte_GB(0x2aa, 0x55); - writeByte_GB(0x555, 0x10); - - dataIn_GB(); - - // Read the status register - byte statusReg = readByte_GB(0); - - // After a completed erase D7 will output 1 - while ((statusReg & 0x80) != 0x80) { - // Blink led - PORTB ^= (1 << 4); - delay(100); - // Update Status - statusReg = readByte_GB(0); - } - - // Blankcheck - println_Msg(F("Blankcheck")); - display_Update(); - - // Read x number of banks - for (int currBank = 0; currBank < romBanks; currBank++) { - // Blink led - PORTB ^= (1 << 4); - - dataOut(); - - // Set ROM bank - writeByte_GB(0x2000, currBank); - dataIn(); - - for (unsigned int currAddr = 0x4000; currAddr < 0x7FFF; currAddr += 512) { - uint8_t readData[512]; - for (int currByte = 0; currByte < 512; currByte++) { - readData[currByte] = readByte_GB(currAddr + currByte); - } - for (int j = 0; j < 512; j++) { - if (readData[j] != 0xFF) { - println_Msg(F("Not empty")); - print_Error(F("Erase failed"), true); - } - } - } - } - - if (MBC == 3) { - println_Msg(F("Writing flash MBC3")); - display_Update(); - - // Write flash - dataOut(); - - uint16_t currAddr = 0; - - for (int currBank = 1; currBank < romBanks; currBank++) { - // Blink led - PORTB ^= (1 << 4); - - // Set ROM bank - writeByte_GB(0x2100, currBank); - - if (currBank > 1) { - currAddr = 0x4000; - } - - while (currAddr <= 0x7FFF) { - myFile.read(sdBuffer, 512); - - for (int currByte = 0; currByte < 512; currByte++) { - // Write command sequence - writeByte_GB(0x555, 0xaa); - writeByte_GB(0x2aa, 0x55); - writeByte_GB(0x555, 0xa0); - // Write current byte - writeByte_GB(currAddr + currByte, sdBuffer[currByte]); - - // Set data pins to input - dataIn(); - - // Setting CS(PH3) and OE/RD(PH6) LOW - PORTH &= ~((1 << 3) | (1 << 6)); - - // Busy check - while ((PINC & 0x80) != (sdBuffer[currByte] & 0x80)) { - } - - // Switch CS(PH3) and OE/RD(PH6) to HIGH - PORTH |= (1 << 3) | (1 << 6); - - // Set data pins to output - dataOut(); - } - currAddr += 512; - } - } - } - - else if (MBC == 5) { - println_Msg(F("Writing flash MBC5")); - display_Update(); - - // Write flash - dataOut(); - - for (int currBank = 0; currBank < romBanks; currBank++) { - // Blink led - PORTB ^= (1 << 4); - - // Set ROM bank - writeByte_GB(0x2000, currBank); - // 0x2A8000 fix - writeByte_GB(0x4000, 0x0); - - for (unsigned int currAddr = 0x4000; currAddr < 0x7FFF; currAddr += 512) { - myFile.read(sdBuffer, 512); - - for (int currByte = 0; currByte < 512; currByte++) { - // Write command sequence - writeByte_GB(0x555, 0xaa); - writeByte_GB(0x2aa, 0x55); - writeByte_GB(0x555, 0xa0); - // Write current byte - writeByte_GB(currAddr + currByte, sdBuffer[currByte]); - - // Set data pins to input - dataIn(); - - // Setting CS(PH3) and OE/RD(PH6) LOW - PORTH &= ~((1 << 3) | (1 << 6)); - - // Busy check - while ((PINC & 0x80) != (sdBuffer[currByte] & 0x80)) { - } - - // Switch CS(PH3) and OE/RD(PH6) to HIGH - PORTH |= (1 << 3) | (1 << 6); - - // Set data pins to output - dataOut(); - } - } - } - } - - // Set data pins to input again - dataIn_GB(); - - println_Msg(F("Verifying")); - display_Update(); - - // Go back to file beginning - myFile.seekSet(0); - unsigned int addr = 0; - writeErrors = 0; - - // Verify flashrom - uint16_t romAddress = 0; - - // Read number of banks and switch banks - for (uint16_t bank = 1; bank < romBanks; bank++) { - // Switch data pins to output - dataOut(); - - if (romType >= 5) { // MBC2 and above - writeByte_GB(0x2100, bank); // Set ROM bank - } - else { // MBC1 - writeByte_GB(0x6000, 0); // Set ROM Mode - writeByte_GB(0x4000, bank >> 5); // Set bits 5 & 6 (01100000) of ROM bank - writeByte_GB(0x2000, bank & 0x1F); // Set bits 0 & 4 (00011111) of ROM bank - } - - // Switch data pins to intput - dataIn_GB(); - - if (bank > 1) { - romAddress = 0x4000; - } - // Blink led - PORTB ^= (1 << 4); - - // Read up to 7FFF per bank - while (romAddress <= 0x7FFF) { - // Fill sdBuffer - myFile.read(sdBuffer, 512); - // Compare - for (int i = 0; i < 512; i++) { - if (readByte_GB(romAddress + i) != sdBuffer[i]) { - writeErrors++; - } - } - romAddress += 512; - } - } - // Close the file: - myFile.close(); - - if (writeErrors == 0) { - println_Msg(F("OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - } - else { - println_Msg(F("Can't open file")); - display_Update(); - } -} - -//****************************************** -// End of File +//****************************************** +// GAME BOY MODULE +//****************************************** + +/****************************************** + Variables + *****************************************/ +// Game Boy +int sramBanks; +int romBanks; +uint16_t sramEndAddress = 0; + +/****************************************** + Menu + *****************************************/ +// GBx start menu +static const char gbxMenuItem1[] PROGMEM = "Game Boy (Color)"; +static const char gbxMenuItem2[] PROGMEM = "Game Boy Advance"; +static const char gbxMenuItem3[] PROGMEM = "NPower GB Memory"; +static const char* const menuOptionsGBx[] PROGMEM = {gbxMenuItem1, gbxMenuItem2, gbxMenuItem3}; + +// GB menu items +static const char GBMenuItem1[] PROGMEM = "Read Rom"; +static const char GBMenuItem2[] PROGMEM = "Read Save"; +static const char GBMenuItem3[] PROGMEM = "Write Save"; +static const char GBMenuItem4[] PROGMEM = "Flash MBC3 cart"; +static const char GBMenuItem5[] PROGMEM = "Flash MBC5 cart"; +static const char GBMenuItem6[] PROGMEM = "Reset"; +static const char* const menuOptionsGB[] PROGMEM = {GBMenuItem1, GBMenuItem2, GBMenuItem3, GBMenuItem4, GBMenuItem5, GBMenuItem6}; + +// Start menu for both GB and GBA +void gbxMenu() { + // create menu with title and 3 options to choose from + unsigned char gbType; + // Copy menuOptions out of progmem + convertPgm(menuOptionsGBx, 3); + gbType = question_box("Select Game Boy", menuOptions, 3, 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; + + case 2: + display_Clear(); + display_Update(); + setup_GBM(); + mode = mode_GBM; + break; + } +} + +void gbMenu() { + // create menu with title and 6 options to choose from + unsigned char mainMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsGB, 6); + mainMenu = question_box("GB Cart Reader", menuOptions, 6, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + case 0: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readROM_GB(); + compare_checksum_GB(); + break; + + case 1: + display_Clear(); + // Does cartridge have SRAM + if (sramEndAddress > 0) { + // Change working dir to root + sd.chdir("/"); + readSRAM_GB(); + } + else { + print_Error(F("Cart has no Sram"), false); + } + break; + + case 2: + display_Clear(); + // Does cartridge have SRAM + if (sramEndAddress > 0) { + // Change working dir to root + sd.chdir("/"); + writeSRAM_GB(); + unsigned long wrErrors; + wrErrors = verifySRAM_GB(); + if (wrErrors == 0) { + println_Msg(F("Verified OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(wrErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + } + else { + print_Error(F("Cart has no Sram"), false); + } + break; + + case 3: + // Change working dir to root + sd.chdir("/"); + //MBC3 + writeFlash_GB(3); + // Reset + wait(); + asm volatile (" jmp 0"); + break; + + case 4: + // Change working dir to root + sd.chdir("/"); + //MBC5 + writeFlash_GB(5); + // Reset + wait(); + asm volatile (" jmp 0"); + break; + + case 5: + asm volatile (" jmp 0"); + break; + } + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +/****************************************** + Setup + *****************************************/ +void setup_GB() { + // Set RST(PH0) to Input + DDRH &= ~(1 << 0); + // Activate Internal Pullup Resistors + PORTH |= (1 << 0); + + // Set Address Pins to Output + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + + // Set Control Pins to Output CS(PH3) WR(PH5) RD(PH6) + DDRH |= (1 << 3) | (1 << 5) | (1 << 6); + // Output a high signal on all pins, pins are active low therefore everything is disabled now + PORTH |= (1 << 3) | (1 << 5) | (1 << 6); + + // Set Data Pins (D0-D7) to Input + DDRC = 0x00; + // Disable Internal Pullups + //PORTC = 0x00; + + delay(400); + + // Print start page + getCartInfo_GB(); + display_Clear(); + if (strcmp(checksumStr, "00") != 0) { + println_Msg(F("GB Cart Info")); + print_Msg(F("Name: ")); + println_Msg(romName); + print_Msg(F("Rom Type: ")); + switch (romType) { + case 0: print_Msg(F("ROM ONLY")); break; + case 1: print_Msg(F("MBC1")); break; + case 2: print_Msg(F("MBC1+RAM")); break; + case 3: print_Msg(F("MBC1+RAM")); break; + case 5: print_Msg(F("MBC2")); break; + case 6: print_Msg(F("MBC2")); break; + case 8: print_Msg(F("ROM+RAM")); break; + case 9: print_Msg(F("ROM ONLY")); break; + case 11: print_Msg(F("MMM01")); break; + case 12: print_Msg(F("MMM01+RAM")); break; + case 13: print_Msg(F("MMM01+RAM")); break; + case 15: print_Msg(F("MBC3+TIMER")); break; + case 16: print_Msg(F("MBC3+TIMER+RAM")); break; + case 17: print_Msg(F("MBC3")); break; + case 18: print_Msg(F("MBC3+RAM")); break; + case 19: print_Msg(F("MBC3+RAM")); break; + case 21: print_Msg(F("MBC4")); break; + case 22: print_Msg(F("MBC4+RAM")); break; + case 23: print_Msg(F("MBC4+RAM")); break; + case 25: print_Msg(F("MBC5")); break; + case 26: print_Msg(F("MBC5+RAM")); break; + case 27: print_Msg(F("MBC5+RAM")); break; + case 28: print_Msg(F("MBC5+RUMBLE")); break; + case 29: print_Msg(F("MBC5+RUMBLE+RAM")); break; + case 30: print_Msg(F("MBC5+RUMBLE+RAM")); break; + case 252: print_Msg(F("Gameboy Camera")); break; + default: print_Msg(F("Not found")); + } + println_Msg(F(" ")); + print_Msg(F("Rom Size: ")); + switch (romSize) { + case 0: print_Msg(F("32KB")); break; + case 1: print_Msg(F("64KB")); break; + case 2: print_Msg(F("128KB")); break; + case 3: print_Msg(F("256KB")); break; + case 4: print_Msg(F("512KB")); break; + case 5: + if (romType == 1 || romType == 2 || romType == 3) { + print_Msg(F("1MB")); + } + else { + print_Msg(F("1MB")); + } + break; + case 6: + if (romType == 1 || romType == 2 || romType == 3) { + print_Msg(F("2MB")); + } + else { + print_Msg(F("2MB")); + } + break; + case 7: print_Msg(F("4MB")); break; + case 82: print_Msg(F("1.1MB")); break; + case 83: print_Msg(F("1.2MB")); break; + case 84: print_Msg(F("1.5MB)")); break; + default: print_Msg(F("Not found")); + } + println_Msg(F("")); + print_Msg(F("Banks: ")); + println_Msg(romBanks); + + print_Msg(F("Sram Size: ")); + switch (sramSize) { + case 0: + if (romType == 6) { + print_Msg(F("512B")); + } + else { + print_Msg(F("None")); + } + break; + case 1: print_Msg(F("2KB")); break; + case 2: print_Msg(F("8KB")); break; + case 3: print_Msg(F("32KB")); break; + case 4: print_Msg(F("128KB")); break; + default: print_Msg(F("Not found")); + } + println_Msg(F("")); + print_Msg(F("Checksum: ")); + println_Msg(checksumStr); + display_Update(); + + // Wait for user input + println_Msg(F("Press Button...")); + display_Update(); + wait(); + } + else { + print_Error(F("GAMEPAK ERROR"), true); + } +} + +/****************************************** + I/O Functions + *****************************************/ + +/****************************************** + Low level functions +*****************************************/ +// Switch data pins to read +void dataIn_GB() { + // Set to Input + DDRC = 0x00; +} + +byte readByte_GB(word myAddress) { + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Switch CS(PH3) and RD(PH6) to LOW + PORTH &= ~((1 << 3) | (1 << 6)); + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Read + byte tempByte = PINC; + + // Switch CS(PH3) and RD(PH6) to HIGH + PORTH |= (1 << 3) | (1 << 6); + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + return tempByte; +} + +void writeByte_GB(int myAddress, uint8_t myData) { + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + PORTC = myData; + + // Arduino running at 16Mhz -> one nop = 62.5ns + // Wait till output is stable + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Pull WR(PH5) low + PORTH &= ~(1 << 5); + + // Leave WE low for at least 60ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Pull WR(PH5) HIGH + PORTH |= (1 << 5); + + // Leave WE high for at least 50ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); +} + +/****************************************** + Game Boy functions +*****************************************/ +// Read Cartridge Header +void getCartInfo_GB() { + romType = readByte_GB(0x0147); + romSize = readByte_GB(0x0148); + sramSize = readByte_GB(0x0149); + + // ROM banks + romBanks = 2; // Default 32K + if (romSize >= 1) { // Calculate rom size + romBanks = 2 << romSize; + } + + // RAM banks + sramBanks = 0; // Default 0K RAM + if (romType == 6) { + sramBanks = 1; + } + if (sramSize == 2) { + sramBanks = 1; + } + if (sramSize == 3) { + sramBanks = 4; + } + if (sramSize == 4) { + sramBanks = 16; + } + if (sramSize == 5) { + sramBanks = 8; + } + + // RAM end address + if (romType == 6) { + sramEndAddress = 0xA1FF; // MBC2 512bytes (nibbles) + } + if (sramSize == 1) { + sramEndAddress = 0xA7FF; // 2K RAM + } + if (sramSize > 1) { + sramEndAddress = 0xBFFF; // 8K RAM + } + + // Get Checksum as string + sprintf(checksumStr, "%02X%02X", readByte_GB(0x014E), readByte_GB(0x014F)); + + // Get name + byte myByte = 0; + byte myLength = 0; + + for (int addr = 0x0134; addr <= 0x13C; addr++) { + myByte = readByte_GB(addr); + if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 15) { + romName[myLength] = char(myByte); + myLength++; + } + } +} + +// Dump ROM +void readROM_GB() { + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".GB"); + + // create a new folder for the rom file + EEPROM_readAnything(10, foldern); + sprintf(folder, "GB/ROM/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("Can't create file on SD"), true); + } + + uint16_t romAddress = 0; + + // Read number of banks and switch banks + for (uint16_t bank = 1; bank < romBanks; bank++) { + // Switch data pins to output + dataOut(); + + if (romType >= 5) { // MBC2 and above + writeByte_GB(0x2100, bank); // Set ROM bank + } + else { // MBC1 + writeByte_GB(0x6000, 0); // Set ROM Mode + writeByte_GB(0x4000, bank >> 5); // Set bits 5 & 6 (01100000) of ROM bank + writeByte_GB(0x2000, bank & 0x1F); // Set bits 0 & 4 (00011111) of ROM bank + } + + // Switch data pins to intput + dataIn_GB(); + + if (bank > 1) { + romAddress = 0x4000; + } + + // Read up to 7FFF per bank + while (romAddress <= 0x7FFF) { + uint8_t readData[512]; + for (int i = 0; i < 512; i++) { + readData[i] = readByte_GB(romAddress + i); + } + myFile.write(readData, 512); + romAddress += 512; + } + } + + // Close the file: + myFile.close(); +} + +unsigned int calc_checksum_GB (char* fileName, char* folder) { + unsigned int calcChecksum = 0; + int calcFilesize = 0; + unsigned long i = 0; + int c = 0; + + if (strcmp(folder, "root") != 0) + sd.chdir(folder); + + // If file exists + if (myFile.open(fileName, O_READ)) { + calcFilesize = myFile.fileSize() * 8 / 1024 / 1024; + for (i = 0; i < (myFile.fileSize() / 512); i++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksum += sdBuffer[c]; + } + } + myFile.close(); + sd.chdir(); + // Subtract checksum bytes + calcChecksum -= readByte_GB(0x014E); + calcChecksum -= readByte_GB(0x014F); + + // Return result + return (calcChecksum); + } + // Else show error + else { + print_Error(F("DUMP ROM 1ST"), false); + return 0; + } +} + +boolean compare_checksum_GB() { + + println_Msg(F("Calculating Checksum")); + display_Update(); + + strcpy(fileName, romName); + strcat(fileName, ".GB"); + + // last used rom folder + EEPROM_readAnything(10, foldern); + sprintf(folder, "GB/ROM/%s/%d", romName, foldern - 1); + + char calcsumStr[5]; + sprintf(calcsumStr, "%04X", calc_checksum_GB(fileName, folder)); + + if (strcmp(calcsumStr, checksumStr) == 0) { + print_Msg(F("Result: ")); + println_Msg(calcsumStr); + println_Msg(F("Checksum matches")); + display_Update(); + return 1; + } + else { + print_Msg(F("Result: ")); + println_Msg(calcsumStr); + print_Error(F("Checksum Error"), false); + return 0; + } +} + +// Read RAM +void readSRAM_GB() { + // Does cartridge have RAM + if (sramEndAddress > 0) { + + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".sav"); + + // create a new folder for the save file + EEPROM_readAnything(10, foldern); + sprintf(folder, "GB/SAVE/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + + dataIn_GB(); + + // MBC2 Fix (unknown why this fixes it, maybe has to read ROM before RAM?) + readByte_GB(0x0134); + + dataOut(); + if (romType <= 4) { // MBC1 + writeByte_GB(0x6000, 1); // Set RAM Mode + } + + // Initialise MBC + writeByte_GB(0x0000, 0x0A); + + // Switch SRAM banks + for (uint8_t bank = 0; bank < sramBanks; bank++) { + dataOut(); + writeByte_GB(0x4000, bank); + + // Read SRAM + dataIn_GB(); + for (uint16_t sramAddress = 0xA000; sramAddress <= sramEndAddress; sramAddress += 64) { + uint8_t readData[64]; + for (uint8_t i = 0; i < 64; i++) { + readData[i] = readByte_GB(sramAddress + i); + } + myFile.write(readData, 64); + } + } + + // Disable SRAM + dataOut(); + writeByte_GB(0x0000, 0x00); + dataIn_GB(); + + // Close the file: + myFile.close(); + + // Signal end of process + print_Msg(F("Saved to ")); + print_Msg(folder); + println_Msg(F("/")); + display_Update(); + } + else { + print_Error(F("Cart has no SRAM"), false); + } +} + +// Write RAM +void writeSRAM_GB() { + // Does cartridge have SRAM + if (sramEndAddress > 0) { + + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser("Select sav file"); + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + + //open file on sd card + if (myFile.open(filePath, O_READ)) { + // Set pins to input + dataIn_GB(); + + // MBC2 Fix (unknown why this fixes it, maybe has to read ROM before RAM?) + readByte_GB(0x0134); + + dataOut(); + + if (romType <= 4) { // MBC1 + writeByte_GB(0x6000, 1); // Set RAM Mode + } + + // Initialise MBC + writeByte_GB(0x0000, 0x0A); + + // Switch RAM banks + for (uint8_t bank = 0; bank < sramBanks; bank++) { + writeByte_GB(0x4000, bank); + + // Write RAM + for (uint16_t sramAddress = 0xA000; sramAddress <= sramEndAddress; sramAddress++) { + // Pull CS(PH3) LOW + PORTH &= ~(1 << 3); + // Write to RAM + writeByte_GB(sramAddress, myFile.read()); + asm volatile("nop"); + asm volatile("nop"); + asm volatile("nop"); + // Pull CS(PH3) HIGH + PORTH |= (1 << 3) ; + } + } + // Disable RAM + writeByte_GB(0x0000, 0x00); + + // Set pins to input + dataIn_GB(); + + // Close the file: + myFile.close(); + display_Clear(); + println_Msg(F("SRAM writing finished")); + display_Update(); + + } + else { + print_Error(F("File doesnt exist"), false); + } + } + else { + print_Error(F("Cart has no SRAM"), false); + } +} + +// Check if the SRAM was written without any error +unsigned long verifySRAM_GB() { + + //open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Variable for errors + writeErrors = 0; + + dataIn_GB(); + + // MBC2 Fix (unknown why this fixes it, maybe has to read ROM before RAM?) + readByte_GB(0x0134); + + // Does cartridge have RAM + if (sramEndAddress > 0) { + dataOut(); + if (romType <= 4) { // MBC1 + writeByte_GB(0x6000, 1); // Set RAM Mode + } + + // Initialise MBC + writeByte_GB(0x0000, 0x0A); + + // Switch SRAM banks + for (uint8_t bank = 0; bank < sramBanks; bank++) { + dataOut(); + writeByte_GB(0x4000, bank); + + // Read SRAM + dataIn_GB(); + for (uint16_t sramAddress = 0xA000; sramAddress <= sramEndAddress; sramAddress += 64) { + //fill sdBuffer + myFile.read(sdBuffer, 64); + for (int c = 0; c < 64; c++) { + if (readByte_GB(sramAddress + c) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + dataOut(); + // Disable RAM + writeByte_GB(0x0000, 0x00); + dataIn_GB(); + } + // Close the file: + myFile.close(); + return writeErrors; + } + else { + print_Error(F("Can't open file"), true); + } +} + +// Write 29F032 flashrom +// A0-A13 directly connected to cart edge -> 16384(0x0-0x3FFF) bytes per bank -> 256(0x0-0xFF) banks +// A14-A21 connected to MBC5 +void writeFlash_GB(byte MBC) { + // Launch filebrowser + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser("Select file"); + display_Clear(); + + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + // Get rom size from file + myFile.seekCur(0x147); + romType = myFile.read(); + romSize = myFile.read(); + // Go back to file beginning + myFile.seekSet(0); + + // ROM banks + romBanks = 2; // Default 32K + if (romSize >= 1) { // Calculate rom size + romBanks = 2 << romSize; + } + + // Set data pins to output + dataOut(); + + // Set ROM bank hi 0 + writeByte_GB(0x3000, 0); + // Set ROM bank low 0 + writeByte_GB(0x2000, 0); + delay(100); + + // Reset flash + writeByte_GB(0x555, 0xf0); + delay(100); + + // ID command sequence + writeByte_GB(0x555, 0xaa); + writeByte_GB(0x2aa, 0x55); + writeByte_GB(0x555, 0x90); + + dataIn_GB(); + + // Read the two id bytes into a string + sprintf(flashid, "%02X%02X", readByte_GB(0), readByte_GB(1)); + + if (strcmp(flashid, "04D4") == 0) { + println_Msg(F("MBM29F033C")); + print_Msg(F("Banks: ")); + print_Msg(romBanks); + println_Msg(F("/256")); + display_Update(); + } + else if (strcmp(flashid, "0141") == 0) { + println_Msg(F("AM29F032B")); + print_Msg(F("Banks: ")); + print_Msg(romBanks); + println_Msg(F("/256")); + display_Update(); + } + else if (strcmp(flashid, "01AD") == 0) { + println_Msg(F("AM29F016B")); + print_Msg(F("Banks: ")); + print_Msg(romBanks); + println_Msg(F("/128")); + display_Update(); + } + else if (strcmp(flashid, "01D5") == 0) { + println_Msg(F("AM29F080B")); + print_Msg(F("Banks: ")); + print_Msg(romBanks); + println_Msg(F("/64")); + display_Update(); + } + else { + print_Msg(F("Flash ID: ")); + println_Msg(flashid); + display_Update(); + print_Error(F("Unknown flashrom"), true); + } + dataOut(); + + // Reset flash + writeByte_GB(0x555, 0xf0); + + delay(100); + println_Msg(F("Erasing flash")); + display_Update(); + + // Erase flash + writeByte_GB(0x555, 0xaa); + writeByte_GB(0x2aa, 0x55); + writeByte_GB(0x555, 0x80); + writeByte_GB(0x555, 0xaa); + writeByte_GB(0x2aa, 0x55); + writeByte_GB(0x555, 0x10); + + dataIn_GB(); + + // Read the status register + byte statusReg = readByte_GB(0); + + // After a completed erase D7 will output 1 + while ((statusReg & 0x80) != 0x80) { + // Blink led + PORTB ^= (1 << 4); + delay(100); + // Update Status + statusReg = readByte_GB(0); + } + + // Blankcheck + println_Msg(F("Blankcheck")); + display_Update(); + + // Read x number of banks + for (int currBank = 0; currBank < romBanks; currBank++) { + // Blink led + PORTB ^= (1 << 4); + + dataOut(); + + // Set ROM bank + writeByte_GB(0x2000, currBank); + dataIn(); + + for (unsigned int currAddr = 0x4000; currAddr < 0x7FFF; currAddr += 512) { + uint8_t readData[512]; + for (int currByte = 0; currByte < 512; currByte++) { + readData[currByte] = readByte_GB(currAddr + currByte); + } + for (int j = 0; j < 512; j++) { + if (readData[j] != 0xFF) { + println_Msg(F("Not empty")); + print_Error(F("Erase failed"), true); + } + } + } + } + + if (MBC == 3) { + println_Msg(F("Writing flash MBC3")); + display_Update(); + + // Write flash + dataOut(); + + uint16_t currAddr = 0; + + for (int currBank = 1; currBank < romBanks; currBank++) { + // Blink led + PORTB ^= (1 << 4); + + // Set ROM bank + writeByte_GB(0x2100, currBank); + + if (currBank > 1) { + currAddr = 0x4000; + } + + while (currAddr <= 0x7FFF) { + myFile.read(sdBuffer, 512); + + for (int currByte = 0; currByte < 512; currByte++) { + // Write command sequence + writeByte_GB(0x555, 0xaa); + writeByte_GB(0x2aa, 0x55); + writeByte_GB(0x555, 0xa0); + // Write current byte + writeByte_GB(currAddr + currByte, sdBuffer[currByte]); + + // Set data pins to input + dataIn(); + + // Setting CS(PH3) and OE/RD(PH6) LOW + PORTH &= ~((1 << 3) | (1 << 6)); + + // Busy check + while ((PINC & 0x80) != (sdBuffer[currByte] & 0x80)) { + } + + // Switch CS(PH3) and OE/RD(PH6) to HIGH + PORTH |= (1 << 3) | (1 << 6); + + // Set data pins to output + dataOut(); + } + currAddr += 512; + } + } + } + + else if (MBC == 5) { + println_Msg(F("Writing flash MBC5")); + display_Update(); + + // Write flash + dataOut(); + + for (int currBank = 0; currBank < romBanks; currBank++) { + // Blink led + PORTB ^= (1 << 4); + + // Set ROM bank + writeByte_GB(0x2000, currBank); + // 0x2A8000 fix + writeByte_GB(0x4000, 0x0); + + for (unsigned int currAddr = 0x4000; currAddr < 0x7FFF; currAddr += 512) { + myFile.read(sdBuffer, 512); + + for (int currByte = 0; currByte < 512; currByte++) { + // Write command sequence + writeByte_GB(0x555, 0xaa); + writeByte_GB(0x2aa, 0x55); + writeByte_GB(0x555, 0xa0); + // Write current byte + writeByte_GB(currAddr + currByte, sdBuffer[currByte]); + + // Set data pins to input + dataIn(); + + // Setting CS(PH3) and OE/RD(PH6) LOW + PORTH &= ~((1 << 3) | (1 << 6)); + + // Busy check + while ((PINC & 0x80) != (sdBuffer[currByte] & 0x80)) { + } + + // Switch CS(PH3) and OE/RD(PH6) to HIGH + PORTH |= (1 << 3) | (1 << 6); + + // Set data pins to output + dataOut(); + } + } + } + } + + // Set data pins to input again + dataIn_GB(); + + println_Msg(F("Verifying")); + display_Update(); + + // Go back to file beginning + myFile.seekSet(0); + unsigned int addr = 0; + writeErrors = 0; + + // Verify flashrom + uint16_t romAddress = 0; + + // Read number of banks and switch banks + for (uint16_t bank = 1; bank < romBanks; bank++) { + // Switch data pins to output + dataOut(); + + if (romType >= 5) { // MBC2 and above + writeByte_GB(0x2100, bank); // Set ROM bank + } + else { // MBC1 + writeByte_GB(0x6000, 0); // Set ROM Mode + writeByte_GB(0x4000, bank >> 5); // Set bits 5 & 6 (01100000) of ROM bank + writeByte_GB(0x2000, bank & 0x1F); // Set bits 0 & 4 (00011111) of ROM bank + } + + // Switch data pins to intput + dataIn_GB(); + + if (bank > 1) { + romAddress = 0x4000; + } + // Blink led + PORTB ^= (1 << 4); + + // Read up to 7FFF per bank + while (romAddress <= 0x7FFF) { + // Fill sdBuffer + myFile.read(sdBuffer, 512); + // Compare + for (int i = 0; i < 512; i++) { + if (readByte_GB(romAddress + i) != sdBuffer[i]) { + writeErrors++; + } + } + romAddress += 512; + } + } + // Close the file: + myFile.close(); + + if (writeErrors == 0) { + println_Msg(F("OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + } + else { + println_Msg(F("Can't open file")); + display_Update(); + } +} + +//****************************************** +// End of File //****************************************** diff --git a/Cart_Reader/GBA.ino b/Cart_Reader/GBA.ino index d171199..2c6e54b 100644 --- a/Cart_Reader/GBA.ino +++ b/Cart_Reader/GBA.ino @@ -1,2634 +1,2634 @@ -//****************************************** -// GAME BOY ADVANCE MODULE -//****************************************** - -/****************************************** - Variables - *****************************************/ -char calcChecksumStr[5]; -boolean readType; - -/****************************************** - Menu - *****************************************/ -// GBA menu items -static const char GBAMenuItem1[] PROGMEM = "Read Rom"; -static const char GBAMenuItem2[] PROGMEM = "Read Save"; -static const char GBAMenuItem3[] PROGMEM = "Write Save"; -static const char GBAMenuItem4[] PROGMEM = "Force Savetype"; -static const char GBAMenuItem5[] PROGMEM = "Flash Repro"; -static const char GBAMenuItem6[] PROGMEM = "Reset"; -static const char* const menuOptionsGBA[] PROGMEM = {GBAMenuItem1, GBAMenuItem2, GBAMenuItem3, GBAMenuItem4, GBAMenuItem5, GBAMenuItem6}; - -// Rom menu -static const char GBARomItem1[] PROGMEM = "1MB"; -static const char GBARomItem2[] PROGMEM = "2MB"; -static const char GBARomItem3[] PROGMEM = "4MB"; -static const char GBARomItem4[] PROGMEM = "8MB"; -static const char GBARomItem5[] PROGMEM = "16MB"; -static const char GBARomItem6[] PROGMEM = "32MB"; -static const char* const romOptionsGBA[] PROGMEM = {GBARomItem1, GBARomItem2, GBARomItem3, GBARomItem4, GBARomItem5, GBARomItem6}; - -// Save menu -static const char GBASaveItem1[] PROGMEM = "4K EEPROM"; -static const char GBASaveItem2[] PROGMEM = "64K EEPROM"; -static const char GBASaveItem3[] PROGMEM = "256K SRAM/FRAM"; -static const char GBASaveItem4[] PROGMEM = "512K SRAM/FRAM"; -static const char GBASaveItem5[] PROGMEM = "512K FLASHROM"; -static const char GBASaveItem6[] PROGMEM = "1M FLASHROM"; -static const char* const saveOptionsGBA[] PROGMEM = {GBASaveItem1, GBASaveItem2, GBASaveItem3, GBASaveItem4, GBASaveItem5, GBASaveItem6}; - -void gbaMenu() { - // create menu with title and 4 options to choose from - unsigned char mainMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsGBA, 6); - mainMenu = question_box("GBA Cart Reader", menuOptions, 6, 0); - - // wait for user choice to come back from the question box menu - switch (mainMenu) - { - case 0: - // Read rom - switch (cartSize) - { - case 0: - // create submenu with title and 4 options to choose from - unsigned char GBARomMenu; - // Copy menuOptions out of progmem - convertPgm(romOptionsGBA, 6); - GBARomMenu = question_box("Select ROM size", menuOptions, 6, 0); - - // wait for user choice to come back from the question box menu - switch (GBARomMenu) - { - case 0: - // 1MB - cartSize = 0x100000; - break; - - case 1: - // 2MB - cartSize = 0x200000; - break; - - case 2: - // 4MB - cartSize = 0x400000; - break; - - case 3: - // 8MB - cartSize = 0x800000; - break; - - case 4: - // 16MB - cartSize = 0x1000000; - break; - - case 5: - // 32MB - cartSize = 0x2000000; - break; - } - break; - - case 1: - // 1MB - cartSize = 0x100000; - break; - - case 4: - // 4MB - cartSize = 0x400000; - break; - - case 8: - // 8MB - cartSize = 0x800000; - break; - - case 16: - // 16MB - cartSize = 0x1000000; - break; - - case 32: - // 32MB - cartSize = 0x2000000; - break; - } - display_Clear(); - // Change working dir to root - sd.chdir("/"); - readROM_GBA(); - sd.chdir("/"); - compare_checksum_GBA(); - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - break; - - case 1: - // Read save - if (saveType == 0) { - // create submenu with title and 6 options to choose from - unsigned char GBASaveMenu; - // Copy menuOptions out of progmem - convertPgm(saveOptionsGBA, 6); - GBASaveMenu = question_box("Select save type", menuOptions, 6, 0); - - // wait for user choice to come back from the question box menu - switch (GBASaveMenu) - { - case 0: - // 4K EEPROM - saveType = 1; - break; - - case 1: - // 64K EEPROM - saveType = 2; - break; - - case 2: - // 256K SRAM/FRAM - saveType = 3; - break; - - case 3: - // 512K SRAM/FRAM - saveType = 6; - break; - - case 4: - // 512K FLASH - saveType = 4; - break; - - case 5: - // 1024K FLASH - saveType = 5; - break; - } - } - switch (saveType) - { - case 1: - display_Clear(); - sd.chdir("/"); - // 4K EEPROM - readEeprom_GBA(4); - setROM_GBA(); - break; - - case 2: - display_Clear(); - sd.chdir("/"); - // 64K EEPROM - readEeprom_GBA(64); - setROM_GBA(); - break; - - case 3: - display_Clear(); - sd.chdir("/"); - // 256K SRAM/FRAM - readSRAM_GBA(1, 32768, 0); - setROM_GBA(); - break; - - case 4: - display_Clear(); - sd.chdir("/"); - // 512K FLASH - readFLASH_GBA(1, 65536, 0); - setROM_GBA(); - break; - - case 5: - display_Clear(); - sd.chdir("/"); - // 1024K FLASH (divided into two banks) - switchBank_GBA(0x0); - setROM_GBA(); - readFLASH_GBA(1, 65536, 0); - switchBank_GBA(0x1); - setROM_GBA(); - readFLASH_GBA(0, 65536, 65536); - setROM_GBA(); - break; - - case 6: - display_Clear(); - sd.chdir("/"); - // 512K SRAM/FRAM - readSRAM_GBA(1, 65536, 0); - setROM_GBA(); - break; - } - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - break; - - case 2: - // Write save - if (saveType == 0) { - // create submenu with title and 6 options to choose from - unsigned char GBASavesMenu; - // Copy menuOptions out of progmem - convertPgm(saveOptionsGBA, 6); - GBASavesMenu = question_box("Select save type", menuOptions, 6, 0); - // wait for user choice to come back from the question box menu - switch (GBASavesMenu) - { - case 0: - // 4K EEPROM - saveType = 1; - break; - - case 1: - // 64K EEPROM - saveType = 2; - break; - - case 2: - // 256K SRAM/FRAM - saveType = 3; - break; - - case 3: - // 512K SRAM/FRAM - saveType = 6; - break; - - case 4: - // 512K FLASH - saveType = 4; - break; - - case 5: - // 1024K FLASH - saveType = 5; - break; - } - } - - switch (saveType) - { - case 1: - display_Clear(); - sd.chdir("/"); - // 4K EEPROM - writeEeprom_GBA(4); - writeErrors = verifyEEP_GBA(4); - if (writeErrors == 0) { - println_Msg(F("Verified OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - setROM_GBA(); - break; - - case 2: - display_Clear(); - sd.chdir("/"); - // 64K EEPROM - writeEeprom_GBA(64); - writeErrors = verifyEEP_GBA(64); - if (writeErrors == 0) { - println_Msg(F("Verified OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - setROM_GBA(); - break; - - case 3: - display_Clear(); - // Change working dir to root - sd.chdir("/"); - // 256K SRAM/FRAM - writeSRAM_GBA(1, 32768, 0); - writeErrors = verifySRAM_GBA(32768, 0); - if (writeErrors == 0) { - println_Msg(F("Verified OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - setROM_GBA(); - break; - - case 4: - display_Clear(); - sd.chdir("/"); - // 512K FLASH - idFlash_GBA(); - resetFLASH_GBA(); - if (strcmp(flashid, "BFD4") != 0) { - println_Msg(F("Flashrom Type not supported")); - print_Msg(F("ID: ")); - println_Msg(flashid); - print_Error(F(""), true); - } - eraseFLASH_GBA(); - if (blankcheckFLASH_GBA(65536)) { - writeFLASH_GBA(1, 65536, 0); - verifyFLASH_GBA(65536, 0); - } - else { - print_Error(F("Erase failed"), false); - } - setROM_GBA(); - break; - - case 5: - display_Clear(); - sd.chdir("/"); - // 1M FLASH - idFlash_GBA(); - resetFLASH_GBA(); - if (strcmp(flashid, "C209") != 0) { - println_Msg(F("Flashrom Type not supported")); - print_Msg(F("ID: ")); - println_Msg(flashid); - print_Error(F(""), true); - } - eraseFLASH_GBA(); - // 131072 bytes are divided into two 65536 byte banks - switchBank_GBA(0x0); - setROM_GBA(); - if (blankcheckFLASH_GBA(65536)) { - writeFLASH_GBA(1, 65536, 0); - verifyFLASH_GBA(65536, 0); - } - else { - print_Error(F("Erase failed"), false); - } - switchBank_GBA(0x1); - setROM_GBA(); - if (blankcheckFLASH_GBA(65536)) { - writeFLASH_GBA(0, 65536, 65536); - verifyFLASH_GBA(65536, 65536); - } - else { - print_Error(F("Erase failed"), false); - } - setROM_GBA(); - break; - - case 6: - display_Clear(); - // Change working dir to root - sd.chdir("/"); - // 512K SRAM/FRAM - writeSRAM_GBA(1, 65536, 0); - writeErrors = verifySRAM_GBA(65536, 0); - if (writeErrors == 0) { - println_Msg(F("Verified OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - setROM_GBA(); - break; - } - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - break; - - case 3: - display_Clear(); - // create submenu with title and 7 options to choose from - unsigned char GBASaveMenu; - // Copy menuOptions out of progmem - convertPgm(saveOptionsGBA, 6); - GBASaveMenu = question_box("Select save type", menuOptions, 6, 0); - - // wait for user choice to come back from the question box menu - switch (GBASaveMenu) - { - case 0: - // 4K EEPROM - saveType = 1; - break; - - case 1: - // 64K EEPROM - saveType = 2; - break; - - case 2: - // 256K SRAM/FRAM - saveType = 3; - break; - - case 3: - // 512K SRAM/FRAM - saveType = 6; - break; - - case 4: - // 512K FLASH - saveType = 4; - break; - - case 5: - // 1024K FLASH - saveType = 5; - break; - } - display_Clear(); - break; - - case 4: - display_Clear(); - flashRepro_GBA(); - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - asm volatile (" jmp 0"); - break; - - case 5: - asm volatile (" jmp 0"); - break; - } - -} - -/****************************************** - Setup - *****************************************/ -void setup_GBA() { - setROM_GBA(); - - // Print start page - getCartInfo_GBA(); - display_Clear(); - - print_Msg(F("Name: ")); - println_Msg(romName); - print_Msg(F("Cart ID: ")); - println_Msg(cartID); - print_Msg(F("Rom Size: ")); - if (cartSize == 0) - println_Msg(F("Unknown")); - else { - print_Msg(cartSize); - println_Msg(F("MB")); - } - print_Msg(F("Save: ")); - switch (saveType) - { - case 0: - println_Msg(F("Unknown")); - break; - - case 1: - println_Msg(F("4K Eeprom")); - break; - - case 2: - println_Msg(F("64K Eeprom")); - break; - - case 3: - println_Msg(F("256K Sram")); - break; - - case 4: - println_Msg(F("512K Flash")); - break; - - case 5: - println_Msg(F("1024K Flash")); - break; - } - - print_Msg(F("Checksum: ")); - println_Msg(checksumStr); - print_Msg(F("Version: 1.")); - println_Msg(romVersion); - - // Wait for user input - println_Msg(F("Press Button...")); - display_Update(); - wait(); -} - -/****************************************** - Low level functions -*****************************************/ -void setROM_GBA() { - // Set address/data pins to OUTPUT - // AD0-AD7 - DDRF = 0xFF; - // AD8-AD15 - DDRK = 0xFF; - // AD16-AD23 - DDRC = 0xFF; - - // Set Control Pins to Output CS_SRAM(PH0) CS_ROM(PH3) WR(PH5) RD(PH6) - // CLK is N/C and IRQ is conected to GND inside the cartridge - DDRH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); - // Output a high signal on CS_SRAM(PH0) CS_ROM(PH3) WR(PH5) RD(PH6) - // At power-on all the control lines are high/disabled - PORTH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); - - delay(500); -} - -word readWord_GBA(unsigned long myAddress) { - // Set address/data ports to output - DDRF = 0xFF; - DDRK = 0xFF; - DDRC = 0xFF; - - // Divide address by two to get word addressing - myAddress = myAddress >> 1; - - // Output address to address pins, - PORTF = myAddress; - PORTK = myAddress >> 8; - PORTC = myAddress >> 16; - - // Pull CS(PH3) to LOW - PORTH &= ~ (1 << 3); - - // Set address/data ports to input - PORTF = 0x0; - PORTK = 0x0; - DDRF = 0x0; - DDRK = 0x0; - - // Pull RD(PH6) to LOW - PORTH &= ~ (1 << 6); - - // Delay here or read error with repro - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - word myWord = (PINK << 8) | PINF; - - // Switch RD(PH6) to HIGH - PORTH |= (1 << 6); - - // Switch CS_ROM(PH3) to HIGH - PORTH |= (1 << 3); - - return myWord; -} - -void writeWord_GBA(unsigned long myAddress, word myWord) { - // Set address/data ports to output - DDRF = 0xFF; - DDRK = 0xFF; - DDRC = 0xFF; - - // Divide address by two to get word addressing - myAddress = myAddress >> 1; - - // Output address to address pins, - PORTF = myAddress; - PORTK = myAddress >> 8; - PORTC = myAddress >> 16; - - // Pull CS(PH3) to LOW - PORTH &= ~ (1 << 3); - - __asm__("nop\n\t""nop\n\t"); - - // Output data - PORTF = myWord & 0xFF; - PORTK = myWord >> 8; - - // Pull WR(PH5) to LOW - PORTH &= ~ (1 << 5); - - __asm__("nop\n\t""nop\n\t"); - - // Switch WR(PH5) to HIGH - PORTH |= (1 << 5); - - // Switch CS_ROM(PH3) to HIGH - PORTH |= (1 << 3); -} - -// This function swaps bit at positions p1 and p2 in an integer n -word swapBits(word n, word p1, word p2) -{ - // Move p1'th to rightmost side - word bit1 = (n >> p1) & 1; - - // Move p2'th to rightmost side - word bit2 = (n >> p2) & 1; - - // XOR the two bits */ - word x = (bit1 ^ bit2); - - // Put the xor bit back to their original positions - x = (x << p1) | (x << p2); - - // XOR 'x' with the original number so that the two sets are swapped - word result = n ^ x; - - return result; -} - -// Some repros have D0 and D1 switched -word readWord_GAB(unsigned long myAddress) { - word tempWord = swapBits(readWord_GBA(myAddress), 0, 1); - return tempWord; -} - -void writeWord_GAB(unsigned long myAddress, word myWord) { - writeWord_GBA(myAddress, swapBits(myWord, 0, 1)); -} - -byte readByte_GBA(unsigned long myAddress) { - // Set address ports to output - DDRF = 0xFF; - DDRK = 0xFF; - // Set data port to input - DDRC = 0x0; - - // Output address to address pins, - PORTF = myAddress; - PORTK = myAddress >> 8; - - // Pull OE_SRAM(PH6) to LOW - PORTH &= ~(1 << 6); - // Pull CE_SRAM(PH0) to LOW - PORTH &= ~(1 << 0); - - // Hold address for at least 25ns and wait 150ns before access - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Read byte - byte tempByte = PINC; - - // Pull CE_SRAM(PH0) HIGH - PORTH |= (1 << 0); - // Pull OE_SRAM(PH6) HIGH - PORTH |= (1 << 6); - - return tempByte; -} - -void writeByte_GBA(unsigned long myAddress, byte myData) { - // Set address ports to output - DDRF = 0xFF; - DDRK = 0xFF; - // Set data port to output - DDRC = 0xFF; - - // Output address to address pins - PORTF = myAddress; - PORTK = myAddress >> 8; - // Output data to data pins - PORTC = myData; - - // Wait till output is stable - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Pull WE_SRAM(PH5) to LOW - PORTH &= ~(1 << 5); - // Pull CE_SRAM(PH0) to LOW - PORTH &= ~(1 << 0); - - // Leave WR low for at least 60ns - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Pull CE_SRAM(PH0) HIGH - PORTH |= (1 << 0); - // Pull WE_SRAM(PH5) HIGH - PORTH |= (1 << 5); - - // Leave WR high for at least 50ns - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); -} - -/****************************************** - GBA ROM Functions -*****************************************/ -// Read info out of rom header -void getCartInfo_GBA() { - // Read Header into array - for (int currWord = 0; currWord < 192; currWord += 2) { - word tempWord = readWord_GBA(currWord); - - sdBuffer[currWord] = tempWord & 0xFF; - sdBuffer[currWord + 1] = (tempWord >> 8) & 0xFF; - } - - // Compare Nintendo logo against known checksum, 156 bytes starting at 0x04 - word logoChecksum = 0; - for (int currByte = 0x4; currByte < 0xA0; currByte++) { - logoChecksum += sdBuffer[currByte]; - } - - if (logoChecksum != 0x4B1B) { - print_Error(F("CARTRIDGE ERROR"), false); - strcpy(romName, "ERROR"); - println_Msg(F("")); - println_Msg(F("")); - println_Msg(F("")); - println_Msg(F("Press Button to")); - println_Msg(F("ignore or powercycle")); - println_Msg(F("to try again")); - display_Update(); - wait(); - } - else { - char tempStr2[2]; - char tempStr[5]; - char sizeStr[3]; - char saveStr[2]; - - // cart not in list - cartSize = 0; - saveType = 0; - - // Get cart ID - cartID[0] = char(sdBuffer[0xAC]); - cartID[1] = char(sdBuffer[0xAD]); - cartID[2] = char(sdBuffer[0xAE]); - cartID[3] = char(sdBuffer[0xAF]); - - if (myFile.open("gba.txt", O_READ)) { - // Loop through file - while (myFile.available()) { - // Read 4 bytes into String, do it one at a time so byte order doesn't get mixed up - sprintf(tempStr, "%c", myFile.read()); - for (byte i = 0; i < 3; i++) { - sprintf(tempStr2, "%c", myFile.read()); - strcat(tempStr, tempStr2); - } - - // Check if string is a match - if (strcmp(tempStr, cartID) == 0) { - // Skip the , in the file - myFile.seekSet(myFile.curPosition() + 1); - - // Read the next ascii character and subtract 48 to convert to decimal - cartSize = myFile.read() - 48; - // Remove leading 0 for single digit cart sizes - if (cartSize != 0) { - cartSize = cartSize * 10 + myFile.read() - 48; - } - else { - cartSize = myFile.read() - 48; - } - - // Skip the , in the file - myFile.seekSet(myFile.curPosition() + 1); - - // Read the next ascii character and subtract 48 to convert to decimal - saveType = myFile.read() - 48; - } - // If no match, empty string, advance by 7 and try again - else { - myFile.seekSet(myFile.curPosition() + 7); - } - } - // Close the file: - myFile.close(); - } - else { - print_Error(F("GBA.txt missing"), true); - } - - // Get name - byte myByte = 0; - byte myLength = 0; - for (int addr = 0xA0; addr <= 0xAB; addr++) { - myByte = sdBuffer[addr]; - if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 15) { - romName[myLength] = char(myByte); - myLength++; - } - } - - // Get ROM version - romVersion = sdBuffer[0xBC]; - - // Get Checksum as string - sprintf(checksumStr, "%02X", sdBuffer[0xBD]); - - // Calculate Checksum - int calcChecksum = 0x00; - for (int n = 0xA0; n < 0xBD; n++) { - calcChecksum -= sdBuffer[n]; - } - calcChecksum = (calcChecksum - 0x19) & 0xFF; - // Turn into string - sprintf(calcChecksumStr, "%02X", calcChecksum); - - // Compare checksum - if (strcmp(calcChecksumStr, checksumStr) != 0) { - print_Msg(F("Result: ")); - println_Msg(calcChecksumStr); - print_Error(F("Checksum Error"), false); - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - } - } -} - -// Dump ROM -void readROM_GBA() { - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".gba"); - - // create a new folder for the rom file - EEPROM_readAnything(10, foldern); - sprintf(folder, "GBA/ROM/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - //clear the screen - display_Clear(); - print_Msg(F("Saving to ")); - print_Msg(folder); - println_Msg(F("/...")); - display_Update(); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - //open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("Can't create file on SD"), true); - } - - // Read rom - for (int myAddress = 0; myAddress < cartSize; myAddress += 512) { - // Blink led - if (myAddress % 16384 == 0) - PORTB ^= (1 << 4); - - for (int currWord = 0; currWord < 512; currWord += 2) { - word tempWord = readWord_GBA(myAddress + currWord); - sdBuffer[currWord] = tempWord & 0xFF; - sdBuffer[currWord + 1] = (tempWord >> 8) & 0xFF; - } - - // Write to SD - myFile.write(sdBuffer, 512); - } - - // Close the file: - myFile.close(); -} - -// Calculate the checksum of the dumped rom -boolean compare_checksum_GBA () { - println_Msg(F("Calculating Checksum")); - display_Update(); - - strcpy(fileName, romName); - strcat(fileName, ".gba"); - - // last used rom folder - EEPROM_readAnything(10, foldern); - sprintf(folder, "GBA/ROM/%s/%d", romName, foldern - 1); - sd.chdir(folder); - - // If file exists - if (myFile.open(fileName, O_READ)) { - // Read rom header - myFile.read(sdBuffer, 512); - myFile.close(); - - // Calculate Checksum - int calcChecksum = 0x00; - for (int n = 0xA0; n < 0xBD; n++) { - calcChecksum -= sdBuffer[n]; - } - calcChecksum = (calcChecksum - 0x19) & 0xFF; - - // Turn into string - sprintf(calcChecksumStr, "%02X", calcChecksum); - - if (strcmp(calcChecksumStr, checksumStr) == 0) { - println_Msg(F("Checksum matches")); - display_Update(); - return 1; - } - else { - print_Msg(F("Result: ")); - println_Msg(calcChecksumStr); - print_Error(F("Checksum Error"), false); - return 0; - } - } - // Else show error - else { - print_Error(F("Failed to open rom"), false); - return 0; - } -} - - -/****************************************** - GBA SRAM SAVE Functions -*****************************************/ -void readSRAM_GBA(boolean browseFile, unsigned long sramSize, uint32_t pos) { - if (browseFile) { - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".srm"); - - // create a new folder for the save file - EEPROM_readAnything(10, foldern); - sprintf(folder, "GBA/SAVE/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - // Save location - print_Msg(F("Saving to ")); - print_Msg(folder); - println_Msg(F("/...")); - display_Update(); - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - } - - //open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("SD Error"), true); - } - - // Seek to a new position in the file - if (pos != 0) - myFile.seekCur(pos); - - for (unsigned long currAddress = 0; currAddress < sramSize; currAddress += 512) { - for (int c = 0; c < 512; c++) { - // Read byte - sdBuffer[c] = readByte_GBA(currAddress + c); - } - - // Write sdBuffer to file - myFile.write(sdBuffer, 512); - } - // Close the file: - myFile.close(); - - // Signal end of process - println_Msg(F("Done")); - display_Update(); -} - -void writeSRAM_GBA(boolean browseFile, unsigned long sramSize, uint32_t pos) { - if (browseFile) { - filePath[0] = '\0'; - sd.chdir("/"); - fileBrowser("Select srm file"); - // Create filepath - sprintf(filePath, "%s/%s", filePath, fileName); - display_Clear(); - } - - //open file on sd card - if (myFile.open(filePath, O_READ)) { - - // Seek to a new position in the file - if (pos != 0) - myFile.seekCur(pos); - - for (unsigned long currAddress = 0; currAddress < sramSize; currAddress += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - - for (int c = 0; c < 512; c++) { - // Write byte - writeByte_GBA(currAddress + c, sdBuffer[c]); - } - } - // Close the file: - myFile.close(); - println_Msg(F("SRAM writing finished")); - display_Update(); - - } - else { - print_Error(F("File doesnt exist"), false); - } -} - -unsigned long verifySRAM_GBA(unsigned long sramSize, uint32_t pos) { - //open file on sd card - if (myFile.open(filePath, O_READ)) { - // Variable for errors - writeErrors = 0; - - // Seek to a new position in the file - if (pos != 0) - myFile.seekCur(pos); - - for (unsigned long currAddress = 0; currAddress < sramSize; currAddress += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - - for (int c = 0; c < 512; c++) { - // Read byte - if (readByte_GBA(currAddress + c) != sdBuffer[c]) { - writeErrors++; - } - } - } - // Close the file: - myFile.close(); - return writeErrors; - } - else { - print_Error(F("Can't open file"), false); - } -} - -/****************************************** - GBA FRAM SAVE Functions -*****************************************/ -// MB85R256 FRAM (Ferroelectric Random Access Memory) 32,768 words x 8 bits -void readFRAM_GBA (unsigned long framSize) { - // Output a HIGH signal on CS_ROM(PH3) WE_SRAM(PH5) - PORTH |= (1 << 3) | (1 << 5); - - // Set address ports to output - DDRF = 0xFF; - DDRK = 0xFF; - - // Set data pins to input - DDRC = 0x00; - - // Output a LOW signal on CE_SRAM(PH0) and OE_SRAM(PH6) - PORTH &= ~((1 << 0) | (1 << 6)); - - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".srm"); - - // create a new folder for the save file - EEPROM_readAnything(10, foldern); - sprintf(folder, "GBA/SAVE/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - // Save location - print_Msg(F("Saving to ")); - print_Msg(folder); - println_Msg(F("/...")); - display_Update(); - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - //open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("SD Error"), true); - } - for (unsigned long currAddress = 0; currAddress < framSize; currAddress += 512) { - for (int c = 0; c < 512; c++) { - // Pull OE_SRAM(PH6) HIGH - PORTH |= (1 << 6); - - // Set address - PORTF = (currAddress + c) & 0xFF; - PORTK = ((currAddress + c) >> 8) & 0xFF; - - // Arduino running at 16Mhz -> one nop = 62.5ns - // Leave CS_SRAM HIGH for at least 85ns - __asm__("nop\n\t""nop\n\t"); - - // Pull OE_SRAM(PH6) LOW - PORTH &= ~ (1 << 6); - - // Hold address for at least 25ns and wait 150ns before access - __asm__("nop\n\t""nop\n\t""nop\n\t"); - - // Read byte - sdBuffer[c] = PINC; - } - // Write sdBuffer to file - myFile.write(sdBuffer, 512); - } - // Close the file: - myFile.close(); - - // Signal end of process - println_Msg(F("Done")); - display_Update(); -} - -// Write file to SRAM -void writeFRAM_GBA (boolean browseFile, unsigned long framSize) { - // Output a HIGH signal on CS_ROM(PH3) and OE_SRAM(PH6) - PORTH |= (1 << 3) | (1 << 6); - - // Set address ports to output - DDRF = 0xFF; - DDRK = 0xFF; - - // Set data port to output - DDRC = 0xFF; - - // Output a LOW signal on CE_SRAM(PH0) and WE_SRAM(PH5) - PORTH &= ~((1 << 0) | (1 << 5)); - - if (browseFile) { - filePath[0] = '\0'; - sd.chdir("/"); - fileBrowser("Select srm file"); - // Create filepath - sprintf(filePath, "%s/%s", filePath, fileName); - display_Clear(); - } - else - sprintf(filePath, "%s", fileName); - - //open file on sd card - if (myFile.open(filePath, O_READ)) { - for (unsigned long currAddress = 0; currAddress < framSize; currAddress += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - - for (int c = 0; c < 512; c++) { - // Output Data on PORTC - PORTC = sdBuffer[c]; - - // Arduino running at 16Mhz -> one nop = 62.5ns - // Data setup time 50ns - __asm__("nop\n\t"); - - // Pull WE_SRAM (PH5) HIGH - PORTH |= (1 << 5); - - // Set address - PORTF = (currAddress + c) & 0xFF; - PORTK = ((currAddress + c) >> 8) & 0xFF; - - // Leave WE_SRAM (PH5) HIGH for at least 85ns - __asm__("nop\n\t""nop\n\t"); - - // Pull WE_SRAM (PH5) LOW - PORTH &= ~ (1 << 5); - - // Hold address for at least 25ns and wait 150ns before next write - __asm__("nop\n\t""nop\n\t""nop\n\t"); - } - } - // Close the file: - myFile.close(); - println_Msg(F("SRAM writing finished")); - display_Update(); - - } - else { - print_Error(F("File doesnt exist"), false); - } -} - -// Check if the SRAM was written without any error -unsigned long verifyFRAM_GBA(unsigned long framSize) { - // Output a HIGH signal on CS_ROM(PH3) WE_SRAM(PH5) - PORTH |= (1 << 3) | (1 << 5); - - // Set address ports to output - DDRF = 0xFF; - DDRK = 0xFF; - - // Set data pins to input - DDRC = 0x00; - - // Output a LOW signal on CE_SRAM(PH0) and OE_SRAM(PH6) - PORTH &= ~((1 << 0) | (1 << 6)); - - //open file on sd card - if (myFile.open(filePath, O_READ)) { - - // Variable for errors - writeErrors = 0; - - for (unsigned long currAddress = 0; currAddress < framSize; currAddress += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - - for (int c = 0; c < 512; c++) { - // Pull OE_SRAM(PH6) HIGH - PORTH |= (1 << 6); - - // Set address - PORTF = (currAddress + c) & 0xFF; - PORTK = ((currAddress + c) >> 8) & 0xFF; - - // Arduino running at 16Mhz -> one nop = 62.5ns - // Leave CS_SRAM HIGH for at least 85ns - __asm__("nop\n\t""nop\n\t"); - - // Pull OE_SRAM(PH6) LOW - PORTH &= ~ (1 << 6); - - // Hold address for at least 25ns and wait 150ns before access - __asm__("nop\n\t""nop\n\t""nop\n\t"); - - // Read byte - if (PINC != sdBuffer[c]) { - writeErrors++; - } - } - } - - // Close the file: - myFile.close(); - return writeErrors; - } - else { - print_Error(F("Can't open file"), false); - } -} - -/****************************************** - GBA FLASH SAVE Functions -*****************************************/ -// SST 39VF512 Flashrom -void idFlash_GBA() { - // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) and OE_FLASH(PH6) - PORTH |= (1 << 3) | (1 << 5) | (1 << 6); - - // Set address ports to output - DDRF = 0xFF; - DDRK = 0xFF; - // Set data pins to output - DDRC = 0xFF; - - // Output a LOW signal on CE_FLASH(PH0) - PORTH &= ~(1 << 0); - - // ID command sequence - writeByteFlash_GBA(0x5555, 0xaa); - writeByteFlash_GBA(0x2aaa, 0x55); - writeByteFlash_GBA(0x5555, 0x90); - - // Set data pins to input - DDRC = 0x00; - - // Output a LOW signal on OE_FLASH(PH6) - PORTH &= ~(1 << 6); - - // Wait 150ns before reading ID - // Arduino running at 16Mhz -> one nop = 62.5ns - __asm__("nop\n\t""nop\n\t""nop\n\t"); - - // Read the two id bytes into a string - sprintf(flashid, "%02X%02X", readByteFlash_GBA(0), readByteFlash_GBA(1)); - - // Set CS_FLASH(PH0) high - PORTH |= (1 << 0); -} - -// Reset FLASH -void resetFLASH_GBA() { - // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) and OE_FLASH(PH6) - PORTH |= (1 << 3) | (1 << 5) | (1 << 6); - - // Set address ports to output - DDRF = 0xFF; - DDRK = 0xFF; - // Set data pins to output - DDRC = 0xFF; - - // Output a LOW signal on CE_FLASH(PH0) - PORTH &= ~(1 << 0); - - // Reset command sequence - writeByteFlash_GBA(0x5555, 0xAA); - writeByteFlash_GBA(0x2AAA, 0x55); - writeByteFlash_GBA(0x5555, 0xf0); - writeByteFlash_GBA(0x5555, 0xf0); - - // Set CS_FLASH(PH0) high - PORTH |= (1 << 0); - - // Wait - delay(100); -} - -byte readByteFlash_GBA(unsigned long myAddress) { - // Set address - PORTF = myAddress & 0xFF; - PORTK = (myAddress >> 8) & 0xFF; - - // Wait until byte is ready to read - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Read byte - byte tempByte = PINC; - - // Arduino running at 16Mhz -> one nop = 62.5ns - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - return tempByte; -} - -void writeByteFlash_GBA(unsigned long myAddress, byte myData) { - PORTF = myAddress & 0xFF; - PORTK = (myAddress >> 8) & 0xFF; - PORTC = myData; - - // Arduino running at 16Mhz -> one nop = 62.5ns - // Wait till output is stable - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Switch WE_FLASH(PH5) to LOW - PORTH &= ~(1 << 5); - - // Leave WE low for at least 40ns - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Switch WE_FLASH(PH5) to HIGH - PORTH |= (1 << 5); - - // Leave WE high for a bit - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); -} - -// Erase FLASH -void eraseFLASH_GBA() { - // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) and OE_FLASH(PH6) - PORTH |= (1 << 3) | (1 << 5) | (1 << 6); - - // Set address ports to output - DDRF = 0xFF; - DDRK = 0xFF; - // Set data pins to output - DDRC = 0xFF; - - // Output a LOW signal on CE_FLASH(PH0) - PORTH &= ~(1 << 0); - - // Erase command sequence - writeByteFlash_GBA(0x5555, 0xaa); - writeByteFlash_GBA(0x2aaa, 0x55); - writeByteFlash_GBA(0x5555, 0x80); - writeByteFlash_GBA(0x5555, 0xaa); - writeByteFlash_GBA(0x2aaa, 0x55); - writeByteFlash_GBA(0x5555, 0x10); - - // Set CS_FLASH(PH0) high - PORTH |= (1 << 0); - - // Wait until all is erased - delay(500); -} - -boolean blankcheckFLASH_GBA (unsigned long flashSize) { - // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) - PORTH |= (1 << 3) | (1 << 5); - - // Set address ports to output - DDRF = 0xFF; - DDRK = 0xFF; - // Set address to 0 - PORTF = 0x00; - PORTK = 0x00; - - // Set data pins to input - DDRC = 0x00; - // Disable Pullups - //PORTC = 0x00; - - boolean blank = 1; - - // Output a LOW signal on CE_FLASH(PH0) - PORTH &= ~(1 << 0); - - // Output a LOW signal on OE_FLASH(PH6) - PORTH &= ~(1 << 6); - - for (unsigned long currAddress = 0; currAddress < flashSize; currAddress += 512) { - // Fill buffer - for (int c = 0; c < 512; c++) { - // Read byte - sdBuffer[c] = readByteFlash_GBA(currAddress + c); - } - // Check buffer - for (unsigned long currByte = 0; currByte < 512; currByte++) { - if (sdBuffer[currByte] != 0xFF) { - currByte = 512; - currAddress = flashSize; - blank = 0; - } - } - } - // Set CS_FLASH(PH0) high - PORTH |= (1 << 0); - - return blank; -} - -// The MX29L010 is 131072 bytes in size and has 16 sectors per bank -// each sector is 4096 bytes, there are 32 sectors total -// therefore the bank size is 65536 bytes, so we have two banks in total -void switchBank_GBA(byte bankNum) { - // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) and OE_FLASH(PH6) - PORTH |= (1 << 3) | (1 << 5) | (1 << 6); - - // Set address ports to output - DDRF = 0xFF; - DDRK = 0xFF; - // Set data pins to output - DDRC = 0xFF; - - // Output a LOW signal on CE_FLASH(PH0) - PORTH &= ~(1 << 0); - - // Switch bank command sequence - writeByte_GBA(0x5555, 0xAA); - writeByte_GBA(0x2AAA, 0x55); - writeByte_GBA(0x5555, 0xB0); - writeByte_GBA(0x0000, bankNum); - - // Set CS_FLASH(PH0) high - PORTH |= (1 << 0); -} - -void readFLASH_GBA (boolean browseFile, unsigned long flashSize, uint32_t pos) { - // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) - PORTH |= (1 << 3) | (1 << 5); - - // Set address ports to output - DDRF = 0xFF; - DDRK = 0xFF; - // Set address to 0 - PORTF = 0x00; - PORTK = 0x00; - - // Set data pins to input - DDRC = 0x00; - - if (browseFile) { - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".fla"); - - // create a new folder for the save file - EEPROM_readAnything(10, foldern); - - sprintf(folder, "GBA/SAVE/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - // Save location - print_Msg(F("Saving to ")); - print_Msg(folder); - println_Msg(F("/...")); - display_Update(); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - } - - //open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("SD Error"), true); - } - - // Seek to a new position in the file - if (pos != 0) - myFile.seekCur(pos); - - // Output a LOW signal on CE_FLASH(PH0) - PORTH &= ~(1 << 0); - - // Output a LOW signal on OE_FLASH(PH6) - PORTH &= ~(1 << 6); - - for (unsigned long currAddress = 0; currAddress < flashSize; currAddress += 512) { - for (int c = 0; c < 512; c++) { - // Read byte - sdBuffer[c] = readByteFlash_GBA(currAddress + c); - } - // Write sdBuffer to file - myFile.write(sdBuffer, 512); - } - myFile.close(); - - // Set CS_FLASH(PH0) high - PORTH |= (1 << 0); - - // Signal end of process - println_Msg(F("Done")); - display_Update(); -} - -void busyCheck_GBA(int currByte) { - // Set data pins to input - DDRC = 0x00; - // Output a LOW signal on OE_FLASH(PH6) - PORTH &= ~(1 << 6); - // Read PINC - while (PINC != sdBuffer[currByte]) {} - // Output a HIGH signal on OE_FLASH(PH6) - PORTH |= (1 << 6); - // Set data pins to output - DDRC = 0xFF; -} - -void writeFLASH_GBA (boolean browseFile, unsigned long flashSize, uint32_t pos) { - // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) and OE_FLASH(PH6) - PORTH |= (1 << 3) | (1 << 5) | (1 << 6); - - // Set address ports to output - DDRF = 0xFF; - DDRK = 0xFF; - // Set data port to output - DDRC = 0xFF; - - if (browseFile) { - filePath[0] = '\0'; - sd.chdir("/"); - fileBrowser("Select fla file"); - // Create filepath - sprintf(filePath, "%s/%s", filePath, fileName); - display_Clear(); - } - - print_Msg(F("Writing flash...")); - display_Update(); - - //open file on sd card - if (myFile.open(filePath, O_READ)) { - - // Seek to a new position in the file - if (pos != 0) - myFile.seekCur(pos); - - // Output a LOW signal on CE_FLASH(PH0) - PORTH &= ~(1 << 0); - - for (unsigned long currAddress = 0; currAddress < flashSize; currAddress += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - - for (int c = 0; c < 512; c++) { - // Write command sequence - writeByteFlash_GBA(0x5555, 0xaa); - writeByteFlash_GBA(0x2aaa, 0x55); - writeByteFlash_GBA(0x5555, 0xa0); - // Write current byte - writeByteFlash_GBA(currAddress + c, sdBuffer[c]); - - // Wait - busyCheck_GBA(c); - } - } - // Set CS_FLASH(PH0) high - PORTH |= (1 << 0); - - // Close the file: - myFile.close(); - println_Msg(F("done")); - display_Update(); - - } - else { - println_Msg(F("Error")); - print_Error(F("File doesnt exist"), false); - } -} - -// Check if the Flashrom was written without any error -void verifyFLASH_GBA(unsigned long flashSize, uint32_t pos) { - // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) - PORTH |= (1 << 3) | (1 << 5); - - // Set address ports to output - DDRF = 0xFF; - DDRK = 0xFF; - - // Set data pins to input - DDRC = 0x00; - - // Output a LOW signal on CE_FLASH(PH0) and OE_FLASH(PH6) - PORTH &= ~((1 << 0) | (1 << 6)); - - // Signal beginning of process - print_Msg(F("Verify...")); - display_Update(); - - unsigned long wrError = 0; - - //open file on sd card - if (!myFile.open(filePath, O_READ)) { - print_Error(F("SD Error"), true); - } - - // Seek to a new position in the file - if (pos != 0) - myFile.seekCur(pos); - - for (unsigned long currAddress = 0; currAddress < flashSize; currAddress += 512) { - myFile.read(sdBuffer, 512); - - for (int c = 0; c < 512; c++) { - // Read byte - if (sdBuffer[c] != readByteFlash_GBA(currAddress + c)) { - wrError++; - } - } - } - myFile.close(); - - // Set CS_FLASH(PH0) high - PORTH |= (1 << 0); - - if (wrError == 0) { - println_Msg(F("OK")); - } - else { - print_Msg(wrError); - print_Error(F(" Errors"), false); - } -} - -/****************************************** - GBA Eeprom SAVE Functions -*****************************************/ -// Write eeprom from file -void writeEeprom_GBA(word eepSize) { - // Launch Filebrowser - filePath[0] = '\0'; - sd.chdir("/"); - fileBrowser("Select eep file"); - // Create filepath - sprintf(filePath, "%s/%s", filePath, fileName); - display_Clear(); - - print_Msg(F("Writing eeprom...")); - display_Update(); - - //open file on sd card - if (myFile.open(filePath, O_READ)) { - for (word i = 0; i < eepSize * 16; i += 64) { - // Fill romBuffer - myFile.read(sdBuffer, 512); - // Disable interrupts for more uniform clock pulses - noInterrupts(); - // Write 512 bytes - writeBlock_EEP(i, eepSize); - interrupts(); - - // Wait - delayMicroseconds(200); - } - - // Close the file: - myFile.close(); - println_Msg(F("done")); - display_Update(); - } - else { - println_Msg(F("Error")); - print_Error(F("File doesnt exist"), false); - } -} - -// Read eeprom to file -void readEeprom_GBA(word eepSize) { - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".eep"); - - // create a new folder for the save file - EEPROM_readAnything(10, foldern); - - sprintf(folder, "GBA/SAVE/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - // Save location - print_Msg(F("Saving to ")); - print_Msg(folder); - println_Msg(F("/...")); - display_Update(); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - //open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("SD Error"), true); - } - - // Each block contains 8 Bytes, so for a 8KB eeprom 1024 blocks need to be read - for (word currAddress = 0; currAddress < eepSize * 16; currAddress += 64) { - // Disable interrupts for more uniform clock pulses - noInterrupts(); - // Fill sd Buffer - readBlock_EEP(currAddress, eepSize); - interrupts(); - - // Write sdBuffer to file - myFile.write(sdBuffer, 512); - - // Wait - delayMicroseconds(200); - } - myFile.close(); -} - -// Send address as bits to eeprom -void send_GBA(word currAddr, word numBits) { - for (word addrBit = numBits; addrBit > 0; addrBit--) { - // If you want the k-th bit of n, then do - // (n & ( 1 << k )) >> k - if (((currAddr & ( 1 << (addrBit - 1))) >> (addrBit - 1))) { - // Set A0(PF0) to High - PORTF |= (1 << 0); - // Set WR(PH5) to LOW - PORTH &= ~ (1 << 5); - // Set WR(PH5) to High - PORTH |= (1 << 5); - } - else { - // Set A0(PF0) to Low - PORTF &= ~ (1 << 0); - // Set WR(PH5) to LOW - PORTH &= ~ (1 << 5); - // Set WR(PH5) to High - PORTH |= (1 << 5); - } - } -} - -// Write 512K eeprom block -void writeBlock_EEP(word startAddr, word eepSize) { - // Setup - // Set CS_ROM(PH3) WR(PH5) RD(PH6) to Output - DDRH |= (1 << 3) | (1 << 5) | (1 << 6); - // Set A0(PF0) to Output - DDRF |= (1 << 0); - // Set A23/D7(PC7) to Output - DDRC |= (1 << 7); - - // Set CS_ROM(PH3) WR(PH5) RD(PH6) to High - PORTH |= (1 << 3) | (1 << 5) | (1 << 6); - // Set A0(PF0) to High - PORTF |= (1 << 0); - // Set A23/D7(PC7) to High - PORTC |= (1 << 7); - - __asm__("nop\n\t""nop\n\t"); - - // Write 64*8=512 bytes - for (word currAddr = startAddr; currAddr < startAddr + 64; currAddr++) { - // Set CS_ROM(PH3) to LOW - PORTH &= ~ (1 << 3); - - // Send write request "10" - // Set A0(PF0) to High - PORTF |= (1 << 0); - // Set WR(PH5) to LOW - PORTH &= ~ (1 << 5); - // Set WR(PH5) to High - PORTH |= (1 << 5); - // Set A0(PF0) to LOW - PORTF &= ~ (1 << 0); - // Set WR(PH5) to LOW - PORTH &= ~ (1 << 5); - // Set WR(PH5) to High - PORTH |= (1 << 5); - - // Send either 6 or 14 bit address - if (eepSize == 4) { - send_GBA(currAddr, 6); - } - else { - send_GBA(currAddr, 14); - } - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Send data - for (byte currByte = 0; currByte < 8; currByte++) { - send_GBA(sdBuffer[(currAddr - startAddr) * 8 + currByte], 8); - } - - // Send stop bit - // Set A0(PF0) to LOW - PORTF &= ~ (1 << 0); - // Set WR(PH5) to LOW - PORTH &= ~ (1 << 5); - // WR(PH5) to High - PORTH |= (1 << 5); - - // Set CS_ROM(PH3) to High - PORTH |= (1 << 3); - - // Wait until done - // Set A0(PF0) to Input - DDRF &= ~ (1 << 0); - - do { - // Set CS_ROM(PH3) RD(PH6) to LOW - PORTH &= ~((1 << 3) | (1 << 6)); - // Set CS_ROM(PH3) RD(PH6) to High - PORTH |= (1 << 3) | (1 << 6); - } - while ((PINF & 0x1) == 0); - - // Set A0(PF0) to Output - DDRF |= (1 << 0); - } -} - -// Reads 512 bytes from eeprom -void readBlock_EEP(word startAddress, word eepSize) { - // Setup - // Set CS_ROM(PH3) WR(PH5) RD(PH6) to Output - DDRH |= (1 << 3) | (1 << 5) | (1 << 6); - // Set A0(PF0) to Output - DDRF |= (1 << 0); - // Set A23/D7(PC7) to Output - DDRC |= (1 << 7); - - // Set CS_ROM(PH3) WR(PH5) RD(PH6) to High - PORTH |= (1 << 3) | (1 << 5) | (1 << 6); - // Set A0(PF0) to High - PORTF |= (1 << 0); - // Set A23/D7(PC7) to High - PORTC |= (1 << 7); - - __asm__("nop\n\t""nop\n\t"); - - // Read 64*8=512 bytes - for (word currAddr = startAddress; currAddr < startAddress + 64; currAddr++) { - // Set CS_ROM(PH3) to LOW - PORTH &= ~ (1 << 3); - - // Send read request "11" - // Set A0(PF0) to High - PORTF |= (1 << 0); - // Set WR(PH5) to LOW - PORTH &= ~ (1 << 5); - // Set WR(PH5) to High - PORTH |= (1 << 5); - // Set WR(PH5) to LOW - PORTH &= ~ (1 << 5); - // Set WR(PH5) to High - PORTH |= (1 << 5); - - // Send either 6 or 14 bit address - if (eepSize == 4) { - send_GBA(currAddr, 6); - } - else { - send_GBA(currAddr, 14); - } - - // Send stop bit - // Set A0(PF0) to LOW - PORTF &= ~ (1 << 0); - // Set WR(PH5) to LOW - PORTH &= ~ (1 << 5); - // WR(PH5) to High - PORTH |= (1 << 5); - - // Set CS_ROM(PH3) to High - PORTH |= (1 << 3); - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Read data - // Set A0(PF0) to Input - DDRF &= ~ (1 << 0); - // Set CS_ROM(PH3) to low - PORTH &= ~(1 << 3); - - // Array that holds the bits - bool tempBits[65]; - - // Ignore the first 4 bits - for (byte i = 0; i < 4; i++) { - // Set RD(PH6) to LOW - PORTH &= ~ (1 << 6); - // Set RD(PH6) to High - PORTH |= (1 << 6); - } - - // Read the remaining 64bits into array - for (byte currBit = 0; currBit < 64; currBit++) { - // Set RD(PH6) to LOW - PORTH &= ~ (1 << 6); - // Set RD(PH6) to High - PORTH |= (1 << 6); - - // Read bit from A0(PF0) - tempBits[currBit] = (PINF & 0x1); - } - - // Set CS_ROM(PH3) to High - PORTH |= (1 << 3); - // Set A0(PF0) to High - PORTF |= (1 << 0); - // Set A0(PF0) to Output - DDRF |= (1 << 0); - - // OR 8 bits into one byte for a total of 8 bytes - for (byte j = 0; j < 64; j += 8) { - sdBuffer[((currAddr - startAddress) * 8) + (j / 8)] = tempBits[0 + j] << 7 | tempBits[1 + j] << 6 | tempBits[2 + j] << 5 | tempBits[3 + j] << 4 | tempBits[4 + j] << 3 | tempBits[5 + j] << 2 | tempBits[6 + j] << 1 | tempBits[7 + j]; - } - } -} - -// Check if the SRAM was written without any error -unsigned long verifyEEP_GBA(word eepSize) { - unsigned long wrError = 0; - - //open file on sd card - if (!myFile.open(filePath, O_READ)) { - print_Error(F("SD Error"), true); - } - - // Fill sd Buffer - for (word currAddress = 0; currAddress < eepSize * 16; currAddress += 64) { - // Disable interrupts for more uniform clock pulses - noInterrupts(); - readBlock_EEP(currAddress, eepSize); - interrupts(); - - // Compare - for (int currByte = 0; currByte < 512; currByte++) { - if (sdBuffer[currByte] != myFile.read()) { - wrError++; - } - } - } - myFile.close(); - return wrError; -} - -/****************************************** - GBA REPRO Functions (32MB Intel 4000L0YBQ0 and 16MB MX29GL128E) -*****************************************/ -// Reset to read mode -void resetIntel_GBA(unsigned long partitionSize) { - for (unsigned long currPartition = 0; currPartition < cartSize; currPartition += partitionSize) { - writeWord_GBA(currPartition, 0xFFFF); - } -} - -void resetMX29GL128E_GBA() { - writeWord_GAB(0, 0xF0); -} - -boolean sectorCheckMX29GL128E_GBA() { - boolean sectorProtect = 0; - writeWord_GAB(0xAAA, 0xAA); - writeWord_GAB(0x555, 0x55); - writeWord_GAB(0xAAA, 0x90); - for (unsigned long currSector = 0x0; currSector < 0xFFFFFF; currSector += 0x20000) { - if (readWord_GAB(currSector + 0x04) != 0x0) - sectorProtect = 1; - } - resetMX29GL128E_GBA(); - return sectorProtect; -} - -void idFlashrom_GBA() { - // Send Intel ID command to flashrom - writeWord_GBA(0, 0x90); - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Read flashrom ID - sprintf(flashid, "%02X%02X", ((readWord_GBA(0x2) >> 8) & 0xFF), (readWord_GBA(0x4) & 0xFF)); - - // Intel Strataflash - if (strcmp(flashid, "8802") == 0 || (strcmp(flashid, "8816") == 0)) { - cartSize = 0x2000000; - } - else { - // Send swapped MX29GL128E/MSP55LV128 ID command to flashrom - writeWord_GAB(0xAAA, 0xAA); - writeWord_GAB(0x555, 0x55); - writeWord_GAB(0xAAA, 0x90); - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Read flashrom ID - sprintf(flashid, "%02X%02X", ((readWord_GAB(0x2) >> 8) & 0xFF), (readWord_GAB(0x2) & 0xFF)); - - // MX29GL128E or MSP55LV128 - if (strcmp(flashid, "227E") == 0) { - // MX is 0xC2 and MSP is 0x4 or 0x1 - romType = (readWord_GAB(0x0) & 0xFF); - cartSize = 0x1000000; - resetMX29GL128E_GBA(); - } - else { - println_Msg(flashid); - print_Error(F("Unknown Flashid"), true); - } - } -} - -boolean blankcheckFlashrom_GBA() { - for (unsigned long currSector = 0; currSector < fileSize; currSector += 0x20000) { - // Blink led - PORTB ^= (1 << 4); - - for (unsigned long currByte = 0; currByte < 0x20000; currByte += 2) { - if (readWord_GBA(currSector + currByte) != 0xFFFF) { - return 0; - } - } - } - return 1; -} - -void eraseIntel4000_GBA() { - // If the game is smaller than 16Mbit only erase the needed blocks - unsigned long lastBlock = 0xFFFFFF; - if (fileSize < 0xFFFFFF) - lastBlock = fileSize; - - // Erase 4 blocks with 16kwords each - for (unsigned long currBlock = 0x0; currBlock < 0x1FFFF; currBlock += 0x8000) { - // Unlock Block - writeWord_GBA(currBlock, 0x60); - writeWord_GBA(currBlock, 0xD0); - - // Erase Command - writeWord_GBA(currBlock, 0x20); - writeWord_GBA(currBlock, 0xD0); - - // Read the status register - word statusReg = readWord_GBA(currBlock); - while ((statusReg | 0xFF7F) != 0xFFFF) { - statusReg = readWord_GBA(currBlock); - } - } - - // Erase 126 blocks with 64kwords each - for (unsigned long currBlock = 0x20000; currBlock < lastBlock; currBlock += 0x1FFFF) { - // Unlock Block - writeWord_GBA(currBlock, 0x60); - writeWord_GBA(currBlock, 0xD0); - - // Erase Command - writeWord_GBA(currBlock, 0x20); - writeWord_GBA(currBlock, 0xD0); - - // Read the status register - word statusReg = readWord_GBA(currBlock); - while ((statusReg | 0xFF7F) != 0xFFFF) { - statusReg = readWord_GBA(currBlock); - } - // Blink led - PORTB ^= (1 << 4); - } - - // Erase the second chip - if (fileSize > 0xFFFFFF) { - // 126 blocks with 64kwords each - for (unsigned long currBlock = 0x1000000; currBlock < 0x1FDFFFF; currBlock += 0x1FFFF) { - // Unlock Block - writeWord_GBA(currBlock, 0x60); - writeWord_GBA(currBlock, 0xD0); - - // Erase Command - writeWord_GBA(currBlock, 0x20); - writeWord_GBA(currBlock, 0xD0); - - // Read the status register - word statusReg = readWord_GBA(currBlock); - while ((statusReg | 0xFF7F) != 0xFFFF) { - statusReg = readWord_GBA(currBlock); - } - // Blink led - PORTB ^= (1 << 4); - } - - // 4 blocks with 16kword each - for (unsigned long currBlock = 0x1FE0000; currBlock < 0x1FFFFFF; currBlock += 0x8000) { - // Unlock Block - writeWord_GBA(currBlock, 0x60); - writeWord_GBA(currBlock, 0xD0); - - // Erase Command - writeWord_GBA(currBlock, 0x20); - writeWord_GBA(currBlock, 0xD0); - - // Read the status register - word statusReg = readWord_GBA(currBlock); - while ((statusReg | 0xFF7F) != 0xFFFF) { - statusReg = readWord_GBA(currBlock); - } - // Blink led - PORTB ^= (1 << 4); - } - } -} - -void eraseIntel4400_GBA() { - // If the game is smaller than 32Mbit only erase the needed blocks - unsigned long lastBlock = 0x1FFFFFF; - if (fileSize < 0x1FFFFFF) - lastBlock = fileSize; - - // Erase 4 blocks with 16kwords each - for (unsigned long currBlock = 0x0; currBlock < 0x1FFFF; currBlock += 0x8000) { - // Unlock Block - writeWord_GBA(currBlock, 0x60); - writeWord_GBA(currBlock, 0xD0); - - // Erase Command - writeWord_GBA(currBlock, 0x20); - writeWord_GBA(currBlock, 0xD0); - - // Read the status register - word statusReg = readWord_GBA(currBlock); - while ((statusReg | 0xFF7F) != 0xFFFF) { - statusReg = readWord_GBA(currBlock); - } - } - - // Erase 255 blocks with 64kwords each - for (unsigned long currBlock = 0x20000; currBlock < lastBlock; currBlock += 0x1FFFF) { - // Unlock Block - writeWord_GBA(currBlock, 0x60); - writeWord_GBA(currBlock, 0xD0); - - // Erase Command - writeWord_GBA(currBlock, 0x20); - writeWord_GBA(currBlock, 0xD0); - - // Read the status register - word statusReg = readWord_GBA(currBlock); - while ((statusReg | 0xFF7F) != 0xFFFF) { - statusReg = readWord_GBA(currBlock); - } - // Blink led - PORTB ^= (1 << 4); - } - - /* No need to erase the second chip as max rom size is 32MB - if (fileSize > 0x2000000) { - // 255 blocks with 64kwords each - for (unsigned long currBlock = 0x2000000; currBlock < 0x3FDFFFF; currBlock += 0x1FFFF) { - // Unlock Block - writeWord_GBA(currBlock, 0x60); - writeWord_GBA(currBlock, 0xD0); - - // Erase Command - writeWord_GBA(currBlock, 0x20); - writeWord_GBA(currBlock, 0xD0); - - // Read the status register - word statusReg = readWord_GBA(currBlock); - while ((statusReg | 0xFF7F) != 0xFFFF) { - statusReg = readWord_GBA(currBlock); - } - // Blink led - PORTB ^= (1 << 4); - } - - // 4 blocks with 16kword each - for (unsigned long currBlock = 0x3FE0000; currBlock < 0x3FFFFFF; currBlock += 0x8000) { - // Unlock Block - writeWord_GBA(currBlock, 0x60); - writeWord_GBA(currBlock, 0xD0); - - // Erase Command - writeWord_GBA(currBlock, 0x20); - writeWord_GBA(currBlock, 0xD0); - - // Read the status register - word statusReg = readWord_GBA(currBlock); - while ((statusReg | 0xFF7F) != 0xFFFF) { - statusReg = readWord_GBA(currBlock); - } - // Blink led - PORTB ^= (1 << 4); - } - }*/ -} - -void sectorEraseMSP55LV128_GBA() { - unsigned long lastSector = 0xFFFFFF; - - // Erase 256 sectors with 64kbytes each - unsigned long currSector; - for (currSector = 0x0; currSector < lastSector; currSector += 0x10000) { - writeWord_GAB(0xAAA, 0xAA); - writeWord_GAB(0x555, 0x55); - writeWord_GAB(0xAAA, 0x80); - writeWord_GAB(0xAAA, 0xAA); - writeWord_GAB(0x555, 0x55); - writeWord_GAB(currSector, 0x30); - - // Read the status register - word statusReg = readWord_GAB(currSector); - while ((statusReg | 0xFF7F) != 0xFFFF) { - statusReg = readWord_GAB(currSector); - } - // Blink LED - PORTB ^= (1 << 4); - } -} - -void sectorEraseMX29GL128E_GBA() { - unsigned long lastSector = 0xFFFFFF; - - // Erase 128 sectors with 128kbytes each - unsigned long currSector; - for (currSector = 0x0; currSector < lastSector; currSector += 0x20000) { - writeWord_GAB(0xAAA, 0xAA); - writeWord_GAB(0x555, 0x55); - writeWord_GAB(0xAAA, 0x80); - writeWord_GAB(0xAAA, 0xAA); - writeWord_GAB(0x555, 0x55); - writeWord_GAB(currSector, 0x30); - - // Read the status register - word statusReg = readWord_GAB(currSector); - while ((statusReg | 0xFF7F) != 0xFFFF) { - statusReg = readWord_GAB(currSector); - } - // Blink LED - PORTB ^= (1 << 4); - } -} - -void writeIntel4000_GBA() { - for (unsigned long currBlock = 0; currBlock < fileSize; currBlock += 0x20000) { - // Blink led - PORTB ^= (1 << 4); - - // Write to flashrom - for (unsigned long currSdBuffer = 0; currSdBuffer < 0x20000; currSdBuffer += 512) { - // Fill SD buffer - myFile.read(sdBuffer, 512); - - // Write 32 words at a time - for (int currWriteBuffer = 0; currWriteBuffer < 512; currWriteBuffer += 64) { - // Unlock Block - writeWord_GBA(currBlock + currSdBuffer + currWriteBuffer, 0x60); - writeWord_GBA(currBlock + currSdBuffer + currWriteBuffer, 0xD0); - - // Buffered program command - writeWord_GBA(currBlock + currSdBuffer + currWriteBuffer, 0xE8); - - // Check Status register - word statusReg = readWord_GBA(currBlock + currSdBuffer + currWriteBuffer); - while ((statusReg | 0xFF7F) != 0xFFFF) { - statusReg = readWord_GBA(currBlock + currSdBuffer + currWriteBuffer); - } - - // Write word count (minus 1) - writeWord_GBA(currBlock + currSdBuffer + currWriteBuffer, 0x1F); - - // Write buffer - for (byte currByte = 0; currByte < 64; currByte += 2) { - // Join two bytes into one word - word currWord = ( ( sdBuffer[currWriteBuffer + currByte + 1] & 0xFF ) << 8 ) | ( sdBuffer[currWriteBuffer + currByte] & 0xFF ); - writeWord_GBA(currBlock + currSdBuffer + currWriteBuffer + currByte, currWord); - } - - // Write Buffer to Flash - writeWord_GBA(currBlock + currSdBuffer + currWriteBuffer + 62, 0xD0); - - // Read the status register at last written address - statusReg = readWord_GBA(currBlock + currSdBuffer + currWriteBuffer + 62); - while ((statusReg | 0xFF7F) != 0xFFFF) { - statusReg = readWord_GBA(currBlock + currSdBuffer + currWriteBuffer + 62); - } - } - } - } -} - -void writeMSP55LV128_GBA() { - for (unsigned long currSector = 0; currSector < fileSize; currSector += 0x10000) { - // Blink led - PORTB ^= (1 << 4); - - // Write to flashrom - for (unsigned long currSdBuffer = 0; currSdBuffer < 0x10000; currSdBuffer += 512) { - // Fill SD buffer - myFile.read(sdBuffer, 512); - - // Write 16 words at a time - for (int currWriteBuffer = 0; currWriteBuffer < 512; currWriteBuffer += 32) { - // Write Buffer command - writeWord_GAB(0xAAA, 0xAA); - writeWord_GAB(0x555, 0x55); - writeWord_GAB(currSector, 0x25); - - // Write word count (minus 1) - writeWord_GAB(currSector, 0xF); - - // Write buffer - word currWord; - for (byte currByte = 0; currByte < 32; currByte += 2) { - // Join two bytes into one word - currWord = ( ( sdBuffer[currWriteBuffer + currByte + 1] & 0xFF ) << 8 ) | ( sdBuffer[currWriteBuffer + currByte] & 0xFF ); - writeWord_GBA(currSector + currSdBuffer + currWriteBuffer + currByte, currWord); - } - - // Confirm write buffer - writeWord_GAB(currSector, 0x29); - - // Read the status register - word statusReg = readWord_GAB(currSector + currSdBuffer + currWriteBuffer + 30); - - while ((statusReg | 0xFF7F) != (currWord | 0xFF7F)) { - statusReg = readWord_GAB(currSector + currSdBuffer + currWriteBuffer + 30); - } - } - } - } -} - -void writeMX29GL128E_GBA() { - for (unsigned long currSector = 0; currSector < fileSize; currSector += 0x20000) { - // Blink led - PORTB ^= (1 << 4); - - // Write to flashrom - for (unsigned long currSdBuffer = 0; currSdBuffer < 0x20000; currSdBuffer += 512) { - // Fill SD buffer - myFile.read(sdBuffer, 512); - - // Write 32 words at a time - for (int currWriteBuffer = 0; currWriteBuffer < 512; currWriteBuffer += 64) { - // Write Buffer command - writeWord_GAB(0xAAA, 0xAA); - writeWord_GAB(0x555, 0x55); - writeWord_GAB(currSector, 0x25); - - // Write word count (minus 1) - writeWord_GAB(currSector, 0x1F); - - // Write buffer - word currWord; - for (byte currByte = 0; currByte < 64; currByte += 2) { - // Join two bytes into one word - currWord = ( ( sdBuffer[currWriteBuffer + currByte + 1] & 0xFF ) << 8 ) | ( sdBuffer[currWriteBuffer + currByte] & 0xFF ); - writeWord_GBA(currSector + currSdBuffer + currWriteBuffer + currByte, currWord); - } - - // Confirm write buffer - writeWord_GAB(currSector, 0x29); - - // Read the status register - word statusReg = readWord_GAB(currSector + currSdBuffer + currWriteBuffer + 62); - - while ((statusReg | 0xFF7F) != (currWord | 0xFF7F)) { - statusReg = readWord_GAB(currSector + currSdBuffer + currWriteBuffer + 62); - } - } - } - } -} - -boolean verifyFlashrom_GBA() { - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - writeErrors = 0; - - for (unsigned long currSector = 0; currSector < fileSize; currSector += 131072) { - // Blink led - PORTB ^= (1 << 4); - 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 + 1] & 0xFF ) << 8 ) | ( sdBuffer[currByte] & 0xFF ); - - // Compare both - if (readWord_GBA(currSector + currSdBuffer + currByte) != currWord) { - writeErrors++; - myFile.close(); - return 0; - } - } - } - } - // Close the file: - myFile.close(); - if (writeErrors == 0) { - return 1; - } - else { - return 0; - } - } - else { - print_Error(F("Can't open file"), true); - return 9999; - } -} - -void flashRepro_GBA() { - // Check flashrom ID's - idFlashrom_GBA(); - - if ((strcmp(flashid, "8802") == 0) || (strcmp(flashid, "8816") == 0) || (strcmp(flashid, "227E") == 0)) { - print_Msg(F("ID: ")); - print_Msg(flashid); - print_Msg(F(" Size: ")); - print_Msg(cartSize / 0x100000); - println_Msg(F("MB")); - // MX29GL128E or MSP55LV128(N) - if (strcmp(flashid, "227E") == 0) { - // MX is 0xC2 and MSP55LV128 is 0x4 and MSP55LV128N 0x1 - if (romType == 0xC2) { - println_Msg(F("Macronix MX29GL128E")); - } - else if ((romType == 0x1) || (romType == 0x4)) { - println_Msg(F("Fujitsu MSP55LV128N")); - } - else { - println_Msg(romType); - print_Error(F("Unknown manufacturer"), true); - } - } - // Intel 4000L0YBQ0 - else if (strcmp(flashid, "8802") == 0) { - println_Msg(F("Intel 4000L0YBQ0")); - } - // Intel 4400L0ZDQ0 - else if (strcmp(flashid, "8816") == 0) { - println_Msg(F("Intel 4400L0ZDQ0")); - } - println_Msg(""); - println_Msg(F("This will erase your")); - println_Msg(F("Repro Cartridge.")); - println_Msg(F("Please use 3.3V!")); - println_Msg(""); - println_Msg(F("Press Button")); - display_Update(); - wait(); - - // Launch file browser - filePath[0] = '\0'; - sd.chdir("/"); - fileBrowser("Select gba file"); - display_Clear(); - 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(); - print_Msg(F("File size: ")); - print_Msg(fileSize / 0x100000); - println_Msg(F("MB")); - display_Update(); - - // Erase needed sectors - if (strcmp(flashid, "8802") == 0) { - println_Msg(F("Erasing...")); - display_Update(); - eraseIntel4000_GBA(); - resetIntel_GBA(0x200000); - } - else if (strcmp(flashid, "8816") == 0) { - println_Msg(F("Erasing...")); - display_Update(); - eraseIntel4400_GBA(); - resetIntel_GBA(0x200000); - } - else if (strcmp(flashid, "227E") == 0) { - //if (sectorCheckMX29GL128E_GBA()) { - //print_Error(F("Sector Protected"), true); - //} - //else { - println_Msg(F("Erasing...")); - display_Update(); - if (romType == 0xC2) { - //MX29GL128E - sectorEraseMX29GL128E_GBA(); - } - else if ((romType == 0x1) || (romType == 0x4)) { - //MSP55LV128(N) - sectorEraseMSP55LV128_GBA(); - } - //} - } - /* Skip blankcheck to save time - print_Msg(F("Blankcheck...")); - display_Update(); - if (blankcheckFlashrom_GBA()) { - println_Msg(F("OK")); - */ - - //Write flashrom - print_Msg(F("Writing ")); - println_Msg(filePath); - display_Update(); - if ((strcmp(flashid, "8802") == 0) || (strcmp(flashid, "8816") == 0)) { - writeIntel4000_GBA(); - } - else if (strcmp(flashid, "227E") == 0) { - if (romType == 0xC2) { - //MX29GL128E - writeMX29GL128E_GBA(); - } - else if ((romType == 0x1) || (romType == 0x4)) { - //MSP55LV128(N) - writeMSP55LV128_GBA(); - } - } - - // Close the file: - myFile.close(); - - // Verify - print_Msg(F("Verifying...")); - display_Update(); - if (strcmp(flashid, "8802") == 0) { - // Don't know the correct size so just take some guesses - resetIntel_GBA(0x8000); - delay(1000); - resetIntel_GBA(0x100000); - delay(1000); - resetIntel_GBA(0x200000); - delay(1000); - } - else if (strcmp(flashid, "8816") == 0) { - resetIntel_GBA(0x200000); - delay(1000); - } - - else if (strcmp(flashid, "227E") == 0) { - resetMX29GL128E_GBA(); - delay(1000); - } - if (verifyFlashrom_GBA() == 1) { - println_Msg(F("OK")); - display_Update(); - } - else { - print_Error(F("ERROR"), true); - } - /* Skipped blankcheck - } - else { - print_Error(F("failed"), true); - } - */ - } - else { - print_Error(F("Can't open file"), true); - } - } - else { - print_Msg(F("ID: ")); - println_Msg(flashid); - print_Error(F("Unknown Flash ID"), true); - } -} - -//****************************************** -// End of File +//****************************************** +// GAME BOY ADVANCE MODULE +//****************************************** + +/****************************************** + Variables + *****************************************/ +char calcChecksumStr[5]; +boolean readType; + +/****************************************** + Menu + *****************************************/ +// GBA menu items +static const char GBAMenuItem1[] PROGMEM = "Read Rom"; +static const char GBAMenuItem2[] PROGMEM = "Read Save"; +static const char GBAMenuItem3[] PROGMEM = "Write Save"; +static const char GBAMenuItem4[] PROGMEM = "Force Savetype"; +static const char GBAMenuItem5[] PROGMEM = "Flash Repro"; +static const char GBAMenuItem6[] PROGMEM = "Reset"; +static const char* const menuOptionsGBA[] PROGMEM = {GBAMenuItem1, GBAMenuItem2, GBAMenuItem3, GBAMenuItem4, GBAMenuItem5, GBAMenuItem6}; + +// Rom menu +static const char GBARomItem1[] PROGMEM = "1MB"; +static const char GBARomItem2[] PROGMEM = "2MB"; +static const char GBARomItem3[] PROGMEM = "4MB"; +static const char GBARomItem4[] PROGMEM = "8MB"; +static const char GBARomItem5[] PROGMEM = "16MB"; +static const char GBARomItem6[] PROGMEM = "32MB"; +static const char* const romOptionsGBA[] PROGMEM = {GBARomItem1, GBARomItem2, GBARomItem3, GBARomItem4, GBARomItem5, GBARomItem6}; + +// Save menu +static const char GBASaveItem1[] PROGMEM = "4K EEPROM"; +static const char GBASaveItem2[] PROGMEM = "64K EEPROM"; +static const char GBASaveItem3[] PROGMEM = "256K SRAM/FRAM"; +static const char GBASaveItem4[] PROGMEM = "512K SRAM/FRAM"; +static const char GBASaveItem5[] PROGMEM = "512K FLASHROM"; +static const char GBASaveItem6[] PROGMEM = "1M FLASHROM"; +static const char* const saveOptionsGBA[] PROGMEM = {GBASaveItem1, GBASaveItem2, GBASaveItem3, GBASaveItem4, GBASaveItem5, GBASaveItem6}; + +void gbaMenu() { + // create menu with title and 4 options to choose from + unsigned char mainMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsGBA, 6); + mainMenu = question_box("GBA Cart Reader", menuOptions, 6, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + case 0: + // Read rom + switch (cartSize) + { + case 0: + // create submenu with title and 4 options to choose from + unsigned char GBARomMenu; + // Copy menuOptions out of progmem + convertPgm(romOptionsGBA, 6); + GBARomMenu = question_box("Select ROM size", menuOptions, 6, 0); + + // wait for user choice to come back from the question box menu + switch (GBARomMenu) + { + case 0: + // 1MB + cartSize = 0x100000; + break; + + case 1: + // 2MB + cartSize = 0x200000; + break; + + case 2: + // 4MB + cartSize = 0x400000; + break; + + case 3: + // 8MB + cartSize = 0x800000; + break; + + case 4: + // 16MB + cartSize = 0x1000000; + break; + + case 5: + // 32MB + cartSize = 0x2000000; + break; + } + break; + + case 1: + // 1MB + cartSize = 0x100000; + break; + + case 4: + // 4MB + cartSize = 0x400000; + break; + + case 8: + // 8MB + cartSize = 0x800000; + break; + + case 16: + // 16MB + cartSize = 0x1000000; + break; + + case 32: + // 32MB + cartSize = 0x2000000; + break; + } + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readROM_GBA(); + sd.chdir("/"); + compare_checksum_GBA(); + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + break; + + case 1: + // Read save + if (saveType == 0) { + // create submenu with title and 6 options to choose from + unsigned char GBASaveMenu; + // Copy menuOptions out of progmem + convertPgm(saveOptionsGBA, 6); + GBASaveMenu = question_box("Select save type", menuOptions, 6, 0); + + // wait for user choice to come back from the question box menu + switch (GBASaveMenu) + { + case 0: + // 4K EEPROM + saveType = 1; + break; + + case 1: + // 64K EEPROM + saveType = 2; + break; + + case 2: + // 256K SRAM/FRAM + saveType = 3; + break; + + case 3: + // 512K SRAM/FRAM + saveType = 6; + break; + + case 4: + // 512K FLASH + saveType = 4; + break; + + case 5: + // 1024K FLASH + saveType = 5; + break; + } + } + switch (saveType) + { + case 1: + display_Clear(); + sd.chdir("/"); + // 4K EEPROM + readEeprom_GBA(4); + setROM_GBA(); + break; + + case 2: + display_Clear(); + sd.chdir("/"); + // 64K EEPROM + readEeprom_GBA(64); + setROM_GBA(); + break; + + case 3: + display_Clear(); + sd.chdir("/"); + // 256K SRAM/FRAM + readSRAM_GBA(1, 32768, 0); + setROM_GBA(); + break; + + case 4: + display_Clear(); + sd.chdir("/"); + // 512K FLASH + readFLASH_GBA(1, 65536, 0); + setROM_GBA(); + break; + + case 5: + display_Clear(); + sd.chdir("/"); + // 1024K FLASH (divided into two banks) + switchBank_GBA(0x0); + setROM_GBA(); + readFLASH_GBA(1, 65536, 0); + switchBank_GBA(0x1); + setROM_GBA(); + readFLASH_GBA(0, 65536, 65536); + setROM_GBA(); + break; + + case 6: + display_Clear(); + sd.chdir("/"); + // 512K SRAM/FRAM + readSRAM_GBA(1, 65536, 0); + setROM_GBA(); + break; + } + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + break; + + case 2: + // Write save + if (saveType == 0) { + // create submenu with title and 6 options to choose from + unsigned char GBASavesMenu; + // Copy menuOptions out of progmem + convertPgm(saveOptionsGBA, 6); + GBASavesMenu = question_box("Select save type", menuOptions, 6, 0); + // wait for user choice to come back from the question box menu + switch (GBASavesMenu) + { + case 0: + // 4K EEPROM + saveType = 1; + break; + + case 1: + // 64K EEPROM + saveType = 2; + break; + + case 2: + // 256K SRAM/FRAM + saveType = 3; + break; + + case 3: + // 512K SRAM/FRAM + saveType = 6; + break; + + case 4: + // 512K FLASH + saveType = 4; + break; + + case 5: + // 1024K FLASH + saveType = 5; + break; + } + } + + switch (saveType) + { + case 1: + display_Clear(); + sd.chdir("/"); + // 4K EEPROM + writeEeprom_GBA(4); + writeErrors = verifyEEP_GBA(4); + if (writeErrors == 0) { + println_Msg(F("Verified OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + setROM_GBA(); + break; + + case 2: + display_Clear(); + sd.chdir("/"); + // 64K EEPROM + writeEeprom_GBA(64); + writeErrors = verifyEEP_GBA(64); + if (writeErrors == 0) { + println_Msg(F("Verified OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + setROM_GBA(); + break; + + case 3: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + // 256K SRAM/FRAM + writeSRAM_GBA(1, 32768, 0); + writeErrors = verifySRAM_GBA(32768, 0); + if (writeErrors == 0) { + println_Msg(F("Verified OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + setROM_GBA(); + break; + + case 4: + display_Clear(); + sd.chdir("/"); + // 512K FLASH + idFlash_GBA(); + resetFLASH_GBA(); + if (strcmp(flashid, "BFD4") != 0) { + println_Msg(F("Flashrom Type not supported")); + print_Msg(F("ID: ")); + println_Msg(flashid); + print_Error(F(""), true); + } + eraseFLASH_GBA(); + if (blankcheckFLASH_GBA(65536)) { + writeFLASH_GBA(1, 65536, 0); + verifyFLASH_GBA(65536, 0); + } + else { + print_Error(F("Erase failed"), false); + } + setROM_GBA(); + break; + + case 5: + display_Clear(); + sd.chdir("/"); + // 1M FLASH + idFlash_GBA(); + resetFLASH_GBA(); + if (strcmp(flashid, "C209") != 0) { + println_Msg(F("Flashrom Type not supported")); + print_Msg(F("ID: ")); + println_Msg(flashid); + print_Error(F(""), true); + } + eraseFLASH_GBA(); + // 131072 bytes are divided into two 65536 byte banks + switchBank_GBA(0x0); + setROM_GBA(); + if (blankcheckFLASH_GBA(65536)) { + writeFLASH_GBA(1, 65536, 0); + verifyFLASH_GBA(65536, 0); + } + else { + print_Error(F("Erase failed"), false); + } + switchBank_GBA(0x1); + setROM_GBA(); + if (blankcheckFLASH_GBA(65536)) { + writeFLASH_GBA(0, 65536, 65536); + verifyFLASH_GBA(65536, 65536); + } + else { + print_Error(F("Erase failed"), false); + } + setROM_GBA(); + break; + + case 6: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + // 512K SRAM/FRAM + writeSRAM_GBA(1, 65536, 0); + writeErrors = verifySRAM_GBA(65536, 0); + if (writeErrors == 0) { + println_Msg(F("Verified OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + setROM_GBA(); + break; + } + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + break; + + case 3: + display_Clear(); + // create submenu with title and 7 options to choose from + unsigned char GBASaveMenu; + // Copy menuOptions out of progmem + convertPgm(saveOptionsGBA, 6); + GBASaveMenu = question_box("Select save type", menuOptions, 6, 0); + + // wait for user choice to come back from the question box menu + switch (GBASaveMenu) + { + case 0: + // 4K EEPROM + saveType = 1; + break; + + case 1: + // 64K EEPROM + saveType = 2; + break; + + case 2: + // 256K SRAM/FRAM + saveType = 3; + break; + + case 3: + // 512K SRAM/FRAM + saveType = 6; + break; + + case 4: + // 512K FLASH + saveType = 4; + break; + + case 5: + // 1024K FLASH + saveType = 5; + break; + } + display_Clear(); + break; + + case 4: + display_Clear(); + flashRepro_GBA(); + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + asm volatile (" jmp 0"); + break; + + case 5: + asm volatile (" jmp 0"); + break; + } + +} + +/****************************************** + Setup + *****************************************/ +void setup_GBA() { + setROM_GBA(); + + // Print start page + getCartInfo_GBA(); + display_Clear(); + + print_Msg(F("Name: ")); + println_Msg(romName); + print_Msg(F("Cart ID: ")); + println_Msg(cartID); + print_Msg(F("Rom Size: ")); + if (cartSize == 0) + println_Msg(F("Unknown")); + else { + print_Msg(cartSize); + println_Msg(F("MB")); + } + print_Msg(F("Save: ")); + switch (saveType) + { + case 0: + println_Msg(F("Unknown")); + break; + + case 1: + println_Msg(F("4K Eeprom")); + break; + + case 2: + println_Msg(F("64K Eeprom")); + break; + + case 3: + println_Msg(F("256K Sram")); + break; + + case 4: + println_Msg(F("512K Flash")); + break; + + case 5: + println_Msg(F("1024K Flash")); + break; + } + + print_Msg(F("Checksum: ")); + println_Msg(checksumStr); + print_Msg(F("Version: 1.")); + println_Msg(romVersion); + + // Wait for user input + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +/****************************************** + Low level functions +*****************************************/ +void setROM_GBA() { + // Set address/data pins to OUTPUT + // AD0-AD7 + DDRF = 0xFF; + // AD8-AD15 + DDRK = 0xFF; + // AD16-AD23 + DDRC = 0xFF; + + // Set Control Pins to Output CS_SRAM(PH0) CS_ROM(PH3) WR(PH5) RD(PH6) + // CLK is N/C and IRQ is conected to GND inside the cartridge + DDRH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); + // Output a high signal on CS_SRAM(PH0) CS_ROM(PH3) WR(PH5) RD(PH6) + // At power-on all the control lines are high/disabled + PORTH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); + + delay(500); +} + +word readWord_GBA(unsigned long myAddress) { + // Set address/data ports to output + DDRF = 0xFF; + DDRK = 0xFF; + DDRC = 0xFF; + + // Divide address by two to get word addressing + myAddress = myAddress >> 1; + + // Output address to address pins, + PORTF = myAddress; + PORTK = myAddress >> 8; + PORTC = myAddress >> 16; + + // Pull CS(PH3) to LOW + PORTH &= ~ (1 << 3); + + // Set address/data ports to input + PORTF = 0x0; + PORTK = 0x0; + DDRF = 0x0; + DDRK = 0x0; + + // Pull RD(PH6) to LOW + PORTH &= ~ (1 << 6); + + // Delay here or read error with repro + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + word myWord = (PINK << 8) | PINF; + + // Switch RD(PH6) to HIGH + PORTH |= (1 << 6); + + // Switch CS_ROM(PH3) to HIGH + PORTH |= (1 << 3); + + return myWord; +} + +void writeWord_GBA(unsigned long myAddress, word myWord) { + // Set address/data ports to output + DDRF = 0xFF; + DDRK = 0xFF; + DDRC = 0xFF; + + // Divide address by two to get word addressing + myAddress = myAddress >> 1; + + // Output address to address pins, + PORTF = myAddress; + PORTK = myAddress >> 8; + PORTC = myAddress >> 16; + + // Pull CS(PH3) to LOW + PORTH &= ~ (1 << 3); + + __asm__("nop\n\t""nop\n\t"); + + // Output data + PORTF = myWord & 0xFF; + PORTK = myWord >> 8; + + // Pull WR(PH5) to LOW + PORTH &= ~ (1 << 5); + + __asm__("nop\n\t""nop\n\t"); + + // Switch WR(PH5) to HIGH + PORTH |= (1 << 5); + + // Switch CS_ROM(PH3) to HIGH + PORTH |= (1 << 3); +} + +// This function swaps bit at positions p1 and p2 in an integer n +word swapBits(word n, word p1, word p2) +{ + // Move p1'th to rightmost side + word bit1 = (n >> p1) & 1; + + // Move p2'th to rightmost side + word bit2 = (n >> p2) & 1; + + // XOR the two bits */ + word x = (bit1 ^ bit2); + + // Put the xor bit back to their original positions + x = (x << p1) | (x << p2); + + // XOR 'x' with the original number so that the two sets are swapped + word result = n ^ x; + + return result; +} + +// Some repros have D0 and D1 switched +word readWord_GAB(unsigned long myAddress) { + word tempWord = swapBits(readWord_GBA(myAddress), 0, 1); + return tempWord; +} + +void writeWord_GAB(unsigned long myAddress, word myWord) { + writeWord_GBA(myAddress, swapBits(myWord, 0, 1)); +} + +byte readByte_GBA(unsigned long myAddress) { + // Set address ports to output + DDRF = 0xFF; + DDRK = 0xFF; + // Set data port to input + DDRC = 0x0; + + // Output address to address pins, + PORTF = myAddress; + PORTK = myAddress >> 8; + + // Pull OE_SRAM(PH6) to LOW + PORTH &= ~(1 << 6); + // Pull CE_SRAM(PH0) to LOW + PORTH &= ~(1 << 0); + + // Hold address for at least 25ns and wait 150ns before access + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Read byte + byte tempByte = PINC; + + // Pull CE_SRAM(PH0) HIGH + PORTH |= (1 << 0); + // Pull OE_SRAM(PH6) HIGH + PORTH |= (1 << 6); + + return tempByte; +} + +void writeByte_GBA(unsigned long myAddress, byte myData) { + // Set address ports to output + DDRF = 0xFF; + DDRK = 0xFF; + // Set data port to output + DDRC = 0xFF; + + // Output address to address pins + PORTF = myAddress; + PORTK = myAddress >> 8; + // Output data to data pins + PORTC = myData; + + // Wait till output is stable + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Pull WE_SRAM(PH5) to LOW + PORTH &= ~(1 << 5); + // Pull CE_SRAM(PH0) to LOW + PORTH &= ~(1 << 0); + + // Leave WR low for at least 60ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Pull CE_SRAM(PH0) HIGH + PORTH |= (1 << 0); + // Pull WE_SRAM(PH5) HIGH + PORTH |= (1 << 5); + + // Leave WR high for at least 50ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); +} + +/****************************************** + GBA ROM Functions +*****************************************/ +// Read info out of rom header +void getCartInfo_GBA() { + // Read Header into array + for (int currWord = 0; currWord < 192; currWord += 2) { + word tempWord = readWord_GBA(currWord); + + sdBuffer[currWord] = tempWord & 0xFF; + sdBuffer[currWord + 1] = (tempWord >> 8) & 0xFF; + } + + // Compare Nintendo logo against known checksum, 156 bytes starting at 0x04 + word logoChecksum = 0; + for (int currByte = 0x4; currByte < 0xA0; currByte++) { + logoChecksum += sdBuffer[currByte]; + } + + if (logoChecksum != 0x4B1B) { + print_Error(F("CARTRIDGE ERROR"), false); + strcpy(romName, "ERROR"); + println_Msg(F("")); + println_Msg(F("")); + println_Msg(F("")); + println_Msg(F("Press Button to")); + println_Msg(F("ignore or powercycle")); + println_Msg(F("to try again")); + display_Update(); + wait(); + } + else { + char tempStr2[2]; + char tempStr[5]; + char sizeStr[3]; + char saveStr[2]; + + // cart not in list + cartSize = 0; + saveType = 0; + + // Get cart ID + cartID[0] = char(sdBuffer[0xAC]); + cartID[1] = char(sdBuffer[0xAD]); + cartID[2] = char(sdBuffer[0xAE]); + cartID[3] = char(sdBuffer[0xAF]); + + if (myFile.open("gba.txt", O_READ)) { + // Loop through file + while (myFile.available()) { + // Read 4 bytes into String, do it one at a time so byte order doesn't get mixed up + sprintf(tempStr, "%c", myFile.read()); + for (byte i = 0; i < 3; i++) { + sprintf(tempStr2, "%c", myFile.read()); + strcat(tempStr, tempStr2); + } + + // Check if string is a match + if (strcmp(tempStr, cartID) == 0) { + // Skip the , in the file + myFile.seekSet(myFile.curPosition() + 1); + + // Read the next ascii character and subtract 48 to convert to decimal + cartSize = myFile.read() - 48; + // Remove leading 0 for single digit cart sizes + if (cartSize != 0) { + cartSize = cartSize * 10 + myFile.read() - 48; + } + else { + cartSize = myFile.read() - 48; + } + + // Skip the , in the file + myFile.seekSet(myFile.curPosition() + 1); + + // Read the next ascii character and subtract 48 to convert to decimal + saveType = myFile.read() - 48; + } + // If no match, empty string, advance by 7 and try again + else { + myFile.seekSet(myFile.curPosition() + 7); + } + } + // Close the file: + myFile.close(); + } + else { + print_Error(F("GBA.txt missing"), true); + } + + // Get name + byte myByte = 0; + byte myLength = 0; + for (int addr = 0xA0; addr <= 0xAB; addr++) { + myByte = sdBuffer[addr]; + if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 15) { + romName[myLength] = char(myByte); + myLength++; + } + } + + // Get ROM version + romVersion = sdBuffer[0xBC]; + + // Get Checksum as string + sprintf(checksumStr, "%02X", sdBuffer[0xBD]); + + // Calculate Checksum + int calcChecksum = 0x00; + for (int n = 0xA0; n < 0xBD; n++) { + calcChecksum -= sdBuffer[n]; + } + calcChecksum = (calcChecksum - 0x19) & 0xFF; + // Turn into string + sprintf(calcChecksumStr, "%02X", calcChecksum); + + // Compare checksum + if (strcmp(calcChecksumStr, checksumStr) != 0) { + print_Msg(F("Result: ")); + println_Msg(calcChecksumStr); + print_Error(F("Checksum Error"), false); + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + } + } +} + +// Dump ROM +void readROM_GBA() { + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".gba"); + + // create a new folder for the rom file + EEPROM_readAnything(10, foldern); + sprintf(folder, "GBA/ROM/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + //clear the screen + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("Can't create file on SD"), true); + } + + // Read rom + for (int myAddress = 0; myAddress < cartSize; myAddress += 512) { + // Blink led + if (myAddress % 16384 == 0) + PORTB ^= (1 << 4); + + for (int currWord = 0; currWord < 512; currWord += 2) { + word tempWord = readWord_GBA(myAddress + currWord); + sdBuffer[currWord] = tempWord & 0xFF; + sdBuffer[currWord + 1] = (tempWord >> 8) & 0xFF; + } + + // Write to SD + myFile.write(sdBuffer, 512); + } + + // Close the file: + myFile.close(); +} + +// Calculate the checksum of the dumped rom +boolean compare_checksum_GBA () { + println_Msg(F("Calculating Checksum")); + display_Update(); + + strcpy(fileName, romName); + strcat(fileName, ".gba"); + + // last used rom folder + EEPROM_readAnything(10, foldern); + sprintf(folder, "GBA/ROM/%s/%d", romName, foldern - 1); + sd.chdir(folder); + + // If file exists + if (myFile.open(fileName, O_READ)) { + // Read rom header + myFile.read(sdBuffer, 512); + myFile.close(); + + // Calculate Checksum + int calcChecksum = 0x00; + for (int n = 0xA0; n < 0xBD; n++) { + calcChecksum -= sdBuffer[n]; + } + calcChecksum = (calcChecksum - 0x19) & 0xFF; + + // Turn into string + sprintf(calcChecksumStr, "%02X", calcChecksum); + + if (strcmp(calcChecksumStr, checksumStr) == 0) { + println_Msg(F("Checksum matches")); + display_Update(); + return 1; + } + else { + print_Msg(F("Result: ")); + println_Msg(calcChecksumStr); + print_Error(F("Checksum Error"), false); + return 0; + } + } + // Else show error + else { + print_Error(F("Failed to open rom"), false); + return 0; + } +} + + +/****************************************** + GBA SRAM SAVE Functions +*****************************************/ +void readSRAM_GBA(boolean browseFile, unsigned long sramSize, uint32_t pos) { + if (browseFile) { + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".srm"); + + // create a new folder for the save file + EEPROM_readAnything(10, foldern); + sprintf(folder, "GBA/SAVE/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + // Save location + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + } + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + + // Seek to a new position in the file + if (pos != 0) + myFile.seekCur(pos); + + for (unsigned long currAddress = 0; currAddress < sramSize; currAddress += 512) { + for (int c = 0; c < 512; c++) { + // Read byte + sdBuffer[c] = readByte_GBA(currAddress + c); + } + + // Write sdBuffer to file + myFile.write(sdBuffer, 512); + } + // Close the file: + myFile.close(); + + // Signal end of process + println_Msg(F("Done")); + display_Update(); +} + +void writeSRAM_GBA(boolean browseFile, unsigned long sramSize, uint32_t pos) { + if (browseFile) { + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser("Select srm file"); + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + display_Clear(); + } + + //open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Seek to a new position in the file + if (pos != 0) + myFile.seekCur(pos); + + for (unsigned long currAddress = 0; currAddress < sramSize; currAddress += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + + for (int c = 0; c < 512; c++) { + // Write byte + writeByte_GBA(currAddress + c, sdBuffer[c]); + } + } + // Close the file: + myFile.close(); + println_Msg(F("SRAM writing finished")); + display_Update(); + + } + else { + print_Error(F("File doesnt exist"), false); + } +} + +unsigned long verifySRAM_GBA(unsigned long sramSize, uint32_t pos) { + //open file on sd card + if (myFile.open(filePath, O_READ)) { + // Variable for errors + writeErrors = 0; + + // Seek to a new position in the file + if (pos != 0) + myFile.seekCur(pos); + + for (unsigned long currAddress = 0; currAddress < sramSize; currAddress += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + + for (int c = 0; c < 512; c++) { + // Read byte + if (readByte_GBA(currAddress + c) != sdBuffer[c]) { + writeErrors++; + } + } + } + // Close the file: + myFile.close(); + return writeErrors; + } + else { + print_Error(F("Can't open file"), false); + } +} + +/****************************************** + GBA FRAM SAVE Functions +*****************************************/ +// MB85R256 FRAM (Ferroelectric Random Access Memory) 32,768 words x 8 bits +void readFRAM_GBA (unsigned long framSize) { + // Output a HIGH signal on CS_ROM(PH3) WE_SRAM(PH5) + PORTH |= (1 << 3) | (1 << 5); + + // Set address ports to output + DDRF = 0xFF; + DDRK = 0xFF; + + // Set data pins to input + DDRC = 0x00; + + // Output a LOW signal on CE_SRAM(PH0) and OE_SRAM(PH6) + PORTH &= ~((1 << 0) | (1 << 6)); + + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".srm"); + + // create a new folder for the save file + EEPROM_readAnything(10, foldern); + sprintf(folder, "GBA/SAVE/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + // Save location + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + for (unsigned long currAddress = 0; currAddress < framSize; currAddress += 512) { + for (int c = 0; c < 512; c++) { + // Pull OE_SRAM(PH6) HIGH + PORTH |= (1 << 6); + + // Set address + PORTF = (currAddress + c) & 0xFF; + PORTK = ((currAddress + c) >> 8) & 0xFF; + + // Arduino running at 16Mhz -> one nop = 62.5ns + // Leave CS_SRAM HIGH for at least 85ns + __asm__("nop\n\t""nop\n\t"); + + // Pull OE_SRAM(PH6) LOW + PORTH &= ~ (1 << 6); + + // Hold address for at least 25ns and wait 150ns before access + __asm__("nop\n\t""nop\n\t""nop\n\t"); + + // Read byte + sdBuffer[c] = PINC; + } + // Write sdBuffer to file + myFile.write(sdBuffer, 512); + } + // Close the file: + myFile.close(); + + // Signal end of process + println_Msg(F("Done")); + display_Update(); +} + +// Write file to SRAM +void writeFRAM_GBA (boolean browseFile, unsigned long framSize) { + // Output a HIGH signal on CS_ROM(PH3) and OE_SRAM(PH6) + PORTH |= (1 << 3) | (1 << 6); + + // Set address ports to output + DDRF = 0xFF; + DDRK = 0xFF; + + // Set data port to output + DDRC = 0xFF; + + // Output a LOW signal on CE_SRAM(PH0) and WE_SRAM(PH5) + PORTH &= ~((1 << 0) | (1 << 5)); + + if (browseFile) { + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser("Select srm file"); + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + display_Clear(); + } + else + sprintf(filePath, "%s", fileName); + + //open file on sd card + if (myFile.open(filePath, O_READ)) { + for (unsigned long currAddress = 0; currAddress < framSize; currAddress += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + + for (int c = 0; c < 512; c++) { + // Output Data on PORTC + PORTC = sdBuffer[c]; + + // Arduino running at 16Mhz -> one nop = 62.5ns + // Data setup time 50ns + __asm__("nop\n\t"); + + // Pull WE_SRAM (PH5) HIGH + PORTH |= (1 << 5); + + // Set address + PORTF = (currAddress + c) & 0xFF; + PORTK = ((currAddress + c) >> 8) & 0xFF; + + // Leave WE_SRAM (PH5) HIGH for at least 85ns + __asm__("nop\n\t""nop\n\t"); + + // Pull WE_SRAM (PH5) LOW + PORTH &= ~ (1 << 5); + + // Hold address for at least 25ns and wait 150ns before next write + __asm__("nop\n\t""nop\n\t""nop\n\t"); + } + } + // Close the file: + myFile.close(); + println_Msg(F("SRAM writing finished")); + display_Update(); + + } + else { + print_Error(F("File doesnt exist"), false); + } +} + +// Check if the SRAM was written without any error +unsigned long verifyFRAM_GBA(unsigned long framSize) { + // Output a HIGH signal on CS_ROM(PH3) WE_SRAM(PH5) + PORTH |= (1 << 3) | (1 << 5); + + // Set address ports to output + DDRF = 0xFF; + DDRK = 0xFF; + + // Set data pins to input + DDRC = 0x00; + + // Output a LOW signal on CE_SRAM(PH0) and OE_SRAM(PH6) + PORTH &= ~((1 << 0) | (1 << 6)); + + //open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Variable for errors + writeErrors = 0; + + for (unsigned long currAddress = 0; currAddress < framSize; currAddress += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + + for (int c = 0; c < 512; c++) { + // Pull OE_SRAM(PH6) HIGH + PORTH |= (1 << 6); + + // Set address + PORTF = (currAddress + c) & 0xFF; + PORTK = ((currAddress + c) >> 8) & 0xFF; + + // Arduino running at 16Mhz -> one nop = 62.5ns + // Leave CS_SRAM HIGH for at least 85ns + __asm__("nop\n\t""nop\n\t"); + + // Pull OE_SRAM(PH6) LOW + PORTH &= ~ (1 << 6); + + // Hold address for at least 25ns and wait 150ns before access + __asm__("nop\n\t""nop\n\t""nop\n\t"); + + // Read byte + if (PINC != sdBuffer[c]) { + writeErrors++; + } + } + } + + // Close the file: + myFile.close(); + return writeErrors; + } + else { + print_Error(F("Can't open file"), false); + } +} + +/****************************************** + GBA FLASH SAVE Functions +*****************************************/ +// SST 39VF512 Flashrom +void idFlash_GBA() { + // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) and OE_FLASH(PH6) + PORTH |= (1 << 3) | (1 << 5) | (1 << 6); + + // Set address ports to output + DDRF = 0xFF; + DDRK = 0xFF; + // Set data pins to output + DDRC = 0xFF; + + // Output a LOW signal on CE_FLASH(PH0) + PORTH &= ~(1 << 0); + + // ID command sequence + writeByteFlash_GBA(0x5555, 0xaa); + writeByteFlash_GBA(0x2aaa, 0x55); + writeByteFlash_GBA(0x5555, 0x90); + + // Set data pins to input + DDRC = 0x00; + + // Output a LOW signal on OE_FLASH(PH6) + PORTH &= ~(1 << 6); + + // Wait 150ns before reading ID + // Arduino running at 16Mhz -> one nop = 62.5ns + __asm__("nop\n\t""nop\n\t""nop\n\t"); + + // Read the two id bytes into a string + sprintf(flashid, "%02X%02X", readByteFlash_GBA(0), readByteFlash_GBA(1)); + + // Set CS_FLASH(PH0) high + PORTH |= (1 << 0); +} + +// Reset FLASH +void resetFLASH_GBA() { + // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) and OE_FLASH(PH6) + PORTH |= (1 << 3) | (1 << 5) | (1 << 6); + + // Set address ports to output + DDRF = 0xFF; + DDRK = 0xFF; + // Set data pins to output + DDRC = 0xFF; + + // Output a LOW signal on CE_FLASH(PH0) + PORTH &= ~(1 << 0); + + // Reset command sequence + writeByteFlash_GBA(0x5555, 0xAA); + writeByteFlash_GBA(0x2AAA, 0x55); + writeByteFlash_GBA(0x5555, 0xf0); + writeByteFlash_GBA(0x5555, 0xf0); + + // Set CS_FLASH(PH0) high + PORTH |= (1 << 0); + + // Wait + delay(100); +} + +byte readByteFlash_GBA(unsigned long myAddress) { + // Set address + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + + // Wait until byte is ready to read + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Read byte + byte tempByte = PINC; + + // Arduino running at 16Mhz -> one nop = 62.5ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + return tempByte; +} + +void writeByteFlash_GBA(unsigned long myAddress, byte myData) { + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + PORTC = myData; + + // Arduino running at 16Mhz -> one nop = 62.5ns + // Wait till output is stable + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Switch WE_FLASH(PH5) to LOW + PORTH &= ~(1 << 5); + + // Leave WE low for at least 40ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Switch WE_FLASH(PH5) to HIGH + PORTH |= (1 << 5); + + // Leave WE high for a bit + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); +} + +// Erase FLASH +void eraseFLASH_GBA() { + // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) and OE_FLASH(PH6) + PORTH |= (1 << 3) | (1 << 5) | (1 << 6); + + // Set address ports to output + DDRF = 0xFF; + DDRK = 0xFF; + // Set data pins to output + DDRC = 0xFF; + + // Output a LOW signal on CE_FLASH(PH0) + PORTH &= ~(1 << 0); + + // Erase command sequence + writeByteFlash_GBA(0x5555, 0xaa); + writeByteFlash_GBA(0x2aaa, 0x55); + writeByteFlash_GBA(0x5555, 0x80); + writeByteFlash_GBA(0x5555, 0xaa); + writeByteFlash_GBA(0x2aaa, 0x55); + writeByteFlash_GBA(0x5555, 0x10); + + // Set CS_FLASH(PH0) high + PORTH |= (1 << 0); + + // Wait until all is erased + delay(500); +} + +boolean blankcheckFLASH_GBA (unsigned long flashSize) { + // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) + PORTH |= (1 << 3) | (1 << 5); + + // Set address ports to output + DDRF = 0xFF; + DDRK = 0xFF; + // Set address to 0 + PORTF = 0x00; + PORTK = 0x00; + + // Set data pins to input + DDRC = 0x00; + // Disable Pullups + //PORTC = 0x00; + + boolean blank = 1; + + // Output a LOW signal on CE_FLASH(PH0) + PORTH &= ~(1 << 0); + + // Output a LOW signal on OE_FLASH(PH6) + PORTH &= ~(1 << 6); + + for (unsigned long currAddress = 0; currAddress < flashSize; currAddress += 512) { + // Fill buffer + for (int c = 0; c < 512; c++) { + // Read byte + sdBuffer[c] = readByteFlash_GBA(currAddress + c); + } + // Check buffer + for (unsigned long currByte = 0; currByte < 512; currByte++) { + if (sdBuffer[currByte] != 0xFF) { + currByte = 512; + currAddress = flashSize; + blank = 0; + } + } + } + // Set CS_FLASH(PH0) high + PORTH |= (1 << 0); + + return blank; +} + +// The MX29L010 is 131072 bytes in size and has 16 sectors per bank +// each sector is 4096 bytes, there are 32 sectors total +// therefore the bank size is 65536 bytes, so we have two banks in total +void switchBank_GBA(byte bankNum) { + // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) and OE_FLASH(PH6) + PORTH |= (1 << 3) | (1 << 5) | (1 << 6); + + // Set address ports to output + DDRF = 0xFF; + DDRK = 0xFF; + // Set data pins to output + DDRC = 0xFF; + + // Output a LOW signal on CE_FLASH(PH0) + PORTH &= ~(1 << 0); + + // Switch bank command sequence + writeByte_GBA(0x5555, 0xAA); + writeByte_GBA(0x2AAA, 0x55); + writeByte_GBA(0x5555, 0xB0); + writeByte_GBA(0x0000, bankNum); + + // Set CS_FLASH(PH0) high + PORTH |= (1 << 0); +} + +void readFLASH_GBA (boolean browseFile, unsigned long flashSize, uint32_t pos) { + // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) + PORTH |= (1 << 3) | (1 << 5); + + // Set address ports to output + DDRF = 0xFF; + DDRK = 0xFF; + // Set address to 0 + PORTF = 0x00; + PORTK = 0x00; + + // Set data pins to input + DDRC = 0x00; + + if (browseFile) { + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".fla"); + + // create a new folder for the save file + EEPROM_readAnything(10, foldern); + + sprintf(folder, "GBA/SAVE/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + // Save location + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + } + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + + // Seek to a new position in the file + if (pos != 0) + myFile.seekCur(pos); + + // Output a LOW signal on CE_FLASH(PH0) + PORTH &= ~(1 << 0); + + // Output a LOW signal on OE_FLASH(PH6) + PORTH &= ~(1 << 6); + + for (unsigned long currAddress = 0; currAddress < flashSize; currAddress += 512) { + for (int c = 0; c < 512; c++) { + // Read byte + sdBuffer[c] = readByteFlash_GBA(currAddress + c); + } + // Write sdBuffer to file + myFile.write(sdBuffer, 512); + } + myFile.close(); + + // Set CS_FLASH(PH0) high + PORTH |= (1 << 0); + + // Signal end of process + println_Msg(F("Done")); + display_Update(); +} + +void busyCheck_GBA(int currByte) { + // Set data pins to input + DDRC = 0x00; + // Output a LOW signal on OE_FLASH(PH6) + PORTH &= ~(1 << 6); + // Read PINC + while (PINC != sdBuffer[currByte]) {} + // Output a HIGH signal on OE_FLASH(PH6) + PORTH |= (1 << 6); + // Set data pins to output + DDRC = 0xFF; +} + +void writeFLASH_GBA (boolean browseFile, unsigned long flashSize, uint32_t pos) { + // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) and OE_FLASH(PH6) + PORTH |= (1 << 3) | (1 << 5) | (1 << 6); + + // Set address ports to output + DDRF = 0xFF; + DDRK = 0xFF; + // Set data port to output + DDRC = 0xFF; + + if (browseFile) { + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser("Select fla file"); + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + display_Clear(); + } + + print_Msg(F("Writing flash...")); + display_Update(); + + //open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Seek to a new position in the file + if (pos != 0) + myFile.seekCur(pos); + + // Output a LOW signal on CE_FLASH(PH0) + PORTH &= ~(1 << 0); + + for (unsigned long currAddress = 0; currAddress < flashSize; currAddress += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + + for (int c = 0; c < 512; c++) { + // Write command sequence + writeByteFlash_GBA(0x5555, 0xaa); + writeByteFlash_GBA(0x2aaa, 0x55); + writeByteFlash_GBA(0x5555, 0xa0); + // Write current byte + writeByteFlash_GBA(currAddress + c, sdBuffer[c]); + + // Wait + busyCheck_GBA(c); + } + } + // Set CS_FLASH(PH0) high + PORTH |= (1 << 0); + + // Close the file: + myFile.close(); + println_Msg(F("done")); + display_Update(); + + } + else { + println_Msg(F("Error")); + print_Error(F("File doesnt exist"), false); + } +} + +// Check if the Flashrom was written without any error +void verifyFLASH_GBA(unsigned long flashSize, uint32_t pos) { + // Output a HIGH signal on CS_ROM(PH3) WE_FLASH(PH5) + PORTH |= (1 << 3) | (1 << 5); + + // Set address ports to output + DDRF = 0xFF; + DDRK = 0xFF; + + // Set data pins to input + DDRC = 0x00; + + // Output a LOW signal on CE_FLASH(PH0) and OE_FLASH(PH6) + PORTH &= ~((1 << 0) | (1 << 6)); + + // Signal beginning of process + print_Msg(F("Verify...")); + display_Update(); + + unsigned long wrError = 0; + + //open file on sd card + if (!myFile.open(filePath, O_READ)) { + print_Error(F("SD Error"), true); + } + + // Seek to a new position in the file + if (pos != 0) + myFile.seekCur(pos); + + for (unsigned long currAddress = 0; currAddress < flashSize; currAddress += 512) { + myFile.read(sdBuffer, 512); + + for (int c = 0; c < 512; c++) { + // Read byte + if (sdBuffer[c] != readByteFlash_GBA(currAddress + c)) { + wrError++; + } + } + } + myFile.close(); + + // Set CS_FLASH(PH0) high + PORTH |= (1 << 0); + + if (wrError == 0) { + println_Msg(F("OK")); + } + else { + print_Msg(wrError); + print_Error(F(" Errors"), false); + } +} + +/****************************************** + GBA Eeprom SAVE Functions +*****************************************/ +// Write eeprom from file +void writeEeprom_GBA(word eepSize) { + // Launch Filebrowser + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser("Select eep file"); + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + display_Clear(); + + print_Msg(F("Writing eeprom...")); + display_Update(); + + //open file on sd card + if (myFile.open(filePath, O_READ)) { + for (word i = 0; i < eepSize * 16; i += 64) { + // Fill romBuffer + myFile.read(sdBuffer, 512); + // Disable interrupts for more uniform clock pulses + noInterrupts(); + // Write 512 bytes + writeBlock_EEP(i, eepSize); + interrupts(); + + // Wait + delayMicroseconds(200); + } + + // Close the file: + myFile.close(); + println_Msg(F("done")); + display_Update(); + } + else { + println_Msg(F("Error")); + print_Error(F("File doesnt exist"), false); + } +} + +// Read eeprom to file +void readEeprom_GBA(word eepSize) { + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".eep"); + + // create a new folder for the save file + EEPROM_readAnything(10, foldern); + + sprintf(folder, "GBA/SAVE/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + // Save location + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + + // Each block contains 8 Bytes, so for a 8KB eeprom 1024 blocks need to be read + for (word currAddress = 0; currAddress < eepSize * 16; currAddress += 64) { + // Disable interrupts for more uniform clock pulses + noInterrupts(); + // Fill sd Buffer + readBlock_EEP(currAddress, eepSize); + interrupts(); + + // Write sdBuffer to file + myFile.write(sdBuffer, 512); + + // Wait + delayMicroseconds(200); + } + myFile.close(); +} + +// Send address as bits to eeprom +void send_GBA(word currAddr, word numBits) { + for (word addrBit = numBits; addrBit > 0; addrBit--) { + // If you want the k-th bit of n, then do + // (n & ( 1 << k )) >> k + if (((currAddr & ( 1 << (addrBit - 1))) >> (addrBit - 1))) { + // Set A0(PF0) to High + PORTF |= (1 << 0); + // Set WR(PH5) to LOW + PORTH &= ~ (1 << 5); + // Set WR(PH5) to High + PORTH |= (1 << 5); + } + else { + // Set A0(PF0) to Low + PORTF &= ~ (1 << 0); + // Set WR(PH5) to LOW + PORTH &= ~ (1 << 5); + // Set WR(PH5) to High + PORTH |= (1 << 5); + } + } +} + +// Write 512K eeprom block +void writeBlock_EEP(word startAddr, word eepSize) { + // Setup + // Set CS_ROM(PH3) WR(PH5) RD(PH6) to Output + DDRH |= (1 << 3) | (1 << 5) | (1 << 6); + // Set A0(PF0) to Output + DDRF |= (1 << 0); + // Set A23/D7(PC7) to Output + DDRC |= (1 << 7); + + // Set CS_ROM(PH3) WR(PH5) RD(PH6) to High + PORTH |= (1 << 3) | (1 << 5) | (1 << 6); + // Set A0(PF0) to High + PORTF |= (1 << 0); + // Set A23/D7(PC7) to High + PORTC |= (1 << 7); + + __asm__("nop\n\t""nop\n\t"); + + // Write 64*8=512 bytes + for (word currAddr = startAddr; currAddr < startAddr + 64; currAddr++) { + // Set CS_ROM(PH3) to LOW + PORTH &= ~ (1 << 3); + + // Send write request "10" + // Set A0(PF0) to High + PORTF |= (1 << 0); + // Set WR(PH5) to LOW + PORTH &= ~ (1 << 5); + // Set WR(PH5) to High + PORTH |= (1 << 5); + // Set A0(PF0) to LOW + PORTF &= ~ (1 << 0); + // Set WR(PH5) to LOW + PORTH &= ~ (1 << 5); + // Set WR(PH5) to High + PORTH |= (1 << 5); + + // Send either 6 or 14 bit address + if (eepSize == 4) { + send_GBA(currAddr, 6); + } + else { + send_GBA(currAddr, 14); + } + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Send data + for (byte currByte = 0; currByte < 8; currByte++) { + send_GBA(sdBuffer[(currAddr - startAddr) * 8 + currByte], 8); + } + + // Send stop bit + // Set A0(PF0) to LOW + PORTF &= ~ (1 << 0); + // Set WR(PH5) to LOW + PORTH &= ~ (1 << 5); + // WR(PH5) to High + PORTH |= (1 << 5); + + // Set CS_ROM(PH3) to High + PORTH |= (1 << 3); + + // Wait until done + // Set A0(PF0) to Input + DDRF &= ~ (1 << 0); + + do { + // Set CS_ROM(PH3) RD(PH6) to LOW + PORTH &= ~((1 << 3) | (1 << 6)); + // Set CS_ROM(PH3) RD(PH6) to High + PORTH |= (1 << 3) | (1 << 6); + } + while ((PINF & 0x1) == 0); + + // Set A0(PF0) to Output + DDRF |= (1 << 0); + } +} + +// Reads 512 bytes from eeprom +void readBlock_EEP(word startAddress, word eepSize) { + // Setup + // Set CS_ROM(PH3) WR(PH5) RD(PH6) to Output + DDRH |= (1 << 3) | (1 << 5) | (1 << 6); + // Set A0(PF0) to Output + DDRF |= (1 << 0); + // Set A23/D7(PC7) to Output + DDRC |= (1 << 7); + + // Set CS_ROM(PH3) WR(PH5) RD(PH6) to High + PORTH |= (1 << 3) | (1 << 5) | (1 << 6); + // Set A0(PF0) to High + PORTF |= (1 << 0); + // Set A23/D7(PC7) to High + PORTC |= (1 << 7); + + __asm__("nop\n\t""nop\n\t"); + + // Read 64*8=512 bytes + for (word currAddr = startAddress; currAddr < startAddress + 64; currAddr++) { + // Set CS_ROM(PH3) to LOW + PORTH &= ~ (1 << 3); + + // Send read request "11" + // Set A0(PF0) to High + PORTF |= (1 << 0); + // Set WR(PH5) to LOW + PORTH &= ~ (1 << 5); + // Set WR(PH5) to High + PORTH |= (1 << 5); + // Set WR(PH5) to LOW + PORTH &= ~ (1 << 5); + // Set WR(PH5) to High + PORTH |= (1 << 5); + + // Send either 6 or 14 bit address + if (eepSize == 4) { + send_GBA(currAddr, 6); + } + else { + send_GBA(currAddr, 14); + } + + // Send stop bit + // Set A0(PF0) to LOW + PORTF &= ~ (1 << 0); + // Set WR(PH5) to LOW + PORTH &= ~ (1 << 5); + // WR(PH5) to High + PORTH |= (1 << 5); + + // Set CS_ROM(PH3) to High + PORTH |= (1 << 3); + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Read data + // Set A0(PF0) to Input + DDRF &= ~ (1 << 0); + // Set CS_ROM(PH3) to low + PORTH &= ~(1 << 3); + + // Array that holds the bits + bool tempBits[65]; + + // Ignore the first 4 bits + for (byte i = 0; i < 4; i++) { + // Set RD(PH6) to LOW + PORTH &= ~ (1 << 6); + // Set RD(PH6) to High + PORTH |= (1 << 6); + } + + // Read the remaining 64bits into array + for (byte currBit = 0; currBit < 64; currBit++) { + // Set RD(PH6) to LOW + PORTH &= ~ (1 << 6); + // Set RD(PH6) to High + PORTH |= (1 << 6); + + // Read bit from A0(PF0) + tempBits[currBit] = (PINF & 0x1); + } + + // Set CS_ROM(PH3) to High + PORTH |= (1 << 3); + // Set A0(PF0) to High + PORTF |= (1 << 0); + // Set A0(PF0) to Output + DDRF |= (1 << 0); + + // OR 8 bits into one byte for a total of 8 bytes + for (byte j = 0; j < 64; j += 8) { + sdBuffer[((currAddr - startAddress) * 8) + (j / 8)] = tempBits[0 + j] << 7 | tempBits[1 + j] << 6 | tempBits[2 + j] << 5 | tempBits[3 + j] << 4 | tempBits[4 + j] << 3 | tempBits[5 + j] << 2 | tempBits[6 + j] << 1 | tempBits[7 + j]; + } + } +} + +// Check if the SRAM was written without any error +unsigned long verifyEEP_GBA(word eepSize) { + unsigned long wrError = 0; + + //open file on sd card + if (!myFile.open(filePath, O_READ)) { + print_Error(F("SD Error"), true); + } + + // Fill sd Buffer + for (word currAddress = 0; currAddress < eepSize * 16; currAddress += 64) { + // Disable interrupts for more uniform clock pulses + noInterrupts(); + readBlock_EEP(currAddress, eepSize); + interrupts(); + + // Compare + for (int currByte = 0; currByte < 512; currByte++) { + if (sdBuffer[currByte] != myFile.read()) { + wrError++; + } + } + } + myFile.close(); + return wrError; +} + +/****************************************** + GBA REPRO Functions (32MB Intel 4000L0YBQ0 and 16MB MX29GL128E) +*****************************************/ +// Reset to read mode +void resetIntel_GBA(unsigned long partitionSize) { + for (unsigned long currPartition = 0; currPartition < cartSize; currPartition += partitionSize) { + writeWord_GBA(currPartition, 0xFFFF); + } +} + +void resetMX29GL128E_GBA() { + writeWord_GAB(0, 0xF0); +} + +boolean sectorCheckMX29GL128E_GBA() { + boolean sectorProtect = 0; + writeWord_GAB(0xAAA, 0xAA); + writeWord_GAB(0x555, 0x55); + writeWord_GAB(0xAAA, 0x90); + for (unsigned long currSector = 0x0; currSector < 0xFFFFFF; currSector += 0x20000) { + if (readWord_GAB(currSector + 0x04) != 0x0) + sectorProtect = 1; + } + resetMX29GL128E_GBA(); + return sectorProtect; +} + +void idFlashrom_GBA() { + // Send Intel ID command to flashrom + writeWord_GBA(0, 0x90); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Read flashrom ID + sprintf(flashid, "%02X%02X", ((readWord_GBA(0x2) >> 8) & 0xFF), (readWord_GBA(0x4) & 0xFF)); + + // Intel Strataflash + if (strcmp(flashid, "8802") == 0 || (strcmp(flashid, "8816") == 0)) { + cartSize = 0x2000000; + } + else { + // Send swapped MX29GL128E/MSP55LV128 ID command to flashrom + writeWord_GAB(0xAAA, 0xAA); + writeWord_GAB(0x555, 0x55); + writeWord_GAB(0xAAA, 0x90); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Read flashrom ID + sprintf(flashid, "%02X%02X", ((readWord_GAB(0x2) >> 8) & 0xFF), (readWord_GAB(0x2) & 0xFF)); + + // MX29GL128E or MSP55LV128 + if (strcmp(flashid, "227E") == 0) { + // MX is 0xC2 and MSP is 0x4 or 0x1 + romType = (readWord_GAB(0x0) & 0xFF); + cartSize = 0x1000000; + resetMX29GL128E_GBA(); + } + else { + println_Msg(flashid); + print_Error(F("Unknown Flashid"), true); + } + } +} + +boolean blankcheckFlashrom_GBA() { + for (unsigned long currSector = 0; currSector < fileSize; currSector += 0x20000) { + // Blink led + PORTB ^= (1 << 4); + + for (unsigned long currByte = 0; currByte < 0x20000; currByte += 2) { + if (readWord_GBA(currSector + currByte) != 0xFFFF) { + return 0; + } + } + } + return 1; +} + +void eraseIntel4000_GBA() { + // If the game is smaller than 16Mbit only erase the needed blocks + unsigned long lastBlock = 0xFFFFFF; + if (fileSize < 0xFFFFFF) + lastBlock = fileSize; + + // Erase 4 blocks with 16kwords each + for (unsigned long currBlock = 0x0; currBlock < 0x1FFFF; currBlock += 0x8000) { + // Unlock Block + writeWord_GBA(currBlock, 0x60); + writeWord_GBA(currBlock, 0xD0); + + // Erase Command + writeWord_GBA(currBlock, 0x20); + writeWord_GBA(currBlock, 0xD0); + + // Read the status register + word statusReg = readWord_GBA(currBlock); + while ((statusReg | 0xFF7F) != 0xFFFF) { + statusReg = readWord_GBA(currBlock); + } + } + + // Erase 126 blocks with 64kwords each + for (unsigned long currBlock = 0x20000; currBlock < lastBlock; currBlock += 0x1FFFF) { + // Unlock Block + writeWord_GBA(currBlock, 0x60); + writeWord_GBA(currBlock, 0xD0); + + // Erase Command + writeWord_GBA(currBlock, 0x20); + writeWord_GBA(currBlock, 0xD0); + + // Read the status register + word statusReg = readWord_GBA(currBlock); + while ((statusReg | 0xFF7F) != 0xFFFF) { + statusReg = readWord_GBA(currBlock); + } + // Blink led + PORTB ^= (1 << 4); + } + + // Erase the second chip + if (fileSize > 0xFFFFFF) { + // 126 blocks with 64kwords each + for (unsigned long currBlock = 0x1000000; currBlock < 0x1FDFFFF; currBlock += 0x1FFFF) { + // Unlock Block + writeWord_GBA(currBlock, 0x60); + writeWord_GBA(currBlock, 0xD0); + + // Erase Command + writeWord_GBA(currBlock, 0x20); + writeWord_GBA(currBlock, 0xD0); + + // Read the status register + word statusReg = readWord_GBA(currBlock); + while ((statusReg | 0xFF7F) != 0xFFFF) { + statusReg = readWord_GBA(currBlock); + } + // Blink led + PORTB ^= (1 << 4); + } + + // 4 blocks with 16kword each + for (unsigned long currBlock = 0x1FE0000; currBlock < 0x1FFFFFF; currBlock += 0x8000) { + // Unlock Block + writeWord_GBA(currBlock, 0x60); + writeWord_GBA(currBlock, 0xD0); + + // Erase Command + writeWord_GBA(currBlock, 0x20); + writeWord_GBA(currBlock, 0xD0); + + // Read the status register + word statusReg = readWord_GBA(currBlock); + while ((statusReg | 0xFF7F) != 0xFFFF) { + statusReg = readWord_GBA(currBlock); + } + // Blink led + PORTB ^= (1 << 4); + } + } +} + +void eraseIntel4400_GBA() { + // If the game is smaller than 32Mbit only erase the needed blocks + unsigned long lastBlock = 0x1FFFFFF; + if (fileSize < 0x1FFFFFF) + lastBlock = fileSize; + + // Erase 4 blocks with 16kwords each + for (unsigned long currBlock = 0x0; currBlock < 0x1FFFF; currBlock += 0x8000) { + // Unlock Block + writeWord_GBA(currBlock, 0x60); + writeWord_GBA(currBlock, 0xD0); + + // Erase Command + writeWord_GBA(currBlock, 0x20); + writeWord_GBA(currBlock, 0xD0); + + // Read the status register + word statusReg = readWord_GBA(currBlock); + while ((statusReg | 0xFF7F) != 0xFFFF) { + statusReg = readWord_GBA(currBlock); + } + } + + // Erase 255 blocks with 64kwords each + for (unsigned long currBlock = 0x20000; currBlock < lastBlock; currBlock += 0x1FFFF) { + // Unlock Block + writeWord_GBA(currBlock, 0x60); + writeWord_GBA(currBlock, 0xD0); + + // Erase Command + writeWord_GBA(currBlock, 0x20); + writeWord_GBA(currBlock, 0xD0); + + // Read the status register + word statusReg = readWord_GBA(currBlock); + while ((statusReg | 0xFF7F) != 0xFFFF) { + statusReg = readWord_GBA(currBlock); + } + // Blink led + PORTB ^= (1 << 4); + } + + /* No need to erase the second chip as max rom size is 32MB + if (fileSize > 0x2000000) { + // 255 blocks with 64kwords each + for (unsigned long currBlock = 0x2000000; currBlock < 0x3FDFFFF; currBlock += 0x1FFFF) { + // Unlock Block + writeWord_GBA(currBlock, 0x60); + writeWord_GBA(currBlock, 0xD0); + + // Erase Command + writeWord_GBA(currBlock, 0x20); + writeWord_GBA(currBlock, 0xD0); + + // Read the status register + word statusReg = readWord_GBA(currBlock); + while ((statusReg | 0xFF7F) != 0xFFFF) { + statusReg = readWord_GBA(currBlock); + } + // Blink led + PORTB ^= (1 << 4); + } + + // 4 blocks with 16kword each + for (unsigned long currBlock = 0x3FE0000; currBlock < 0x3FFFFFF; currBlock += 0x8000) { + // Unlock Block + writeWord_GBA(currBlock, 0x60); + writeWord_GBA(currBlock, 0xD0); + + // Erase Command + writeWord_GBA(currBlock, 0x20); + writeWord_GBA(currBlock, 0xD0); + + // Read the status register + word statusReg = readWord_GBA(currBlock); + while ((statusReg | 0xFF7F) != 0xFFFF) { + statusReg = readWord_GBA(currBlock); + } + // Blink led + PORTB ^= (1 << 4); + } + }*/ +} + +void sectorEraseMSP55LV128_GBA() { + unsigned long lastSector = 0xFFFFFF; + + // Erase 256 sectors with 64kbytes each + unsigned long currSector; + for (currSector = 0x0; currSector < lastSector; currSector += 0x10000) { + writeWord_GAB(0xAAA, 0xAA); + writeWord_GAB(0x555, 0x55); + writeWord_GAB(0xAAA, 0x80); + writeWord_GAB(0xAAA, 0xAA); + writeWord_GAB(0x555, 0x55); + writeWord_GAB(currSector, 0x30); + + // Read the status register + word statusReg = readWord_GAB(currSector); + while ((statusReg | 0xFF7F) != 0xFFFF) { + statusReg = readWord_GAB(currSector); + } + // Blink LED + PORTB ^= (1 << 4); + } +} + +void sectorEraseMX29GL128E_GBA() { + unsigned long lastSector = 0xFFFFFF; + + // Erase 128 sectors with 128kbytes each + unsigned long currSector; + for (currSector = 0x0; currSector < lastSector; currSector += 0x20000) { + writeWord_GAB(0xAAA, 0xAA); + writeWord_GAB(0x555, 0x55); + writeWord_GAB(0xAAA, 0x80); + writeWord_GAB(0xAAA, 0xAA); + writeWord_GAB(0x555, 0x55); + writeWord_GAB(currSector, 0x30); + + // Read the status register + word statusReg = readWord_GAB(currSector); + while ((statusReg | 0xFF7F) != 0xFFFF) { + statusReg = readWord_GAB(currSector); + } + // Blink LED + PORTB ^= (1 << 4); + } +} + +void writeIntel4000_GBA() { + for (unsigned long currBlock = 0; currBlock < fileSize; currBlock += 0x20000) { + // Blink led + PORTB ^= (1 << 4); + + // Write to flashrom + for (unsigned long currSdBuffer = 0; currSdBuffer < 0x20000; currSdBuffer += 512) { + // Fill SD buffer + myFile.read(sdBuffer, 512); + + // Write 32 words at a time + for (int currWriteBuffer = 0; currWriteBuffer < 512; currWriteBuffer += 64) { + // Unlock Block + writeWord_GBA(currBlock + currSdBuffer + currWriteBuffer, 0x60); + writeWord_GBA(currBlock + currSdBuffer + currWriteBuffer, 0xD0); + + // Buffered program command + writeWord_GBA(currBlock + currSdBuffer + currWriteBuffer, 0xE8); + + // Check Status register + word statusReg = readWord_GBA(currBlock + currSdBuffer + currWriteBuffer); + while ((statusReg | 0xFF7F) != 0xFFFF) { + statusReg = readWord_GBA(currBlock + currSdBuffer + currWriteBuffer); + } + + // Write word count (minus 1) + writeWord_GBA(currBlock + currSdBuffer + currWriteBuffer, 0x1F); + + // Write buffer + for (byte currByte = 0; currByte < 64; currByte += 2) { + // Join two bytes into one word + word currWord = ( ( sdBuffer[currWriteBuffer + currByte + 1] & 0xFF ) << 8 ) | ( sdBuffer[currWriteBuffer + currByte] & 0xFF ); + writeWord_GBA(currBlock + currSdBuffer + currWriteBuffer + currByte, currWord); + } + + // Write Buffer to Flash + writeWord_GBA(currBlock + currSdBuffer + currWriteBuffer + 62, 0xD0); + + // Read the status register at last written address + statusReg = readWord_GBA(currBlock + currSdBuffer + currWriteBuffer + 62); + while ((statusReg | 0xFF7F) != 0xFFFF) { + statusReg = readWord_GBA(currBlock + currSdBuffer + currWriteBuffer + 62); + } + } + } + } +} + +void writeMSP55LV128_GBA() { + for (unsigned long currSector = 0; currSector < fileSize; currSector += 0x10000) { + // Blink led + PORTB ^= (1 << 4); + + // Write to flashrom + for (unsigned long currSdBuffer = 0; currSdBuffer < 0x10000; currSdBuffer += 512) { + // Fill SD buffer + myFile.read(sdBuffer, 512); + + // Write 16 words at a time + for (int currWriteBuffer = 0; currWriteBuffer < 512; currWriteBuffer += 32) { + // Write Buffer command + writeWord_GAB(0xAAA, 0xAA); + writeWord_GAB(0x555, 0x55); + writeWord_GAB(currSector, 0x25); + + // Write word count (minus 1) + writeWord_GAB(currSector, 0xF); + + // Write buffer + word currWord; + for (byte currByte = 0; currByte < 32; currByte += 2) { + // Join two bytes into one word + currWord = ( ( sdBuffer[currWriteBuffer + currByte + 1] & 0xFF ) << 8 ) | ( sdBuffer[currWriteBuffer + currByte] & 0xFF ); + writeWord_GBA(currSector + currSdBuffer + currWriteBuffer + currByte, currWord); + } + + // Confirm write buffer + writeWord_GAB(currSector, 0x29); + + // Read the status register + word statusReg = readWord_GAB(currSector + currSdBuffer + currWriteBuffer + 30); + + while ((statusReg | 0xFF7F) != (currWord | 0xFF7F)) { + statusReg = readWord_GAB(currSector + currSdBuffer + currWriteBuffer + 30); + } + } + } + } +} + +void writeMX29GL128E_GBA() { + for (unsigned long currSector = 0; currSector < fileSize; currSector += 0x20000) { + // Blink led + PORTB ^= (1 << 4); + + // Write to flashrom + for (unsigned long currSdBuffer = 0; currSdBuffer < 0x20000; currSdBuffer += 512) { + // Fill SD buffer + myFile.read(sdBuffer, 512); + + // Write 32 words at a time + for (int currWriteBuffer = 0; currWriteBuffer < 512; currWriteBuffer += 64) { + // Write Buffer command + writeWord_GAB(0xAAA, 0xAA); + writeWord_GAB(0x555, 0x55); + writeWord_GAB(currSector, 0x25); + + // Write word count (minus 1) + writeWord_GAB(currSector, 0x1F); + + // Write buffer + word currWord; + for (byte currByte = 0; currByte < 64; currByte += 2) { + // Join two bytes into one word + currWord = ( ( sdBuffer[currWriteBuffer + currByte + 1] & 0xFF ) << 8 ) | ( sdBuffer[currWriteBuffer + currByte] & 0xFF ); + writeWord_GBA(currSector + currSdBuffer + currWriteBuffer + currByte, currWord); + } + + // Confirm write buffer + writeWord_GAB(currSector, 0x29); + + // Read the status register + word statusReg = readWord_GAB(currSector + currSdBuffer + currWriteBuffer + 62); + + while ((statusReg | 0xFF7F) != (currWord | 0xFF7F)) { + statusReg = readWord_GAB(currSector + currSdBuffer + currWriteBuffer + 62); + } + } + } + } +} + +boolean verifyFlashrom_GBA() { + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + writeErrors = 0; + + for (unsigned long currSector = 0; currSector < fileSize; currSector += 131072) { + // Blink led + PORTB ^= (1 << 4); + 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 + 1] & 0xFF ) << 8 ) | ( sdBuffer[currByte] & 0xFF ); + + // Compare both + if (readWord_GBA(currSector + currSdBuffer + currByte) != currWord) { + writeErrors++; + myFile.close(); + return 0; + } + } + } + } + // Close the file: + myFile.close(); + if (writeErrors == 0) { + return 1; + } + else { + return 0; + } + } + else { + print_Error(F("Can't open file"), true); + return 9999; + } +} + +void flashRepro_GBA() { + // Check flashrom ID's + idFlashrom_GBA(); + + if ((strcmp(flashid, "8802") == 0) || (strcmp(flashid, "8816") == 0) || (strcmp(flashid, "227E") == 0)) { + print_Msg(F("ID: ")); + print_Msg(flashid); + print_Msg(F(" Size: ")); + print_Msg(cartSize / 0x100000); + println_Msg(F("MB")); + // MX29GL128E or MSP55LV128(N) + if (strcmp(flashid, "227E") == 0) { + // MX is 0xC2 and MSP55LV128 is 0x4 and MSP55LV128N 0x1 + if (romType == 0xC2) { + println_Msg(F("Macronix MX29GL128E")); + } + else if ((romType == 0x1) || (romType == 0x4)) { + println_Msg(F("Fujitsu MSP55LV128N")); + } + else { + println_Msg(romType); + print_Error(F("Unknown manufacturer"), true); + } + } + // Intel 4000L0YBQ0 + else if (strcmp(flashid, "8802") == 0) { + println_Msg(F("Intel 4000L0YBQ0")); + } + // Intel 4400L0ZDQ0 + else if (strcmp(flashid, "8816") == 0) { + println_Msg(F("Intel 4400L0ZDQ0")); + } + println_Msg(""); + println_Msg(F("This will erase your")); + println_Msg(F("Repro Cartridge.")); + println_Msg(F("Please use 3.3V!")); + println_Msg(""); + println_Msg(F("Press Button")); + display_Update(); + wait(); + + // Launch file browser + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser("Select gba file"); + display_Clear(); + 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(); + print_Msg(F("File size: ")); + print_Msg(fileSize / 0x100000); + println_Msg(F("MB")); + display_Update(); + + // Erase needed sectors + if (strcmp(flashid, "8802") == 0) { + println_Msg(F("Erasing...")); + display_Update(); + eraseIntel4000_GBA(); + resetIntel_GBA(0x200000); + } + else if (strcmp(flashid, "8816") == 0) { + println_Msg(F("Erasing...")); + display_Update(); + eraseIntel4400_GBA(); + resetIntel_GBA(0x200000); + } + else if (strcmp(flashid, "227E") == 0) { + //if (sectorCheckMX29GL128E_GBA()) { + //print_Error(F("Sector Protected"), true); + //} + //else { + println_Msg(F("Erasing...")); + display_Update(); + if (romType == 0xC2) { + //MX29GL128E + sectorEraseMX29GL128E_GBA(); + } + else if ((romType == 0x1) || (romType == 0x4)) { + //MSP55LV128(N) + sectorEraseMSP55LV128_GBA(); + } + //} + } + /* Skip blankcheck to save time + print_Msg(F("Blankcheck...")); + display_Update(); + if (blankcheckFlashrom_GBA()) { + println_Msg(F("OK")); + */ + + //Write flashrom + print_Msg(F("Writing ")); + println_Msg(filePath); + display_Update(); + if ((strcmp(flashid, "8802") == 0) || (strcmp(flashid, "8816") == 0)) { + writeIntel4000_GBA(); + } + else if (strcmp(flashid, "227E") == 0) { + if (romType == 0xC2) { + //MX29GL128E + writeMX29GL128E_GBA(); + } + else if ((romType == 0x1) || (romType == 0x4)) { + //MSP55LV128(N) + writeMSP55LV128_GBA(); + } + } + + // Close the file: + myFile.close(); + + // Verify + print_Msg(F("Verifying...")); + display_Update(); + if (strcmp(flashid, "8802") == 0) { + // Don't know the correct size so just take some guesses + resetIntel_GBA(0x8000); + delay(1000); + resetIntel_GBA(0x100000); + delay(1000); + resetIntel_GBA(0x200000); + delay(1000); + } + else if (strcmp(flashid, "8816") == 0) { + resetIntel_GBA(0x200000); + delay(1000); + } + + else if (strcmp(flashid, "227E") == 0) { + resetMX29GL128E_GBA(); + delay(1000); + } + if (verifyFlashrom_GBA() == 1) { + println_Msg(F("OK")); + display_Update(); + } + else { + print_Error(F("ERROR"), true); + } + /* Skipped blankcheck + } + else { + print_Error(F("failed"), true); + } + */ + } + else { + print_Error(F("Can't open file"), true); + } + } + else { + print_Msg(F("ID: ")); + println_Msg(flashid); + print_Error(F("Unknown Flash ID"), true); + } +} + +//****************************************** +// End of File //****************************************** diff --git a/Cart_Reader/MD.ino b/Cart_Reader/MD.ino index e0179aa..ea63991 100644 --- a/Cart_Reader/MD.ino +++ b/Cart_Reader/MD.ino @@ -1,499 +1,499 @@ -//****************************************** -// SEGA MEGA DRIVE MODULE -//****************************************** - -/****************************************** - Variables - *****************************************/ -unsigned long sramEnd; - -/****************************************** - Menu - *****************************************/ -// MD menu items -static const char MDMenuItem1[] PROGMEM = "Read Rom"; -static const char MDMenuItem2[] PROGMEM = "Read Save"; -static const char MDMenuItem3[] PROGMEM = "Write Save"; -static const char MDMenuItem4[] PROGMEM = "Write Flashcart"; -static const char MDMenuItem5[] PROGMEM = "Reset"; -static const char* const menuOptionsMD[] PROGMEM = {MDMenuItem1, MDMenuItem2, MDMenuItem3, MDMenuItem4, MDMenuItem5}; - -// Sega start menu -void segaMenu() { - display_Clear(); - display_Update(); - setup_MD(); - mode = mode_MD; -} - -void mdMenu() { - // create menu with title and 5 options to choose from - unsigned char mainMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsMD, 5); - mainMenu = question_box("MEGA DRIVE Reader", menuOptions, 3, 0); - - // wait for user choice to come back from the question box menu - switch (mainMenu) - { - case 0: - display_Clear(); - // Change working dir to root - sd.chdir("/"); - readROM_MD(); - //compare_checksum_MD(); - break; - - case 1: - display_Clear(); - // Does cartridge have SRAM - if ((saveType == 1) || (saveType == 2)) { - // Change working dir to root - sd.chdir("/"); - println_Msg(F("Reading Sram...")); - display_Update(); - enableSram_MD(1); - readSram_MD(); - enableSram_MD(0); - } - else { - print_Error(F("Cart has no Sram"), false); - } - break; - - case 2: - display_Clear(); - // Does cartridge have SRAM - if ((saveType == 1) || (saveType == 2)) { - // Change working dir to root - sd.chdir("/"); - // Launch file browser - fileBrowser("Select srm file"); - display_Clear(); - enableSram_MD(1); - writeSram_MD(); - writeErrors = verifySram_MD(); - enableSram_MD(0); - if (writeErrors == 0) { - println_Msg(F("Sram verified OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - } - else { - print_Error(F("Cart has no Sram"), false); - } - break; - - /*case 3: - // Change working dir to root - sd.chdir("/"); - writeFlash_MD(); - // Reset - wait(); - asm volatile (" jmp 0"); - break; - - case 4: - asm volatile (" jmp 0"); - break;*/ - } - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); -} - -/****************************************** - Setup - *****************************************/ -void setup_MD() { - // Set Address Pins to Output - //A0-A7 - DDRF = 0xFF; - //A8-A15 - DDRK = 0xFF; - //A16-A23 - DDRL = 0xFF; - - // Set Control Pins to Output RST(PH0) CS(PH3) WRH(PH4) WRL(PH5) OE(PH6) - DDRH |= (1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); - - // Set TIME(PJ0) to Output - DDRJ |= (1 << 0); - - // Set Data Pins (D0-D15) to Input - DDRC = 0x00; - DDRA = 0x00; - - // Setting RST(PH0) CS(PH3) WRH(PH4) WRL(PH5) OE(PH6) HIGH - PORTH |= (1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); - - // Setting TIME(PJ0) HIGH - PORTJ |= (1 << 0); - - delay(200); - - // Print all the info - getCartInfo_MD(); -} - -/****************************************** - I/O Functions - *****************************************/ - -/****************************************** - Low level functions -*****************************************/ -void writeWord_MD(unsigned long myAddress, word myData) { - PORTF = myAddress & 0xFF; - PORTK = (myAddress >> 8) & 0xFF; - PORTL = (myAddress >> 16) & 0xFF; - PORTC = myData; - PORTA = (myData >> 8) & 0xFF; - - // Arduino running at 16Mhz -> one nop = 62.5ns - // Wait till output is stable - __asm__("nop\n\t""nop\n\t"); - - // Switch WR(PH5) to LOW - PORTH &= ~(1 << 5); - // Setting CS(PH3) LOW - PORTH &= ~(1 << 3); - - // Leave WR low for at least 200ns - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Setting CS(PH3) HIGH - PORTH |= (1 << 3); - // Switch WR(PH5) to HIGH - PORTH |= (1 << 5); - - // Leave WR high for at least 50ns - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); -} - -word readWord_MD(unsigned long myAddress) { - PORTF = myAddress & 0xFF; - PORTK = (myAddress >> 8) & 0xFF; - PORTL = (myAddress >> 16) & 0xFF; - - // Arduino running at 16Mhz -> one nop = 62.5ns - __asm__("nop\n\t"); - - // Setting CS(PH3) LOW - PORTH &= ~(1 << 3); - // Setting OE(PH6) LOW - PORTH &= ~(1 << 6); - - // Long delay here or there will be read errors - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Read - word tempWord = ( ( PINA & 0xFF ) << 8 ) | ( PINC & 0xFF ); - - // Setting CS(PH3) HIGH - PORTH |= (1 << 3); - // Setting OE(PH6) HIGH - PORTH |= (1 << 6); - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - return tempWord; -} - -// Switch data pins to write -void dataOut_MD() { - DDRC = 0xFF; - DDRA = 0xFF; -} - -// Switch data pins to read -void dataIn_MD() { - DDRC = 0x00; - DDRA = 0x00; -} - -/****************************************** - MEGA DRIVE functions -*****************************************/ -void getCartInfo_MD() { - // Set control - dataIn_MD(); - - cartSize = ((long(readWord_MD(0xD2)) << 16) | readWord_MD(0xD3)) + 1; - - // Check if cart has sram - if ((readWord_MD(0xD8) == 0x5241) && (readWord_MD(0xD9) == 0xF820)) { - // Get sram start and end - sramBase = ((long(readWord_MD(0xDA)) << 16) | readWord_MD(0xDB)); - sramEnd = ((long(readWord_MD(0xDC)) << 16) | readWord_MD(0xDD)); - - // Check alignment of sram - if (sramBase == 0x200001) { - // low byte - saveType = 1; - sramSize = (sramEnd - sramBase + 2) / 2; - // Right shift sram base address so [A21] is set to high 0x200000 = 0b001[0]00000000000000000000 - sramBase = sramBase >> 1; - } - else if (sramBase == 0x200000) { - // high byte - saveType = 2; - sramSize = (sramEnd - sramBase + 1) / 2; - // Right shift sram base address so [A21] is set to high 0x200000 = 0b001[0]00000000000000000000 - sramBase = sramBase / 2; - } - else - print_Error(F("Unknown Sram Base"), true); - } - else { - // Either no save or eeprom save - saveType = 0; - sramSize = 0; - } - - // Get name - for (byte c = 0; c < 48; c += 2) { - // split word - word myWord = readWord_MD((0x150 + c) / 2); - byte loByte = myWord & 0xFF; - byte hiByte = myWord >> 8; - - // write to buffer - sdBuffer[c] = hiByte; - sdBuffer[c + 1] = loByte; - } - byte myLength = 0; - for (unsigned int i = 0; i < 48; i++) { - if (((char(sdBuffer[i]) >= 48 && char(sdBuffer[i]) <= 57) || (char(sdBuffer[i]) >= 65 && char(sdBuffer[i]) <= 122)) && myLength < 15) { - romName[myLength] = char(sdBuffer[i]); - myLength++; - } - } - - display_Clear(); - println_Msg(F("Cart Info")); - println_Msg(F(" ")); - print_Msg(F("Name: ")); - println_Msg(romName); - print_Msg(F("Size: ")); - print_Msg(cartSize * 8 / 1024 / 1024 ); - println_Msg(F(" MBit")); - print_Msg(F("Sram: ")); - if (sramSize > 0) { - print_Msg(sramSize * 8 / 1024); - println_Msg(F(" KBit")); - } - else - println_Msg(F("None")); - println_Msg(F(" ")); - - // Wait for user input - if (enable_OLED) { - println_Msg(F("Press Button...")); - display_Update(); - wait(); - } -} - -// Read rom and save to the SD card -void readROM_MD() { - // Set control - dataIn_MD(); - - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".MD"); - - // create a new folder - EEPROM_readAnything(10, foldern); - sprintf(folder, "MD/ROM/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - display_Clear(); - print_Msg(F("Saving to ")); - print_Msg(folder); - println_Msg(F("/...")); - display_Update(); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - // Open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("SD Error"), true); - } - - word d = 0; - for (unsigned long currBuffer = 0; currBuffer < cartSize / 2; currBuffer += 256) { - // Blink led - if (currBuffer % 16384 == 0) - PORTB ^= (1 << 4); - - for (int currWord = 0; currWord < 256; currWord++) { - word myWord = readWord_MD(currBuffer + currWord); - // Split word into two bytes - // Left - sdBuffer[d] = (( myWord >> 8 ) & 0xFF); - // Right - sdBuffer[d + 1] = (myWord & 0xFF); - d += 2; - } - myFile.write(sdBuffer, 512); - d = 0; - } - // Close the file: - myFile.close(); -} - -/****************************************** - SRAM functions -*****************************************/ -// Sonic 3 sram enable -void enableSram_MD(boolean enableSram) { - dataOut_MD(); - - // Set D0 to either 1(enable SRAM) or 0(enable ROM) - PORTC = enableSram; - - // Strobe TIME(PJ0) LOW to latch the data - PORTJ &= ~(1 << 0); - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - // Set TIME(PJ0) HIGH - PORTJ |= (1 << 0); - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - dataIn_MD(); -} - -// Write sram to cartridge -void writeSram_MD() { - dataOut_MD(); - - // Create filepath - sprintf(filePath, "%s/%s", filePath, fileName); - println_Msg(F("Writing...")); - println_Msg(filePath); - display_Update(); - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - // Write to the lower byte - if (saveType == 1) { - for (unsigned long currByte = sramBase; currByte < sramBase + sramSize; currByte++) { - writeWord_MD(currByte, (myFile.read() & 0xFF)); - } - } - // Write to the upper byte - else if (saveType == 2) { - for (unsigned long currByte = sramBase; currByte < sramBase + sramSize; currByte++) { - writeWord_MD(currByte, ((myFile.read() << 8 ) & 0xFF)); - } - } - else - print_Error(F("Unknown save type"), false); - - // Close the file: - myFile.close(); - println_Msg(F("Done")); - display_Update(); - } - else { - print_Error(F("SD Error"), true); - } - dataIn_MD(); -} - -// Read sram and save to the SD card -void readSram_MD() { - dataIn_MD(); - - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".srm"); - - // create a new folder for the save file - EEPROM_readAnything(10, foldern); - sprintf(folder, "MD/SAVE/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - // Open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("SD Error"), true); - } - - for (unsigned long currBuffer = sramBase; currBuffer < sramBase + sramSize; currBuffer += 512) { - for (int currWord = 0; currWord < 512; currWord++) { - word myWord = readWord_MD(currBuffer + currWord); - - if (saveType == 2) { - // Only use the upper byte - sdBuffer[currWord] = (( myWord >> 8 ) & 0xFF); - } - else if (saveType == 1) { - // Only use the lower byte - sdBuffer[currWord] = (myWord & 0xFF); - } - } - myFile.write(sdBuffer, 512); - } - // Close the file: - myFile.close(); - print_Msg(F("Saved to ")); - print_Msg(folder); - println_Msg(F("/")); - display_Update(); -} - -unsigned long verifySram_MD() { - dataIn_MD(); - writeErrors = 0; - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - for (unsigned long currBuffer = sramBase; currBuffer < sramBase + sramSize; currBuffer += 512) { - for (int currWord = 0; currWord < 512; currWord++) { - word myWord = readWord_MD(currBuffer + currWord); - - if (saveType == 2) { - // Only use the upper byte - sdBuffer[currWord] = (( myWord >> 8 ) & 0xFF); - } - else if (saveType == 1) { - // Only use the lower byte - sdBuffer[currWord] = (myWord & 0xFF); - } - } - // Check sdBuffer content against file on sd card - for (int i = 0; i < 512; i++) { - if (myFile.read() != sdBuffer[i]) { - writeErrors++; - } - } - } - - // Close the file: - myFile.close(); - } - else { - print_Error(F("SD Error"), true); - } - // Return 0 if verified ok, or number of errors - return writeErrors; -} - -//****************************************** -// End of File +//****************************************** +// SEGA MEGA DRIVE MODULE +//****************************************** + +/****************************************** + Variables + *****************************************/ +unsigned long sramEnd; + +/****************************************** + Menu + *****************************************/ +// MD menu items +static const char MDMenuItem1[] PROGMEM = "Read Rom"; +static const char MDMenuItem2[] PROGMEM = "Read Save"; +static const char MDMenuItem3[] PROGMEM = "Write Save"; +static const char MDMenuItem4[] PROGMEM = "Write Flashcart"; +static const char MDMenuItem5[] PROGMEM = "Reset"; +static const char* const menuOptionsMD[] PROGMEM = {MDMenuItem1, MDMenuItem2, MDMenuItem3, MDMenuItem4, MDMenuItem5}; + +// Sega start menu +void segaMenu() { + display_Clear(); + display_Update(); + setup_MD(); + mode = mode_MD; +} + +void mdMenu() { + // create menu with title and 5 options to choose from + unsigned char mainMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsMD, 5); + mainMenu = question_box("MEGA DRIVE Reader", menuOptions, 3, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + case 0: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readROM_MD(); + //compare_checksum_MD(); + break; + + case 1: + display_Clear(); + // Does cartridge have SRAM + if ((saveType == 1) || (saveType == 2)) { + // Change working dir to root + sd.chdir("/"); + println_Msg(F("Reading Sram...")); + display_Update(); + enableSram_MD(1); + readSram_MD(); + enableSram_MD(0); + } + else { + print_Error(F("Cart has no Sram"), false); + } + break; + + case 2: + display_Clear(); + // Does cartridge have SRAM + if ((saveType == 1) || (saveType == 2)) { + // Change working dir to root + sd.chdir("/"); + // Launch file browser + fileBrowser("Select srm file"); + display_Clear(); + enableSram_MD(1); + writeSram_MD(); + writeErrors = verifySram_MD(); + enableSram_MD(0); + if (writeErrors == 0) { + println_Msg(F("Sram verified OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + } + else { + print_Error(F("Cart has no Sram"), false); + } + break; + + /*case 3: + // Change working dir to root + sd.chdir("/"); + writeFlash_MD(); + // Reset + wait(); + asm volatile (" jmp 0"); + break; + + case 4: + asm volatile (" jmp 0"); + break;*/ + } + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +/****************************************** + Setup + *****************************************/ +void setup_MD() { + // Set Address Pins to Output + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output RST(PH0) CS(PH3) WRH(PH4) WRL(PH5) OE(PH6) + DDRH |= (1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Set TIME(PJ0) to Output + DDRJ |= (1 << 0); + + // Set Data Pins (D0-D15) to Input + DDRC = 0x00; + DDRA = 0x00; + + // Setting RST(PH0) CS(PH3) WRH(PH4) WRL(PH5) OE(PH6) HIGH + PORTH |= (1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Setting TIME(PJ0) HIGH + PORTJ |= (1 << 0); + + delay(200); + + // Print all the info + getCartInfo_MD(); +} + +/****************************************** + I/O Functions + *****************************************/ + +/****************************************** + Low level functions +*****************************************/ +void writeWord_MD(unsigned long myAddress, word myData) { + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + PORTL = (myAddress >> 16) & 0xFF; + PORTC = myData; + PORTA = (myData >> 8) & 0xFF; + + // Arduino running at 16Mhz -> one nop = 62.5ns + // Wait till output is stable + __asm__("nop\n\t""nop\n\t"); + + // Switch WR(PH5) to LOW + PORTH &= ~(1 << 5); + // Setting CS(PH3) LOW + PORTH &= ~(1 << 3); + + // Leave WR low for at least 200ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Setting CS(PH3) HIGH + PORTH |= (1 << 3); + // Switch WR(PH5) to HIGH + PORTH |= (1 << 5); + + // Leave WR high for at least 50ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); +} + +word readWord_MD(unsigned long myAddress) { + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + PORTL = (myAddress >> 16) & 0xFF; + + // Arduino running at 16Mhz -> one nop = 62.5ns + __asm__("nop\n\t"); + + // Setting CS(PH3) LOW + PORTH &= ~(1 << 3); + // Setting OE(PH6) LOW + PORTH &= ~(1 << 6); + + // Long delay here or there will be read errors + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Read + word tempWord = ( ( PINA & 0xFF ) << 8 ) | ( PINC & 0xFF ); + + // Setting CS(PH3) HIGH + PORTH |= (1 << 3); + // Setting OE(PH6) HIGH + PORTH |= (1 << 6); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + return tempWord; +} + +// Switch data pins to write +void dataOut_MD() { + DDRC = 0xFF; + DDRA = 0xFF; +} + +// Switch data pins to read +void dataIn_MD() { + DDRC = 0x00; + DDRA = 0x00; +} + +/****************************************** + MEGA DRIVE functions +*****************************************/ +void getCartInfo_MD() { + // Set control + dataIn_MD(); + + cartSize = ((long(readWord_MD(0xD2)) << 16) | readWord_MD(0xD3)) + 1; + + // Check if cart has sram + if ((readWord_MD(0xD8) == 0x5241) && (readWord_MD(0xD9) == 0xF820)) { + // Get sram start and end + sramBase = ((long(readWord_MD(0xDA)) << 16) | readWord_MD(0xDB)); + sramEnd = ((long(readWord_MD(0xDC)) << 16) | readWord_MD(0xDD)); + + // Check alignment of sram + if (sramBase == 0x200001) { + // low byte + saveType = 1; + sramSize = (sramEnd - sramBase + 2) / 2; + // Right shift sram base address so [A21] is set to high 0x200000 = 0b001[0]00000000000000000000 + sramBase = sramBase >> 1; + } + else if (sramBase == 0x200000) { + // high byte + saveType = 2; + sramSize = (sramEnd - sramBase + 1) / 2; + // Right shift sram base address so [A21] is set to high 0x200000 = 0b001[0]00000000000000000000 + sramBase = sramBase / 2; + } + else + print_Error(F("Unknown Sram Base"), true); + } + else { + // Either no save or eeprom save + saveType = 0; + sramSize = 0; + } + + // Get name + for (byte c = 0; c < 48; c += 2) { + // split word + word myWord = readWord_MD((0x150 + c) / 2); + byte loByte = myWord & 0xFF; + byte hiByte = myWord >> 8; + + // write to buffer + sdBuffer[c] = hiByte; + sdBuffer[c + 1] = loByte; + } + byte myLength = 0; + for (unsigned int i = 0; i < 48; i++) { + if (((char(sdBuffer[i]) >= 48 && char(sdBuffer[i]) <= 57) || (char(sdBuffer[i]) >= 65 && char(sdBuffer[i]) <= 122)) && myLength < 15) { + romName[myLength] = char(sdBuffer[i]); + myLength++; + } + } + + display_Clear(); + println_Msg(F("Cart Info")); + println_Msg(F(" ")); + print_Msg(F("Name: ")); + println_Msg(romName); + print_Msg(F("Size: ")); + print_Msg(cartSize * 8 / 1024 / 1024 ); + println_Msg(F(" MBit")); + print_Msg(F("Sram: ")); + if (sramSize > 0) { + print_Msg(sramSize * 8 / 1024); + println_Msg(F(" KBit")); + } + else + println_Msg(F("None")); + println_Msg(F(" ")); + + // Wait for user input + if (enable_OLED) { + println_Msg(F("Press Button...")); + display_Update(); + wait(); + } +} + +// Read rom and save to the SD card +void readROM_MD() { + // Set control + dataIn_MD(); + + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".MD"); + + // create a new folder + EEPROM_readAnything(10, foldern); + sprintf(folder, "MD/ROM/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + // Open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + + word d = 0; + for (unsigned long currBuffer = 0; currBuffer < cartSize / 2; currBuffer += 256) { + // Blink led + if (currBuffer % 16384 == 0) + PORTB ^= (1 << 4); + + for (int currWord = 0; currWord < 256; currWord++) { + word myWord = readWord_MD(currBuffer + currWord); + // Split word into two bytes + // Left + sdBuffer[d] = (( myWord >> 8 ) & 0xFF); + // Right + sdBuffer[d + 1] = (myWord & 0xFF); + d += 2; + } + myFile.write(sdBuffer, 512); + d = 0; + } + // Close the file: + myFile.close(); +} + +/****************************************** + SRAM functions +*****************************************/ +// Sonic 3 sram enable +void enableSram_MD(boolean enableSram) { + dataOut_MD(); + + // Set D0 to either 1(enable SRAM) or 0(enable ROM) + PORTC = enableSram; + + // Strobe TIME(PJ0) LOW to latch the data + PORTJ &= ~(1 << 0); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + // Set TIME(PJ0) HIGH + PORTJ |= (1 << 0); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + dataIn_MD(); +} + +// Write sram to cartridge +void writeSram_MD() { + dataOut_MD(); + + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + println_Msg(F("Writing...")); + println_Msg(filePath); + display_Update(); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + // Write to the lower byte + if (saveType == 1) { + for (unsigned long currByte = sramBase; currByte < sramBase + sramSize; currByte++) { + writeWord_MD(currByte, (myFile.read() & 0xFF)); + } + } + // Write to the upper byte + else if (saveType == 2) { + for (unsigned long currByte = sramBase; currByte < sramBase + sramSize; currByte++) { + writeWord_MD(currByte, ((myFile.read() << 8 ) & 0xFF)); + } + } + else + print_Error(F("Unknown save type"), false); + + // Close the file: + myFile.close(); + println_Msg(F("Done")); + display_Update(); + } + else { + print_Error(F("SD Error"), true); + } + dataIn_MD(); +} + +// Read sram and save to the SD card +void readSram_MD() { + dataIn_MD(); + + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".srm"); + + // create a new folder for the save file + EEPROM_readAnything(10, foldern); + sprintf(folder, "MD/SAVE/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + // Open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + + for (unsigned long currBuffer = sramBase; currBuffer < sramBase + sramSize; currBuffer += 512) { + for (int currWord = 0; currWord < 512; currWord++) { + word myWord = readWord_MD(currBuffer + currWord); + + if (saveType == 2) { + // Only use the upper byte + sdBuffer[currWord] = (( myWord >> 8 ) & 0xFF); + } + else if (saveType == 1) { + // Only use the lower byte + sdBuffer[currWord] = (myWord & 0xFF); + } + } + myFile.write(sdBuffer, 512); + } + // Close the file: + myFile.close(); + print_Msg(F("Saved to ")); + print_Msg(folder); + println_Msg(F("/")); + display_Update(); +} + +unsigned long verifySram_MD() { + dataIn_MD(); + writeErrors = 0; + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + for (unsigned long currBuffer = sramBase; currBuffer < sramBase + sramSize; currBuffer += 512) { + for (int currWord = 0; currWord < 512; currWord++) { + word myWord = readWord_MD(currBuffer + currWord); + + if (saveType == 2) { + // Only use the upper byte + sdBuffer[currWord] = (( myWord >> 8 ) & 0xFF); + } + else if (saveType == 1) { + // Only use the lower byte + sdBuffer[currWord] = (myWord & 0xFF); + } + } + // Check sdBuffer content against file on sd card + for (int i = 0; i < 512; i++) { + if (myFile.read() != sdBuffer[i]) { + writeErrors++; + } + } + } + + // Close the file: + myFile.close(); + } + else { + print_Error(F("SD Error"), true); + } + // Return 0 if verified ok, or number of errors + return writeErrors; +} + +//****************************************** +// End of File //****************************************** diff --git a/Cart_Reader/N64.ino b/Cart_Reader/N64.ino index 0f3b1cd..d6ead1e 100644 --- a/Cart_Reader/N64.ino +++ b/Cart_Reader/N64.ino @@ -1,3023 +1,3023 @@ -//****************************************** -// NINTENDO 64 MODULE -//****************************************** - -/****************************************** - Defines - *****************************************/ -// These two macros toggle the eepDataPin/ControllerDataPin between input and output -// External 1K pull-up resistor from eepDataPin to VCC required -// 0x10 = 00010000 -> Port H Pin 4 -#define N64_HIGH DDRH &= ~0x10 -#define N64_LOW DDRH |= 0x10 -// Read the current state(0/1) of the eepDataPin -#define N64_QUERY (PINH & 0x10) - -/****************************************** - Variables - *****************************************/ -// Received N64 Eeprom data bits, 1 page -bool tempBits[65]; -int eepPages; - -// N64 Controller -// 256 bits of received Controller data -char N64_raw_dump[257]; -// Array that holds one Controller Pak block of 32 bytes -byte myBlock[33]; -String rawStr = ""; // above char array read into a string -struct { - char stick_x; - char stick_y; -} -N64_status; -//stings that hold the buttons -String button = "N/A"; -String lastbutton = "N/A"; - -// Rom base address -unsigned long romBase = 0x10000000; - -// Flashram type -byte flashramType = 1; -boolean MN63F81MPN = false; - -/****************************************** - Menu -*****************************************/ -// N64 start menu -static const char n64MenuItem1[] PROGMEM = "Cart Slot"; -static const char n64MenuItem2[] PROGMEM = "Controller"; -static const char n64MenuItem3[] PROGMEM = "Flash Repro"; -static const char* const menuOptionsN64[] PROGMEM = {n64MenuItem1, n64MenuItem2, n64MenuItem3}; - -// N64 controller menu items -static const char N64ContMenuItem1[] PROGMEM = "Test Controller"; -static const char N64ContMenuItem2[] PROGMEM = "Read ControllerPak"; -static const char N64ContMenuItem3[] PROGMEM = "Write ControllerPak"; -static const char N64ContMenuItem4[] PROGMEM = "Reset"; -static const char* const menuOptionsN64Controller[] PROGMEM = {N64ContMenuItem1, N64ContMenuItem2, N64ContMenuItem3, N64ContMenuItem4}; - -// N64 cart menu items -static const char N64CartMenuItem1[] PROGMEM = "Read Rom"; -static const char N64CartMenuItem2[] PROGMEM = "Read Save"; -static const char N64CartMenuItem3[] PROGMEM = "Write Save"; -static const char N64CartMenuItem4[] PROGMEM = "Force Savetype"; -static const char N64CartMenuItem5[] PROGMEM = "Reset"; -static const char* const menuOptionsN64Cart[] PROGMEM = {N64CartMenuItem1, N64CartMenuItem2, N64CartMenuItem3, N64CartMenuItem4, N64CartMenuItem5}; - -// N64 CRC32 error menu items -static const char N64CRCMenuItem1[] PROGMEM = "Recalc CRC"; -static const char N64CRCMenuItem2[] PROGMEM = "Redump"; -static const char N64CRCMenuItem3[] PROGMEM = "Ignore"; -static const char N64CRCMenuItem4[] PROGMEM = "Reset"; -static const char* const menuOptionsN64CRC[] PROGMEM = {N64CRCMenuItem1, N64CRCMenuItem2, N64CRCMenuItem3, N64CRCMenuItem4}; - -// Rom menu -static const char N64RomItem1[] PROGMEM = "4MB"; -static const char N64RomItem2[] PROGMEM = "8MB"; -static const char N64RomItem3[] PROGMEM = "12MB"; -static const char N64RomItem4[] PROGMEM = "16MB"; -static const char N64RomItem5[] PROGMEM = "32MB"; -static const char N64RomItem6[] PROGMEM = "64MB"; -static const char* const romOptionsN64[] PROGMEM = {N64RomItem1, N64RomItem2, N64RomItem3, N64RomItem4, N64RomItem5, N64RomItem6}; - -// Save menu -static const char N64SaveItem1[] PROGMEM = "None"; -static const char N64SaveItem2[] PROGMEM = "4K EEPROM"; -static const char N64SaveItem3[] PROGMEM = "16K EEPROM"; -static const char N64SaveItem4[] PROGMEM = "SRAM"; -static const char N64SaveItem5[] PROGMEM = "FLASHRAM"; -static const char* const saveOptionsN64[] PROGMEM = {N64SaveItem1, N64SaveItem2, N64SaveItem3, N64SaveItem4, N64SaveItem5}; - -// N64 start menu -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; - - } -} - -// N64 Controller Menu -void n64ControllerMenu() { - // create menu with title and 4 options to choose from - unsigned char mainMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsN64Controller, 4); - mainMenu = question_box("N64 Controller", menuOptions, 4, 0); - - // wait for user choice to come back from the question box menu - switch (mainMenu) - { - case 0: - display_Clear(); - display_Update(); - readController(); - break; - - case 1: - display_Clear(); - display_Update(); - readMPK(); - println_Msg(F("")); - println_Msg(F("Press Button.")); - display_Update(); - wait(); - break; - - case 2: - display_Clear(); - display_Update(); - // Change to root - filePath[0] = '\0'; - sd.chdir("/"); - // Launch file browser - fileBrowser("Select mpk file"); - display_Clear(); - display_Update(); - writeMPK(); - verifyMPK(); - println_Msg(F("")); - println_Msg(F("Press Button.")); - display_Update(); - wait(); - break; - - case 3: - asm volatile (" jmp 0"); - break; - } -} - -// N64 Cartridge Menu -void n64CartMenu() { - // create menu with title and 4 options to choose from - unsigned char mainMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsN64Cart, 5); - mainMenu = question_box("N64 Cart Reader", menuOptions, 5, 0); - - // wait for user choice to come back from the question box menu - switch (mainMenu) - { - case 0: - sd.chdir("/"); - readRom_N64(); - break; - - case 1: - sd.chdir("/"); - display_Clear(); - - if (saveType == 1) { - println_Msg(F("Reading Sram...")); - display_Update(); - readSram(32768, 1); - } - else if (saveType == 4) { - getFramType(); - println_Msg(F("Reading Flashram...")); - display_Update(); - readFram(flashramType); - } - else if ((saveType == 5) || (saveType == 6)) { - println_Msg(F("Reading Eep...")); - display_Update(); - readEeprom(); - } - else { - print_Error(F("Savetype Error"), false); - } - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - break; - - case 2: - filePath[0] = '\0'; - sd.chdir("/"); - if (saveType == 1) { - // Launch file browser - fileBrowser("Select sra file"); - display_Clear(); - - writeSram(32768); - writeErrors = verifySram(32768, 1); - if (writeErrors == 0) { - println_Msg(F("Sram verified OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - } - else if (saveType == 4) { - // Launch file browser - fileBrowser("Select fla file"); - display_Clear(); - getFramType(); - writeFram(flashramType); - print_Msg(F("Verifying...")); - display_Update(); - writeErrors = verifyFram(flashramType); - if (writeErrors == 0) { - println_Msg(F("OK")); - display_Update(); - } - else { - println_Msg(""); - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - } - else if ((saveType == 5) || (saveType == 6)) { - // Launch file browser - fileBrowser("Select eep file"); - display_Clear(); - - writeEeprom(); - writeErrors = verifyEeprom(); - if (writeErrors == 0) { - println_Msg(F("Eeprom verified OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - } - else { - print_Error(F("Savetype Error"), false); - } - println_Msg(F("Press Button...")); - display_Update(); - wait(); - break; - - case 3: - // create submenu with title and 6 options to choose from - unsigned char N64SaveMenu; - // Copy menuOptions out of progmem - convertPgm(saveOptionsN64, 5); - N64SaveMenu = question_box("Select save type", menuOptions, 5, 0); - - // wait for user choice to come back from the question box menu - switch (N64SaveMenu) - { - case 0: - // None - saveType = 0; - break; - - case 1: - // 4K EEPROM - saveType = 5; - eepPages = 64; - break; - - case 2: - // 16K EEPROM - saveType = 6; - eepPages = 256; - break; - - case 3: - // SRAM - saveType = 1; - break; - - case 4: - // FLASHRAM - saveType = 4; - break; - } - break; - - case 4: - asm volatile (" jmp 0"); - break; - } -} - -/****************************************** - Setup - *****************************************/ -void setup_N64_Controller() { - // Output a low signal - PORTH &= ~(1 << 4); - // Set Controller Data Pin(PH4) to Input - DDRH &= ~(1 << 4); -} - -void setup_N64_Cart() { - // Set Address Pins to Output and set them low - //A0-A7 - DDRF = 0xFF; - PORTF = 0x00; - //A8-A15 - DDRK = 0xFF; - PORTK = 0x00; - - // Set Control Pins to Output RESET(PH0) WR(PH5) RD(PH6) aleL(PC0) aleH(PC1) - DDRH |= (1 << 0) | (1 << 5) | (1 << 6); - DDRC |= (1 << 0) | (1 << 1); - // Pull RESET(PH0) low until we are ready - PORTH &= ~(1 << 0); - // Output a high signal on WR(PH5) RD(PH6), pins are active low therefore everything is disabled now - PORTH |= (1 << 5) | (1 << 6); - // Pull aleL(PC0) low and aleH(PC1) high - PORTC &= ~(1 << 0); - PORTC |= (1 << 1); - - // Set Eeprom Clock Pin(PH1) to Output - DDRH |= (1 << 1); - // Output a high signal - PORTH |= (1 << 1); - - // Set Eeprom Data Pin(PH4) to Input - DDRH &= ~(1 << 4); - // Activate Internal Pullup Resistors - //PORTH |= (1 << 4); - - // Set sram base address - sramBase = 0x08000000; - - // Wait until all is stable - delay(300); - - // Pull RESET(PH0) high to start eeprom - PORTH |= (1 << 0); -} - -/****************************************** - Low level functions - *****************************************/ -// Switch Cartridge address/data pins to write -void adOut_N64() { - //A0-A7 - DDRF = 0xFF; - PORTF = 0x00; - //A8-A15 - DDRK = 0xFF; - PORTK = 0x00; -} - -// Switch Cartridge address/data pins to read -void adIn_N64() { - //A0-A7 - DDRF = 0x00; - //A8-A15 - DDRK = 0x00; -} - -// Set Cartridge address -void setAddress_N64(unsigned long myAddress) { - // Set address pins to output - adOut_N64(); - - // Split address into two words - word myAdrLowOut = myAddress & 0xFFFF; - word myAdrHighOut = myAddress >> 16; - - // Switch WR(PH5) RD(PH6) ale_L(PC0) ale_H(PC1) to high (since the pins are active low) - PORTH |= (1 << 5) | (1 << 6); - PORTC |= (1 << 1); - __asm__("nop\n\t"); - PORTC |= (1 << 0); - - // Output high part to address pins - PORTF = myAdrHighOut & 0xFF; - PORTK = (myAdrHighOut >> 8) & 0xFF; - - // Leave ale_H high for additional 62.5ns - __asm__("nop\n\t"); - - // Pull ale_H(PC1) low - PORTC &= ~(1 << 1); - - // Output low part to address pins - PORTF = myAdrLowOut & 0xFF; - PORTK = (myAdrLowOut >> 8) & 0xFF; - - // Leave ale_L high for ~125ns - __asm__("nop\n\t""nop\n\t"); - - // Pull ale_L(PC0) low - PORTC &= ~(1 << 0); - - // Wait ~600ns just to be sure address is set - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Set data pins to input - adIn_N64(); -} - -// Read one word out of the cartridge -word readWord_N64() { - // Pull read(PH6) low - PORTH &= ~(1 << 6); - - // Wait ~310ns - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Join bytes from PINF and PINK into a word - word tempWord = ( ( PINK & 0xFF ) << 8 ) | ( PINF & 0xFF ); - - // Pull read(PH6) high - PORTH |= (1 << 6); - - // Wait 62.5ns - __asm__("nop\n\t"); - return tempWord; -} - -// Write one word to data pins of the cartridge -void writeWord_N64(word myWord) { - // Set address pins to output - adOut_N64(); - - // Output word to AD0-AD15 - PORTF = myWord & 0xFF; - PORTK = (myWord >> 8) & 0xFF; - - // Wait ~62.5ns - __asm__("nop\n\t"); - - // Pull write(PH5) low - PORTH &= ~(1 << 5); - - // Wait ~310ns - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Pull write(PH5) high - PORTH |= (1 << 5); - - // Wait ~125ns - __asm__("nop\n\t""nop\n\t"); - - // Set data pins to input - adIn_N64(); -} - -/****************************************** - N64 Controller CRC Functions - *****************************************/ -static word addrCRC(word address) { - // CRC table - word xor_table[16] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x1F, 0x0B, 0x16, 0x19, 0x07, 0x0E, 0x1C, 0x0D, 0x1A, 0x01 }; - word crc = 0; - // Make sure we have a valid address - address &= ~0x1F; - // Go through each bit in the address, and if set, xor the right value into the output - for (int i = 15; i >= 5; i--) { - // Is this bit set? - if ( ((address >> i) & 0x1)) { - crc ^= xor_table[i]; - } - } - // Just in case - crc &= 0x1F; - // Create a new address with the CRC appended - return address | crc; -} - -static byte dataCRC(byte * data) { - byte ret = 0; - for (byte i = 0; i <= 32; i++) { - for (byte j = 7; j >= 0; j--) { - int tmp = 0; - if (ret & 0x80) { - tmp = 0x85; - } - ret <<= 1; - if ( i < 32 ) { - if (data[i] & (0x01 << j)) { - ret |= 0x1; - } - } - ret ^= tmp; - } - } - return ret; -} - -/****************************************** - N64 Controller Protocol Functions - *****************************************/ -void N64_send(unsigned char *buffer, char length) { - // Send these bytes - char bits; - bool bit; - - // This routine is very carefully timed by examining the assembly output. - // Do not change any statements, it could throw the timings off - // - // We get 16 cycles per microsecond, which should be plenty, but we need to - // be conservative. Most assembly ops take 1 cycle, but a few take 2 - // - // I use manually constructed for-loops out of gotos so I have more control - // over the outputted assembly. I can insert nops where it was impossible - // with a for loop - - asm volatile (";Starting outer for loop"); -outer_loop: - { - asm volatile (";Starting inner for loop"); - bits = 8; -inner_loop: - { - // Starting a bit, set the line low - asm volatile (";Setting line to low"); - N64_LOW; // 1 op, 2 cycles - - asm volatile (";branching"); - if (*buffer >> 7) { - asm volatile (";Bit is a 1"); - // 1 bit - // remain low for 1us, then go high for 3us - // nop block 1 - asm volatile ("nop\nnop\nnop\nnop\nnop\n"); - - asm volatile (";Setting line to high"); - N64_HIGH; - - // nop block 2 - // we'll wait only 2us to sync up with both conditions - // at the bottom of the if statement - asm volatile ("nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - ); - - } - else { - asm volatile (";Bit is a 0"); - // 0 bit - // remain low for 3us, then go high for 1us - // nop block 3 - asm volatile ("nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\n"); - - asm volatile (";Setting line to high"); - N64_HIGH; - - // wait for 1us - asm volatile ("; end of conditional branch, need to wait 1us more before next bit"); - - } - // end of the if, the line is high and needs to remain - // high for exactly 16 more cycles, regardless of the previous - // branch path - - asm volatile (";finishing inner loop body"); - --bits; - if (bits != 0) { - // nop block 4 - // this block is why a for loop was impossible - asm volatile ("nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\n"); - // rotate bits - asm volatile (";rotating out bits"); - *buffer <<= 1; - - goto inner_loop; - } // fall out of inner loop - } - asm volatile (";continuing outer loop"); - // In this case: the inner loop exits and the outer loop iterates, - // there are /exactly/ 16 cycles taken up by the necessary operations. - // So no nops are needed here (that was lucky!) - --length; - if (length != 0) { - ++buffer; - goto outer_loop; - } // fall out of outer loop - } -} - -void N64_stop() { - // send a single stop (1) bit - // nop block 5 - asm volatile ("nop\nnop\nnop\nnop\n"); - N64_LOW; - // wait 1 us, 16 cycles, then raise the line - // 16-2=14 - // nop block 6 - asm volatile ("nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\n"); - N64_HIGH; -} - -void N64_get(word bitcount) { - // listen for the expected bitcount/8 bytes of data back from the controller and - // blast it out to the N64_raw_dump array, one bit per byte for extra speed. - asm volatile (";Starting to listen"); - unsigned char timeout; - char *bitbin = N64_raw_dump; - - // Again, using gotos here to make the assembly more predictable and - // optimization easier (please don't kill me) -read_loop: - timeout = 0x3f; - // wait for line to go low - while (N64_QUERY) { - if (!--timeout) - return; - } - // wait approx 2us and poll the line - asm volatile ( - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - "nop\nnop\nnop\nnop\nnop\n" - ); - *bitbin = N64_QUERY; - ++bitbin; - --bitcount; - if (bitcount == 0) - return; - - // wait for line to go high again - // it may already be high, so this should just drop through - timeout = 0x3f; - while (!N64_QUERY) { - if (!--timeout) - return; - } - goto read_loop; -} - -/****************************************** - N64 Controller Functions - *****************************************/ -void get_button() -{ - // Command to send to the gamecube - // The last bit is rumble, flip it to rumble - // yes this does need to be inside the loop, the - // array gets mutilated when it goes through N64_send - unsigned char command[] = { - 0x01 - }; - - // don't want interrupts getting in the way - noInterrupts(); - // send those 3 bytes - N64_send(command, 1); - N64_stop(); - // read in 32bits of data and dump it to N64_raw_dump - N64_get(32); - // end of time sensitive code - interrupts(); - - // The get_N64_status function sloppily dumps its data 1 bit per byte - // into the get_status_extended char array. It's our job to go through - // that and put each piece neatly into the struct N64_status - int i; - memset(&N64_status, 0, sizeof(N64_status)); - - // bits: joystick x value - // These are 8 bit values centered at 0x80 (128) - for (i = 0; i < 8; i++) { - N64_status.stick_x |= N64_raw_dump[16 + i] ? (0x80 >> i) : 0; - } - for (i = 0; i < 8; i++) { - N64_status.stick_y |= N64_raw_dump[24 + i] ? (0x80 >> i) : 0; - } - - // read char array N64_raw_dump into string rawStr - rawStr = ""; - for (i = 0; i < 16; i++) { - rawStr = rawStr + String(N64_raw_dump[i], DEC); - } - - // Buttons (A,B,Z,S,DU,DD,DL,DR,0,0,L,R,CU,CD,CL,CR) - if (rawStr.substring(0, 16) == "0000000000000000") { - lastbutton = button; - button = "Press a button"; - } - else - { - for (int i = 0; i < 16; i++) - { - // seems to be 16, 8 or 4 depending on what pin is used - if (N64_raw_dump[i] == 16) - { - switch (i) - { - case 7: - button = "D-Right"; - break; - - case 6: - button = "D-Left"; - break; - - case 5: - button = "D-Down"; - break; - - case 4: - button = "D-Up"; - break; - - case 3: - button = "START"; - break; - - case 2: - button = "Z"; - break; - - case 1: - button = "B"; - break; - - case 0: - button = "A"; - break; - - case 15: - button = "C-Right"; - break; - - case 14: - button = "C-Left"; - break; - - case 13: - button = "C-Down"; - break; - - case 12: - button = "C-Up"; - break; - - case 11: - button = "R"; - break; - - case 10: - button = "L"; - break; - } - } - } - } -} - -void readController() { - bool quit = 1; - - while (quit) { - display_Clear(); - - // Get Button and analog stick - get_button(); - - println_Msg(F("Controller Test")); - println_Msg(""); - println_Msg(button); - println_Msg(""); - String stickx = String("X: " + String(N64_status.stick_x, DEC) + " "); - println_Msg(stickx); - String sticky = String("Y: " + String(N64_status.stick_y, DEC) + " "); - println_Msg(sticky); - println_Msg(""); - println_Msg(F("Press START to quit")); - - display_Update(); - delay(100); - - if (button == "START") { - quit = 0; - } - } -} - -/****************************************** - N64 Controller Pak Functions - (connected via Controller) - *****************************************/ -// read 32bytes from controller pak -void readBlock(word myAddress) { - // Calculate the address CRC - word myAddressCRC = addrCRC(myAddress); - - // Read Controller Pak command - unsigned char command[] = {0x02}; - // Address Command - unsigned char addressHigh[] = {(unsigned char)(myAddressCRC >> 8)}; - unsigned char addressLow[] = {(unsigned char)(myAddressCRC & 0xff)}; - - // don't want interrupts getting in the way - noInterrupts(); - // send those 3 bytes - N64_send(command, 1); - N64_send(addressHigh, 1); - N64_send(addressLow, 1); - N64_stop(); - // read in data - N64_get(256); - // end of time sensitive code - interrupts(); - - // Empty N64_raw_dump into myBlock - for (word i = 0; i < 256; i += 8) { - boolean byteFlipped[9]; - - // Flip byte order - byteFlipped[0] = N64_raw_dump[i + 7]; - byteFlipped[1] = N64_raw_dump[i + 6]; - byteFlipped[2] = N64_raw_dump[i + 5]; - byteFlipped[3] = N64_raw_dump[i + 4]; - byteFlipped[4] = N64_raw_dump[i + 3]; - byteFlipped[5] = N64_raw_dump[i + 2]; - byteFlipped[6] = N64_raw_dump[i + 1]; - byteFlipped[7] = N64_raw_dump[i + 0]; - - // Join bits into one byte - unsigned char myByte = 0; - for (byte j = 0; j < 8; ++j) { - if (byteFlipped[j]) { - myByte |= 1 << j; - } - } - // Save byte into block array - myBlock[i / 8] = myByte; - } -} - -// reads the MPK file to the sd card -void readMPK() { - // Change to root - sd.chdir("/"); - // Make MPK directory - sd.mkdir("N64/MPK", true); - // Change to MPK directory - sd.chdir("N64/MPK"); - - // Get name, add extension and convert to char array for sd lib - EEPROM_readAnything(10, foldern); - sprintf(fileName, "%d", foldern); - strcat(fileName, ".mpk"); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - //open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("Can't open file on SD"), true); - } - - println_Msg(F("Please wait...")); - display_Update(); - - // Controller paks, which all have 32kB of space, are mapped between 0x0000 – 0x7FFF - for (word i = 0x0000; i < 0x8000; i += 32) { - // Read one block of the Controller Pak into array myBlock - readBlock(i); - // Write block to SD card - for (byte j = 0; j < 32; j++) { - myFile.write(myBlock[j]); - } - } - // Close the file: - myFile.close(); - print_Msg(F("Saved as N64/MPK/")); - println_Msg(fileName); - display_Update(); -} - -void writeMPK() { - // Create filepath - sprintf(filePath, "%s/%s", filePath, fileName); - println_Msg(F("Writing...")); - println_Msg(filePath); - display_Update(); - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - for (word myAddress = 0x0000; myAddress < 0x8000; myAddress += 32) { - // Read 32 bytes into SD buffer - myFile.read(sdBuffer, 32); - - // Calculate the address CRC - word myAddressCRC = addrCRC(myAddress); - - // Write Controller Pak command - unsigned char command[] = {0x03}; - // Address Command - unsigned char addressHigh[] = {(unsigned char)(myAddressCRC >> 8)}; - unsigned char addressLow[] = {(unsigned char)(myAddressCRC & 0xff)}; - - // don't want interrupts getting in the way - noInterrupts(); - // Send write command - N64_send(command, 1); - // Send block number - N64_send(addressHigh, 1); - N64_send(addressLow, 1); - // Send data to write - N64_send(sdBuffer, 32); - // Send stop - N64_stop(); - // Enable interrupts - interrupts(); - } - // Close the file: - myFile.close(); - println_Msg(F("Done")); - display_Update(); - } - else { - print_Error(F("Can't create file on SD"), true); - } -} - -// verifies if write was successful -void verifyMPK() { - writeErrors = 0; - - println_Msg(F("Verifying...")); - display_Update(); - - //open file on sd card - if (!myFile.open(filePath, O_RDWR | O_CREAT)) { - print_Error(F("Can't create file on SD"), true); - } - - // Controller paks, which all have 32kB of space, are mapped between 0x0000 – 0x7FFF - for (word i = 0x0000; i < 0x8000; i += 32) { - // Read one block of the Controller Pak into array myBlock - readBlock(i); - // Check against file on SD card - for (byte j = 0; j < 32; j++) { - if (myFile.read() != myBlock[j]) { - writeErrors++; - } - } - } - // Close the file: - myFile.close(); - if (writeErrors == 0) { - println_Msg(F("OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } -} - -/****************************************** - N64 Cartridge functions -*****************************************/ -void printCartInfo_N64() { - // Check cart - getCartInfo_N64(); - - // Print start page - if (cartSize != 0) { - println_Msg(F("N64 Cartridge Info")); - println_Msg(F("")); - print_Msg(F("Name: ")); - println_Msg(romName); - print_Msg(F("ID: ")); - print_Msg(cartID); - print_Msg(F(" Size: ")); - print_Msg(cartSize); - println_Msg(F("MB")); - print_Msg(F("Save: ")); - switch (saveType) { - case 1: - println_Msg(F("Sram")); - break; - case 4: - println_Msg(F("Flashram")); - break; - case 5: - println_Msg(F("4K Eeprom")); - eepPages = 64; - break; - case 6: - println_Msg(F("16K Eeprom")); - eepPages = 256; - break; - default: - println_Msg(F("unknown")); - break; - } - print_Msg(F("Version: 1.")); - println_Msg(romVersion); - - // Wait for user input - println_Msg(F(" ")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - } - else { - // Display error - println_Msg(F("GAMEPAK ERROR")); - println_Msg(""); - print_Msg(F("Name: ")); - println_Msg(romName); - print_Msg(F("ID: ")); - println_Msg(cartID); - println_Msg(""); - display_Update(); - - strcpy(romName, "GPERROR"); - print_Error(F("Cartridge unknown"), false); - wait(); - - // Set cartsize manually - unsigned char N64RomMenu; - // Copy menuOptions out of progmem - convertPgm(romOptionsN64, 6); - N64RomMenu = question_box("Select ROM size", menuOptions, 6, 0); - - // wait for user choice to come back from the question box menu - switch (N64RomMenu) - { - case 0: - // 4MB - cartSize = 4; - break; - - case 1: - // 8MB - cartSize = 8; - break; - - case 2: - // 12MB - cartSize = 12; - break; - - case 3: - // 16MB - cartSize = 16; - break; - - case 4: - // 32MB - cartSize = 32; - break; - - case 5: - // 64MB - cartSize = 64; - break; - } - } -} - -// CRC32 lookup table -static const uint32_t crc_32_tab[] PROGMEM = { /* CRC polynomial 0xedb88320 */ - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d -}; - -// Calculate dumped rom's CRC32 -inline uint32_t updateCRC32(uint8_t ch, uint32_t crc) { - uint32_t idx = ((crc) ^ (ch)) & 0xff; - uint32_t tab_value = pgm_read_dword(crc_32_tab + idx); - return tab_value ^ ((crc) >> 8); -} - -// Read rom from sd -uint32_t crc32() { - if (myFile.open(fileName, O_READ)) { - uint32_t oldcrc32 = 0xFFFFFFFF; - - for (unsigned long currByte = 0; currByte < cartSize * 2048; currByte++) { - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - oldcrc32 = updateCRC32(sdBuffer[c], oldcrc32); - } - } - // Close the file: - myFile.close(); - return ~oldcrc32; - } - else { - print_Error(F("File not found"), true); - } -} - -// look-up the calculated crc in the file n64.txt on sd card -boolean searchCRC(char crcStr[9]) { - boolean result = 0; - char tempStr2[2]; - char tempStr1[9]; - char tempStr[5]; - - // Change to root dir - sd.chdir("/"); - - if (myFile.open("n64.txt", O_READ)) { - // Loop through file - while (myFile.available()) { - // Read 8 bytes into String, do it one at a time so byte order doesn't get mixed up - sprintf(tempStr1, "%c", myFile.read()); - for (byte i = 0; i < 7; i++) { - sprintf(tempStr2, "%c", myFile.read()); - strcat(tempStr1, tempStr2); - } - - // Check if string is a match - if (strcmp(tempStr1, crcStr) == 0) { - // Skip the , in the file - myFile.seekSet(myFile.curPosition() + 1); - - // Read 4 bytes into String, do it one at a time so byte order doesn't get mixed up - sprintf(tempStr, "%c", myFile.read()); - for (byte i = 0; i < 3; i++) { - sprintf(tempStr2, "%c", myFile.read()); - strcat(tempStr, tempStr2); - } - - if (strcmp(tempStr, cartID) == 0) { - result = 1; - break; - } - else { - result = 0; - break; - } - } - // If no match, empty string, advance by 12 and try again - else { - myFile.seekSet(myFile.curPosition() + 12); - } - } - // Close the file: - myFile.close(); - return result; - } - else { - print_Error(F("N64.txt missing"), true); - } -} - -// look-up cart id in file n64.txt on sd card -void getCartInfo_N64() { - char tempStr2[2]; - char tempStr[5]; - char sizeStr[3]; - char saveStr[2]; - - // cart not in list - cartSize = 0; - saveType = 0; - - // Read cart id - idCart(); - - if (myFile.open("n64.txt", O_READ)) { - // Skip over the first crc - myFile.seekSet(myFile.curPosition() + 9); - // Loop through file - while (myFile.available()) { - // Read 4 bytes into String, do it one at a time so byte order doesn't get mixed up - sprintf(tempStr, "%c", myFile.read()); - for (byte i = 0; i < 3; i++) { - sprintf(tempStr2, "%c", myFile.read()); - strcat(tempStr, tempStr2); - } - - // Check if string is a match - if (strcmp(tempStr, cartID) == 0) { - // Skip the , in the file - myFile.seekSet(myFile.curPosition() + 1); - - // Read the next ascii character and subtract 48 to convert to decimal - cartSize = myFile.read() - 48; - // Remove leading 0 for single digit cart sizes - if (cartSize != 0) { - cartSize = cartSize * 10 + myFile.read() - 48; - } - else { - cartSize = myFile.read() - 48; - } - - // Skip the , in the file - myFile.seekSet(myFile.curPosition() + 1); - - // Read the next ascii character and subtract 48 to convert to decimal - saveType = myFile.read() - 48; - } - // If no match, empty string, advance by 16 and try again - else { - myFile.seekSet(myFile.curPosition() + 16); - } - } - // Close the file: - myFile.close(); - } - else { - print_Error(F("N64.txt missing"), true); - } -} - -// Read rom ID -void idCart() { - // Set the address - setAddress_N64(romBase); - // Read first 64 bytes of rom - for (int c = 0; c < 64; c += 2) { - // split word - word myWord = readWord_N64(); - byte loByte = myWord & 0xFF; - byte hiByte = myWord >> 8; - - // write to buffer - sdBuffer[c] = hiByte; - sdBuffer[c + 1] = loByte; - } - - // Get cart id - cartID[0] = sdBuffer[0x3B]; - cartID[1] = sdBuffer[0x3C]; - cartID[2] = sdBuffer[0x3D]; - cartID[3] = sdBuffer[0x3E]; - - // Get rom version - romVersion = sdBuffer[0x3F]; - - // Get name - byte myLength = 0; - for (unsigned int i = 0; i < 20; i++) { - if (((char(sdBuffer[0x20 + i]) >= 48 && char(sdBuffer[0x20 + i]) <= 57) || (char(sdBuffer[0x20 + i]) >= 65 && char(sdBuffer[0x20 + i]) <= 122)) && myLength < 15) { - romName[myLength] = char(sdBuffer[0x20 + i]); - myLength++; - } - } -} - -/****************************************** - Eeprom functions -*****************************************/ -// Send a clock pulse of 2us length, 50% duty, 500kHz -void pulseClock_N64(unsigned int times) { - for (unsigned int i = 0; i < (times * 2); i++) { - // Switch the clock pin to 0 if it's 1 and 0 if it's 1 - PORTH ^= (1 << 1); - // without the delay the clock pulse would be 1.5us and 666kHz - //__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t")); - } -} - -// Send one byte of data to eeprom -void sendData(byte data) { - for (byte i = 0; i < 8; i++) { - // pull data line low - N64_LOW; - - // if current bit is 1, pull high after ~1us, 2 cycles - if (data >> 7) { - pulseClock_N64(2); - N64_HIGH; - pulseClock_N64(6); - } - // if current bit is 0 pull high after ~3us, 6 cycles - else { - pulseClock_N64(6); - N64_HIGH; - pulseClock_N64(2); - } - - // rotate to the next bit - data <<= 1; - } -} - -// Send stop bit to eeprom -void sendStop() { - N64_LOW; - pulseClock_N64(2); - N64_HIGH; - pulseClock_N64(4); -} - -// Capture 8 bytes in 64 bits into bit array tempBits -void readData() { - for (byte i = 0; i < 64; i++) { - - // pulse clock until we get response from eeprom - while (N64_QUERY) { - pulseClock_N64(1); - } - - // Skip over the 1us low part of a high bit - pulseClock_N64(3); - - // Read bit - tempBits[i] = N64_QUERY; - - // wait for line to go high again - while (!N64_QUERY) { - pulseClock_N64(1); - } - } -} - -// Write Eeprom to cartridge -void writeEeprom() { - if ((saveType == 5) || (saveType == 6)) { - - // Create filepath - sprintf(filePath, "%s/%s", filePath, fileName); - println_Msg(F("Writing...")); - println_Msg(filePath); - display_Update(); - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - - for (byte i = 0; i < (eepPages / 64); i++) { - myFile.read(sdBuffer, 512); - // Disable interrupts for more uniform clock pulses - noInterrupts(); - - for (byte pageNumber = 0; pageNumber < 64; pageNumber++) { - // Wait ~50ms between page writes or eeprom will have write errors - pulseClock_N64(26000); - - // Send write command - sendData(0x05); - // Send page number - sendData(pageNumber + (i * 64)); - // Send data to write - for (byte j = 0; j < 8; j++) { - sendData(sdBuffer[(pageNumber * 8) + j]); - } - sendStop(); - } - interrupts(); - } - - // Close the file: - myFile.close(); - println_Msg(F("Done")); - display_Update(); - } - else { - print_Error(F("SD Error"), true); - } - } - else { - print_Error(F("Savetype Error"), true); - } -} - -// Dump Eeprom to SD -void readEeprom() { - if ((saveType == 5) || (saveType == 6)) { - - // Wait 0.6ms - pulseClock_N64(300); - - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".eep"); - - // create a new folder for the save file - EEPROM_readAnything(10, foldern); - sprintf(folder, "N64/SAVE/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - // Open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("Can't create file on SD"), true); - } - - for (byte i = 0; i < (eepPages / 64); i++) { - // Disable interrupts for more uniform clock pulses - noInterrupts(); - - for (byte pageNumber = 0; pageNumber < 64; pageNumber++) { - // Send read command - sendData(0x04); - // Send Page number - sendData(pageNumber + (i * 64)); - // Send stop bit - sendStop(); - - // read data - readData(); - sendStop(); - - // OR 8 bits into one byte for a total of 8 bytes - for (byte j = 0; j < 64; j += 8) { - sdBuffer[(pageNumber * 8) + (j / 8)] = tempBits[0 + j] << 7 | tempBits[1 + j] << 6 | tempBits[2 + j] << 5 | tempBits[3 + j] << 4 | tempBits[4 + j] << 3 | tempBits[5 + j] << 2 | tempBits[6 + j] << 1 | tempBits[7 + j]; - } - // Wait ~0.6ms between pages or eeprom will lock up - pulseClock_N64(300); - } - interrupts(); - - // Write 64 pages at once to the SD card - myFile.write(sdBuffer, 512); - } - // Close the file: - myFile.close(); - //clear the screen - display_Clear(); - print_Msg(F("Saved to ")); - print_Msg(folder); - println_Msg(F("/")); - display_Update(); - } - else { - print_Error(F("Savetype Error"), true); - } -} - -// Check if a write succeeded, returns 0 if all is ok and number of errors if not -unsigned long verifyEeprom() { - if ((saveType == 5) || (saveType == 6)) { - writeErrors = 0; - - // Wait 0.6ms - pulseClock_N64(300); - - print_Msg(F("Verifying against ")); - println_Msg(filePath); - display_Update(); - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - - for (byte i = 0; i < (eepPages / 64); i++) { - // Disable interrupts for more uniform clock pulses - noInterrupts(); - - for (byte pageNumber = 0; pageNumber < 64; pageNumber++) { - // Send read command - sendData(0x04); - // Send Page number - sendData(pageNumber + (i * 64)); - // Send stop bit - sendStop(); - - // read data - readData(); - sendStop(); - - - - // OR 8 bits into one byte for a total of 8 bytes - for (byte j = 0; j < 64; j += 8) { - sdBuffer[(pageNumber * 8) + (j / 8)] = tempBits[0 + j] << 7 | tempBits[1 + j] << 6 | tempBits[2 + j] << 5 | tempBits[3 + j] << 4 | tempBits[4 + j] << 3 | tempBits[5 + j] << 2 | tempBits[6 + j] << 1 | tempBits[7 + j]; - } - // Wait ~0.6ms between pages or eeprom will lock up - pulseClock_N64(300); - } - interrupts(); - - // Check sdBuffer content against file on sd card - for (int c = 0; c < 512; c++) { - if (myFile.read() != sdBuffer[c]) { - writeErrors++; - } - } - } - // Close the file: - myFile.close(); - } - else { - // SD Error - writeErrors = 999999; - print_Error(F("SD Error"), true); - } - // Return 0 if verified ok, or number of errors - return writeErrors; - } - else { - print_Error(F("Savetype Error"), true); - } -} - -/****************************************** - SRAM functions -*****************************************/ -// Write sram to cartridge -void writeSram(unsigned long sramSize) { - if (saveType == 1) { - // Create filepath - sprintf(filePath, "%s/%s", filePath, fileName); - println_Msg(F("Writing...")); - println_Msg(filePath); - display_Update(); - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - for (unsigned long currByte = sramBase; currByte < (sramBase + sramSize); currByte += 512) { - - // Read save from SD into buffer - myFile.read(sdBuffer, 512); - - // Set the address for the next 512 bytes - setAddress_N64(currByte); - - for (int c = 0; c < 512; c += 2) { - // Join bytes to word - word myWord = ( ( sdBuffer[c] & 0xFF ) << 8 ) | ( sdBuffer[c + 1] & 0xFF ); - - // Write word - writeWord_N64(myWord); - } - } - // Close the file: - myFile.close(); - println_Msg(F("Done")); - display_Update(); - } - else { - print_Error(F("SD Error"), true); - } - - } - else { - print_Error(F("Savetype Error"), true); - } -} - -// Read sram and save to the SD card -void readSram(unsigned long sramSize, byte flashramType) { - int offset = 512; - int bufferSize = 512; - if (flashramType == 2) { - offset = 64; - bufferSize = 128; - } - - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - - if (saveType == 4) { - strcat(fileName, ".fla"); - } - else if (saveType == 1) { - strcat(fileName, ".sra"); - } - else { - print_Error(F("Savetype Error"), true); - } - - // create a new folder for the save file - EEPROM_readAnything(10, foldern); - sprintf(folder, "N64/SAVE/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - // Open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("SD Error"), true); - } - - for (unsigned long currByte = sramBase; currByte < (sramBase + (sramSize / flashramType)); currByte += offset) { - // Set the address - setAddress_N64(currByte); - - for (int c = 0; c < bufferSize; c += 2) { - // split word - word myWord = readWord_N64(); - byte loByte = myWord & 0xFF; - byte hiByte = myWord >> 8; - - // write to buffer - sdBuffer[c] = hiByte; - sdBuffer[c + 1] = loByte; - } - myFile.write(sdBuffer, bufferSize); - } - // Close the file: - myFile.close(); - print_Msg(F("Saved to ")); - print_Msg(folder); - println_Msg(F("/")); - display_Update(); -} - -unsigned long verifySram(unsigned long sramSize, byte flashramType) { - writeErrors = 0; - - int offset = 512; - int bufferSize = 512; - if (flashramType == 2) { - offset = 64; - bufferSize = 128; - } - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - for (unsigned long currByte = sramBase; currByte < (sramBase + (sramSize / flashramType)); currByte += offset) { - // Set the address - setAddress_N64(currByte); - - for (int c = 0; c < bufferSize; c += 2) { - // split word - word myWord = readWord_N64(); - byte loByte = myWord & 0xFF; - byte hiByte = myWord >> 8; - - // write to buffer - sdBuffer[c] = hiByte; - sdBuffer[c + 1] = loByte; - } - // Check sdBuffer content against file on sd card - for (int i = 0; i < bufferSize; i++) { - if (myFile.read() != sdBuffer[i]) { - writeErrors++; - } - } - } - // Close the file: - myFile.close(); - } - else { - print_Error(F("SD Error"), true); - } - // Return 0 if verified ok, or number of errors - return writeErrors; -} - -/****************************************** - Flashram functions -*****************************************/ -// Send a command to the flashram command register -void sendFramCmd (unsigned long myCommand) { - // Split command into two words - word myComLowOut = myCommand & 0xFFFF; - word myComHighOut = myCommand >> 16; - - // Set address to command register - setAddress_N64(0x08010000); - // Send command - writeWord_N64(myComHighOut); - writeWord_N64(myComLowOut); -} - -// Init fram -void initFram() { - // FRAM_EXECUTE_CMD - sendFramCmd(0xD2000000); - delay(10); - // FRAM_EXECUTE_CMD - sendFramCmd(0xD2000000); - delay(10); - //FRAM_STATUS_MODE_CMD - sendFramCmd(0xE1000000); - delay(10); -} - -void writeFram(byte flashramType) { - if (saveType == 4) { - // Erase fram - eraseFram(); - - // Check if empty - if (blankcheck_N64(flashramType) == 0) { - println_Msg(F("OK")); - display_Update(); - } - else { - println_Msg("FAIL"); - display_Update(); - } - - // Create filepath - sprintf(filePath, "%s/%s", filePath, fileName); - print_Msg(F("Writing ")); - println_Msg(filePath); - display_Update(); - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - // Init fram - initFram(); - - // Write all 8 fram banks - print_Msg(F("Bank ")); - for (byte bank = 0; bank < 8; bank++) { - print_Msg(bank); - print_Msg(F(" ")); - display_Update(); - - // Write one bank of 128*128 bytes - for (byte offset = 0; offset < 128; offset++) { - // Read save from SD into buffer - myFile.read(sdBuffer, 128); - - // FRAM_WRITE_MODE_CMD - sendFramCmd(0xB4000000); - delay(1); - - // Set the address for the next 128 bytes - setAddress_N64(0x08000000); - - // Send 128 bytes, 64 words - for (byte c = 0; c < 128; c += 2) { - // Join two bytes into one word - word myWord = ( ( sdBuffer[c] & 0xFF ) << 8 ) | ( sdBuffer[c + 1] & 0xFF ); - // Write word - writeWord_N64(myWord); - } - // Delay between each "DMA" - delay(1); - - //FRAM_WRITE_OFFSET_CMD + offset - sendFramCmd((0xA5000000 | (((bank * 128) + offset) & 0xFFFF))); - delay(1); - - // FRAM_EXECUTE_CMD - sendFramCmd(0xD2000000); - while (waitForFram(flashramType)) { - delay(1); - } - } - // Delay between banks - delay(20); - } - println_Msg(""); - // Close the file: - myFile.close(); - } - else { - print_Error(F("SD Error"), true); - } - } - else { - print_Error(F("Savetype Error"), true); - } -} - -// Delete all 8 flashram banks -void eraseFram() { - if (saveType == 4) { - print_Msg(F("Erasing...")); - display_Update(); - - // Init fram - initFram(); - - // Erase fram - // 0x4B00007F 0x4B0000FF 0x4B00017F 0x4B0001FF 0x4B00027F 0x4B0002FF 0x4B00037F 0x4B0003FF - for (unsigned long bank = 0x4B00007F; bank < 0x4B00047F; bank += 0x80) { - sendFramCmd(bank); - delay(10); - // FRAM_ERASE_MODE_CMD - sendFramCmd(0x78000000); - delay(10); - // FRAM_EXECUTE_CMD - sendFramCmd(0xD2000000); - while (waitForFram(flashramType)) { - delay(1); - } - } - } - else { - print_Error(F("Savetype Error"), true); - } -} - -// Read flashram -void readFram(byte flashramType) { - if (saveType == 4) { - // Put flashram into read mode - // FRAM_READ_MODE_CMD - sendFramCmd(0xF0000000); - // Read Flashram - readSram(131072, flashramType); - } - else { - print_Error(F("Savetype Error"), true); - } -} - -// Verify flashram -unsigned long verifyFram(byte flashramType) { - // Put flashram into read mode - // FRAM_READ_MODE_CMD - sendFramCmd(0xF0000000); - writeErrors = verifySram(131072, flashramType); - return writeErrors; -} - -// Blankcheck flashram -unsigned long blankcheck_N64(byte flashramType) { - writeErrors = 0; - - int offset = 512; - int bufferSize = 512; - if (flashramType == 2) { - offset = 64; - bufferSize = 128; - } - - // Put flashram into read mode - // FRAM_READ_MODE_CMD - sendFramCmd(0xF0000000); - - // Read Flashram - for (unsigned long currByte = sramBase; currByte < (sramBase + (131072 / flashramType)); currByte += offset) { - // Set the address for the next 512 bytes - setAddress_N64(currByte); - - for (int c = 0; c < bufferSize; c += 2) { - // split word - word myWord = readWord_N64(); - byte loByte = myWord & 0xFF; - byte hiByte = myWord >> 8; - - // write to buffer - sdBuffer[c] = hiByte; - sdBuffer[c + 1] = loByte; - } - // Check sdBuffer content against file on sd card - for (int i = 0; i < bufferSize; i++) { - if (0xFF != sdBuffer[i]) { - writeErrors++; - } - } - } - // Return 0 if verified ok, or number of errors - return writeErrors; -} - -// Wait until current operation is done -byte waitForFram(byte flashramType) { - byte framStatus = 0; - byte statusMXL1100[] = {0x11, 0x11, 0x80, 0x01, 0x00, 0xC2, 0x00, 0x1E}; - byte statusMXL1101[] = {0x11, 0x11, 0x80, 0x01, 0x00, 0xC2, 0x00, 0x1D}; - byte statusMN63F81[] = {0x11, 0x11, 0x80, 0x01, 0x00, 0x32, 0x00, 0xF1}; - - // FRAM_STATUS_MODE_CMD - sendFramCmd(0xE1000000); - delay(1); - - // Set address to Fram status register - setAddress_N64(0x08000000); - - // Read Status - for (byte c = 0; c < 8; c += 2) { - // split word - word myWord = readWord_N64(); - byte loByte = myWord & 0xFF; - byte hiByte = myWord >> 8; - - // write to buffer - sdBuffer[c] = hiByte; - sdBuffer[c + 1] = loByte; - } - - if (flashramType == 2) { - for (byte c = 0; c < 8; c++) { - if (statusMXL1100[c] != sdBuffer[c]) { - framStatus = 1; - } - } - } - else if (flashramType == 1) { - //MX29L1101 - if (MN63F81MPN == false) { - for (byte c = 0; c < 8; c++) { - if (statusMXL1101[c] != sdBuffer[c]) { - framStatus = 1; - } - } - } - //MN63F81MPN - else if (MN63F81MPN == true) { - for (byte c = 0; c < 8; c++) { - if (statusMN63F81[c] != sdBuffer[c]) { - framStatus = 1; - } - } - } - } - return framStatus; -} - -// Get flashram type -void getFramType() { - - // FRAM_STATUS_MODE_CMD - sendFramCmd(0xE1000000); - delay(10); - - // Set address to Fram status register - setAddress_N64(0x08000000); - - // Read Status - for (byte c = 0; c < 8; c += 2) { - // split word - word myWord = readWord_N64(); - byte loByte = myWord & 0xFF; - byte hiByte = myWord >> 8; - - // write to buffer - sdBuffer[c] = hiByte; - sdBuffer[c + 1] = loByte; - } - //MX29L1100 - if (sdBuffer[7] == 0x1e ) { - flashramType = 2; - println_Msg(F("Type: MX29L1100")); - display_Update(); - } - //MX29L1101 - else if (sdBuffer[7] == 0x1d ) { - flashramType = 1; - MN63F81MPN = false; - println_Msg(F("Type: MX29L1101")); - display_Update(); - } - //MN63F81MPN - else if (sdBuffer[7] == 0xf1 ) { - flashramType = 1; - MN63F81MPN = true; - println_Msg(F("Type: MN63F81MPN")); - display_Update(); - } - // Type unknown - else { - for (byte c = 0; c < 8; c++) { - print_Msg(sdBuffer[c], HEX); - print_Msg(F(", ")); - } - print_Error(F("Flashram unknown"), true); - } -} - -/****************************************** - Rom functions -*****************************************/ -// Read rom and save to the SD card -void readRom_N64() { - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".Z64"); - - // create a new folder - EEPROM_readAnything(10, foldern); - sprintf(folder, "N64/ROM/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - display_Clear(); - print_Msg(F("Saving to ")); - print_Msg(folder); - println_Msg(F("/...")); - display_Update(); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - -readn64rom: - // Open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("SD Error"), true); - } - - for (unsigned long currByte = romBase; currByte < (romBase + (cartSize * 1024 * 1024)); currByte += 512) { - // Blink led - if (currByte % 16384 == 0) - PORTB ^= (1 << 4); - - // Set the address for the next 512 bytes - setAddress_N64(currByte); - - for (int c = 0; c < 512; c += 2) { - // split word - word myWord = readWord_N64(); - byte loByte = myWord & 0xFF; - byte hiByte = myWord >> 8; - - // write to buffer - sdBuffer[c] = hiByte; - sdBuffer[c + 1] = loByte; - } - myFile.write(sdBuffer, 512); - } - // Close the file: - myFile.close(); - - if (n64crc) { -calcn64crc: - // Calculate Checksum and convert to string - println_Msg(F("Calculating CRC..")); - display_Update(); - char crcStr[9]; - sprintf(crcStr, "%08lx", crc32()); - // Print checksum - println_Msg(crcStr); - display_Update(); - - // Search n64.txt for crc - if (searchCRC(crcStr)) { - // Dump was a known good rom - println_Msg(F("Checksum matches")); - } - else { - // Dump was bad or unknown - rgb.setColor(255, 0, 0); - // N64 CRC32 error Menu - unsigned char CRCMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsN64CRC, 4); - - char tempStr3[20]; - strcpy(tempStr3, "CRC ERROR "); - strcat(tempStr3, crcStr); - - CRCMenu = question_box(tempStr3, menuOptions, 4, 1); - - // wait for user choice to come back from the question box menu - switch (CRCMenu) - { - case 0: - // Change to last directory - sd.chdir(folder); - display_Clear(); - // Calculate CRC again - rgb.setColor(0, 0, 0); - goto calcn64crc; - break; - - case 1: - // Change to last directory - sd.chdir(folder); - // Delete old file - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("SD Error"), true); - } - if (!myFile.remove()) { - print_Error(F("Delete Error"), true); - } - // Dump again - display_Clear(); - println_Msg(F("Reading Rom...")); - display_Update(); - rgb.setColor(0, 0, 0); - goto readn64rom; - break; - - case 2: - // Return to N64 menu - break; - - case 3: - // Reset - asm volatile (" jmp 0"); - break; - } - } - display_Update(); - } - println_Msg(F("Done.")); - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); -} - -/****************************************** - N64 Repro Flashrom Functions - *****************************************/ -void flashRepro_N64() { - // Check flashrom ID's - idFlashrom_N64(); - - // If the ID is known continue - if (cartSize != 0) { - // Print flashrom name - if ((strcmp(flashid, "227E") == 0) && (strcmp(cartID, "2201") == 0)) { - print_Msg(F("Spansion S29GL256N")); - if (cartSize == 64) - println_Msg(F(" x2")); - else - println_Msg(""); - } - else if ((strcmp(flashid, "227E") == 0) && (strcmp(cartID, "2101") == 0)) { - print_Msg(F("Spansion S29GL128N")); - } - else if ((strcmp(flashid, "22C9") == 0) || (strcmp(flashid, "22CB") == 0)) { - print_Msg(F("Macronix MX29LV640")); - if (cartSize == 16) - println_Msg(F(" x2")); - else - println_Msg(""); - } - else if (strcmp(flashid, "8816") == 0) - println_Msg(F("Intel 4400L0ZDQ0")); - 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")); - else if ((strcmp(flashid, "227E") == 0) && (strcmp(cartID, "3901") == 0)) - println_Msg(F("Intel 512M29EW")); - - // Print info - print_Msg(F("ID: ")); - print_Msg(flashid); - print_Msg(F(" Size: ")); - print_Msg(cartSize); - println_Msg(F("MB")); - println_Msg(""); - println_Msg(F("This will erase your")); - println_Msg(F("Repro Cartridge.")); - println_Msg(F("Attention: Use 3.3V!")); - println_Msg(""); - println_Msg(F("Press Button")); - display_Update(); - wait(); - - // Launch file browser - filePath[0] = '\0'; - sd.chdir("/"); - fileBrowser("Select z64 file"); - display_Clear(); - 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(); - print_Msg(F("File size: ")); - print_Msg(fileSize / 1048576); - println_Msg(F("MB")); - display_Update(); - - // Compare file size to flashrom size - if ((fileSize / 1048576) > cartSize) { - print_Error(F("File too big"), true); - } - - // Erase needed sectors - 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(); - resetIntel4400_N64(); - } - else if ((strcmp(flashid, "22C9") == 0) || (strcmp(flashid, "22CB") == 0)) { - // Macronix MX29LV640, C9 is top boot and CB is bottom boot block - eraseFlashrom_N64(0x8000); - } - - // Check if erase was successful - if (blankcheckFlashrom_N64()) { - // Write flashrom - println_Msg(F("OK")); - print_Msg(F("Writing ")); - println_Msg(filePath); - display_Update(); - - - if ((strcmp(cartID, "3901") == 0) && (strcmp(flashid, "227E") == 0)) { - // Intel 512M29EW(64MB) with 0x20000 sector size and 128 byte buffer - writeFlashBuffer_N64(0x20000, 128); - } - else if (strcmp(flashid, "227E") == 0) { - // Spansion S29GL128N/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(); - } - else if ((strcmp(flashid, "8813") == 0) || (strcmp(flashid, "8816") == 0)) { - // Intel 4400L0ZDQ0 - writeIntel4400_N64(); - resetIntel4400_N64(); - } - - // Close the file: - myFile.close(); - - // Verify - print_Msg(F("Verifying...")); - display_Update(); - writeErrors = verifyFlashrom_N64(); - if (writeErrors == 0) { - println_Msg(F("OK")); - display_Update(); - } - else { - print_Msg(writeErrors); - print_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - } - else { - // Close the file - myFile.close(); - print_Error(F("failed"), false); - } - } - else { - print_Error(F("Can't open file"), false); - } - } - // If the ID is unknown show error message - else { - print_Msg(F("Vendor: ")); - println_Msg(vendorID); - print_Msg(F("ID: ")); - print_Msg(flashid); - print_Msg(F(" ")); - println_Msg(cartID); - print_Error(F("Unknown flashrom"), false); - } - - println_Msg(F("Press Button...")); - display_Update(); - wait(); - display_Clear(); - display_Update(); -} - -// Reset to read mode -void resetIntel4400_N64() { - for (unsigned long currPartition = 0; currPartition < (cartSize * 0x100000); currPartition += 0x20000) { - setAddress_N64(romBase + currPartition); - writeWord_N64(0xFF); - } -} - -// 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 - setAddress_N64(flashBase); - writeWord_N64(0xF0); - delay(100); -} - -void idFlashrom_N64() { - // Set size to 0 if no ID is found - cartSize = 0; - - // 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); - - // Read 1 byte vendor ID - setAddress_N64(romBase); - sprintf(vendorID, "%02X", readWord_N64()); - // Read 2 bytes flashrom ID - sprintf(flashid, "%04X", readWord_N64()); - // Read 2 bytes secondary flashrom ID - setAddress_N64(romBase + 0x1C); - sprintf(cartID, "%04X", ((readWord_N64() << 8) | (readWord_N64() & 0xFF))); - - // Spansion S29GL256N(32MB/64MB) with either one or two flashrom chips - if ((strcmp(cartID, "2201") == 0) && (strcmp(flashid, "227E") == 0)) { - cartSize = 32; - - // Reset flashrom - resetFlashrom_N64(romBase); - - // Test for second flashrom chip at 0x2000000 (32MB) - 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); - - char tempID[5]; - setAddress_N64(romBase + 0x2000000); - // Read manufacturer ID - readWord_N64(); - // Read flashrom ID - sprintf(tempID, "%04X", readWord_N64()); - - // Check if second flashrom chip is present - if (strcmp(tempID, "227E") == 0) { - cartSize = 64; - } - resetFlashrom_N64(romBase + 0x2000000); - } - - // Macronix MX29LV640(8MB/16MB) with either one or two flashrom chips - else if ((strcmp(flashid, "22C9") == 0) || (strcmp(flashid, "22CB") == 0)) { - cartSize = 8; - - resetFlashrom_N64(romBase + 0x800000); - - // Test for second flashrom chip at 0x800000 (8MB) - setAddress_N64(romBase + 0x800000 + (0x555 << 1)); - writeWord_N64(0xAA); - setAddress_N64(romBase + 0x800000 + (0x2AA << 1)); - writeWord_N64(0x55); - setAddress_N64(romBase + 0x800000 + (0x555 << 1)); - writeWord_N64(0x90); - - char tempID[5]; - setAddress_N64(romBase + 0x800000); - // Read manufacturer ID - readWord_N64(); - // Read flashrom ID - sprintf(tempID, "%04X", readWord_N64()); - - // Check if second flashrom chip is present - if ((strcmp(tempID, "22C9") == 0) || (strcmp(tempID, "22CB") == 0)) { - cartSize = 16; - } - resetFlashrom_N64(romBase + 0x800000); - } - - // Intel 4400L0ZDQ0 (64MB) - else if (strcmp(flashid, "8816") == 0) { - // Found first flashrom chip, set to 32MB - cartSize = 32; - resetIntel4400_N64(); - - // Test if second half of the flashrom might be hidden - 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); - - // Read manufacturer ID - setAddress_N64(romBase + 0x2000000); - readWord_N64(); - // Read flashrom ID - sprintf(cartID, "%04X", readWord_N64()); - if (strcmp(cartID, "8813") == 0) { - cartSize = 64; - strncpy(flashid , cartID, 5); - } - resetIntel4400_N64(); - // Empty cartID string - cartID[0] = '\0'; - } - - //Fujitsu MSP55LV512/Spansion S29GL512N (64MB) - else if ((strcmp(cartID, "2301") == 0) && (strcmp(flashid, "227E") == 0)) { - cartSize = 64; - // Reset flashrom - resetFlashrom_N64(romBase); - } - - // Spansion S29GL128N(16MB) with one flashrom chip - else if ((strcmp(cartID, "2101") == 0) && (strcmp(flashid, "227E") == 0)) { - cartSize = 16; - // Reset flashrom - resetFlashrom_N64(romBase); - } - - // Intel 512M29EW(64MB) with one flashrom chip - else if ((strcmp(cartID, "3901") == 0) && (strcmp(flashid, "227E") == 0)) { - cartSize = 64; - // Reset flashrom - resetFlashrom_N64(romBase); - } - - // Unknown 227E type - else if (strcmp(flashid, "227E") == 0) { - cartSize = 0; - // Reset flashrom - resetFlashrom_N64(romBase); - } - - //Test for Fujitsu MSP55LV100S (64MB) - else { - // Send flashrom ID command - setAddress_N64(romBase + (0x555 << 1)); - writeWord_N64(0xAAAA); - setAddress_N64(romBase + (0x2AA << 1)); - writeWord_N64(0x5555); - setAddress_N64(romBase + (0x555 << 1)); - writeWord_N64(0x9090); - - setAddress_N64(romBase); - // Read 1 byte vendor ID - readWord_N64(); - // Read 2 bytes flashrom ID - sprintf(cartID, "%04X", readWord_N64()); - - if (strcmp(cartID, "7E7E") == 0) { - resetMSP55LV100_N64(romBase); - cartSize = 64; - strncpy(flashid , cartID, 5); - } - } -} - -// Erase Intel flashrom -void eraseIntel4400_N64() { - unsigned long flashBase = romBase; - - print_Msg(F("Erasing...")); - display_Update(); - - // If the game is smaller than 32Mbit only erase the needed blocks - unsigned long lastBlock = 0x1FFFFFF; - if (fileSize < 0x1FFFFFF) - lastBlock = fileSize; - - // Erase 4 blocks with 16kwords each - for (unsigned long currBlock = 0x0; currBlock < 0x1FFFF; currBlock += 0x8000) { - // Unlock block command - setAddress_N64(flashBase + currBlock); - writeWord_N64(0x60); - setAddress_N64(flashBase + currBlock); - writeWord_N64(0xD0); - // Erase command - setAddress_N64(flashBase + currBlock); - writeWord_N64(0x20); - setAddress_N64(flashBase + currBlock); - writeWord_N64(0xD0); - - // Read the status register - setAddress_N64(flashBase + currBlock); - word statusReg = readWord_N64(); - while ((statusReg | 0xFF7F) != 0xFFFF) { - setAddress_N64(flashBase + currBlock); - statusReg = readWord_N64(); - } - } - - // Erase up to 255 blocks with 64kwords each - for (unsigned long currBlock = 0x20000; currBlock < lastBlock; currBlock += 0x1FFFF) { - // Unlock block command - setAddress_N64(flashBase + currBlock); - writeWord_N64(0x60); - setAddress_N64(flashBase + currBlock); - writeWord_N64(0xD0); - // Erase command - setAddress_N64(flashBase + currBlock); - writeWord_N64(0x20); - setAddress_N64(flashBase + currBlock); - writeWord_N64(0xD0); - - // Read the status register - setAddress_N64(flashBase + currBlock); - word statusReg = readWord_N64(); - while ((statusReg | 0xFF7F) != 0xFFFF) { - setAddress_N64(flashBase + currBlock); - statusReg = readWord_N64(); - } - - // Blink led - PORTB ^= (1 << 4); - } - - // Check if we should erase the second chip too - if ((cartSize = 64) && (fileSize > 0x2000000)) { - // Switch base address to second chip - flashBase = romBase + 0x2000000; - - // 255 blocks with 64kwords each - for (unsigned long currBlock = 0x0; currBlock < 0x1FDFFFF; currBlock += 0x1FFFF) { - // Unlock block command - setAddress_N64(flashBase + currBlock); - writeWord_N64(0x60); - setAddress_N64(flashBase + currBlock); - writeWord_N64(0xD0); - // Erase command - setAddress_N64(flashBase + currBlock); - writeWord_N64(0x20); - setAddress_N64(flashBase + currBlock); - writeWord_N64(0xD0); - - // Read the status register - setAddress_N64(flashBase + currBlock); - word statusReg = readWord_N64(); - while ((statusReg | 0xFF7F) != 0xFFFF) { - setAddress_N64(flashBase + currBlock); - statusReg = readWord_N64(); - } - - // Blink led - PORTB ^= (1 << 4); - } - - // 4 blocks with 16kword each - for (unsigned long currBlock = 0x1FE0000; currBlock < 0x1FFFFFF; currBlock += 0x8000) { - // Unlock block command - setAddress_N64(flashBase + currBlock); - writeWord_N64(0x60); - setAddress_N64(flashBase + currBlock); - writeWord_N64(0xD0); - // Erase command - setAddress_N64(flashBase + currBlock); - writeWord_N64(0x20); - setAddress_N64(flashBase + currBlock); - writeWord_N64(0xD0); - - // Read the status register - setAddress_N64(flashBase + currBlock); - word statusReg = readWord_N64(); - while ((statusReg | 0xFF7F) != 0xFFFF) { - setAddress_N64(flashBase + currBlock); - statusReg = readWord_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; - - print_Msg(F("Erasing...")); - display_Update(); - - for (unsigned long currSector = 0; currSector < fileSize; currSector += sectorSize) { - // Blink led - PORTB ^= (1 << 4); - - // Spansion S29GL256N(32MB/64MB) with two flashrom chips - if ((currSector == 0x2000000) && (strcmp(cartID, "2201") == 0) && (strcmp(flashid, "227E") == 0)) { - // Change to second chip - flashBase = romBase + 0x2000000; - } - // Macronix MX29LV640(8MB/16MB) with two flashrom chips - else if ((currSector == 0x800000) && ((strcmp(flashid, "22C9") == 0) || (strcmp(flashid, "22CB") == 0))) { - flashBase = romBase + 0x800000; - } - - // Send Erase Command - setAddress_N64(flashBase + (0x555 << 1)); - writeWord_N64(0xAA); - setAddress_N64(flashBase + (0x2AA << 1)); - writeWord_N64(0x55); - setAddress_N64(flashBase + (0x555 << 1)); - writeWord_N64(0x80); - setAddress_N64(flashBase + (0x555 << 1)); - writeWord_N64(0xAA); - setAddress_N64(flashBase + (0x2AA << 1)); - writeWord_N64(0x55); - setAddress_N64(romBase + currSector); - writeWord_N64(0x30); - - // Read the status register - setAddress_N64(romBase + currSector); - word statusReg = readWord_N64(); - while ((statusReg | 0xFF7F) != 0xFFFF) { - setAddress_N64(romBase + currSector); - statusReg = readWord_N64(); - } - } -} - -boolean blankcheckFlashrom_N64() { - for (unsigned long currByte = romBase; currByte < romBase + fileSize; currByte += 512) { - // Blink led - if (currByte % 131072 == 0) - PORTB ^= (1 << 4); - - // Set the address - setAddress_N64(currByte); - - for (int c = 0; c < 512; c += 2) { - if (readWord_N64() != 0xFFFF) { - return 0; - } - } - } - return 1; -} - -// Write Intel flashrom -void writeIntel4400_N64() { - for (unsigned long currSector = 0; currSector < fileSize; currSector += 131072) { - // Blink led - PORTB ^= (1 << 4); - - // Write to flashrom - for (unsigned long currSdBuffer = 0; currSdBuffer < 131072; currSdBuffer += 512) { - // Fill SD buffer - myFile.read(sdBuffer, 512); - - // Write 32 words at a time - for (int currWriteBuffer = 0; currWriteBuffer < 512; currWriteBuffer += 64) { - // Buffered program command - setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer); - writeWord_N64(0xE8); - - // Check Status register - setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer); - word statusReg = readWord_N64(); - while ((statusReg | 0xFF7F) != 0xFFFF) { - setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer); - statusReg = readWord_N64(); - } - - // Write word count (minus 1) - setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer); - writeWord_N64(0x1F); - - // Write buffer - for (byte currByte = 0; currByte < 64; currByte += 2) { - // Join two bytes into one word - word currWord = ( ( sdBuffer[currWriteBuffer + currByte] & 0xFF ) << 8 ) | ( sdBuffer[currWriteBuffer + currByte + 1] & 0xFF ); - setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + currByte); - writeWord_N64(currWord); - } - - // Write Buffer to Flash - setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + 62); - writeWord_N64(0xD0); - - // Read the status register at last written address - setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + 62); - statusReg = readWord_N64(); - while ((statusReg | 0xFF7F) != 0xFFFF) { - setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + 62); - statusReg = readWord_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) { - unsigned long flashBase = romBase; - - for (unsigned long currSector = 0; currSector < fileSize; currSector += sectorSize) { - // Blink led - PORTB ^= (1 << 4); - - // Spansion S29GL256N(32MB/64MB) with two flashrom chips - if ((currSector == 0x2000000) && (strcmp(cartID, "2201") == 0)) { - flashBase = romBase + 0x2000000; - } - - // 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 += bufferSize) { - - // 2 unlock commands - setAddress_N64(flashBase + (0x555 << 1)); - writeWord_N64(0xAA); - setAddress_N64(flashBase + (0x2AA << 1)); - writeWord_N64(0x55); - - // Write buffer load command at sector address - setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer); - writeWord_N64(0x25); - // Write word count (minus 1) at sector address - setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer); - writeWord_N64((bufferSize / 2) - 1); - - // Define variable before loop so we can use it later when reading the status register - word currWord; - - for (byte currByte = 0; currByte < bufferSize; 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 + bufferSize - 2); - writeWord_N64(0x29); - - // Read the status register at last written address - setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + bufferSize - 2); - word statusReg = readWord_N64(); - while ((statusReg | 0xFF7F) != (currWord | 0xFF7F)) { - setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + bufferSize - 2); - statusReg = readWord_N64(); - } - } - } - } -} - -// Write MX29LV640 flashrom without write buffer -void writeFlashrom_N64() { - unsigned long flashBase = romBase; - - for (unsigned long currSector = 0; currSector < fileSize; currSector += 0x8000) { - // Blink led - PORTB ^= (1 << 4); - - // Macronix MX29LV640(8MB/16MB) with two flashrom chips - if (currSector == 0x800000) { - flashBase = romBase + 0x800000; - } - - // Write to flashrom - for (unsigned long currSdBuffer = 0; currSdBuffer < 0x8000; 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 ); - // 2 unlock commands - setAddress_N64(flashBase + (0x555 << 1)); - writeWord_N64(0xAA); - setAddress_N64(flashBase + (0x2AA << 1)); - writeWord_N64(0x55); - // Program command - setAddress_N64(flashBase + (0x555 << 1)); - writeWord_N64(0xA0); - // Write word - setAddress_N64(romBase + currSector + currSdBuffer + currByte); - writeWord_N64(currWord); - - // Read the status register - setAddress_N64(romBase + currSector + currSdBuffer + currByte); - word statusReg = readWord_N64(); - while ((statusReg | 0xFF7F) != (currWord | 0xFF7F)) { - setAddress_N64(romBase + currSector + currSdBuffer + currByte); - statusReg = readWord_N64(); - } - } - } - } -} - -unsigned long verifyFlashrom_N64() { - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - writeErrors = 0; - - for (unsigned long currSector = 0; currSector < fileSize; currSector += 131072) { - // Blink led - PORTB ^= (1 << 4); - 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 + currSector + currSdBuffer + currByte); - // Compare both - if (readWord_N64() != currWord) { - writeErrors++; - // Abord if too many errors - if (writeErrors > 20) { - print_Msg(F("More than ")); - // Close the file: - myFile.close(); - return writeErrors; - } - } - } - } - } - // Close the file: - myFile.close(); - return writeErrors; - } - else { - println_Msg(F("Can't open file")); - display_Update(); - return 9999; - } -} - -//****************************************** -// End of File +//****************************************** +// NINTENDO 64 MODULE +//****************************************** + +/****************************************** + Defines + *****************************************/ +// These two macros toggle the eepDataPin/ControllerDataPin between input and output +// External 1K pull-up resistor from eepDataPin to VCC required +// 0x10 = 00010000 -> Port H Pin 4 +#define N64_HIGH DDRH &= ~0x10 +#define N64_LOW DDRH |= 0x10 +// Read the current state(0/1) of the eepDataPin +#define N64_QUERY (PINH & 0x10) + +/****************************************** + Variables + *****************************************/ +// Received N64 Eeprom data bits, 1 page +bool tempBits[65]; +int eepPages; + +// N64 Controller +// 256 bits of received Controller data +char N64_raw_dump[257]; +// Array that holds one Controller Pak block of 32 bytes +byte myBlock[33]; +String rawStr = ""; // above char array read into a string +struct { + char stick_x; + char stick_y; +} +N64_status; +//stings that hold the buttons +String button = "N/A"; +String lastbutton = "N/A"; + +// Rom base address +unsigned long romBase = 0x10000000; + +// Flashram type +byte flashramType = 1; +boolean MN63F81MPN = false; + +/****************************************** + Menu +*****************************************/ +// N64 start menu +static const char n64MenuItem1[] PROGMEM = "Cart Slot"; +static const char n64MenuItem2[] PROGMEM = "Controller"; +static const char n64MenuItem3[] PROGMEM = "Flash Repro"; +static const char* const menuOptionsN64[] PROGMEM = {n64MenuItem1, n64MenuItem2, n64MenuItem3}; + +// N64 controller menu items +static const char N64ContMenuItem1[] PROGMEM = "Test Controller"; +static const char N64ContMenuItem2[] PROGMEM = "Read ControllerPak"; +static const char N64ContMenuItem3[] PROGMEM = "Write ControllerPak"; +static const char N64ContMenuItem4[] PROGMEM = "Reset"; +static const char* const menuOptionsN64Controller[] PROGMEM = {N64ContMenuItem1, N64ContMenuItem2, N64ContMenuItem3, N64ContMenuItem4}; + +// N64 cart menu items +static const char N64CartMenuItem1[] PROGMEM = "Read Rom"; +static const char N64CartMenuItem2[] PROGMEM = "Read Save"; +static const char N64CartMenuItem3[] PROGMEM = "Write Save"; +static const char N64CartMenuItem4[] PROGMEM = "Force Savetype"; +static const char N64CartMenuItem5[] PROGMEM = "Reset"; +static const char* const menuOptionsN64Cart[] PROGMEM = {N64CartMenuItem1, N64CartMenuItem2, N64CartMenuItem3, N64CartMenuItem4, N64CartMenuItem5}; + +// N64 CRC32 error menu items +static const char N64CRCMenuItem1[] PROGMEM = "Recalc CRC"; +static const char N64CRCMenuItem2[] PROGMEM = "Redump"; +static const char N64CRCMenuItem3[] PROGMEM = "Ignore"; +static const char N64CRCMenuItem4[] PROGMEM = "Reset"; +static const char* const menuOptionsN64CRC[] PROGMEM = {N64CRCMenuItem1, N64CRCMenuItem2, N64CRCMenuItem3, N64CRCMenuItem4}; + +// Rom menu +static const char N64RomItem1[] PROGMEM = "4MB"; +static const char N64RomItem2[] PROGMEM = "8MB"; +static const char N64RomItem3[] PROGMEM = "12MB"; +static const char N64RomItem4[] PROGMEM = "16MB"; +static const char N64RomItem5[] PROGMEM = "32MB"; +static const char N64RomItem6[] PROGMEM = "64MB"; +static const char* const romOptionsN64[] PROGMEM = {N64RomItem1, N64RomItem2, N64RomItem3, N64RomItem4, N64RomItem5, N64RomItem6}; + +// Save menu +static const char N64SaveItem1[] PROGMEM = "None"; +static const char N64SaveItem2[] PROGMEM = "4K EEPROM"; +static const char N64SaveItem3[] PROGMEM = "16K EEPROM"; +static const char N64SaveItem4[] PROGMEM = "SRAM"; +static const char N64SaveItem5[] PROGMEM = "FLASHRAM"; +static const char* const saveOptionsN64[] PROGMEM = {N64SaveItem1, N64SaveItem2, N64SaveItem3, N64SaveItem4, N64SaveItem5}; + +// N64 start menu +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; + + } +} + +// N64 Controller Menu +void n64ControllerMenu() { + // create menu with title and 4 options to choose from + unsigned char mainMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsN64Controller, 4); + mainMenu = question_box("N64 Controller", menuOptions, 4, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + case 0: + display_Clear(); + display_Update(); + readController(); + break; + + case 1: + display_Clear(); + display_Update(); + readMPK(); + println_Msg(F("")); + println_Msg(F("Press Button.")); + display_Update(); + wait(); + break; + + case 2: + display_Clear(); + display_Update(); + // Change to root + filePath[0] = '\0'; + sd.chdir("/"); + // Launch file browser + fileBrowser("Select mpk file"); + display_Clear(); + display_Update(); + writeMPK(); + verifyMPK(); + println_Msg(F("")); + println_Msg(F("Press Button.")); + display_Update(); + wait(); + break; + + case 3: + asm volatile (" jmp 0"); + break; + } +} + +// N64 Cartridge Menu +void n64CartMenu() { + // create menu with title and 4 options to choose from + unsigned char mainMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsN64Cart, 5); + mainMenu = question_box("N64 Cart Reader", menuOptions, 5, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + case 0: + sd.chdir("/"); + readRom_N64(); + break; + + case 1: + sd.chdir("/"); + display_Clear(); + + if (saveType == 1) { + println_Msg(F("Reading Sram...")); + display_Update(); + readSram(32768, 1); + } + else if (saveType == 4) { + getFramType(); + println_Msg(F("Reading Flashram...")); + display_Update(); + readFram(flashramType); + } + else if ((saveType == 5) || (saveType == 6)) { + println_Msg(F("Reading Eep...")); + display_Update(); + readEeprom(); + } + else { + print_Error(F("Savetype Error"), false); + } + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + break; + + case 2: + filePath[0] = '\0'; + sd.chdir("/"); + if (saveType == 1) { + // Launch file browser + fileBrowser("Select sra file"); + display_Clear(); + + writeSram(32768); + writeErrors = verifySram(32768, 1); + if (writeErrors == 0) { + println_Msg(F("Sram verified OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + } + else if (saveType == 4) { + // Launch file browser + fileBrowser("Select fla file"); + display_Clear(); + getFramType(); + writeFram(flashramType); + print_Msg(F("Verifying...")); + display_Update(); + writeErrors = verifyFram(flashramType); + if (writeErrors == 0) { + println_Msg(F("OK")); + display_Update(); + } + else { + println_Msg(""); + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + } + else if ((saveType == 5) || (saveType == 6)) { + // Launch file browser + fileBrowser("Select eep file"); + display_Clear(); + + writeEeprom(); + writeErrors = verifyEeprom(); + if (writeErrors == 0) { + println_Msg(F("Eeprom verified OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + } + else { + print_Error(F("Savetype Error"), false); + } + println_Msg(F("Press Button...")); + display_Update(); + wait(); + break; + + case 3: + // create submenu with title and 6 options to choose from + unsigned char N64SaveMenu; + // Copy menuOptions out of progmem + convertPgm(saveOptionsN64, 5); + N64SaveMenu = question_box("Select save type", menuOptions, 5, 0); + + // wait for user choice to come back from the question box menu + switch (N64SaveMenu) + { + case 0: + // None + saveType = 0; + break; + + case 1: + // 4K EEPROM + saveType = 5; + eepPages = 64; + break; + + case 2: + // 16K EEPROM + saveType = 6; + eepPages = 256; + break; + + case 3: + // SRAM + saveType = 1; + break; + + case 4: + // FLASHRAM + saveType = 4; + break; + } + break; + + case 4: + asm volatile (" jmp 0"); + break; + } +} + +/****************************************** + Setup + *****************************************/ +void setup_N64_Controller() { + // Output a low signal + PORTH &= ~(1 << 4); + // Set Controller Data Pin(PH4) to Input + DDRH &= ~(1 << 4); +} + +void setup_N64_Cart() { + // Set Address Pins to Output and set them low + //A0-A7 + DDRF = 0xFF; + PORTF = 0x00; + //A8-A15 + DDRK = 0xFF; + PORTK = 0x00; + + // Set Control Pins to Output RESET(PH0) WR(PH5) RD(PH6) aleL(PC0) aleH(PC1) + DDRH |= (1 << 0) | (1 << 5) | (1 << 6); + DDRC |= (1 << 0) | (1 << 1); + // Pull RESET(PH0) low until we are ready + PORTH &= ~(1 << 0); + // Output a high signal on WR(PH5) RD(PH6), pins are active low therefore everything is disabled now + PORTH |= (1 << 5) | (1 << 6); + // Pull aleL(PC0) low and aleH(PC1) high + PORTC &= ~(1 << 0); + PORTC |= (1 << 1); + + // Set Eeprom Clock Pin(PH1) to Output + DDRH |= (1 << 1); + // Output a high signal + PORTH |= (1 << 1); + + // Set Eeprom Data Pin(PH4) to Input + DDRH &= ~(1 << 4); + // Activate Internal Pullup Resistors + //PORTH |= (1 << 4); + + // Set sram base address + sramBase = 0x08000000; + + // Wait until all is stable + delay(300); + + // Pull RESET(PH0) high to start eeprom + PORTH |= (1 << 0); +} + +/****************************************** + Low level functions + *****************************************/ +// Switch Cartridge address/data pins to write +void adOut_N64() { + //A0-A7 + DDRF = 0xFF; + PORTF = 0x00; + //A8-A15 + DDRK = 0xFF; + PORTK = 0x00; +} + +// Switch Cartridge address/data pins to read +void adIn_N64() { + //A0-A7 + DDRF = 0x00; + //A8-A15 + DDRK = 0x00; +} + +// Set Cartridge address +void setAddress_N64(unsigned long myAddress) { + // Set address pins to output + adOut_N64(); + + // Split address into two words + word myAdrLowOut = myAddress & 0xFFFF; + word myAdrHighOut = myAddress >> 16; + + // Switch WR(PH5) RD(PH6) ale_L(PC0) ale_H(PC1) to high (since the pins are active low) + PORTH |= (1 << 5) | (1 << 6); + PORTC |= (1 << 1); + __asm__("nop\n\t"); + PORTC |= (1 << 0); + + // Output high part to address pins + PORTF = myAdrHighOut & 0xFF; + PORTK = (myAdrHighOut >> 8) & 0xFF; + + // Leave ale_H high for additional 62.5ns + __asm__("nop\n\t"); + + // Pull ale_H(PC1) low + PORTC &= ~(1 << 1); + + // Output low part to address pins + PORTF = myAdrLowOut & 0xFF; + PORTK = (myAdrLowOut >> 8) & 0xFF; + + // Leave ale_L high for ~125ns + __asm__("nop\n\t""nop\n\t"); + + // Pull ale_L(PC0) low + PORTC &= ~(1 << 0); + + // Wait ~600ns just to be sure address is set + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Set data pins to input + adIn_N64(); +} + +// Read one word out of the cartridge +word readWord_N64() { + // Pull read(PH6) low + PORTH &= ~(1 << 6); + + // Wait ~310ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Join bytes from PINF and PINK into a word + word tempWord = ( ( PINK & 0xFF ) << 8 ) | ( PINF & 0xFF ); + + // Pull read(PH6) high + PORTH |= (1 << 6); + + // Wait 62.5ns + __asm__("nop\n\t"); + return tempWord; +} + +// Write one word to data pins of the cartridge +void writeWord_N64(word myWord) { + // Set address pins to output + adOut_N64(); + + // Output word to AD0-AD15 + PORTF = myWord & 0xFF; + PORTK = (myWord >> 8) & 0xFF; + + // Wait ~62.5ns + __asm__("nop\n\t"); + + // Pull write(PH5) low + PORTH &= ~(1 << 5); + + // Wait ~310ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Pull write(PH5) high + PORTH |= (1 << 5); + + // Wait ~125ns + __asm__("nop\n\t""nop\n\t"); + + // Set data pins to input + adIn_N64(); +} + +/****************************************** + N64 Controller CRC Functions + *****************************************/ +static word addrCRC(word address) { + // CRC table + word xor_table[16] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x1F, 0x0B, 0x16, 0x19, 0x07, 0x0E, 0x1C, 0x0D, 0x1A, 0x01 }; + word crc = 0; + // Make sure we have a valid address + address &= ~0x1F; + // Go through each bit in the address, and if set, xor the right value into the output + for (int i = 15; i >= 5; i--) { + // Is this bit set? + if ( ((address >> i) & 0x1)) { + crc ^= xor_table[i]; + } + } + // Just in case + crc &= 0x1F; + // Create a new address with the CRC appended + return address | crc; +} + +static byte dataCRC(byte * data) { + byte ret = 0; + for (byte i = 0; i <= 32; i++) { + for (byte j = 7; j >= 0; j--) { + int tmp = 0; + if (ret & 0x80) { + tmp = 0x85; + } + ret <<= 1; + if ( i < 32 ) { + if (data[i] & (0x01 << j)) { + ret |= 0x1; + } + } + ret ^= tmp; + } + } + return ret; +} + +/****************************************** + N64 Controller Protocol Functions + *****************************************/ +void N64_send(unsigned char *buffer, char length) { + // Send these bytes + char bits; + bool bit; + + // This routine is very carefully timed by examining the assembly output. + // Do not change any statements, it could throw the timings off + // + // We get 16 cycles per microsecond, which should be plenty, but we need to + // be conservative. Most assembly ops take 1 cycle, but a few take 2 + // + // I use manually constructed for-loops out of gotos so I have more control + // over the outputted assembly. I can insert nops where it was impossible + // with a for loop + + asm volatile (";Starting outer for loop"); +outer_loop: + { + asm volatile (";Starting inner for loop"); + bits = 8; +inner_loop: + { + // Starting a bit, set the line low + asm volatile (";Setting line to low"); + N64_LOW; // 1 op, 2 cycles + + asm volatile (";branching"); + if (*buffer >> 7) { + asm volatile (";Bit is a 1"); + // 1 bit + // remain low for 1us, then go high for 3us + // nop block 1 + asm volatile ("nop\nnop\nnop\nnop\nnop\n"); + + asm volatile (";Setting line to high"); + N64_HIGH; + + // nop block 2 + // we'll wait only 2us to sync up with both conditions + // at the bottom of the if statement + asm volatile ("nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + ); + + } + else { + asm volatile (";Bit is a 0"); + // 0 bit + // remain low for 3us, then go high for 1us + // nop block 3 + asm volatile ("nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\n"); + + asm volatile (";Setting line to high"); + N64_HIGH; + + // wait for 1us + asm volatile ("; end of conditional branch, need to wait 1us more before next bit"); + + } + // end of the if, the line is high and needs to remain + // high for exactly 16 more cycles, regardless of the previous + // branch path + + asm volatile (";finishing inner loop body"); + --bits; + if (bits != 0) { + // nop block 4 + // this block is why a for loop was impossible + asm volatile ("nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\n"); + // rotate bits + asm volatile (";rotating out bits"); + *buffer <<= 1; + + goto inner_loop; + } // fall out of inner loop + } + asm volatile (";continuing outer loop"); + // In this case: the inner loop exits and the outer loop iterates, + // there are /exactly/ 16 cycles taken up by the necessary operations. + // So no nops are needed here (that was lucky!) + --length; + if (length != 0) { + ++buffer; + goto outer_loop; + } // fall out of outer loop + } +} + +void N64_stop() { + // send a single stop (1) bit + // nop block 5 + asm volatile ("nop\nnop\nnop\nnop\n"); + N64_LOW; + // wait 1 us, 16 cycles, then raise the line + // 16-2=14 + // nop block 6 + asm volatile ("nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\n"); + N64_HIGH; +} + +void N64_get(word bitcount) { + // listen for the expected bitcount/8 bytes of data back from the controller and + // blast it out to the N64_raw_dump array, one bit per byte for extra speed. + asm volatile (";Starting to listen"); + unsigned char timeout; + char *bitbin = N64_raw_dump; + + // Again, using gotos here to make the assembly more predictable and + // optimization easier (please don't kill me) +read_loop: + timeout = 0x3f; + // wait for line to go low + while (N64_QUERY) { + if (!--timeout) + return; + } + // wait approx 2us and poll the line + asm volatile ( + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + "nop\nnop\nnop\nnop\nnop\n" + ); + *bitbin = N64_QUERY; + ++bitbin; + --bitcount; + if (bitcount == 0) + return; + + // wait for line to go high again + // it may already be high, so this should just drop through + timeout = 0x3f; + while (!N64_QUERY) { + if (!--timeout) + return; + } + goto read_loop; +} + +/****************************************** + N64 Controller Functions + *****************************************/ +void get_button() +{ + // Command to send to the gamecube + // The last bit is rumble, flip it to rumble + // yes this does need to be inside the loop, the + // array gets mutilated when it goes through N64_send + unsigned char command[] = { + 0x01 + }; + + // don't want interrupts getting in the way + noInterrupts(); + // send those 3 bytes + N64_send(command, 1); + N64_stop(); + // read in 32bits of data and dump it to N64_raw_dump + N64_get(32); + // end of time sensitive code + interrupts(); + + // The get_N64_status function sloppily dumps its data 1 bit per byte + // into the get_status_extended char array. It's our job to go through + // that and put each piece neatly into the struct N64_status + int i; + memset(&N64_status, 0, sizeof(N64_status)); + + // bits: joystick x value + // These are 8 bit values centered at 0x80 (128) + for (i = 0; i < 8; i++) { + N64_status.stick_x |= N64_raw_dump[16 + i] ? (0x80 >> i) : 0; + } + for (i = 0; i < 8; i++) { + N64_status.stick_y |= N64_raw_dump[24 + i] ? (0x80 >> i) : 0; + } + + // read char array N64_raw_dump into string rawStr + rawStr = ""; + for (i = 0; i < 16; i++) { + rawStr = rawStr + String(N64_raw_dump[i], DEC); + } + + // Buttons (A,B,Z,S,DU,DD,DL,DR,0,0,L,R,CU,CD,CL,CR) + if (rawStr.substring(0, 16) == "0000000000000000") { + lastbutton = button; + button = "Press a button"; + } + else + { + for (int i = 0; i < 16; i++) + { + // seems to be 16, 8 or 4 depending on what pin is used + if (N64_raw_dump[i] == 16) + { + switch (i) + { + case 7: + button = "D-Right"; + break; + + case 6: + button = "D-Left"; + break; + + case 5: + button = "D-Down"; + break; + + case 4: + button = "D-Up"; + break; + + case 3: + button = "START"; + break; + + case 2: + button = "Z"; + break; + + case 1: + button = "B"; + break; + + case 0: + button = "A"; + break; + + case 15: + button = "C-Right"; + break; + + case 14: + button = "C-Left"; + break; + + case 13: + button = "C-Down"; + break; + + case 12: + button = "C-Up"; + break; + + case 11: + button = "R"; + break; + + case 10: + button = "L"; + break; + } + } + } + } +} + +void readController() { + bool quit = 1; + + while (quit) { + display_Clear(); + + // Get Button and analog stick + get_button(); + + println_Msg(F("Controller Test")); + println_Msg(""); + println_Msg(button); + println_Msg(""); + String stickx = String("X: " + String(N64_status.stick_x, DEC) + " "); + println_Msg(stickx); + String sticky = String("Y: " + String(N64_status.stick_y, DEC) + " "); + println_Msg(sticky); + println_Msg(""); + println_Msg(F("Press START to quit")); + + display_Update(); + delay(100); + + if (button == "START") { + quit = 0; + } + } +} + +/****************************************** + N64 Controller Pak Functions + (connected via Controller) + *****************************************/ +// read 32bytes from controller pak +void readBlock(word myAddress) { + // Calculate the address CRC + word myAddressCRC = addrCRC(myAddress); + + // Read Controller Pak command + unsigned char command[] = {0x02}; + // Address Command + unsigned char addressHigh[] = {(unsigned char)(myAddressCRC >> 8)}; + unsigned char addressLow[] = {(unsigned char)(myAddressCRC & 0xff)}; + + // don't want interrupts getting in the way + noInterrupts(); + // send those 3 bytes + N64_send(command, 1); + N64_send(addressHigh, 1); + N64_send(addressLow, 1); + N64_stop(); + // read in data + N64_get(256); + // end of time sensitive code + interrupts(); + + // Empty N64_raw_dump into myBlock + for (word i = 0; i < 256; i += 8) { + boolean byteFlipped[9]; + + // Flip byte order + byteFlipped[0] = N64_raw_dump[i + 7]; + byteFlipped[1] = N64_raw_dump[i + 6]; + byteFlipped[2] = N64_raw_dump[i + 5]; + byteFlipped[3] = N64_raw_dump[i + 4]; + byteFlipped[4] = N64_raw_dump[i + 3]; + byteFlipped[5] = N64_raw_dump[i + 2]; + byteFlipped[6] = N64_raw_dump[i + 1]; + byteFlipped[7] = N64_raw_dump[i + 0]; + + // Join bits into one byte + unsigned char myByte = 0; + for (byte j = 0; j < 8; ++j) { + if (byteFlipped[j]) { + myByte |= 1 << j; + } + } + // Save byte into block array + myBlock[i / 8] = myByte; + } +} + +// reads the MPK file to the sd card +void readMPK() { + // Change to root + sd.chdir("/"); + // Make MPK directory + sd.mkdir("N64/MPK", true); + // Change to MPK directory + sd.chdir("N64/MPK"); + + // Get name, add extension and convert to char array for sd lib + EEPROM_readAnything(10, foldern); + sprintf(fileName, "%d", foldern); + strcat(fileName, ".mpk"); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("Can't open file on SD"), true); + } + + println_Msg(F("Please wait...")); + display_Update(); + + // Controller paks, which all have 32kB of space, are mapped between 0x0000 – 0x7FFF + for (word i = 0x0000; i < 0x8000; i += 32) { + // Read one block of the Controller Pak into array myBlock + readBlock(i); + // Write block to SD card + for (byte j = 0; j < 32; j++) { + myFile.write(myBlock[j]); + } + } + // Close the file: + myFile.close(); + print_Msg(F("Saved as N64/MPK/")); + println_Msg(fileName); + display_Update(); +} + +void writeMPK() { + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + println_Msg(F("Writing...")); + println_Msg(filePath); + display_Update(); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + for (word myAddress = 0x0000; myAddress < 0x8000; myAddress += 32) { + // Read 32 bytes into SD buffer + myFile.read(sdBuffer, 32); + + // Calculate the address CRC + word myAddressCRC = addrCRC(myAddress); + + // Write Controller Pak command + unsigned char command[] = {0x03}; + // Address Command + unsigned char addressHigh[] = {(unsigned char)(myAddressCRC >> 8)}; + unsigned char addressLow[] = {(unsigned char)(myAddressCRC & 0xff)}; + + // don't want interrupts getting in the way + noInterrupts(); + // Send write command + N64_send(command, 1); + // Send block number + N64_send(addressHigh, 1); + N64_send(addressLow, 1); + // Send data to write + N64_send(sdBuffer, 32); + // Send stop + N64_stop(); + // Enable interrupts + interrupts(); + } + // Close the file: + myFile.close(); + println_Msg(F("Done")); + display_Update(); + } + else { + print_Error(F("Can't create file on SD"), true); + } +} + +// verifies if write was successful +void verifyMPK() { + writeErrors = 0; + + println_Msg(F("Verifying...")); + display_Update(); + + //open file on sd card + if (!myFile.open(filePath, O_RDWR | O_CREAT)) { + print_Error(F("Can't create file on SD"), true); + } + + // Controller paks, which all have 32kB of space, are mapped between 0x0000 – 0x7FFF + for (word i = 0x0000; i < 0x8000; i += 32) { + // Read one block of the Controller Pak into array myBlock + readBlock(i); + // Check against file on SD card + for (byte j = 0; j < 32; j++) { + if (myFile.read() != myBlock[j]) { + writeErrors++; + } + } + } + // Close the file: + myFile.close(); + if (writeErrors == 0) { + println_Msg(F("OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } +} + +/****************************************** + N64 Cartridge functions +*****************************************/ +void printCartInfo_N64() { + // Check cart + getCartInfo_N64(); + + // Print start page + if (cartSize != 0) { + println_Msg(F("N64 Cartridge Info")); + println_Msg(F("")); + print_Msg(F("Name: ")); + println_Msg(romName); + print_Msg(F("ID: ")); + print_Msg(cartID); + print_Msg(F(" Size: ")); + print_Msg(cartSize); + println_Msg(F("MB")); + print_Msg(F("Save: ")); + switch (saveType) { + case 1: + println_Msg(F("Sram")); + break; + case 4: + println_Msg(F("Flashram")); + break; + case 5: + println_Msg(F("4K Eeprom")); + eepPages = 64; + break; + case 6: + println_Msg(F("16K Eeprom")); + eepPages = 256; + break; + default: + println_Msg(F("unknown")); + break; + } + print_Msg(F("Version: 1.")); + println_Msg(romVersion); + + // Wait for user input + println_Msg(F(" ")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + } + else { + // Display error + println_Msg(F("GAMEPAK ERROR")); + println_Msg(""); + print_Msg(F("Name: ")); + println_Msg(romName); + print_Msg(F("ID: ")); + println_Msg(cartID); + println_Msg(""); + display_Update(); + + strcpy(romName, "GPERROR"); + print_Error(F("Cartridge unknown"), false); + wait(); + + // Set cartsize manually + unsigned char N64RomMenu; + // Copy menuOptions out of progmem + convertPgm(romOptionsN64, 6); + N64RomMenu = question_box("Select ROM size", menuOptions, 6, 0); + + // wait for user choice to come back from the question box menu + switch (N64RomMenu) + { + case 0: + // 4MB + cartSize = 4; + break; + + case 1: + // 8MB + cartSize = 8; + break; + + case 2: + // 12MB + cartSize = 12; + break; + + case 3: + // 16MB + cartSize = 16; + break; + + case 4: + // 32MB + cartSize = 32; + break; + + case 5: + // 64MB + cartSize = 64; + break; + } + } +} + +// CRC32 lookup table +static const uint32_t crc_32_tab[] PROGMEM = { /* CRC polynomial 0xedb88320 */ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +// Calculate dumped rom's CRC32 +inline uint32_t updateCRC32(uint8_t ch, uint32_t crc) { + uint32_t idx = ((crc) ^ (ch)) & 0xff; + uint32_t tab_value = pgm_read_dword(crc_32_tab + idx); + return tab_value ^ ((crc) >> 8); +} + +// Read rom from sd +uint32_t crc32() { + if (myFile.open(fileName, O_READ)) { + uint32_t oldcrc32 = 0xFFFFFFFF; + + for (unsigned long currByte = 0; currByte < cartSize * 2048; currByte++) { + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + oldcrc32 = updateCRC32(sdBuffer[c], oldcrc32); + } + } + // Close the file: + myFile.close(); + return ~oldcrc32; + } + else { + print_Error(F("File not found"), true); + } +} + +// look-up the calculated crc in the file n64.txt on sd card +boolean searchCRC(char crcStr[9]) { + boolean result = 0; + char tempStr2[2]; + char tempStr1[9]; + char tempStr[5]; + + // Change to root dir + sd.chdir("/"); + + if (myFile.open("n64.txt", O_READ)) { + // Loop through file + while (myFile.available()) { + // Read 8 bytes into String, do it one at a time so byte order doesn't get mixed up + sprintf(tempStr1, "%c", myFile.read()); + for (byte i = 0; i < 7; i++) { + sprintf(tempStr2, "%c", myFile.read()); + strcat(tempStr1, tempStr2); + } + + // Check if string is a match + if (strcmp(tempStr1, crcStr) == 0) { + // Skip the , in the file + myFile.seekSet(myFile.curPosition() + 1); + + // Read 4 bytes into String, do it one at a time so byte order doesn't get mixed up + sprintf(tempStr, "%c", myFile.read()); + for (byte i = 0; i < 3; i++) { + sprintf(tempStr2, "%c", myFile.read()); + strcat(tempStr, tempStr2); + } + + if (strcmp(tempStr, cartID) == 0) { + result = 1; + break; + } + else { + result = 0; + break; + } + } + // If no match, empty string, advance by 12 and try again + else { + myFile.seekSet(myFile.curPosition() + 12); + } + } + // Close the file: + myFile.close(); + return result; + } + else { + print_Error(F("N64.txt missing"), true); + } +} + +// look-up cart id in file n64.txt on sd card +void getCartInfo_N64() { + char tempStr2[2]; + char tempStr[5]; + char sizeStr[3]; + char saveStr[2]; + + // cart not in list + cartSize = 0; + saveType = 0; + + // Read cart id + idCart(); + + if (myFile.open("n64.txt", O_READ)) { + // Skip over the first crc + myFile.seekSet(myFile.curPosition() + 9); + // Loop through file + while (myFile.available()) { + // Read 4 bytes into String, do it one at a time so byte order doesn't get mixed up + sprintf(tempStr, "%c", myFile.read()); + for (byte i = 0; i < 3; i++) { + sprintf(tempStr2, "%c", myFile.read()); + strcat(tempStr, tempStr2); + } + + // Check if string is a match + if (strcmp(tempStr, cartID) == 0) { + // Skip the , in the file + myFile.seekSet(myFile.curPosition() + 1); + + // Read the next ascii character and subtract 48 to convert to decimal + cartSize = myFile.read() - 48; + // Remove leading 0 for single digit cart sizes + if (cartSize != 0) { + cartSize = cartSize * 10 + myFile.read() - 48; + } + else { + cartSize = myFile.read() - 48; + } + + // Skip the , in the file + myFile.seekSet(myFile.curPosition() + 1); + + // Read the next ascii character and subtract 48 to convert to decimal + saveType = myFile.read() - 48; + } + // If no match, empty string, advance by 16 and try again + else { + myFile.seekSet(myFile.curPosition() + 16); + } + } + // Close the file: + myFile.close(); + } + else { + print_Error(F("N64.txt missing"), true); + } +} + +// Read rom ID +void idCart() { + // Set the address + setAddress_N64(romBase); + // Read first 64 bytes of rom + for (int c = 0; c < 64; c += 2) { + // split word + word myWord = readWord_N64(); + byte loByte = myWord & 0xFF; + byte hiByte = myWord >> 8; + + // write to buffer + sdBuffer[c] = hiByte; + sdBuffer[c + 1] = loByte; + } + + // Get cart id + cartID[0] = sdBuffer[0x3B]; + cartID[1] = sdBuffer[0x3C]; + cartID[2] = sdBuffer[0x3D]; + cartID[3] = sdBuffer[0x3E]; + + // Get rom version + romVersion = sdBuffer[0x3F]; + + // Get name + byte myLength = 0; + for (unsigned int i = 0; i < 20; i++) { + if (((char(sdBuffer[0x20 + i]) >= 48 && char(sdBuffer[0x20 + i]) <= 57) || (char(sdBuffer[0x20 + i]) >= 65 && char(sdBuffer[0x20 + i]) <= 122)) && myLength < 15) { + romName[myLength] = char(sdBuffer[0x20 + i]); + myLength++; + } + } +} + +/****************************************** + Eeprom functions +*****************************************/ +// Send a clock pulse of 2us length, 50% duty, 500kHz +void pulseClock_N64(unsigned int times) { + for (unsigned int i = 0; i < (times * 2); i++) { + // Switch the clock pin to 0 if it's 1 and 0 if it's 1 + PORTH ^= (1 << 1); + // without the delay the clock pulse would be 1.5us and 666kHz + //__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t")); + } +} + +// Send one byte of data to eeprom +void sendData(byte data) { + for (byte i = 0; i < 8; i++) { + // pull data line low + N64_LOW; + + // if current bit is 1, pull high after ~1us, 2 cycles + if (data >> 7) { + pulseClock_N64(2); + N64_HIGH; + pulseClock_N64(6); + } + // if current bit is 0 pull high after ~3us, 6 cycles + else { + pulseClock_N64(6); + N64_HIGH; + pulseClock_N64(2); + } + + // rotate to the next bit + data <<= 1; + } +} + +// Send stop bit to eeprom +void sendStop() { + N64_LOW; + pulseClock_N64(2); + N64_HIGH; + pulseClock_N64(4); +} + +// Capture 8 bytes in 64 bits into bit array tempBits +void readData() { + for (byte i = 0; i < 64; i++) { + + // pulse clock until we get response from eeprom + while (N64_QUERY) { + pulseClock_N64(1); + } + + // Skip over the 1us low part of a high bit + pulseClock_N64(3); + + // Read bit + tempBits[i] = N64_QUERY; + + // wait for line to go high again + while (!N64_QUERY) { + pulseClock_N64(1); + } + } +} + +// Write Eeprom to cartridge +void writeEeprom() { + if ((saveType == 5) || (saveType == 6)) { + + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + println_Msg(F("Writing...")); + println_Msg(filePath); + display_Update(); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + + for (byte i = 0; i < (eepPages / 64); i++) { + myFile.read(sdBuffer, 512); + // Disable interrupts for more uniform clock pulses + noInterrupts(); + + for (byte pageNumber = 0; pageNumber < 64; pageNumber++) { + // Wait ~50ms between page writes or eeprom will have write errors + pulseClock_N64(26000); + + // Send write command + sendData(0x05); + // Send page number + sendData(pageNumber + (i * 64)); + // Send data to write + for (byte j = 0; j < 8; j++) { + sendData(sdBuffer[(pageNumber * 8) + j]); + } + sendStop(); + } + interrupts(); + } + + // Close the file: + myFile.close(); + println_Msg(F("Done")); + display_Update(); + } + else { + print_Error(F("SD Error"), true); + } + } + else { + print_Error(F("Savetype Error"), true); + } +} + +// Dump Eeprom to SD +void readEeprom() { + if ((saveType == 5) || (saveType == 6)) { + + // Wait 0.6ms + pulseClock_N64(300); + + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".eep"); + + // create a new folder for the save file + EEPROM_readAnything(10, foldern); + sprintf(folder, "N64/SAVE/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + // Open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("Can't create file on SD"), true); + } + + for (byte i = 0; i < (eepPages / 64); i++) { + // Disable interrupts for more uniform clock pulses + noInterrupts(); + + for (byte pageNumber = 0; pageNumber < 64; pageNumber++) { + // Send read command + sendData(0x04); + // Send Page number + sendData(pageNumber + (i * 64)); + // Send stop bit + sendStop(); + + // read data + readData(); + sendStop(); + + // OR 8 bits into one byte for a total of 8 bytes + for (byte j = 0; j < 64; j += 8) { + sdBuffer[(pageNumber * 8) + (j / 8)] = tempBits[0 + j] << 7 | tempBits[1 + j] << 6 | tempBits[2 + j] << 5 | tempBits[3 + j] << 4 | tempBits[4 + j] << 3 | tempBits[5 + j] << 2 | tempBits[6 + j] << 1 | tempBits[7 + j]; + } + // Wait ~0.6ms between pages or eeprom will lock up + pulseClock_N64(300); + } + interrupts(); + + // Write 64 pages at once to the SD card + myFile.write(sdBuffer, 512); + } + // Close the file: + myFile.close(); + //clear the screen + display_Clear(); + print_Msg(F("Saved to ")); + print_Msg(folder); + println_Msg(F("/")); + display_Update(); + } + else { + print_Error(F("Savetype Error"), true); + } +} + +// Check if a write succeeded, returns 0 if all is ok and number of errors if not +unsigned long verifyEeprom() { + if ((saveType == 5) || (saveType == 6)) { + writeErrors = 0; + + // Wait 0.6ms + pulseClock_N64(300); + + print_Msg(F("Verifying against ")); + println_Msg(filePath); + display_Update(); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + + for (byte i = 0; i < (eepPages / 64); i++) { + // Disable interrupts for more uniform clock pulses + noInterrupts(); + + for (byte pageNumber = 0; pageNumber < 64; pageNumber++) { + // Send read command + sendData(0x04); + // Send Page number + sendData(pageNumber + (i * 64)); + // Send stop bit + sendStop(); + + // read data + readData(); + sendStop(); + + + + // OR 8 bits into one byte for a total of 8 bytes + for (byte j = 0; j < 64; j += 8) { + sdBuffer[(pageNumber * 8) + (j / 8)] = tempBits[0 + j] << 7 | tempBits[1 + j] << 6 | tempBits[2 + j] << 5 | tempBits[3 + j] << 4 | tempBits[4 + j] << 3 | tempBits[5 + j] << 2 | tempBits[6 + j] << 1 | tempBits[7 + j]; + } + // Wait ~0.6ms between pages or eeprom will lock up + pulseClock_N64(300); + } + interrupts(); + + // Check sdBuffer content against file on sd card + for (int c = 0; c < 512; c++) { + if (myFile.read() != sdBuffer[c]) { + writeErrors++; + } + } + } + // Close the file: + myFile.close(); + } + else { + // SD Error + writeErrors = 999999; + print_Error(F("SD Error"), true); + } + // Return 0 if verified ok, or number of errors + return writeErrors; + } + else { + print_Error(F("Savetype Error"), true); + } +} + +/****************************************** + SRAM functions +*****************************************/ +// Write sram to cartridge +void writeSram(unsigned long sramSize) { + if (saveType == 1) { + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + println_Msg(F("Writing...")); + println_Msg(filePath); + display_Update(); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + for (unsigned long currByte = sramBase; currByte < (sramBase + sramSize); currByte += 512) { + + // Read save from SD into buffer + myFile.read(sdBuffer, 512); + + // Set the address for the next 512 bytes + setAddress_N64(currByte); + + for (int c = 0; c < 512; c += 2) { + // Join bytes to word + word myWord = ( ( sdBuffer[c] & 0xFF ) << 8 ) | ( sdBuffer[c + 1] & 0xFF ); + + // Write word + writeWord_N64(myWord); + } + } + // Close the file: + myFile.close(); + println_Msg(F("Done")); + display_Update(); + } + else { + print_Error(F("SD Error"), true); + } + + } + else { + print_Error(F("Savetype Error"), true); + } +} + +// Read sram and save to the SD card +void readSram(unsigned long sramSize, byte flashramType) { + int offset = 512; + int bufferSize = 512; + if (flashramType == 2) { + offset = 64; + bufferSize = 128; + } + + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + + if (saveType == 4) { + strcat(fileName, ".fla"); + } + else if (saveType == 1) { + strcat(fileName, ".sra"); + } + else { + print_Error(F("Savetype Error"), true); + } + + // create a new folder for the save file + EEPROM_readAnything(10, foldern); + sprintf(folder, "N64/SAVE/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + // Open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + + for (unsigned long currByte = sramBase; currByte < (sramBase + (sramSize / flashramType)); currByte += offset) { + // Set the address + setAddress_N64(currByte); + + for (int c = 0; c < bufferSize; c += 2) { + // split word + word myWord = readWord_N64(); + byte loByte = myWord & 0xFF; + byte hiByte = myWord >> 8; + + // write to buffer + sdBuffer[c] = hiByte; + sdBuffer[c + 1] = loByte; + } + myFile.write(sdBuffer, bufferSize); + } + // Close the file: + myFile.close(); + print_Msg(F("Saved to ")); + print_Msg(folder); + println_Msg(F("/")); + display_Update(); +} + +unsigned long verifySram(unsigned long sramSize, byte flashramType) { + writeErrors = 0; + + int offset = 512; + int bufferSize = 512; + if (flashramType == 2) { + offset = 64; + bufferSize = 128; + } + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + for (unsigned long currByte = sramBase; currByte < (sramBase + (sramSize / flashramType)); currByte += offset) { + // Set the address + setAddress_N64(currByte); + + for (int c = 0; c < bufferSize; c += 2) { + // split word + word myWord = readWord_N64(); + byte loByte = myWord & 0xFF; + byte hiByte = myWord >> 8; + + // write to buffer + sdBuffer[c] = hiByte; + sdBuffer[c + 1] = loByte; + } + // Check sdBuffer content against file on sd card + for (int i = 0; i < bufferSize; i++) { + if (myFile.read() != sdBuffer[i]) { + writeErrors++; + } + } + } + // Close the file: + myFile.close(); + } + else { + print_Error(F("SD Error"), true); + } + // Return 0 if verified ok, or number of errors + return writeErrors; +} + +/****************************************** + Flashram functions +*****************************************/ +// Send a command to the flashram command register +void sendFramCmd (unsigned long myCommand) { + // Split command into two words + word myComLowOut = myCommand & 0xFFFF; + word myComHighOut = myCommand >> 16; + + // Set address to command register + setAddress_N64(0x08010000); + // Send command + writeWord_N64(myComHighOut); + writeWord_N64(myComLowOut); +} + +// Init fram +void initFram() { + // FRAM_EXECUTE_CMD + sendFramCmd(0xD2000000); + delay(10); + // FRAM_EXECUTE_CMD + sendFramCmd(0xD2000000); + delay(10); + //FRAM_STATUS_MODE_CMD + sendFramCmd(0xE1000000); + delay(10); +} + +void writeFram(byte flashramType) { + if (saveType == 4) { + // Erase fram + eraseFram(); + + // Check if empty + if (blankcheck_N64(flashramType) == 0) { + println_Msg(F("OK")); + display_Update(); + } + else { + println_Msg("FAIL"); + display_Update(); + } + + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + print_Msg(F("Writing ")); + println_Msg(filePath); + display_Update(); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + // Init fram + initFram(); + + // Write all 8 fram banks + print_Msg(F("Bank ")); + for (byte bank = 0; bank < 8; bank++) { + print_Msg(bank); + print_Msg(F(" ")); + display_Update(); + + // Write one bank of 128*128 bytes + for (byte offset = 0; offset < 128; offset++) { + // Read save from SD into buffer + myFile.read(sdBuffer, 128); + + // FRAM_WRITE_MODE_CMD + sendFramCmd(0xB4000000); + delay(1); + + // Set the address for the next 128 bytes + setAddress_N64(0x08000000); + + // Send 128 bytes, 64 words + for (byte c = 0; c < 128; c += 2) { + // Join two bytes into one word + word myWord = ( ( sdBuffer[c] & 0xFF ) << 8 ) | ( sdBuffer[c + 1] & 0xFF ); + // Write word + writeWord_N64(myWord); + } + // Delay between each "DMA" + delay(1); + + //FRAM_WRITE_OFFSET_CMD + offset + sendFramCmd((0xA5000000 | (((bank * 128) + offset) & 0xFFFF))); + delay(1); + + // FRAM_EXECUTE_CMD + sendFramCmd(0xD2000000); + while (waitForFram(flashramType)) { + delay(1); + } + } + // Delay between banks + delay(20); + } + println_Msg(""); + // Close the file: + myFile.close(); + } + else { + print_Error(F("SD Error"), true); + } + } + else { + print_Error(F("Savetype Error"), true); + } +} + +// Delete all 8 flashram banks +void eraseFram() { + if (saveType == 4) { + print_Msg(F("Erasing...")); + display_Update(); + + // Init fram + initFram(); + + // Erase fram + // 0x4B00007F 0x4B0000FF 0x4B00017F 0x4B0001FF 0x4B00027F 0x4B0002FF 0x4B00037F 0x4B0003FF + for (unsigned long bank = 0x4B00007F; bank < 0x4B00047F; bank += 0x80) { + sendFramCmd(bank); + delay(10); + // FRAM_ERASE_MODE_CMD + sendFramCmd(0x78000000); + delay(10); + // FRAM_EXECUTE_CMD + sendFramCmd(0xD2000000); + while (waitForFram(flashramType)) { + delay(1); + } + } + } + else { + print_Error(F("Savetype Error"), true); + } +} + +// Read flashram +void readFram(byte flashramType) { + if (saveType == 4) { + // Put flashram into read mode + // FRAM_READ_MODE_CMD + sendFramCmd(0xF0000000); + // Read Flashram + readSram(131072, flashramType); + } + else { + print_Error(F("Savetype Error"), true); + } +} + +// Verify flashram +unsigned long verifyFram(byte flashramType) { + // Put flashram into read mode + // FRAM_READ_MODE_CMD + sendFramCmd(0xF0000000); + writeErrors = verifySram(131072, flashramType); + return writeErrors; +} + +// Blankcheck flashram +unsigned long blankcheck_N64(byte flashramType) { + writeErrors = 0; + + int offset = 512; + int bufferSize = 512; + if (flashramType == 2) { + offset = 64; + bufferSize = 128; + } + + // Put flashram into read mode + // FRAM_READ_MODE_CMD + sendFramCmd(0xF0000000); + + // Read Flashram + for (unsigned long currByte = sramBase; currByte < (sramBase + (131072 / flashramType)); currByte += offset) { + // Set the address for the next 512 bytes + setAddress_N64(currByte); + + for (int c = 0; c < bufferSize; c += 2) { + // split word + word myWord = readWord_N64(); + byte loByte = myWord & 0xFF; + byte hiByte = myWord >> 8; + + // write to buffer + sdBuffer[c] = hiByte; + sdBuffer[c + 1] = loByte; + } + // Check sdBuffer content against file on sd card + for (int i = 0; i < bufferSize; i++) { + if (0xFF != sdBuffer[i]) { + writeErrors++; + } + } + } + // Return 0 if verified ok, or number of errors + return writeErrors; +} + +// Wait until current operation is done +byte waitForFram(byte flashramType) { + byte framStatus = 0; + byte statusMXL1100[] = {0x11, 0x11, 0x80, 0x01, 0x00, 0xC2, 0x00, 0x1E}; + byte statusMXL1101[] = {0x11, 0x11, 0x80, 0x01, 0x00, 0xC2, 0x00, 0x1D}; + byte statusMN63F81[] = {0x11, 0x11, 0x80, 0x01, 0x00, 0x32, 0x00, 0xF1}; + + // FRAM_STATUS_MODE_CMD + sendFramCmd(0xE1000000); + delay(1); + + // Set address to Fram status register + setAddress_N64(0x08000000); + + // Read Status + for (byte c = 0; c < 8; c += 2) { + // split word + word myWord = readWord_N64(); + byte loByte = myWord & 0xFF; + byte hiByte = myWord >> 8; + + // write to buffer + sdBuffer[c] = hiByte; + sdBuffer[c + 1] = loByte; + } + + if (flashramType == 2) { + for (byte c = 0; c < 8; c++) { + if (statusMXL1100[c] != sdBuffer[c]) { + framStatus = 1; + } + } + } + else if (flashramType == 1) { + //MX29L1101 + if (MN63F81MPN == false) { + for (byte c = 0; c < 8; c++) { + if (statusMXL1101[c] != sdBuffer[c]) { + framStatus = 1; + } + } + } + //MN63F81MPN + else if (MN63F81MPN == true) { + for (byte c = 0; c < 8; c++) { + if (statusMN63F81[c] != sdBuffer[c]) { + framStatus = 1; + } + } + } + } + return framStatus; +} + +// Get flashram type +void getFramType() { + + // FRAM_STATUS_MODE_CMD + sendFramCmd(0xE1000000); + delay(10); + + // Set address to Fram status register + setAddress_N64(0x08000000); + + // Read Status + for (byte c = 0; c < 8; c += 2) { + // split word + word myWord = readWord_N64(); + byte loByte = myWord & 0xFF; + byte hiByte = myWord >> 8; + + // write to buffer + sdBuffer[c] = hiByte; + sdBuffer[c + 1] = loByte; + } + //MX29L1100 + if (sdBuffer[7] == 0x1e ) { + flashramType = 2; + println_Msg(F("Type: MX29L1100")); + display_Update(); + } + //MX29L1101 + else if (sdBuffer[7] == 0x1d ) { + flashramType = 1; + MN63F81MPN = false; + println_Msg(F("Type: MX29L1101")); + display_Update(); + } + //MN63F81MPN + else if (sdBuffer[7] == 0xf1 ) { + flashramType = 1; + MN63F81MPN = true; + println_Msg(F("Type: MN63F81MPN")); + display_Update(); + } + // Type unknown + else { + for (byte c = 0; c < 8; c++) { + print_Msg(sdBuffer[c], HEX); + print_Msg(F(", ")); + } + print_Error(F("Flashram unknown"), true); + } +} + +/****************************************** + Rom functions +*****************************************/ +// Read rom and save to the SD card +void readRom_N64() { + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".Z64"); + + // create a new folder + EEPROM_readAnything(10, foldern); + sprintf(folder, "N64/ROM/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + +readn64rom: + // Open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + + for (unsigned long currByte = romBase; currByte < (romBase + (cartSize * 1024 * 1024)); currByte += 512) { + // Blink led + if (currByte % 16384 == 0) + PORTB ^= (1 << 4); + + // Set the address for the next 512 bytes + setAddress_N64(currByte); + + for (int c = 0; c < 512; c += 2) { + // split word + word myWord = readWord_N64(); + byte loByte = myWord & 0xFF; + byte hiByte = myWord >> 8; + + // write to buffer + sdBuffer[c] = hiByte; + sdBuffer[c + 1] = loByte; + } + myFile.write(sdBuffer, 512); + } + // Close the file: + myFile.close(); + + if (n64crc) { +calcn64crc: + // Calculate Checksum and convert to string + println_Msg(F("Calculating CRC..")); + display_Update(); + char crcStr[9]; + sprintf(crcStr, "%08lx", crc32()); + // Print checksum + println_Msg(crcStr); + display_Update(); + + // Search n64.txt for crc + if (searchCRC(crcStr)) { + // Dump was a known good rom + println_Msg(F("Checksum matches")); + } + else { + // Dump was bad or unknown + rgb.setColor(255, 0, 0); + // N64 CRC32 error Menu + unsigned char CRCMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsN64CRC, 4); + + char tempStr3[20]; + strcpy(tempStr3, "CRC ERROR "); + strcat(tempStr3, crcStr); + + CRCMenu = question_box(tempStr3, menuOptions, 4, 1); + + // wait for user choice to come back from the question box menu + switch (CRCMenu) + { + case 0: + // Change to last directory + sd.chdir(folder); + display_Clear(); + // Calculate CRC again + rgb.setColor(0, 0, 0); + goto calcn64crc; + break; + + case 1: + // Change to last directory + sd.chdir(folder); + // Delete old file + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + if (!myFile.remove()) { + print_Error(F("Delete Error"), true); + } + // Dump again + display_Clear(); + println_Msg(F("Reading Rom...")); + display_Update(); + rgb.setColor(0, 0, 0); + goto readn64rom; + break; + + case 2: + // Return to N64 menu + break; + + case 3: + // Reset + asm volatile (" jmp 0"); + break; + } + } + display_Update(); + } + println_Msg(F("Done.")); + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +/****************************************** + N64 Repro Flashrom Functions + *****************************************/ +void flashRepro_N64() { + // Check flashrom ID's + idFlashrom_N64(); + + // If the ID is known continue + if (cartSize != 0) { + // Print flashrom name + if ((strcmp(flashid, "227E") == 0) && (strcmp(cartID, "2201") == 0)) { + print_Msg(F("Spansion S29GL256N")); + if (cartSize == 64) + println_Msg(F(" x2")); + else + println_Msg(""); + } + else if ((strcmp(flashid, "227E") == 0) && (strcmp(cartID, "2101") == 0)) { + print_Msg(F("Spansion S29GL128N")); + } + else if ((strcmp(flashid, "22C9") == 0) || (strcmp(flashid, "22CB") == 0)) { + print_Msg(F("Macronix MX29LV640")); + if (cartSize == 16) + println_Msg(F(" x2")); + else + println_Msg(""); + } + else if (strcmp(flashid, "8816") == 0) + println_Msg(F("Intel 4400L0ZDQ0")); + 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")); + else if ((strcmp(flashid, "227E") == 0) && (strcmp(cartID, "3901") == 0)) + println_Msg(F("Intel 512M29EW")); + + // Print info + print_Msg(F("ID: ")); + print_Msg(flashid); + print_Msg(F(" Size: ")); + print_Msg(cartSize); + println_Msg(F("MB")); + println_Msg(""); + println_Msg(F("This will erase your")); + println_Msg(F("Repro Cartridge.")); + println_Msg(F("Attention: Use 3.3V!")); + println_Msg(""); + println_Msg(F("Press Button")); + display_Update(); + wait(); + + // Launch file browser + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser("Select z64 file"); + display_Clear(); + 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(); + print_Msg(F("File size: ")); + print_Msg(fileSize / 1048576); + println_Msg(F("MB")); + display_Update(); + + // Compare file size to flashrom size + if ((fileSize / 1048576) > cartSize) { + print_Error(F("File too big"), true); + } + + // Erase needed sectors + 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(); + resetIntel4400_N64(); + } + else if ((strcmp(flashid, "22C9") == 0) || (strcmp(flashid, "22CB") == 0)) { + // Macronix MX29LV640, C9 is top boot and CB is bottom boot block + eraseFlashrom_N64(0x8000); + } + + // Check if erase was successful + if (blankcheckFlashrom_N64()) { + // Write flashrom + println_Msg(F("OK")); + print_Msg(F("Writing ")); + println_Msg(filePath); + display_Update(); + + + if ((strcmp(cartID, "3901") == 0) && (strcmp(flashid, "227E") == 0)) { + // Intel 512M29EW(64MB) with 0x20000 sector size and 128 byte buffer + writeFlashBuffer_N64(0x20000, 128); + } + else if (strcmp(flashid, "227E") == 0) { + // Spansion S29GL128N/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(); + } + else if ((strcmp(flashid, "8813") == 0) || (strcmp(flashid, "8816") == 0)) { + // Intel 4400L0ZDQ0 + writeIntel4400_N64(); + resetIntel4400_N64(); + } + + // Close the file: + myFile.close(); + + // Verify + print_Msg(F("Verifying...")); + display_Update(); + writeErrors = verifyFlashrom_N64(); + if (writeErrors == 0) { + println_Msg(F("OK")); + display_Update(); + } + else { + print_Msg(writeErrors); + print_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + } + else { + // Close the file + myFile.close(); + print_Error(F("failed"), false); + } + } + else { + print_Error(F("Can't open file"), false); + } + } + // If the ID is unknown show error message + else { + print_Msg(F("Vendor: ")); + println_Msg(vendorID); + print_Msg(F("ID: ")); + print_Msg(flashid); + print_Msg(F(" ")); + println_Msg(cartID); + print_Error(F("Unknown flashrom"), false); + } + + println_Msg(F("Press Button...")); + display_Update(); + wait(); + display_Clear(); + display_Update(); +} + +// Reset to read mode +void resetIntel4400_N64() { + for (unsigned long currPartition = 0; currPartition < (cartSize * 0x100000); currPartition += 0x20000) { + setAddress_N64(romBase + currPartition); + writeWord_N64(0xFF); + } +} + +// 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 + setAddress_N64(flashBase); + writeWord_N64(0xF0); + delay(100); +} + +void idFlashrom_N64() { + // Set size to 0 if no ID is found + cartSize = 0; + + // 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); + + // Read 1 byte vendor ID + setAddress_N64(romBase); + sprintf(vendorID, "%02X", readWord_N64()); + // Read 2 bytes flashrom ID + sprintf(flashid, "%04X", readWord_N64()); + // Read 2 bytes secondary flashrom ID + setAddress_N64(romBase + 0x1C); + sprintf(cartID, "%04X", ((readWord_N64() << 8) | (readWord_N64() & 0xFF))); + + // Spansion S29GL256N(32MB/64MB) with either one or two flashrom chips + if ((strcmp(cartID, "2201") == 0) && (strcmp(flashid, "227E") == 0)) { + cartSize = 32; + + // Reset flashrom + resetFlashrom_N64(romBase); + + // Test for second flashrom chip at 0x2000000 (32MB) + 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); + + char tempID[5]; + setAddress_N64(romBase + 0x2000000); + // Read manufacturer ID + readWord_N64(); + // Read flashrom ID + sprintf(tempID, "%04X", readWord_N64()); + + // Check if second flashrom chip is present + if (strcmp(tempID, "227E") == 0) { + cartSize = 64; + } + resetFlashrom_N64(romBase + 0x2000000); + } + + // Macronix MX29LV640(8MB/16MB) with either one or two flashrom chips + else if ((strcmp(flashid, "22C9") == 0) || (strcmp(flashid, "22CB") == 0)) { + cartSize = 8; + + resetFlashrom_N64(romBase + 0x800000); + + // Test for second flashrom chip at 0x800000 (8MB) + setAddress_N64(romBase + 0x800000 + (0x555 << 1)); + writeWord_N64(0xAA); + setAddress_N64(romBase + 0x800000 + (0x2AA << 1)); + writeWord_N64(0x55); + setAddress_N64(romBase + 0x800000 + (0x555 << 1)); + writeWord_N64(0x90); + + char tempID[5]; + setAddress_N64(romBase + 0x800000); + // Read manufacturer ID + readWord_N64(); + // Read flashrom ID + sprintf(tempID, "%04X", readWord_N64()); + + // Check if second flashrom chip is present + if ((strcmp(tempID, "22C9") == 0) || (strcmp(tempID, "22CB") == 0)) { + cartSize = 16; + } + resetFlashrom_N64(romBase + 0x800000); + } + + // Intel 4400L0ZDQ0 (64MB) + else if (strcmp(flashid, "8816") == 0) { + // Found first flashrom chip, set to 32MB + cartSize = 32; + resetIntel4400_N64(); + + // Test if second half of the flashrom might be hidden + 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); + + // Read manufacturer ID + setAddress_N64(romBase + 0x2000000); + readWord_N64(); + // Read flashrom ID + sprintf(cartID, "%04X", readWord_N64()); + if (strcmp(cartID, "8813") == 0) { + cartSize = 64; + strncpy(flashid , cartID, 5); + } + resetIntel4400_N64(); + // Empty cartID string + cartID[0] = '\0'; + } + + //Fujitsu MSP55LV512/Spansion S29GL512N (64MB) + else if ((strcmp(cartID, "2301") == 0) && (strcmp(flashid, "227E") == 0)) { + cartSize = 64; + // Reset flashrom + resetFlashrom_N64(romBase); + } + + // Spansion S29GL128N(16MB) with one flashrom chip + else if ((strcmp(cartID, "2101") == 0) && (strcmp(flashid, "227E") == 0)) { + cartSize = 16; + // Reset flashrom + resetFlashrom_N64(romBase); + } + + // Intel 512M29EW(64MB) with one flashrom chip + else if ((strcmp(cartID, "3901") == 0) && (strcmp(flashid, "227E") == 0)) { + cartSize = 64; + // Reset flashrom + resetFlashrom_N64(romBase); + } + + // Unknown 227E type + else if (strcmp(flashid, "227E") == 0) { + cartSize = 0; + // Reset flashrom + resetFlashrom_N64(romBase); + } + + //Test for Fujitsu MSP55LV100S (64MB) + else { + // Send flashrom ID command + setAddress_N64(romBase + (0x555 << 1)); + writeWord_N64(0xAAAA); + setAddress_N64(romBase + (0x2AA << 1)); + writeWord_N64(0x5555); + setAddress_N64(romBase + (0x555 << 1)); + writeWord_N64(0x9090); + + setAddress_N64(romBase); + // Read 1 byte vendor ID + readWord_N64(); + // Read 2 bytes flashrom ID + sprintf(cartID, "%04X", readWord_N64()); + + if (strcmp(cartID, "7E7E") == 0) { + resetMSP55LV100_N64(romBase); + cartSize = 64; + strncpy(flashid , cartID, 5); + } + } +} + +// Erase Intel flashrom +void eraseIntel4400_N64() { + unsigned long flashBase = romBase; + + print_Msg(F("Erasing...")); + display_Update(); + + // If the game is smaller than 32Mbit only erase the needed blocks + unsigned long lastBlock = 0x1FFFFFF; + if (fileSize < 0x1FFFFFF) + lastBlock = fileSize; + + // Erase 4 blocks with 16kwords each + for (unsigned long currBlock = 0x0; currBlock < 0x1FFFF; currBlock += 0x8000) { + // Unlock block command + setAddress_N64(flashBase + currBlock); + writeWord_N64(0x60); + setAddress_N64(flashBase + currBlock); + writeWord_N64(0xD0); + // Erase command + setAddress_N64(flashBase + currBlock); + writeWord_N64(0x20); + setAddress_N64(flashBase + currBlock); + writeWord_N64(0xD0); + + // Read the status register + setAddress_N64(flashBase + currBlock); + word statusReg = readWord_N64(); + while ((statusReg | 0xFF7F) != 0xFFFF) { + setAddress_N64(flashBase + currBlock); + statusReg = readWord_N64(); + } + } + + // Erase up to 255 blocks with 64kwords each + for (unsigned long currBlock = 0x20000; currBlock < lastBlock; currBlock += 0x1FFFF) { + // Unlock block command + setAddress_N64(flashBase + currBlock); + writeWord_N64(0x60); + setAddress_N64(flashBase + currBlock); + writeWord_N64(0xD0); + // Erase command + setAddress_N64(flashBase + currBlock); + writeWord_N64(0x20); + setAddress_N64(flashBase + currBlock); + writeWord_N64(0xD0); + + // Read the status register + setAddress_N64(flashBase + currBlock); + word statusReg = readWord_N64(); + while ((statusReg | 0xFF7F) != 0xFFFF) { + setAddress_N64(flashBase + currBlock); + statusReg = readWord_N64(); + } + + // Blink led + PORTB ^= (1 << 4); + } + + // Check if we should erase the second chip too + if ((cartSize = 64) && (fileSize > 0x2000000)) { + // Switch base address to second chip + flashBase = romBase + 0x2000000; + + // 255 blocks with 64kwords each + for (unsigned long currBlock = 0x0; currBlock < 0x1FDFFFF; currBlock += 0x1FFFF) { + // Unlock block command + setAddress_N64(flashBase + currBlock); + writeWord_N64(0x60); + setAddress_N64(flashBase + currBlock); + writeWord_N64(0xD0); + // Erase command + setAddress_N64(flashBase + currBlock); + writeWord_N64(0x20); + setAddress_N64(flashBase + currBlock); + writeWord_N64(0xD0); + + // Read the status register + setAddress_N64(flashBase + currBlock); + word statusReg = readWord_N64(); + while ((statusReg | 0xFF7F) != 0xFFFF) { + setAddress_N64(flashBase + currBlock); + statusReg = readWord_N64(); + } + + // Blink led + PORTB ^= (1 << 4); + } + + // 4 blocks with 16kword each + for (unsigned long currBlock = 0x1FE0000; currBlock < 0x1FFFFFF; currBlock += 0x8000) { + // Unlock block command + setAddress_N64(flashBase + currBlock); + writeWord_N64(0x60); + setAddress_N64(flashBase + currBlock); + writeWord_N64(0xD0); + // Erase command + setAddress_N64(flashBase + currBlock); + writeWord_N64(0x20); + setAddress_N64(flashBase + currBlock); + writeWord_N64(0xD0); + + // Read the status register + setAddress_N64(flashBase + currBlock); + word statusReg = readWord_N64(); + while ((statusReg | 0xFF7F) != 0xFFFF) { + setAddress_N64(flashBase + currBlock); + statusReg = readWord_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; + + print_Msg(F("Erasing...")); + display_Update(); + + for (unsigned long currSector = 0; currSector < fileSize; currSector += sectorSize) { + // Blink led + PORTB ^= (1 << 4); + + // Spansion S29GL256N(32MB/64MB) with two flashrom chips + if ((currSector == 0x2000000) && (strcmp(cartID, "2201") == 0) && (strcmp(flashid, "227E") == 0)) { + // Change to second chip + flashBase = romBase + 0x2000000; + } + // Macronix MX29LV640(8MB/16MB) with two flashrom chips + else if ((currSector == 0x800000) && ((strcmp(flashid, "22C9") == 0) || (strcmp(flashid, "22CB") == 0))) { + flashBase = romBase + 0x800000; + } + + // Send Erase Command + setAddress_N64(flashBase + (0x555 << 1)); + writeWord_N64(0xAA); + setAddress_N64(flashBase + (0x2AA << 1)); + writeWord_N64(0x55); + setAddress_N64(flashBase + (0x555 << 1)); + writeWord_N64(0x80); + setAddress_N64(flashBase + (0x555 << 1)); + writeWord_N64(0xAA); + setAddress_N64(flashBase + (0x2AA << 1)); + writeWord_N64(0x55); + setAddress_N64(romBase + currSector); + writeWord_N64(0x30); + + // Read the status register + setAddress_N64(romBase + currSector); + word statusReg = readWord_N64(); + while ((statusReg | 0xFF7F) != 0xFFFF) { + setAddress_N64(romBase + currSector); + statusReg = readWord_N64(); + } + } +} + +boolean blankcheckFlashrom_N64() { + for (unsigned long currByte = romBase; currByte < romBase + fileSize; currByte += 512) { + // Blink led + if (currByte % 131072 == 0) + PORTB ^= (1 << 4); + + // Set the address + setAddress_N64(currByte); + + for (int c = 0; c < 512; c += 2) { + if (readWord_N64() != 0xFFFF) { + return 0; + } + } + } + return 1; +} + +// Write Intel flashrom +void writeIntel4400_N64() { + for (unsigned long currSector = 0; currSector < fileSize; currSector += 131072) { + // Blink led + PORTB ^= (1 << 4); + + // Write to flashrom + for (unsigned long currSdBuffer = 0; currSdBuffer < 131072; currSdBuffer += 512) { + // Fill SD buffer + myFile.read(sdBuffer, 512); + + // Write 32 words at a time + for (int currWriteBuffer = 0; currWriteBuffer < 512; currWriteBuffer += 64) { + // Buffered program command + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer); + writeWord_N64(0xE8); + + // Check Status register + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer); + word statusReg = readWord_N64(); + while ((statusReg | 0xFF7F) != 0xFFFF) { + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer); + statusReg = readWord_N64(); + } + + // Write word count (minus 1) + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer); + writeWord_N64(0x1F); + + // Write buffer + for (byte currByte = 0; currByte < 64; currByte += 2) { + // Join two bytes into one word + word currWord = ( ( sdBuffer[currWriteBuffer + currByte] & 0xFF ) << 8 ) | ( sdBuffer[currWriteBuffer + currByte + 1] & 0xFF ); + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + currByte); + writeWord_N64(currWord); + } + + // Write Buffer to Flash + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + 62); + writeWord_N64(0xD0); + + // Read the status register at last written address + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + 62); + statusReg = readWord_N64(); + while ((statusReg | 0xFF7F) != 0xFFFF) { + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + 62); + statusReg = readWord_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) { + unsigned long flashBase = romBase; + + for (unsigned long currSector = 0; currSector < fileSize; currSector += sectorSize) { + // Blink led + PORTB ^= (1 << 4); + + // Spansion S29GL256N(32MB/64MB) with two flashrom chips + if ((currSector == 0x2000000) && (strcmp(cartID, "2201") == 0)) { + flashBase = romBase + 0x2000000; + } + + // 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 += bufferSize) { + + // 2 unlock commands + setAddress_N64(flashBase + (0x555 << 1)); + writeWord_N64(0xAA); + setAddress_N64(flashBase + (0x2AA << 1)); + writeWord_N64(0x55); + + // Write buffer load command at sector address + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer); + writeWord_N64(0x25); + // Write word count (minus 1) at sector address + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer); + writeWord_N64((bufferSize / 2) - 1); + + // Define variable before loop so we can use it later when reading the status register + word currWord; + + for (byte currByte = 0; currByte < bufferSize; 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 + bufferSize - 2); + writeWord_N64(0x29); + + // Read the status register at last written address + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + bufferSize - 2); + word statusReg = readWord_N64(); + while ((statusReg | 0xFF7F) != (currWord | 0xFF7F)) { + setAddress_N64(romBase + currSector + currSdBuffer + currWriteBuffer + bufferSize - 2); + statusReg = readWord_N64(); + } + } + } + } +} + +// Write MX29LV640 flashrom without write buffer +void writeFlashrom_N64() { + unsigned long flashBase = romBase; + + for (unsigned long currSector = 0; currSector < fileSize; currSector += 0x8000) { + // Blink led + PORTB ^= (1 << 4); + + // Macronix MX29LV640(8MB/16MB) with two flashrom chips + if (currSector == 0x800000) { + flashBase = romBase + 0x800000; + } + + // Write to flashrom + for (unsigned long currSdBuffer = 0; currSdBuffer < 0x8000; 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 ); + // 2 unlock commands + setAddress_N64(flashBase + (0x555 << 1)); + writeWord_N64(0xAA); + setAddress_N64(flashBase + (0x2AA << 1)); + writeWord_N64(0x55); + // Program command + setAddress_N64(flashBase + (0x555 << 1)); + writeWord_N64(0xA0); + // Write word + setAddress_N64(romBase + currSector + currSdBuffer + currByte); + writeWord_N64(currWord); + + // Read the status register + setAddress_N64(romBase + currSector + currSdBuffer + currByte); + word statusReg = readWord_N64(); + while ((statusReg | 0xFF7F) != (currWord | 0xFF7F)) { + setAddress_N64(romBase + currSector + currSdBuffer + currByte); + statusReg = readWord_N64(); + } + } + } + } +} + +unsigned long verifyFlashrom_N64() { + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + writeErrors = 0; + + for (unsigned long currSector = 0; currSector < fileSize; currSector += 131072) { + // Blink led + PORTB ^= (1 << 4); + 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 + currSector + currSdBuffer + currByte); + // Compare both + if (readWord_N64() != currWord) { + writeErrors++; + // Abord if too many errors + if (writeErrors > 20) { + print_Msg(F("More than ")); + // Close the file: + myFile.close(); + return writeErrors; + } + } + } + } + } + // Close the file: + myFile.close(); + return writeErrors; + } + else { + println_Msg(F("Can't open file")); + display_Update(); + return 9999; + } +} + +//****************************************** +// End of File //****************************************** diff --git a/Cart_Reader/NP.ino b/Cart_Reader/NP.ino index 0996ab1..2f414a2 100644 --- a/Cart_Reader/NP.ino +++ b/Cart_Reader/NP.ino @@ -1,2337 +1,2337 @@ -//****************************************** -// NINTENDO POWER MODULE -// (GB Memory starts at around line 1460) -//****************************************** - -/****************************************** - SF Memory Cassette -******************************************/ - -/****************************************** - SF Memory Clock Source -******************************************/ -// The clock signal for the SF Memory cassette -// is generated with the Adafruit Clock Generator -// or a similar external clock source - -/****************************************** - Variables - *****************************************/ -// SF Memory status -byte sfmReady = 0; - -// SF Memory Menu -boolean hasMenu = true; -byte numGames = 0; - -// Arrays that hold game info -int gameSize[8]; -int saveSize[8]; -byte gameAddress[8]; -byte gameVersion[8]; -char gameCode[8][10]; -boolean hirom[8]; - -/****************************************** - Menu -*****************************************/ -// SFM menu items -static const char sfmMenuItem1[] PROGMEM = "Game Menu"; -static const char sfmMenuItem2[] PROGMEM = "Flash Menu"; -static const char sfmMenuItem3[] PROGMEM = "Reset"; -static const char* const menuOptionsSFM[] PROGMEM = {sfmMenuItem1, sfmMenuItem2, sfmMenuItem3}; - -// SFM flash menu items -static const char sfmFlashMenuItem1[] PROGMEM = "Read Flash"; -static const char sfmFlashMenuItem2[] PROGMEM = "Write Flash"; -static const char sfmFlashMenuItem3[] PROGMEM = "Print Mapping"; -static const char sfmFlashMenuItem4[] PROGMEM = "Read Mapping"; -static const char sfmFlashMenuItem5[] PROGMEM = "Write Mapping"; -static const char sfmFlashMenuItem6[] PROGMEM = "Back"; -static const char* const menuOptionsSFMFlash[] PROGMEM = {sfmFlashMenuItem1, sfmFlashMenuItem2, sfmFlashMenuItem3, sfmFlashMenuItem4, sfmFlashMenuItem5, sfmFlashMenuItem6}; - -// SFM game menu items -static const char sfmGameMenuItem1[] PROGMEM = "Read Sram"; -static const char sfmGameMenuItem2[] PROGMEM = "Read Game"; -static const char sfmGameMenuItem3[] PROGMEM = "Write Sram"; -static const char sfmGameMenuItem4[] PROGMEM = "Switch Game"; -static const char sfmGameMenuItem5[] PROGMEM = "Reset"; -static const char* const menuOptionsSFMGame[] PROGMEM = {sfmGameMenuItem1, sfmGameMenuItem2, sfmGameMenuItem3, sfmGameMenuItem4, sfmGameMenuItem5}; - -void sfmMenu() { - // create menu with title and 3 options to choose from - unsigned char mainMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsSFM, 3); - mainMenu = question_box("SF Memory", menuOptions, 3, 0); - - // wait for user choice to come back from the question box menu - switch (mainMenu) - { - // Game menu - case 0: - sfmGameMenu(); - break; - // Flash menu - case 1: - mode = mode_SFM_Flash; - break; - // Reset - case 2: - asm volatile (" jmp 0"); - break; - } -} - -void sfmGameMenu() { - // Switch to hirom all - if (send_SFM(0x04) == 0x2A) { - delay(300); - - // Fill arrays with data - getGames(); - - if (hasMenu) { - // Create submenu options - char menuOptionsSFMGames[8][20]; - for (int i = 0; i < (numGames); i++) { - strncpy(menuOptionsSFMGames[i], gameCode[i], 10); - } - - // Create menu with title and numGames options to choose from - unsigned char gameSubMenu; - // wait for user choice to come back from the question box menu - gameSubMenu = question_box("Select Game", menuOptionsSFMGames, numGames, 0); - - // Switch to game - send_SFM(gameSubMenu + 0x80); - delay(200); - // Check for successfull switch - byte timeout = 0; - while (readBank_SNES(0, 0x2400) != 0x7D) { - delay(200); - // Try again - send_SFM(gameSubMenu + 0x80); - delay(200); - timeout++; - // Abort, something is wrong - if (timeout == 5) { - display_Clear(); - print_Msg(F("Game ")); - print_Msg(gameSubMenu + 0x80, HEX); - println_Msg(F(" Timeout")); - println_Msg(readBank_SNES(0, 0x2400), HEX); - println_Msg(F("")); - print_Error(F("Powercycle SFM cart"), true); - } - } - // Copy gameCode to romName in case of japanese chars in romName - strcpy(romName, gameCode[gameSubMenu + 1]); - - // Print info - getCartInfo_SNES(); - mode = mode_SFM_Game; - } - else { - // No menu so switch to only game - // Switch to game - send_SFM(0x80); - delay(200); - - // Copy gameCode to romName in case of japanese chars in romName - strcpy(romName, gameCode[0]); - - // Print info - getCartInfo_SNES(); - mode = mode_SFM_Game; - } - } - else { - print_Error(F("Switch to HiRom failed"), false); - } -} - -void sfmGameOptions() { - // create menu with title and 5 options to choose from - unsigned char gameSubMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsSFMGame, 5); - gameSubMenu = question_box("SFM Game Menu", menuOptions, 5, 0); - - // wait for user choice to come back from the question box menu - switch (gameSubMenu) - { - // Read sram - case 0: - display_Clear(); - // Change working dir to root - sd.chdir("/"); - readSRAM(); - break; - - // Read rom - case 1: - display_Clear(); - // Change working dir to root - sd.chdir("/"); - readROM_SNES(); - compare_checksum(); - break; - - // Write sram - case 2: - display_Clear(); - // Change working dir to root - sd.chdir("/"); - writeSRAM(1); - unsigned long wrErrors; - wrErrors = verifySRAM(); - if (wrErrors == 0) { - println_Msg(F("Verified OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(wrErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - break; - - // Switch game - case 3: - sfmGameMenu(); - break; - - // Reset - case 4: - asm volatile (" jmp 0"); - break; - } - if (gameSubMenu != 3) { - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - } -} - -void sfmFlashMenu() { - // create menu with title and 6 options to choose from - unsigned char flashSubMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsSFMFlash, 6); - flashSubMenu = question_box("SFM Flash Menu", menuOptions, 6, 0); - - // wait for user choice to come back from the question box menu - switch (flashSubMenu) - { - // Read Flash - case 0: - // Clear screen - display_Clear(); - - // Reset to root directory - sd.chdir("/"); - - // Reset to HIROM ALL - romType = 1; - print_Msg(F("Switch to HiRom...")); - display_Update(); - if (send_SFM(0x04) == 0x2A) { - println_Msg(F("OK")); - display_Update(); - - // Reset flash - resetFlash_SFM(0xC0); - resetFlash_SFM(0xE0); - - flashSize = 4194304; - numBanks = 64; - - // Get name, add extension and convert to char array for sd lib - EEPROM_readAnything(10, foldern); - sprintf(fileName, "SFM%d", foldern); - strcat(fileName, ".bin"); - sd.mkdir("NP", true); - sd.chdir("NP"); - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - // Read flash - readFlash_SFM(); - } - else { - print_Error(F("Switch to HiRom failed"), false); - } - break; - - // Write Flash - case 1: - // Clear screen - display_Clear(); - - // Print warning - println_Msg(F("Attention")); - println_Msg(F("This will erase your")); - println_Msg(F("NP Cartridge.")); - println_Msg(""); - println_Msg(F("Press Button")); - println_Msg(F("to continue")); - display_Update(); - wait(); - - // Clear screen - display_Clear(); - - filePath[0] = '\0'; - sd.chdir("/"); - // Launch file browser - fileBrowser("Select 4MB file"); - display_Clear(); - sprintf(filePath, "%s/%s", filePath, fileName); - flashSize = 2097152; - numBanks = 32; - println_Msg(F("Writing 1st rom")); - display_Update(); - // Program 1st flashrom - write_SFM(0xC0, 0); - display_Clear(); - println_Msg(F("Writing 2nd rom")); - display_Update(); - // Program 2nd flashrom - write_SFM(0xE0, 2097152); - break; - - // Print mapping - case 2: - // Clear screen - display_Clear(); - - // Reset to root directory - sd.chdir("/"); - - // Reset to HIROM ALL - romType = 1; - print_Msg(F("Switch to HiRom...")); - display_Update(); - if (send_SFM(0x04) == 0x2A) { - println_Msg(F("OK")); - display_Update(); - idFlash_SFM(0xC0); - if (strcmp(flashid, "c2f3") == 0) { - idFlash_SFM(0xE0); - if (strcmp(flashid, "c2f3") == 0) { - // Reset flash - resetFlash_SFM(0xC0); - resetFlash_SFM(0xE0); - delay(100); - // Clear screen - display_Clear(); - printMapping(); - resetFlash_SFM(0xC0); - resetFlash_SFM(0xE0); - } - else { - print_Error(F("Error: Wrong Flash ID"), true); - } - } - else { - print_Error(F("Error: Wrong Flash ID"), true); - } - } - else { - print_Error(F("failed"), false); - } - break; - - // Read mapping - case 3: - // Clear screen - display_Clear(); - - // Reset to root directory - sd.chdir("/"); - - // Reset to HIROM ALL - romType = 1; - print_Msg(F("Switch to HiRom...")); - display_Update(); - if (send_SFM(0x04) == 0x2A) { - println_Msg(F("OK")); - display_Update(); - idFlash_SFM(0xC0); - if (strcmp(flashid, "c2f3") == 0) { - idFlash_SFM(0xE0); - if (strcmp(flashid, "c2f3") == 0) { - // Reset flash - resetFlash_SFM(0xC0); - resetFlash_SFM(0xE0); - delay(100); - readMapping(); - resetFlash_SFM(0xC0); - resetFlash_SFM(0xE0); - } - else { - print_Error(F("Error: Wrong Flash ID"), true); - } - } - else { - print_Error(F("Error: Wrong Flash ID"), true); - } - } - else { - print_Error(F("failed"), false); - } - break; - - // Write mapping - case 4: - // Clear screen - display_Clear(); - - // Print warning - println_Msg(F("Attention")); - println_Msg(F("This will erase your")); - println_Msg(F("NP Cartridge.")); - println_Msg(""); - println_Msg(F("Press Button")); - println_Msg(F("to continue")); - display_Update(); - wait(); - - // Clear screen - display_Clear(); - - // Reset to root directory - sd.chdir("/"); - - // Erase mapping - eraseMapping(0xD0); - eraseMapping(0xE0); - print_Msg(F("Blankcheck...")); - display_Update(); - if (blankcheckMapping_SFM()) { - println_Msg(F("OK")); - display_Update(); - } - else { - println_Msg(F("Nope")); - break; - } - - // Clear screen - display_Clear(); - - // Clear filepath - filePath[0] = '\0'; - - // Reset to root directory - sd.chdir("/"); - - // Launch file browser - fileBrowser("Select MAP file"); - display_Clear(); - sprintf(filePath, "%s/%s", filePath, fileName); - display_Update(); - - // Write mapping - writeMapping_SFM(0xD0, 0); - writeMapping_SFM(0xE0, 256); - break; - - // Go back - case 5: - mode = mode_SFM; - break; - } - if (flashSubMenu != 5) { - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - } -} - -// Read the games from the menu area -void getGames() { - // Set data pins to input - dataIn(); - // Set control pins to input - controlIn_SNES(); - - // Check if menu is present - byte menuString[] = {0x4D, 0x45, 0x4E, 0x55, 0x20, 0x50, 0x52, 0x4F, 0x47, 0x52, 0x41, 0x4D}; - for (int i = 0; i < 12; i++) { - if (menuString[i] != readBank_SNES(0xC0, 0x7FC0 + i)) { - hasMenu = false; - } - } - - if (hasMenu) { - // Count number of games - for (word i = 0x0000; i < 0xE000; i += 0x2000) { - if (readBank_SNES(0xC6, i) == numGames ) - numGames++; - } - - // Get game info - for (int i = 0; i < numGames; i++) { - // Read starting address and size - gameAddress[i] = 0xC0 + readBank_SNES(0xC6, i * 0x2000 + 0x01) * 0x8; - gameSize[i] = readBank_SNES(0xC6, i * 0x2000 + 0x03) * 128; - saveSize[i] = readBank_SNES(0xC6, i * 0x2000 + 0x05) / 8; - - //check if hirom - if (readBank_SNES(gameAddress[i], 0xFFD5) == 0x31) { - hirom[i] = true; - } - else if (readBank_SNES(gameAddress[i], 0xFFD5) == 0x21) { - hirom[i] = true; - } - else { - hirom[i] = false; - } - - if (hirom[i]) { - gameVersion[i] = readBank_SNES(gameAddress[i], 0xFFDB); - } else { - gameVersion[i] = readBank_SNES(gameAddress[i], 0x7FDB); - } - - // Read game code - byte myByte = 0; - byte myLength = 0; - for (int j = 0; j < 9; j++) { - myByte = readBank_SNES(0xC6, i * 0x2000 + 0x07 + j); - // Remove funny characters - if (((char(myByte) >= 44 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 9) { - gameCode[i][myLength] = char(myByte); - myLength++; - } - } - // End char array in case game code is less than 9 chars - gameCode[i][myLength] = '\0'; - } - } - else { - //check if hirom - if (readBank_SNES(0xC0, 0xFFD5) == 0x31) { - hirom[0] = true; - } - else { - hirom[0] = false; - } - - if (hirom[0]) { - gameVersion[0] = readBank_SNES(0xC0, 0xFFDB); - gameCode[0][0] = 'G'; - gameCode[0][1] = 'A'; - gameCode[0][2] = 'M'; - gameCode[0][3] = 'E'; - gameCode[0][4] = '-'; - gameCode[0][5] = char(readBank_SNES(0xC0, 0xFFB2)); - gameCode[0][6] = char(readBank_SNES(0xC0, 0xFFB3)); - gameCode[0][7] = char(readBank_SNES(0xC0, 0xFFB4)); - gameCode[0][8] = char(readBank_SNES(0xC0, 0xFFB5)); - gameCode[0][9] = '\0'; - - byte romSizeExp = readBank_SNES(0xC0, 0xFFD7) - 7; - gameSize[0] = 1; - while (romSizeExp--) - gameSize[0] *= 2; - } - else { - gameVersion[0] = readBank_SNES(0xC0, 0x7FDB); - gameCode[0][0] = 'G'; - gameCode[0][1] = 'A'; - gameCode[0][2] = 'M'; - gameCode[0][3] = 'E'; - gameCode[0][4] = '-'; - gameCode[0][5] = char(readBank_SNES(0xC0, 0x7FB2)); - gameCode[0][6] = char(readBank_SNES(0xC0, 0x7FB3)); - gameCode[0][7] = char(readBank_SNES(0xC0, 0x7FB4)); - gameCode[0][8] = char(readBank_SNES(0xC0, 0x7FB5)); - gameCode[0][9] = '\0'; - - byte romSizeExp = readBank_SNES(0xC0, 0x7FD7) - 7; - gameSize[0] = 1; - while (romSizeExp--) - gameSize[0] *= 2; - } - } -} - -/****************************************** - Setup - *****************************************/ -void setup_SFM() { - // Set cicrstPin(PG1) to Output - DDRG |= (1 << 1); - // Output a high signal to disable snesCIC if installed - PORTG |= (1 << 1); - // Set cichstPin(PG0) to Input - DDRG &= ~(1 << 0); - - // Adafruit Clock Generator - clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0); - clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); - clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); - // Half Clock - //clockgen.set_freq(1073863600ULL, SI5351_CLK0); - // Full Clock - clockgen.set_freq(2147727200ULL, SI5351_CLK0); - // CIC Clock - //clockgen.set_freq(307200000ULL, SI5351_CLK2); - clockgen.output_enable(SI5351_CLK0, 1); - clockgen.output_enable(SI5351_CLK1, 0); - clockgen.output_enable(SI5351_CLK2, 0); - - // Set Address Pins to Output - //A0-A7 - DDRF = 0xFF; - //A8-A15 - DDRK = 0xFF; - //BA0-BA7 - DDRL = 0xFF; - - // Set Control Pins to Output RST(PH0) CS(PH3) WR(PH5) RD(PH6) - DDRH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); - // Output a high signal on all pins, pins are active low therefore everything is disabled now - PORTH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); - - // Set IRQ(PH4) to Input - DDRH &= ~(1 << 4); - // Activate Internal Pullup Resistors - PORTH |= (1 << 4); - - // Set Data Pins (D0-D7) to Input - DDRC = 0x00; - // Enable Internal Pullups - PORTC = 0xFF; - - // Wait until all is stable - delay(200); - - // Switch to HiRom All - byte timeout = 0; - send_SFM(0x04); - delay(100); - while (readBank_SNES(0, 0x2400) != 0x2A) { - delay(100); - // Try again - send_SFM(0x04); - delay(100); - timeout++; - // Abort, something is wrong - if (timeout == 5) { - println_Msg(F("Hirom All Timeout")); - println_Msg(F("")); - println_Msg(F("")); - print_Error(F("Powercycle SFM cart"), true); - } - } -} - -/****************************************** - 29F1601 flashrom functions (NP) - *****************************************/ -// Reset the MX29F1601 flashrom, startbank is 0xC0 for first and 0xE0 for second flashrom -void resetFlash_SFM(int startBank) { - // Configure control pins - controlOut_SNES(); - // Set data pins to output - dataOut(); - - // Reset command sequence - if (romType) { - writeBank_SNES(startBank, 0x5555L * 2, 0xaa); - writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); - writeBank_SNES(startBank, 0x5555L * 2, 0xf0); - } - else { - writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0xaa); - writeBank_SNES(0, 0x8000 + 0x2AAAL * 2, 0x55); - writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0xf0); - } -} - -// Print flashrom manufacturer and device ID -void idFlash_SFM(int startBank) { - // Configure control pins - controlOut_SNES(); - // Set data pins to output - dataOut(); - - if (romType) { - // ID command sequence - writeBank_SNES(startBank, 0x5555L * 2, 0xaa); - writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); - writeBank_SNES(startBank, 0x5555L * 2, 0x90); - - // Set data pins to input again - dataIn(); - // Set control pins to input - controlIn_SNES(); - - // Read the two id bytes into a string - sprintf(flashid, "%x%x", readBank_SNES(startBank, 0x00), readBank_SNES(startBank, 0x02)); - } - else { - writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0xaa); - writeBank_SNES(0, 0x8000 + 0x2AAAL * 2, 0x55); - writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0x90); - - // Set data pins to input again - dataIn(); - // Set control pins to input - controlIn_SNES(); - - // Read the two id bytes into a string - sprintf(flashid, "%x%x", readBank_SNES(0, 0x8000), readBank_SNES(0, 0x8000 + 0x02)); - } -} - -// Write the flashroms by reading a file from the SD card, pos defines where in the file the reading/writing should start -void writeFlash_SFM(int startBank, uint32_t pos) { - display_Clear(); - print_Msg(F("Writing Bank 0x")); - print_Msg(startBank, HEX); - print_Msg(F("...")); - display_Update(); - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - - // Seek to a new position in the file - if (pos != 0) - myFile.seekCur(pos); - - // Configure control pins - controlOut_SNES(); - // Set data pins to output - dataOut(); - - if (romType) { - // Write hirom - for (int currBank = startBank; currBank < startBank + numBanks; currBank++) { - // Fill SDBuffer with 1 page at a time then write it repeat until all bytes are written - for (unsigned long currByte = 0; currByte < 0x10000; currByte += 128) { - myFile.read(sdBuffer, 128); - // Write command sequence - writeBank_SNES(startBank, 0x5555L * 2, 0xaa); - writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); - writeBank_SNES(startBank, 0x5555L * 2, 0xa0); - - for (byte c = 0; c < 128; c++) { - - // Write one byte of data - writeBank_SNES(currBank, currByte + c, sdBuffer[c]); - - if (c == 127) { - // Write the last byte twice or else it won't write at all - writeBank_SNES(currBank, currByte + c, sdBuffer[c]); - } - } - // Wait until write is finished - busyCheck_SFM(startBank); - } - } - } - else { - // Write lorom - for (int currBank = 0; currBank < numBanks; currBank++) { - for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte += 128) { - myFile.read(sdBuffer, 128); - // Write command sequence - writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0xaa); - writeBank_SNES(0, 0x8000 + 0x2AAAL * 2, 0x55); - writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0xa0); - - for (byte c = 0; c < 128; c++) { - // Write one byte of data - writeBank_SNES(currBank, currByte + c, sdBuffer[c]); - - if (c == 127) { - // Write the last byte twice or else it won't write at all - writeBank_SNES(currBank, currByte + c, sdBuffer[c]); - } - } - // Wait until write is finished - busyCheck_SFM(startBank); - } - } - } - // Close the file: - myFile.close(); - println_Msg(""); - } - else { - print_Error(F("Can't open file on SD"), true); - } -} - -// Delay between write operations based on status register -void busyCheck_SFM(byte startBank) { - // Set data pins to input - dataIn(); - // Set control pins to input and therefore pull CE low and latch status register content - controlIn_SNES(); - - // Read register - readBank_SNES(startBank, 0x0000); - - // Read D7 while D7 = 0 - //1 = B00000001, 1 << 7 = B10000000, PINC = B1XXXXXXX (X = don't care), & = bitwise and - while (!(PINC & (1 << 7))) { - // CE or OE must be toggled with each subsequent status read or the - // completion of a program or erase operation will not be evident. - // Switch RD(PH6) to HIGH - PORTH |= (1 << 6); - - // one nop ~62.5ns - __asm__("nop\n\t"); - - // Switch RD(PH6) to LOW - PORTH &= ~(1 << 6); - - // one nop ~62.5ns - __asm__("nop\n\t"); - } - - // Configure control pins - controlOut_SNES(); - // Set data pins to output - dataOut(); -} - -// Erase the flashrom to 0xFF -void eraseFlash_SFM(int startBank) { - - // Configure control pins - controlOut_SNES(); - // Set data pins to output - dataOut(); - - if (romType) { - // Erase command sequence - writeBank_SNES(startBank, 0x5555L * 2, 0xaa); - writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); - writeBank_SNES(startBank, 0x5555L * 2, 0x80); - writeBank_SNES(startBank, 0x5555L * 2, 0xaa); - writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); - writeBank_SNES(startBank, 0x5555L * 2, 0x10); - } - else { - writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0xaa); - writeBank_SNES(0, 0x8000 + 0x2AAAL * 2, 0x55); - writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0x80); - writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0xaa); - writeBank_SNES(0, 0x8000 + 0x2AAAL * 2, 0x55); - writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0x10); - } - - // Wait for erase to complete - busyCheck_SFM(startBank); -} - -// Check if an erase succeeded, return 1 if blank and 0 if not -byte blankcheck_SFM(int startBank) { - - // Set data pins to input again - dataIn(); - // Set control pins to input - controlIn_SNES(); - - byte blank = 1; - if (romType) { - for (int currBank = startBank; currBank < startBank + numBanks; currBank++) { - for (unsigned long currByte = 0; currByte < 0x10000; currByte++) { - if (readBank_SNES(currBank, currByte) != 0xFF) { - currBank = startBank + numBanks; - blank = 0; - } - } - } - } - else { - for (int currBank = 0; currBank < numBanks; currBank++) { - for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte++) { - if (readBank_SNES(currBank, currByte) != 0xFF) { - currBank = numBanks; - blank = 0; - } - } - } - } - return blank; -} - -// Check if a write succeeded, returns 0 if all is ok and number of errors if not -unsigned long verifyFlash_SFM(int startBank, uint32_t pos) { - unsigned long verified = 0; - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - - // Set file starting position - myFile.seekCur(pos); - - // Set data pins to input - dataIn(); - // Set control pins to input - controlIn_SNES(); - - if (romType) { - for (int currBank = startBank; currBank < startBank + numBanks; currBank++) { - for (unsigned long currByte = 0; currByte < 0x10000; currByte += 512) { - // Fill SDBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if (readBank_SNES(currBank, currByte + c) != sdBuffer[c]) { - verified++; - } - } - } - } - } - else { - for (int currBank = 0; currBank < numBanks; currBank++) { - for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte += 512) { - // Fill SDBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if (readBank_SNES(currBank, currByte + c) != sdBuffer[c]) { - verified++; - } - } - } - } - } - // Close the file: - myFile.close(); - } - else { - // SD Error - verified = 999999; - print_Error(F("Can't open file on SD"), false); - } - // Return 0 if verified ok, or number of errors - return verified; -} - -// Read flashroms and save them to the SD card -void readFlash_SFM() { - // Set data pins to input - dataIn(); - // Set control pins to input - controlIn_SNES(); - - print_Msg(F("Saving as NP/")); - print_Msg(fileName); - println_Msg(F("...")); - display_Update(); - - // Open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("Can't create file on SD"), true); - } - if (romType) { - for (int currBank = 0xC0; currBank < 0xC0 + numBanks; currBank++) { - for (unsigned long currByte = 0; currByte < 0x10000; currByte += 512) { - for (int c = 0; c < 512; c++) { - sdBuffer[c] = readBank_SNES(currBank, currByte + c); - } - myFile.write(sdBuffer, 512); - } - } - } - else { - for (int currBank = 0; currBank < numBanks; currBank++) { - for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte += 512) { - for (int c = 0; c < 512; c++) { - sdBuffer[c] = readBank_SNES(currBank, currByte + c); - } - myFile.write(sdBuffer, 512); - } - } - } - // Close the file: - myFile.close(); - println_Msg(""); - println_Msg(F("Finished reading")); - display_Update(); -} - -// Display protected sectors/banks as 0xc2 and unprotected as 0x00 -void readSectorProtection_SFM(byte startBank) { - - // Configure control pins - controlOut_SNES(); - // Set data pins to output - dataOut(); - - // Display Sector Protection Status - writeBank_SNES(startBank, 0x5555L * 2, 0xaa); - writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); - writeBank_SNES(startBank, 0x5555L * 2, 0x90); - - // Configure control pins - controlIn_SNES(); - // Set data pins to output - dataIn(); - display_Clear(); - for (int i = 0; i <= 0x1F; i++) { - print_Msg(F("Sector: 0x")); - print_Msg(startBank + i, HEX); - print_Msg(F(" Sector Protect: 0x")); - println_Msg(readBank_SNES(startBank + i, 0x04), HEX); - } - display_Update(); -} - -// Read the current mapping from the hidden "page buffer" and print it -void printMapping() { - // Switch to write - dataOut(); - controlOut_SNES(); - - // Reset to defaults - writeBank_SNES(0xC0, 0x0000, 0x38); - writeBank_SNES(0xC0, 0x0000, 0xd0); - // Read Extended Status Register (GSR and PSR) - writeBank_SNES(0xC0, 0x0000, 0x71); - // Page Buffer Swap - writeBank_SNES(0xC0, 0x0000, 0x72); - // Read Page Buffer - writeBank_SNES(0xC0, 0x0000, 0x75); - - // Switch to read - dataIn(); - controlIn_SNES(); - - // Read the mapping out of the first chip - char buffer[3]; - - for (int currByte = 0xFF00; currByte < 0xFF50; currByte += 10) { - for (int c = 0; c < 10; c++) { - itoa (readBank_SNES(0xC0, currByte + c), buffer, 16); - for (int i = 0; i < 2 - strlen(buffer); i++) { - print_Msg("0"); - } - // Now print the significant bits - print_Msg(buffer); - } - println_Msg(""); - } - display_Update(); - - // Switch to write - dataOut(); - controlOut_SNES(); - - // Reset Flash - writeBank_SNES(0xC0, 0x5555L * 2, 0xaa); - writeBank_SNES(0xC0, 0x2AAAL * 2, 0x55); - writeBank_SNES(0xC0, 0x5555L * 2, 0xf0); - - // Reset Flash - writeBank_SNES(0xE0, 0x5555L * 2, 0xaa); - writeBank_SNES(0xE0, 0x2AAAL * 2, 0x55); - writeBank_SNES(0xE0, 0x5555L * 2, 0xf0); - - // Switch to read - dataIn(); - controlIn_SNES(); -} - -// Read the current mapping from the hidden "page buffer" -void readMapping() { - - // Switch to write - dataOut(); - controlOut_SNES(); - - // Reset to defaults - writeBank_SNES(0xC0, 0x0000, 0x38); - writeBank_SNES(0xC0, 0x0000, 0xd0); - // Read Extended Status Register (GSR and PSR) - writeBank_SNES(0xC0, 0x0000, 0x71); - // Page Buffer Swap - writeBank_SNES(0xC0, 0x0000, 0x72); - // Read Page Buffer - writeBank_SNES(0xC0, 0x0000, 0x75); - - // Switch to read - dataIn(); - controlIn_SNES(); - - // Get name, add extension and convert to char array for sd lib - EEPROM_readAnything(10, foldern); - sprintf(fileName, "NP%d", foldern); - strcat(fileName, ".MAP"); - sd.mkdir("NP", true); - sd.chdir("NP"); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - //open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("SD Error"), true); - } - - // Read the mapping info out of the 1st chip - for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { - myFile.write(readBank_SNES(0xC0, currByte)); - } - - // Switch to write - dataOut(); - controlOut_SNES(); - - // Reset to defaults - writeBank_SNES(0xE0, 0x0000, 0x38); - writeBank_SNES(0xE0, 0x0000, 0xd0); - // Read Extended Status Register (GSR and PSR) - writeBank_SNES(0xE0, 0x0000, 0x71); - // Page Buffer Swap - writeBank_SNES(0xE0, 0x0000, 0x72); - // Read Page Buffer - writeBank_SNES(0xE0, 0x0000, 0x75); - - // Switch to read - dataIn(); - controlIn_SNES(); - - // Read the mapping info out of the 1st chip - for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { - myFile.write(readBank_SNES(0xE0, currByte)); - } - - // Close the file: - myFile.close(); - - // Switch to write - dataOut(); - controlOut_SNES(); - - // Reset Flash - writeBank_SNES(0xC0, 0x5555L * 2, 0xaa); - writeBank_SNES(0xC0, 0x2AAAL * 2, 0x55); - writeBank_SNES(0xC0, 0x5555L * 2, 0xf0); - - // Reset Flash - writeBank_SNES(0xE0, 0x5555L * 2, 0xaa); - writeBank_SNES(0xE0, 0x2AAAL * 2, 0x55); - writeBank_SNES(0xE0, 0x5555L * 2, 0xf0); - - // Switch to read - dataIn(); - controlIn_SNES(); - - // Signal end of process - print_Msg(F("Saved to NP/")); - println_Msg(fileName); - display_Update(); -} - -void eraseMapping(byte startBank) { - - if (unlockHirom()) { - // Get ID - idFlash_SFM(startBank); - if (strcmp(flashid, "c2f3") == 0) { - resetFlash_SFM(startBank); - - // Switch to write - dataOut(); - controlOut_SNES(); - - // Prepare to erase/write Page Buffer - writeBank_SNES(startBank, 0x5555L * 2, 0xaa); - writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); - writeBank_SNES(startBank, 0x5555L * 2, 0x77); - // Erase Page Buffer - writeBank_SNES(startBank, 0x5555L * 2, 0xaa); - writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); - writeBank_SNES(startBank, 0x5555L * 2, 0xe0); - - // Wait until complete - busyCheck_SFM(startBank); - - // Switch to read - dataIn(); - controlIn_SNES(); - } - else { - print_Error(F("Error: Wrong Flash ID"), true); - } - } - else { - print_Error(F("Unlock failed"), true); - } -} - -// Check if the current mapping is all 0xFF -byte blankcheckMapping_SFM() { - byte blank = 1; - - // Switch to write - dataOut(); - controlOut_SNES(); - - // Reset to defaults - writeBank_SNES(0xC0, 0x0000, 0x38); - writeBank_SNES(0xC0, 0x0000, 0xd0); - // Read Extended Status Register (GSR and PSR) - writeBank_SNES(0xC0, 0x0000, 0x71); - // Page Buffer Swap - writeBank_SNES(0xC0, 0x0000, 0x72); - // Read Page Buffer - writeBank_SNES(0xC0, 0x0000, 0x75); - - // Switch to read - dataIn(); - controlIn_SNES(); - - // Read the mapping info out of the 1st chip - for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { - if (readBank_SNES(0xC0, currByte) != 0xFF) { - blank = 0; - } - } - - // Switch to write - dataOut(); - controlOut_SNES(); - - // Reset to defaults - writeBank_SNES(0xE0, 0x0000, 0x38); - writeBank_SNES(0xE0, 0x0000, 0xd0); - // Read Extended Status Register (GSR and PSR) - writeBank_SNES(0xE0, 0x0000, 0x71); - // Page Buffer Swap - writeBank_SNES(0xE0, 0x0000, 0x72); - // Read Page Buffer - writeBank_SNES(0xE0, 0x0000, 0x75); - - // Switch to read - dataIn(); - controlIn_SNES(); - - // Read the mapping info out of the 1st chip - for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { - if (readBank_SNES(0xE0, currByte) != 0xFF) { - blank = 0; - } - } - - // Switch to write - dataOut(); - controlOut_SNES(); - - // Reset Flash - writeBank_SNES(0xC0, 0x5555L * 2, 0xaa); - writeBank_SNES(0xC0, 0x2AAAL * 2, 0x55); - writeBank_SNES(0xC0, 0x5555L * 2, 0xf0); - - // Reset Flash - writeBank_SNES(0xE0, 0x5555L * 2, 0xaa); - writeBank_SNES(0xE0, 0x2AAAL * 2, 0x55); - writeBank_SNES(0xE0, 0x5555L * 2, 0xf0); - - // Switch to read - dataIn(); - controlIn_SNES(); - - return blank; -} - -void writeMapping_SFM(byte startBank, uint32_t pos) { - - if (unlockHirom()) { - // Get ID - idFlash_SFM(startBank); - if (strcmp(flashid, "c2f3") == 0) { - resetFlash_SFM(startBank); - - // Switch to write - dataOut(); - controlOut_SNES(); - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - - // Seek to a new position in the file - if (pos != 0) - myFile.seekCur(pos); - - // Write to Page Buffer - for (unsigned long currByte = 0xFF00; currByte < 0xFFFF; currByte += 128) { - // Prepare to erase/write Page Buffer - writeBank_SNES(startBank, 0x5555L * 2, 0xaa); - writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); - writeBank_SNES(startBank, 0x5555L * 2, 0x77); - // Write Page Buffer Command - writeBank_SNES(startBank, 0x5555L * 2, 0xaa); - writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); - writeBank_SNES(startBank, 0x5555L * 2, 0x99); - - myFile.read(sdBuffer, 128); - - for (byte c = 0; c < 128; c++) { - writeBank_SNES(startBank, currByte + c, sdBuffer[c]); - // Write last byte twice - if (c == 127) { - writeBank_SNES(startBank, currByte + c, sdBuffer[c]); - } - } - busyCheck_SFM(startBank); - } - - // Close the file: - myFile.close(); - println_Msg(""); - } - else { - print_Error(F("Can't open file on SD"), false); - } - - // Switch to read - dataIn(); - controlIn_SNES(); - } - else { - print_Error(F("Error: Wrong Flash ID"), true); - } - } - else { - print_Error(F("Unlock failed"), true); - } -} - -/****************************************** - SF Memory functions -*****************************************/ -// Switch to HiRom All and unlock Write Protection -boolean unlockHirom() { - romType = 1; - print_Msg(F("Switch to HiRom...")); - display_Update(); - if (send_SFM(0x04) == 0x2A) { - println_Msg(F("OK")); - display_Update(); - // Unlock Write Protection - print_Msg(F("Enable Write...")); - display_Update(); - send_SFM(0x02); - if (readBank_SNES(0, 0x2401) == 0x4) { - println_Msg(F("OK")); - display_Update(); - return 1; - } - else { - println_Msg(F("failed")); - display_Update(); - return 0; - } - } - else { - println_Msg(F("failed")); - display_Update(); - return 0; - } -} - -// Send a command to the MX15001 chip -byte send_SFM(byte command) { - // Switch to write - dataOut(); - controlOut_SNES(); - - // Write command - writeBank_SNES(0, 0x2400, 0x09); - - // Switch to read - dataIn(); - controlIn_SNES(); - - // Read status - sfmReady = readBank_SNES(0, 0x2400); - - // Switch to write - dataOut(); - controlOut_SNES(); - - writeBank_SNES(0, 0x2401, 0x28); - writeBank_SNES(0, 0x2401, 0x84); - - // NP_CMD_06h, send this only if above read has returned 7Dh, not if it's already returning 2Ah - if (sfmReady == 0x7D) { - writeBank_SNES(0, 0x2400, 0x06); - writeBank_SNES(0, 0x2400, 0x39); - } - - // Write the command - writeBank_SNES(0, 0x2400, command); - - // Switch to read - dataIn(); - controlIn_SNES(); - - // Read status - sfmReady = readBank_SNES(0, 0x2400); - return sfmReady; -} - -// This function will erase and program the NP cart from a 4MB file off the SD card -void write_SFM(int startBank, uint32_t pos) { - // Switch NP cart's mapping - if (unlockHirom()) { - // Get ID - idFlash_SFM(startBank); - if (strcmp(flashid, "c2f3") == 0) { - print_Msg(F("Flash ID: ")); - println_Msg(flashid); - display_Update(); - resetFlash_SFM(startBank); - delay(1000); - // Erase flash - print_Msg(F("Blankcheck...")); - display_Update(); - if (blankcheck_SFM(startBank)) { - println_Msg(F("OK")); - display_Update(); - } - else { - println_Msg(F("Nope")); - display_Clear(); - print_Msg(F("Erasing...")); - display_Update(); - eraseFlash_SFM(startBank); - resetFlash_SFM(startBank); - println_Msg(F("Done")); - print_Msg(F("Blankcheck...")); - display_Update(); - if (blankcheck_SFM(startBank)) { - println_Msg(F("OK")); - display_Update(); - } - else { - print_Error(F("Could not erase flash"), true); - } - } - // Write flash - writeFlash_SFM(startBank, pos); - - // Reset flash - resetFlash_SFM(startBank); - - // Checking for errors - print_Msg(F("Verifying...")); - display_Update(); - writeErrors = verifyFlash_SFM(startBank, pos); - if (writeErrors == 0) { - println_Msg(F("OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), true); - } - } - else { - print_Error(F("Error: Wrong Flash ID"), true); - } - } - else { - print_Error(F("Unlock failed"), true); - } -} - -/****************************************** - GB Memory Cassette -******************************************/ - -/****************************************** - Menu -*****************************************/ -// GBM menu items -static const char gbmMenuItem1[] PROGMEM = "Read ID"; -static const char gbmMenuItem2[] PROGMEM = "Read Flash"; -static const char gbmMenuItem3[] PROGMEM = "Erase Flash"; -static const char gbmMenuItem4[] PROGMEM = "Blankcheck"; -static const char gbmMenuItem5[] PROGMEM = "Write Flash"; -static const char gbmMenuItem6[] PROGMEM = "Read Mapping"; -static const char gbmMenuItem7[] PROGMEM = "Write Mapping"; -static const char* const menuOptionsGBM[] PROGMEM = {gbmMenuItem1, gbmMenuItem2, gbmMenuItem3, gbmMenuItem4, gbmMenuItem5, gbmMenuItem6, gbmMenuItem7}; - -void gbmMenu() { - // create menu with title and 7 options to choose from - unsigned char mainMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsGBM, 7); - mainMenu = question_box("GB Memory Menu", menuOptions, 7, 0); - - // wait for user choice to come back from the question box menu - switch (mainMenu) - { - // Read Flash ID - case 0: - // Clear screen - display_Clear(); - readFlashID_GBM(); - break; - - // Read Flash - case 1: - // Clear screen - display_Clear(); - // Print warning - println_Msg(F("Attention")); - println_Msg(F("Always power cycle")); - println_Msg(F("cartreader directly")); - println_Msg(F("before reading")); - println_Msg(""); - println_Msg(F("Press Button")); - println_Msg(F("to continue")); - display_Update(); - wait(); - // Clear screen - display_Clear(); - - // Reset to root directory - sd.chdir("/"); - - // Enable access to ports 0120h - send_GBM(0x09); - // Map entire flashrom - send_GBM(0x04); - // Disable ports 0x0120... - send_GBM(0x08); - // Read 1MB rom - readROM_GBM(64); - break; - - // Erase Flash - case 2: - // Clear screen - display_Clear(); - // Print warning - println_Msg(F("Attention")); - println_Msg(F("This will erase your")); - println_Msg(F("NP Cartridge.")); - println_Msg(""); - println_Msg(""); - println_Msg(F("Press Button")); - println_Msg(F("to continue")); - display_Update(); - wait(); - // Clear screen - display_Clear(); - eraseFlash_GBM(); - break; - - // Blankcheck Flash - case 3: - // Clear screen - display_Clear(); - if (blankcheckFlash_GBM()) { - println_Msg(F("OK")); - display_Update(); - } - else { - println_Msg(F("ERROR")); - display_Update(); - } - break; - - // Write Flash - case 4: - // Clear screen - display_Clear(); - - filePath[0] = '\0'; - sd.chdir("/"); - // Launch file browser - fileBrowser("Select 1MB file"); - display_Clear(); - sprintf(filePath, "%s/%s", filePath, fileName); - - // Write rom - writeFlash_GBM(); - break; - - // Read mapping - case 5: - // Clear screen - display_Clear(); - - // Reset to root directory - sd.chdir("/"); - - // Read mapping - readMapping_GBM(); - break; - - // Write mapping - case 6: - // Clear screen - display_Clear(); - - // Print warning - println_Msg(F("Attention")); - println_Msg(F("This will erase your")); - println_Msg(F("NP Cartridge's")); - println_Msg(F("mapping data")); - println_Msg(""); - println_Msg(F("Press Button")); - println_Msg(F("to continue")); - display_Update(); - wait(); - - // Reset to root directory - sd.chdir("/"); - - // Clear screen - display_Clear(); - - // Clear filepath - filePath[0] = '\0'; - - // Reset to root directory - sd.chdir("/"); - - // Launch file browser - fileBrowser("Select MAP file"); - display_Clear(); - sprintf(filePath, "%s/%s", filePath, fileName); - display_Update(); - - // Clear screen - display_Clear(); - - // Erase mapping - eraseMapping_GBM(); - if (blankcheckMapping_GBM()) { - println_Msg(F("OK")); - display_Update(); - } - else { - print_Error(F("Erasing failed"), false); - break; - } - - // Write mapping - writeMapping_GBM(); - break; - } - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); -} - -/****************************************** - Setup -*****************************************/ -void setup_GBM() { - // Set RST(PH0) to Input - DDRH &= ~(1 << 0); - // Activate Internal Pullup Resistors - PORTH |= (1 << 0); - - // Set Address Pins to Output - //A0-A7 - DDRF = 0xFF; - //A8-A15 - DDRK = 0xFF; - - // Set Control Pins to Output RST(PH0) CS(PH3) WR(PH5) RD(PH6) - DDRH |= (1 << 3) | (1 << 5) | (1 << 6); - // Output a high signal on all pins, pins are active low therefore everything is disabled now - PORTH |= (1 << 3) | (1 << 5) | (1 << 6); - - // Set Data Pins (D0-D7) to Input - DDRC = 0x00; - - delay(400); - - // Check for Nintendo Power GB Memory cart - byte timeout = 0; - - // First byte of NP register is always 0x21 - while (readByte_GBM(0x120) != 0x21) { - // Enable ports 0x120h (F2) - send_GBM(0x09); - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - timeout++; - if (timeout > 10) { - println_Msg(F("Error: Time Out")); - print_Error(F("Please power cycle"), true); - } - } -} - -/********************** - LOW LEVEL -**********************/ -// Read one word out of the cartridge -byte readByte_GBM(word myAddress) { - // Set data pins to Input - DDRC = 0x0; - PORTF = myAddress & 0xFF; - PORTK = (myAddress >> 8) & 0xFF; - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Switch CS(PH3) and RD(PH6) to LOW - PORTH &= ~(1 << 3); - PORTH &= ~(1 << 6); - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Read - byte tempByte = PINC; - - // Switch CS(PH3) and RD(PH6) to HIGH - PORTH |= (1 << 6); - PORTH |= (1 << 3); - - return tempByte; -} - -// Write one word to data pins of the cartridge -void writeByte_GBM(word myAddress, byte myData) { - // Set data pins to Output - DDRC = 0xFF; - PORTF = myAddress & 0xFF; - PORTK = (myAddress >> 8) & 0xFF; - PORTC = myData; - - // Pull CS(PH3) and write(PH5) low - PORTH &= ~(1 << 3); - PORTH &= ~(1 << 5); - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Pull CS(PH3) and write(PH5) high - PORTH |= (1 << 5); - PORTH |= (1 << 3); - - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Set data pins to Input (or read errors??!) - DDRC = 0x0; -} - -/********************** - HELPER FUNCTIONS -**********************/ -void printSdBuffer(word startByte, word numBytes) { - for (int currByte = 0; currByte < numBytes; currByte += 10) { - for (byte c = 0; c < 10; c++) { - // Convert to char array so we don't lose leading zeros - char currByteStr[2]; - sprintf(currByteStr, "%02X", sdBuffer[startByte + currByte + c]); - print_Msg(currByteStr); - } - // Add a new line every 10 bytes - println_Msg(""); - } - display_Update(); -} - -void readROM_GBM(word numBanks) { - println_Msg(F("Reading Rom...")); - display_Update(); - - // Get name, add extension and convert to char array for sd lib - EEPROM_readAnything(10, foldern); - sprintf(fileName, "GBM%d", foldern); - strcat(fileName, ".bin"); - sd.mkdir("NP", true); - sd.chdir("NP"); - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - // Open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("Can't create file on SD"), true); - } - else { - // Read rom - word currAddress = 0; - - for (word currBank = 1; currBank < numBanks; currBank++) { - // Set rom bank - writeByte_GBM(0x2100, currBank); - - // Switch bank start address - if (currBank > 1) { - currAddress = 0x4000; - } - - for (; currAddress < 0x7FFF; currAddress += 512) { - for (int currByte = 0; currByte < 512; currByte++) { - sdBuffer[currByte] = readByte_GBM(currAddress + currByte); - } - myFile.write(sdBuffer, 512); - } - } - - // Close the file: - myFile.close(); - - // Signal end of process - print_Msg(F("Saved to NP/")); - println_Msg(fileName); - display_Update(); - } -} - -/********************** - GB Memory Functions -**********************/ -void send_GBM(byte myCommand) { - switch (myCommand) { - case 0x01: - //CMD_01h -> ??? - writeByte_GBM(0x0120, 0x01); - writeByte_GBM(0x013F, 0xA5); - break; - - case 0x02: - //CMD_02h -> Write enable Step 2 - writeByte_GBM(0x0120, 0x02); - writeByte_GBM(0x013F, 0xA5); - break; - - case 0x03: - //CMD_03h -> Undo write Step 2 - writeByte_GBM(0x0120, 0x03); - writeByte_GBM(0x013F, 0xA5); - break; - - case 0x04: - //CMD_04h -> Map entire flashrom (MBC4 mode) - writeByte_GBM(0x0120, 0x04); - writeByte_GBM(0x013F, 0xA5); - break; - - case 0x05: - //CMD_05h -> Map menu (MBC5 mode) - writeByte_GBM(0x0120, 0x05); - writeByte_GBM(0x013F, 0xA5); - break; - - case 0x08: - //CMD_08h -> disable writes/reads to/from special Nintendo Power registers (those at 0120h..013Fh) - writeByte_GBM(0x0120, 0x08); - writeByte_GBM(0x013F, 0xA5); - break; - - case 0x09: - //CMD_09h Wakeup -> re-enable access to ports 0120h..013Fh - writeByte_GBM(0x0120, 0x09); - writeByte_GBM(0x0121, 0xAA); - writeByte_GBM(0x0122, 0x55); - writeByte_GBM(0x013F, 0xA5); - break; - - case 0x0A: - //CMD_0Ah -> Write enable Step 1 - writeByte_GBM(0x0120, 0x0A); - writeByte_GBM(0x0125, 0x62); - writeByte_GBM(0x0126, 0x04); - writeByte_GBM(0x013F, 0xA5); - break; - - case 0x10: - //CMD_10h -> disable writes to normal MBC registers (such like 2100h) - writeByte_GBM(0x0120, 0x10); - writeByte_GBM(0x013F, 0xA5); - break; - - case 0x11: - //CMD_11h -> re-enable access to MBC registers like 0x2100 - writeByte_GBM(0x0120, 0x11); - writeByte_GBM(0x013F, 0xA5); - break; - - default: - print_Error(F("Unknown Command"), true); - break; - } -} - -void send_GBM(byte myCommand, word myAddress, byte myData) { - byte myAddrLow = myAddress & 0xFF; - byte myAddrHigh = (myAddress >> 8) & 0xFF; - - switch (myCommand) { - case 0x0F: - // CMD_0Fh -> Write address/byte to flash - writeByte_GBM(0x0120, 0x0F); - writeByte_GBM(0x0125, myAddrHigh); - writeByte_GBM(0x0126, myAddrLow); - writeByte_GBM(0x0127, myData); - writeByte_GBM(0x013F, 0xA5); - break; - - default: - print_Error(F("Unknown Command"), true); - break; - } -} - -void switchGame_GBM(byte myData) { - // Enable ports 0x0120 (F2) - send_GBM(0x09); - - //CMD_C0h -> map selected game without reset - writeByte_GBM(0x0120, 0xC0 & myData); - writeByte_GBM(0x013F, 0xA5); -} - -void resetFlash_GBM() { - // Enable ports 0x0120 (F2) - send_GBM(0x09); - - // Send reset command - writeByte_GBM(0x2100, 0x01); - send_GBM(0x0F, 0x5555, 0xAA); - send_GBM(0x0F, 0x2AAA, 0x55); - send_GBM(0x0F, 0x5555, 0xF0); - delay(100); -} - -boolean readFlashID_GBM() { - // Enable ports 0x0120 (F2) - send_GBM(0x09); - - writeByte_GBM(0x2100, 0x01); - // Read ID command - send_GBM(0x0F, 0x5555, 0xAA); - send_GBM(0x0F, 0x2AAA, 0x55); - send_GBM(0x0F, 0x5555, 0x90); - - // Read the two id bytes into a string - sprintf(flashid, "%02X%02X", readByte_GBM(0), readByte_GBM(1)); - if (strcmp(flashid, "C289") == 0) { - print_Msg(F("Flash ID: ")); - println_Msg(flashid); - display_Update(); - resetFlash_GBM(); - return 1; - } - else { - print_Msg(F("Flash ID: ")); - println_Msg(flashid); - print_Error(F("Unknown Flash ID"), true); - resetFlash_GBM(); - return 0; - } -} - -void eraseFlash_GBM() { - println_Msg(F("Erasing...")); - display_Update(); - - //enable access to ports 0120h - send_GBM(0x09); - // Enable write - send_GBM(0x0A); - send_GBM(0x2); - - // Unprotect sector 0 - send_GBM(0x0F, 0x5555, 0xAA); - send_GBM(0x0F, 0x2AAA, 0x55); - send_GBM(0x0F, 0x5555, 0x60); - send_GBM(0x0F, 0x5555, 0xAA); - send_GBM(0x0F, 0x2AAA, 0x55); - send_GBM(0x0F, 0x5555, 0x40); - - // Wait for unprotect to complete - while ((readByte_GBM(0) & 0x80) != 0x80) {} - - // Send erase command - send_GBM(0x0F, 0x5555, 0xaa); - send_GBM(0x0F, 0x2AAA, 0x55); - send_GBM(0x0F, 0x5555, 0x80); - send_GBM(0x0F, 0x5555, 0xaa); - send_GBM(0x0F, 0x2AAA, 0x55); - send_GBM(0x0F, 0x5555, 0x10); - - // Wait for erase to complete - while ((readByte_GBM(0) & 0x80) != 0x80) {} - - // Reset flashrom - resetFlash_GBM(); -} - -boolean blankcheckFlash_GBM() { - print_Msg(F("Blankcheck...")); - display_Update(); - - //enable access to ports 0120h (F2) - send_GBM(0x09); - - // Map entire flashrom - send_GBM(0x04); - // Disable ports 0x0120... - send_GBM(0x08); - - // Read rom - word currAddress = 0; - - for (byte currBank = 1; currBank < 64; currBank++) { - // Set rom bank - writeByte_GBM(0x2100, currBank); - - // Switch bank start address - if (currBank > 1) { - currAddress = 0x4000; - } - - for (; currAddress < 0x7FFF; currAddress++) { - if (readByte_GBM(currAddress) != 0xFF) { - return 0; - } - } - } - return 1; -} - -void writeFlash_GBM() { - print_Msg(F("Writing...")); - display_Update(); - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - // Get rom size from file - fileSize = myFile.fileSize(); - if ((fileSize / 0x4000) > 64) { - print_Error(F("File is too big."), true); - } - - // Enable access to ports 0120h - send_GBM(0x09); - // Enable write - send_GBM(0x0A); - send_GBM(0x2); - - // Map entire flash rom - send_GBM(0x4); - - // Set bank for unprotect command, writes to 0x5555 need odd bank number - writeByte_GBM(0x2100, 0x1); - - // Disable ports 0x2100 and 0x120 or else those addresses will not be writable - send_GBM(0x10); - send_GBM(0x08); - - // Unprotect sector 0 - writeByte_GBM(0x5555, 0xAA); - writeByte_GBM(0x2AAA, 0x55); - writeByte_GBM(0x5555, 0x60); - writeByte_GBM(0x5555, 0xAA); - writeByte_GBM(0x2AAA, 0x55); - writeByte_GBM(0x5555, 0x40); - - // Check if flashrom is ready for writing or busy - while ((readByte_GBM(0) & 0x80) != 0x80) {} - - // first bank: 0x0000-0x7FFF, - word currAddress = 0x0; - - // Write 63 banks - for (byte currBank = 0x1; currBank < (fileSize / 0x4000); currBank++) { - // Blink led - PORTB ^= (1 << 4); - - // all following banks: 0x4000-0x7FFF - if (currBank > 1) { - currAddress = 0x4000; - } - - // Write single bank in 128 byte steps - for (; currAddress < 0x7FFF; currAddress += 128) { - // Fill SD buffer - myFile.read(sdBuffer, 128); - - // Enable access to ports 0x120 and 0x2100 - send_GBM(0x09); - send_GBM(0x11); - - // Set bank - writeByte_GBM(0x2100, 0x1); - - // Disable ports 0x2100 and 0x120 or else those addresses will not be writable - send_GBM(0x10); - send_GBM(0x08); - - // Write flash buffer command - writeByte_GBM(0x5555, 0xAA); - writeByte_GBM(0x2AAA, 0x55); - writeByte_GBM(0x5555, 0xA0); - - // Wait until flashrom is ready again - while ((readByte_GBM(0) & 0x80) != 0x80) {} - - // Enable access to ports 0x120 and 0x2100 - send_GBM(0x09); - send_GBM(0x11); - - // Set bank - writeByte_GBM(0x2100, currBank); - - // Disable ports 0x2100 and 0x120 or else those addresses will not be writable - send_GBM(0x10); - send_GBM(0x08); - - // Fill flash buffer - for (word currByte = 0; currByte < 128; currByte++) { - writeByte_GBM(currAddress + currByte, sdBuffer[currByte]); - } - // Execute write - writeByte_GBM(currAddress + 127, 0xFF); - - // Wait for write to complete - while ((readByte_GBM(currAddress) & 0x80) != 0x80) {} - } - } - // Close the file: - myFile.close(); - println_Msg(F("Done")); - } - else { - print_Error(F("Can't open file"), false); - } -} - -void readMapping_GBM() { - // Enable ports 0x0120 - send_GBM(0x09); - - // Set WE and WP - send_GBM(0x0A); - send_GBM(0x2); - - // Enable hidden mapping area - writeByte_GBM(0x2100, 0x01); - send_GBM(0x0F, 0x5555, 0xAA); - send_GBM(0x0F, 0x2AAA, 0x55); - send_GBM(0x0F, 0x5555, 0x77); - send_GBM(0x0F, 0x5555, 0xAA); - send_GBM(0x0F, 0x2AAA, 0x55); - send_GBM(0x0F, 0x5555, 0x77); - - // Read mapping - println_Msg(F("Reading Mapping...")); - display_Update(); - - // Get name, add extension and convert to char array for sd lib - EEPROM_readAnything(10, foldern); - sprintf(fileName, "GBM%d", foldern); - strcat(fileName, ".map"); - sd.mkdir("NP", true); - sd.chdir("NP"); - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - // Open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("Can't create file on SD"), true); - } - else { - for (byte currByte = 0; currByte < 128; currByte++) { - sdBuffer[currByte] = readByte_GBM(currByte); - } - myFile.write(sdBuffer, 128); - - // Close the file: - myFile.close(); - - // Signal end of process - printSdBuffer(0, 20); - printSdBuffer(102, 20); - println_Msg(""); - print_Msg(F("Saved to NP/")); - println_Msg(fileName); - display_Update(); - } - - // Reset flash to leave hidden mapping area - resetFlash_GBM(); -} - -void eraseMapping_GBM() { - println_Msg(F("Erasing...")); - display_Update(); - - //enable access to ports 0120h - send_GBM(0x09); - // Enable write - send_GBM(0x0A); - send_GBM(0x2); - - // Unprotect sector 0 - send_GBM(0x0F, 0x5555, 0xAA); - send_GBM(0x0F, 0x2AAA, 0x55); - send_GBM(0x0F, 0x5555, 0x60); - send_GBM(0x0F, 0x5555, 0xAA); - send_GBM(0x0F, 0x2AAA, 0x55); - send_GBM(0x0F, 0x5555, 0x40); - - // Wait for unprotect to complete - while ((readByte_GBM(0) & 0x80) != 0x80) {} - - // Send erase command - send_GBM(0x0F, 0x5555, 0xAA); - send_GBM(0x0F, 0x2AAA, 0x55); - send_GBM(0x0F, 0x5555, 0x60); - send_GBM(0x0F, 0x5555, 0xAA); - send_GBM(0x0F, 0x2AAA, 0x55); - send_GBM(0x0F, 0x5555, 0x04); - - // Wait for erase to complete - while ((readByte_GBM(0) & 0x80) != 0x80) {} - - // Reset flashrom - resetFlash_GBM(); -} - -boolean blankcheckMapping_GBM() { - print_Msg(F("Blankcheck...")); - display_Update(); - - // Enable ports 0x0120 - send_GBM(0x09); - - // Set WE and WP - send_GBM(0x0A); - send_GBM(0x2); - - // Enable hidden mapping area - writeByte_GBM(0x2100, 0x01); - send_GBM(0x0F, 0x5555, 0xAA); - send_GBM(0x0F, 0x2AAA, 0x55); - send_GBM(0x0F, 0x5555, 0x77); - send_GBM(0x0F, 0x5555, 0xAA); - send_GBM(0x0F, 0x2AAA, 0x55); - send_GBM(0x0F, 0x5555, 0x77); - - // Disable ports 0x0120... - send_GBM(0x08); - - // Read rom - for (byte currByte = 0; currByte < 128; currByte++) { - if (readByte_GBM(currByte) != 0xFF) { - return 0; - } - } - return 1; -} - -void writeMapping_GBM() { - print_Msg(F("Writing...")); - display_Update(); - - // Open file on sd card - if (myFile.open(filePath, O_READ)) { - // Get map file size and check if it exceeds 128KByte - if (myFile.fileSize() > 0x80) { - print_Error(F("File is too big."), true); - } - - // Enable access to ports 0120h - send_GBM(0x09); - - // Enable write - send_GBM(0x0A); - send_GBM(0x2); - - // Map entire flash rom - send_GBM(0x4); - - // Set bank, writes to 0x5555 need odd bank number - writeByte_GBM(0x2100, 0x1); - - // Disable ports 0x2100 and 0x120 or else those addresses will not be writable - send_GBM(0x10); - send_GBM(0x08); - - // Unlock write to map area - writeByte_GBM(0x5555, 0xAA); - writeByte_GBM(0x2AAA, 0x55); - writeByte_GBM(0x5555, 0x60); - writeByte_GBM(0x5555, 0xAA); - writeByte_GBM(0x2AAA, 0x55); - writeByte_GBM(0x5555, 0xE0); - - // Check if flashrom is ready for writing or busy - while ((readByte_GBM(0) & 0x80) != 0x80) {} - - // Fill SD buffer - myFile.read(sdBuffer, 128); - - // Enable access to ports 0x120 and 0x2100 - send_GBM(0x09); - send_GBM(0x11); - - // Set bank - writeByte_GBM(0x2100, 0x1); - - // Disable ports 0x2100 and 0x120 or else those addresses will not be writable - send_GBM(0x10); - send_GBM(0x08); - - // Write flash buffer command - writeByte_GBM(0x5555, 0xAA); - writeByte_GBM(0x2AAA, 0x55); - writeByte_GBM(0x5555, 0xA0); - - // Wait until flashrom is ready again - while ((readByte_GBM(0) & 0x80) != 0x80) {} - - // Enable access to ports 0x120 and 0x2100 - send_GBM(0x09); - send_GBM(0x11); - - // Set bank - writeByte_GBM(0x2100, 0); - - // Disable ports 0x2100 and 0x120 or else those addresses will not be writable - send_GBM(0x10); - send_GBM(0x08); - - // Fill flash buffer - for (word currByte = 0; currByte < 128; currByte++) { - // Blink led - PORTB ^= (1 << 4); - - writeByte_GBM(currByte, sdBuffer[currByte]); - } - // Execute write - writeByte_GBM(127, 0xFF); - - // Close the file: - myFile.close(); - println_Msg(F("Done")); - } - else { - print_Error(F("Can't open file"), false); - } -} - -//****************************************** -// End of File +//****************************************** +// NINTENDO POWER MODULE +// (GB Memory starts at around line 1460) +//****************************************** + +/****************************************** + SF Memory Cassette +******************************************/ + +/****************************************** + SF Memory Clock Source +******************************************/ +// The clock signal for the SF Memory cassette +// is generated with the Adafruit Clock Generator +// or a similar external clock source + +/****************************************** + Variables + *****************************************/ +// SF Memory status +byte sfmReady = 0; + +// SF Memory Menu +boolean hasMenu = true; +byte numGames = 0; + +// Arrays that hold game info +int gameSize[8]; +int saveSize[8]; +byte gameAddress[8]; +byte gameVersion[8]; +char gameCode[8][10]; +boolean hirom[8]; + +/****************************************** + Menu +*****************************************/ +// SFM menu items +static const char sfmMenuItem1[] PROGMEM = "Game Menu"; +static const char sfmMenuItem2[] PROGMEM = "Flash Menu"; +static const char sfmMenuItem3[] PROGMEM = "Reset"; +static const char* const menuOptionsSFM[] PROGMEM = {sfmMenuItem1, sfmMenuItem2, sfmMenuItem3}; + +// SFM flash menu items +static const char sfmFlashMenuItem1[] PROGMEM = "Read Flash"; +static const char sfmFlashMenuItem2[] PROGMEM = "Write Flash"; +static const char sfmFlashMenuItem3[] PROGMEM = "Print Mapping"; +static const char sfmFlashMenuItem4[] PROGMEM = "Read Mapping"; +static const char sfmFlashMenuItem5[] PROGMEM = "Write Mapping"; +static const char sfmFlashMenuItem6[] PROGMEM = "Back"; +static const char* const menuOptionsSFMFlash[] PROGMEM = {sfmFlashMenuItem1, sfmFlashMenuItem2, sfmFlashMenuItem3, sfmFlashMenuItem4, sfmFlashMenuItem5, sfmFlashMenuItem6}; + +// SFM game menu items +static const char sfmGameMenuItem1[] PROGMEM = "Read Sram"; +static const char sfmGameMenuItem2[] PROGMEM = "Read Game"; +static const char sfmGameMenuItem3[] PROGMEM = "Write Sram"; +static const char sfmGameMenuItem4[] PROGMEM = "Switch Game"; +static const char sfmGameMenuItem5[] PROGMEM = "Reset"; +static const char* const menuOptionsSFMGame[] PROGMEM = {sfmGameMenuItem1, sfmGameMenuItem2, sfmGameMenuItem3, sfmGameMenuItem4, sfmGameMenuItem5}; + +void sfmMenu() { + // create menu with title and 3 options to choose from + unsigned char mainMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsSFM, 3); + mainMenu = question_box("SF Memory", menuOptions, 3, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + // Game menu + case 0: + sfmGameMenu(); + break; + // Flash menu + case 1: + mode = mode_SFM_Flash; + break; + // Reset + case 2: + asm volatile (" jmp 0"); + break; + } +} + +void sfmGameMenu() { + // Switch to hirom all + if (send_SFM(0x04) == 0x2A) { + delay(300); + + // Fill arrays with data + getGames(); + + if (hasMenu) { + // Create submenu options + char menuOptionsSFMGames[8][20]; + for (int i = 0; i < (numGames); i++) { + strncpy(menuOptionsSFMGames[i], gameCode[i], 10); + } + + // Create menu with title and numGames options to choose from + unsigned char gameSubMenu; + // wait for user choice to come back from the question box menu + gameSubMenu = question_box("Select Game", menuOptionsSFMGames, numGames, 0); + + // Switch to game + send_SFM(gameSubMenu + 0x80); + delay(200); + // Check for successfull switch + byte timeout = 0; + while (readBank_SNES(0, 0x2400) != 0x7D) { + delay(200); + // Try again + send_SFM(gameSubMenu + 0x80); + delay(200); + timeout++; + // Abort, something is wrong + if (timeout == 5) { + display_Clear(); + print_Msg(F("Game ")); + print_Msg(gameSubMenu + 0x80, HEX); + println_Msg(F(" Timeout")); + println_Msg(readBank_SNES(0, 0x2400), HEX); + println_Msg(F("")); + print_Error(F("Powercycle SFM cart"), true); + } + } + // Copy gameCode to romName in case of japanese chars in romName + strcpy(romName, gameCode[gameSubMenu + 1]); + + // Print info + getCartInfo_SNES(); + mode = mode_SFM_Game; + } + else { + // No menu so switch to only game + // Switch to game + send_SFM(0x80); + delay(200); + + // Copy gameCode to romName in case of japanese chars in romName + strcpy(romName, gameCode[0]); + + // Print info + getCartInfo_SNES(); + mode = mode_SFM_Game; + } + } + else { + print_Error(F("Switch to HiRom failed"), false); + } +} + +void sfmGameOptions() { + // create menu with title and 5 options to choose from + unsigned char gameSubMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsSFMGame, 5); + gameSubMenu = question_box("SFM Game Menu", menuOptions, 5, 0); + + // wait for user choice to come back from the question box menu + switch (gameSubMenu) + { + // Read sram + case 0: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readSRAM(); + break; + + // Read rom + case 1: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readROM_SNES(); + compare_checksum(); + break; + + // Write sram + case 2: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + writeSRAM(1); + unsigned long wrErrors; + wrErrors = verifySRAM(); + if (wrErrors == 0) { + println_Msg(F("Verified OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(wrErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + break; + + // Switch game + case 3: + sfmGameMenu(); + break; + + // Reset + case 4: + asm volatile (" jmp 0"); + break; + } + if (gameSubMenu != 3) { + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + } +} + +void sfmFlashMenu() { + // create menu with title and 6 options to choose from + unsigned char flashSubMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsSFMFlash, 6); + flashSubMenu = question_box("SFM Flash Menu", menuOptions, 6, 0); + + // wait for user choice to come back from the question box menu + switch (flashSubMenu) + { + // Read Flash + case 0: + // Clear screen + display_Clear(); + + // Reset to root directory + sd.chdir("/"); + + // Reset to HIROM ALL + romType = 1; + print_Msg(F("Switch to HiRom...")); + display_Update(); + if (send_SFM(0x04) == 0x2A) { + println_Msg(F("OK")); + display_Update(); + + // Reset flash + resetFlash_SFM(0xC0); + resetFlash_SFM(0xE0); + + flashSize = 4194304; + numBanks = 64; + + // Get name, add extension and convert to char array for sd lib + EEPROM_readAnything(10, foldern); + sprintf(fileName, "SFM%d", foldern); + strcat(fileName, ".bin"); + sd.mkdir("NP", true); + sd.chdir("NP"); + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + // Read flash + readFlash_SFM(); + } + else { + print_Error(F("Switch to HiRom failed"), false); + } + break; + + // Write Flash + case 1: + // Clear screen + display_Clear(); + + // Print warning + println_Msg(F("Attention")); + println_Msg(F("This will erase your")); + println_Msg(F("NP Cartridge.")); + println_Msg(""); + println_Msg(F("Press Button")); + println_Msg(F("to continue")); + display_Update(); + wait(); + + // Clear screen + display_Clear(); + + filePath[0] = '\0'; + sd.chdir("/"); + // Launch file browser + fileBrowser("Select 4MB file"); + display_Clear(); + sprintf(filePath, "%s/%s", filePath, fileName); + flashSize = 2097152; + numBanks = 32; + println_Msg(F("Writing 1st rom")); + display_Update(); + // Program 1st flashrom + write_SFM(0xC0, 0); + display_Clear(); + println_Msg(F("Writing 2nd rom")); + display_Update(); + // Program 2nd flashrom + write_SFM(0xE0, 2097152); + break; + + // Print mapping + case 2: + // Clear screen + display_Clear(); + + // Reset to root directory + sd.chdir("/"); + + // Reset to HIROM ALL + romType = 1; + print_Msg(F("Switch to HiRom...")); + display_Update(); + if (send_SFM(0x04) == 0x2A) { + println_Msg(F("OK")); + display_Update(); + idFlash_SFM(0xC0); + if (strcmp(flashid, "c2f3") == 0) { + idFlash_SFM(0xE0); + if (strcmp(flashid, "c2f3") == 0) { + // Reset flash + resetFlash_SFM(0xC0); + resetFlash_SFM(0xE0); + delay(100); + // Clear screen + display_Clear(); + printMapping(); + resetFlash_SFM(0xC0); + resetFlash_SFM(0xE0); + } + else { + print_Error(F("Error: Wrong Flash ID"), true); + } + } + else { + print_Error(F("Error: Wrong Flash ID"), true); + } + } + else { + print_Error(F("failed"), false); + } + break; + + // Read mapping + case 3: + // Clear screen + display_Clear(); + + // Reset to root directory + sd.chdir("/"); + + // Reset to HIROM ALL + romType = 1; + print_Msg(F("Switch to HiRom...")); + display_Update(); + if (send_SFM(0x04) == 0x2A) { + println_Msg(F("OK")); + display_Update(); + idFlash_SFM(0xC0); + if (strcmp(flashid, "c2f3") == 0) { + idFlash_SFM(0xE0); + if (strcmp(flashid, "c2f3") == 0) { + // Reset flash + resetFlash_SFM(0xC0); + resetFlash_SFM(0xE0); + delay(100); + readMapping(); + resetFlash_SFM(0xC0); + resetFlash_SFM(0xE0); + } + else { + print_Error(F("Error: Wrong Flash ID"), true); + } + } + else { + print_Error(F("Error: Wrong Flash ID"), true); + } + } + else { + print_Error(F("failed"), false); + } + break; + + // Write mapping + case 4: + // Clear screen + display_Clear(); + + // Print warning + println_Msg(F("Attention")); + println_Msg(F("This will erase your")); + println_Msg(F("NP Cartridge.")); + println_Msg(""); + println_Msg(F("Press Button")); + println_Msg(F("to continue")); + display_Update(); + wait(); + + // Clear screen + display_Clear(); + + // Reset to root directory + sd.chdir("/"); + + // Erase mapping + eraseMapping(0xD0); + eraseMapping(0xE0); + print_Msg(F("Blankcheck...")); + display_Update(); + if (blankcheckMapping_SFM()) { + println_Msg(F("OK")); + display_Update(); + } + else { + println_Msg(F("Nope")); + break; + } + + // Clear screen + display_Clear(); + + // Clear filepath + filePath[0] = '\0'; + + // Reset to root directory + sd.chdir("/"); + + // Launch file browser + fileBrowser("Select MAP file"); + display_Clear(); + sprintf(filePath, "%s/%s", filePath, fileName); + display_Update(); + + // Write mapping + writeMapping_SFM(0xD0, 0); + writeMapping_SFM(0xE0, 256); + break; + + // Go back + case 5: + mode = mode_SFM; + break; + } + if (flashSubMenu != 5) { + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + } +} + +// Read the games from the menu area +void getGames() { + // Set data pins to input + dataIn(); + // Set control pins to input + controlIn_SNES(); + + // Check if menu is present + byte menuString[] = {0x4D, 0x45, 0x4E, 0x55, 0x20, 0x50, 0x52, 0x4F, 0x47, 0x52, 0x41, 0x4D}; + for (int i = 0; i < 12; i++) { + if (menuString[i] != readBank_SNES(0xC0, 0x7FC0 + i)) { + hasMenu = false; + } + } + + if (hasMenu) { + // Count number of games + for (word i = 0x0000; i < 0xE000; i += 0x2000) { + if (readBank_SNES(0xC6, i) == numGames ) + numGames++; + } + + // Get game info + for (int i = 0; i < numGames; i++) { + // Read starting address and size + gameAddress[i] = 0xC0 + readBank_SNES(0xC6, i * 0x2000 + 0x01) * 0x8; + gameSize[i] = readBank_SNES(0xC6, i * 0x2000 + 0x03) * 128; + saveSize[i] = readBank_SNES(0xC6, i * 0x2000 + 0x05) / 8; + + //check if hirom + if (readBank_SNES(gameAddress[i], 0xFFD5) == 0x31) { + hirom[i] = true; + } + else if (readBank_SNES(gameAddress[i], 0xFFD5) == 0x21) { + hirom[i] = true; + } + else { + hirom[i] = false; + } + + if (hirom[i]) { + gameVersion[i] = readBank_SNES(gameAddress[i], 0xFFDB); + } else { + gameVersion[i] = readBank_SNES(gameAddress[i], 0x7FDB); + } + + // Read game code + byte myByte = 0; + byte myLength = 0; + for (int j = 0; j < 9; j++) { + myByte = readBank_SNES(0xC6, i * 0x2000 + 0x07 + j); + // Remove funny characters + if (((char(myByte) >= 44 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 9) { + gameCode[i][myLength] = char(myByte); + myLength++; + } + } + // End char array in case game code is less than 9 chars + gameCode[i][myLength] = '\0'; + } + } + else { + //check if hirom + if (readBank_SNES(0xC0, 0xFFD5) == 0x31) { + hirom[0] = true; + } + else { + hirom[0] = false; + } + + if (hirom[0]) { + gameVersion[0] = readBank_SNES(0xC0, 0xFFDB); + gameCode[0][0] = 'G'; + gameCode[0][1] = 'A'; + gameCode[0][2] = 'M'; + gameCode[0][3] = 'E'; + gameCode[0][4] = '-'; + gameCode[0][5] = char(readBank_SNES(0xC0, 0xFFB2)); + gameCode[0][6] = char(readBank_SNES(0xC0, 0xFFB3)); + gameCode[0][7] = char(readBank_SNES(0xC0, 0xFFB4)); + gameCode[0][8] = char(readBank_SNES(0xC0, 0xFFB5)); + gameCode[0][9] = '\0'; + + byte romSizeExp = readBank_SNES(0xC0, 0xFFD7) - 7; + gameSize[0] = 1; + while (romSizeExp--) + gameSize[0] *= 2; + } + else { + gameVersion[0] = readBank_SNES(0xC0, 0x7FDB); + gameCode[0][0] = 'G'; + gameCode[0][1] = 'A'; + gameCode[0][2] = 'M'; + gameCode[0][3] = 'E'; + gameCode[0][4] = '-'; + gameCode[0][5] = char(readBank_SNES(0xC0, 0x7FB2)); + gameCode[0][6] = char(readBank_SNES(0xC0, 0x7FB3)); + gameCode[0][7] = char(readBank_SNES(0xC0, 0x7FB4)); + gameCode[0][8] = char(readBank_SNES(0xC0, 0x7FB5)); + gameCode[0][9] = '\0'; + + byte romSizeExp = readBank_SNES(0xC0, 0x7FD7) - 7; + gameSize[0] = 1; + while (romSizeExp--) + gameSize[0] *= 2; + } + } +} + +/****************************************** + Setup + *****************************************/ +void setup_SFM() { + // Set cicrstPin(PG1) to Output + DDRG |= (1 << 1); + // Output a high signal to disable snesCIC if installed + PORTG |= (1 << 1); + // Set cichstPin(PG0) to Input + DDRG &= ~(1 << 0); + + // Adafruit Clock Generator + clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0); + clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); + clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); + // Half Clock + //clockgen.set_freq(1073863600ULL, SI5351_CLK0); + // Full Clock + clockgen.set_freq(2147727200ULL, SI5351_CLK0); + // CIC Clock + //clockgen.set_freq(307200000ULL, SI5351_CLK2); + clockgen.output_enable(SI5351_CLK0, 1); + clockgen.output_enable(SI5351_CLK1, 0); + clockgen.output_enable(SI5351_CLK2, 0); + + // Set Address Pins to Output + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //BA0-BA7 + DDRL = 0xFF; + + // Set Control Pins to Output RST(PH0) CS(PH3) WR(PH5) RD(PH6) + DDRH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); + // Output a high signal on all pins, pins are active low therefore everything is disabled now + PORTH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); + + // Set IRQ(PH4) to Input + DDRH &= ~(1 << 4); + // Activate Internal Pullup Resistors + PORTH |= (1 << 4); + + // Set Data Pins (D0-D7) to Input + DDRC = 0x00; + // Enable Internal Pullups + PORTC = 0xFF; + + // Wait until all is stable + delay(200); + + // Switch to HiRom All + byte timeout = 0; + send_SFM(0x04); + delay(100); + while (readBank_SNES(0, 0x2400) != 0x2A) { + delay(100); + // Try again + send_SFM(0x04); + delay(100); + timeout++; + // Abort, something is wrong + if (timeout == 5) { + println_Msg(F("Hirom All Timeout")); + println_Msg(F("")); + println_Msg(F("")); + print_Error(F("Powercycle SFM cart"), true); + } + } +} + +/****************************************** + 29F1601 flashrom functions (NP) + *****************************************/ +// Reset the MX29F1601 flashrom, startbank is 0xC0 for first and 0xE0 for second flashrom +void resetFlash_SFM(int startBank) { + // Configure control pins + controlOut_SNES(); + // Set data pins to output + dataOut(); + + // Reset command sequence + if (romType) { + writeBank_SNES(startBank, 0x5555L * 2, 0xaa); + writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); + writeBank_SNES(startBank, 0x5555L * 2, 0xf0); + } + else { + writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0xaa); + writeBank_SNES(0, 0x8000 + 0x2AAAL * 2, 0x55); + writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0xf0); + } +} + +// Print flashrom manufacturer and device ID +void idFlash_SFM(int startBank) { + // Configure control pins + controlOut_SNES(); + // Set data pins to output + dataOut(); + + if (romType) { + // ID command sequence + writeBank_SNES(startBank, 0x5555L * 2, 0xaa); + writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); + writeBank_SNES(startBank, 0x5555L * 2, 0x90); + + // Set data pins to input again + dataIn(); + // Set control pins to input + controlIn_SNES(); + + // Read the two id bytes into a string + sprintf(flashid, "%x%x", readBank_SNES(startBank, 0x00), readBank_SNES(startBank, 0x02)); + } + else { + writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0xaa); + writeBank_SNES(0, 0x8000 + 0x2AAAL * 2, 0x55); + writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0x90); + + // Set data pins to input again + dataIn(); + // Set control pins to input + controlIn_SNES(); + + // Read the two id bytes into a string + sprintf(flashid, "%x%x", readBank_SNES(0, 0x8000), readBank_SNES(0, 0x8000 + 0x02)); + } +} + +// Write the flashroms by reading a file from the SD card, pos defines where in the file the reading/writing should start +void writeFlash_SFM(int startBank, uint32_t pos) { + display_Clear(); + print_Msg(F("Writing Bank 0x")); + print_Msg(startBank, HEX); + print_Msg(F("...")); + display_Update(); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Seek to a new position in the file + if (pos != 0) + myFile.seekCur(pos); + + // Configure control pins + controlOut_SNES(); + // Set data pins to output + dataOut(); + + if (romType) { + // Write hirom + for (int currBank = startBank; currBank < startBank + numBanks; currBank++) { + // Fill SDBuffer with 1 page at a time then write it repeat until all bytes are written + for (unsigned long currByte = 0; currByte < 0x10000; currByte += 128) { + myFile.read(sdBuffer, 128); + // Write command sequence + writeBank_SNES(startBank, 0x5555L * 2, 0xaa); + writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); + writeBank_SNES(startBank, 0x5555L * 2, 0xa0); + + for (byte c = 0; c < 128; c++) { + + // Write one byte of data + writeBank_SNES(currBank, currByte + c, sdBuffer[c]); + + if (c == 127) { + // Write the last byte twice or else it won't write at all + writeBank_SNES(currBank, currByte + c, sdBuffer[c]); + } + } + // Wait until write is finished + busyCheck_SFM(startBank); + } + } + } + else { + // Write lorom + for (int currBank = 0; currBank < numBanks; currBank++) { + for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte += 128) { + myFile.read(sdBuffer, 128); + // Write command sequence + writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0xaa); + writeBank_SNES(0, 0x8000 + 0x2AAAL * 2, 0x55); + writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0xa0); + + for (byte c = 0; c < 128; c++) { + // Write one byte of data + writeBank_SNES(currBank, currByte + c, sdBuffer[c]); + + if (c == 127) { + // Write the last byte twice or else it won't write at all + writeBank_SNES(currBank, currByte + c, sdBuffer[c]); + } + } + // Wait until write is finished + busyCheck_SFM(startBank); + } + } + } + // Close the file: + myFile.close(); + println_Msg(""); + } + else { + print_Error(F("Can't open file on SD"), true); + } +} + +// Delay between write operations based on status register +void busyCheck_SFM(byte startBank) { + // Set data pins to input + dataIn(); + // Set control pins to input and therefore pull CE low and latch status register content + controlIn_SNES(); + + // Read register + readBank_SNES(startBank, 0x0000); + + // Read D7 while D7 = 0 + //1 = B00000001, 1 << 7 = B10000000, PINC = B1XXXXXXX (X = don't care), & = bitwise and + while (!(PINC & (1 << 7))) { + // CE or OE must be toggled with each subsequent status read or the + // completion of a program or erase operation will not be evident. + // Switch RD(PH6) to HIGH + PORTH |= (1 << 6); + + // one nop ~62.5ns + __asm__("nop\n\t"); + + // Switch RD(PH6) to LOW + PORTH &= ~(1 << 6); + + // one nop ~62.5ns + __asm__("nop\n\t"); + } + + // Configure control pins + controlOut_SNES(); + // Set data pins to output + dataOut(); +} + +// Erase the flashrom to 0xFF +void eraseFlash_SFM(int startBank) { + + // Configure control pins + controlOut_SNES(); + // Set data pins to output + dataOut(); + + if (romType) { + // Erase command sequence + writeBank_SNES(startBank, 0x5555L * 2, 0xaa); + writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); + writeBank_SNES(startBank, 0x5555L * 2, 0x80); + writeBank_SNES(startBank, 0x5555L * 2, 0xaa); + writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); + writeBank_SNES(startBank, 0x5555L * 2, 0x10); + } + else { + writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0xaa); + writeBank_SNES(0, 0x8000 + 0x2AAAL * 2, 0x55); + writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0x80); + writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0xaa); + writeBank_SNES(0, 0x8000 + 0x2AAAL * 2, 0x55); + writeBank_SNES(1, 0x8000 + 0x1555L * 2, 0x10); + } + + // Wait for erase to complete + busyCheck_SFM(startBank); +} + +// Check if an erase succeeded, return 1 if blank and 0 if not +byte blankcheck_SFM(int startBank) { + + // Set data pins to input again + dataIn(); + // Set control pins to input + controlIn_SNES(); + + byte blank = 1; + if (romType) { + for (int currBank = startBank; currBank < startBank + numBanks; currBank++) { + for (unsigned long currByte = 0; currByte < 0x10000; currByte++) { + if (readBank_SNES(currBank, currByte) != 0xFF) { + currBank = startBank + numBanks; + blank = 0; + } + } + } + } + else { + for (int currBank = 0; currBank < numBanks; currBank++) { + for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte++) { + if (readBank_SNES(currBank, currByte) != 0xFF) { + currBank = numBanks; + blank = 0; + } + } + } + } + return blank; +} + +// Check if a write succeeded, returns 0 if all is ok and number of errors if not +unsigned long verifyFlash_SFM(int startBank, uint32_t pos) { + unsigned long verified = 0; + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Set file starting position + myFile.seekCur(pos); + + // Set data pins to input + dataIn(); + // Set control pins to input + controlIn_SNES(); + + if (romType) { + for (int currBank = startBank; currBank < startBank + numBanks; currBank++) { + for (unsigned long currByte = 0; currByte < 0x10000; currByte += 512) { + // Fill SDBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if (readBank_SNES(currBank, currByte + c) != sdBuffer[c]) { + verified++; + } + } + } + } + } + else { + for (int currBank = 0; currBank < numBanks; currBank++) { + for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte += 512) { + // Fill SDBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if (readBank_SNES(currBank, currByte + c) != sdBuffer[c]) { + verified++; + } + } + } + } + } + // Close the file: + myFile.close(); + } + else { + // SD Error + verified = 999999; + print_Error(F("Can't open file on SD"), false); + } + // Return 0 if verified ok, or number of errors + return verified; +} + +// Read flashroms and save them to the SD card +void readFlash_SFM() { + // Set data pins to input + dataIn(); + // Set control pins to input + controlIn_SNES(); + + print_Msg(F("Saving as NP/")); + print_Msg(fileName); + println_Msg(F("...")); + display_Update(); + + // Open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("Can't create file on SD"), true); + } + if (romType) { + for (int currBank = 0xC0; currBank < 0xC0 + numBanks; currBank++) { + for (unsigned long currByte = 0; currByte < 0x10000; currByte += 512) { + for (int c = 0; c < 512; c++) { + sdBuffer[c] = readBank_SNES(currBank, currByte + c); + } + myFile.write(sdBuffer, 512); + } + } + } + else { + for (int currBank = 0; currBank < numBanks; currBank++) { + for (unsigned long currByte = 0x8000; currByte < 0x10000; currByte += 512) { + for (int c = 0; c < 512; c++) { + sdBuffer[c] = readBank_SNES(currBank, currByte + c); + } + myFile.write(sdBuffer, 512); + } + } + } + // Close the file: + myFile.close(); + println_Msg(""); + println_Msg(F("Finished reading")); + display_Update(); +} + +// Display protected sectors/banks as 0xc2 and unprotected as 0x00 +void readSectorProtection_SFM(byte startBank) { + + // Configure control pins + controlOut_SNES(); + // Set data pins to output + dataOut(); + + // Display Sector Protection Status + writeBank_SNES(startBank, 0x5555L * 2, 0xaa); + writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); + writeBank_SNES(startBank, 0x5555L * 2, 0x90); + + // Configure control pins + controlIn_SNES(); + // Set data pins to output + dataIn(); + display_Clear(); + for (int i = 0; i <= 0x1F; i++) { + print_Msg(F("Sector: 0x")); + print_Msg(startBank + i, HEX); + print_Msg(F(" Sector Protect: 0x")); + println_Msg(readBank_SNES(startBank + i, 0x04), HEX); + } + display_Update(); +} + +// Read the current mapping from the hidden "page buffer" and print it +void printMapping() { + // Switch to write + dataOut(); + controlOut_SNES(); + + // Reset to defaults + writeBank_SNES(0xC0, 0x0000, 0x38); + writeBank_SNES(0xC0, 0x0000, 0xd0); + // Read Extended Status Register (GSR and PSR) + writeBank_SNES(0xC0, 0x0000, 0x71); + // Page Buffer Swap + writeBank_SNES(0xC0, 0x0000, 0x72); + // Read Page Buffer + writeBank_SNES(0xC0, 0x0000, 0x75); + + // Switch to read + dataIn(); + controlIn_SNES(); + + // Read the mapping out of the first chip + char buffer[3]; + + for (int currByte = 0xFF00; currByte < 0xFF50; currByte += 10) { + for (int c = 0; c < 10; c++) { + itoa (readBank_SNES(0xC0, currByte + c), buffer, 16); + for (int i = 0; i < 2 - strlen(buffer); i++) { + print_Msg("0"); + } + // Now print the significant bits + print_Msg(buffer); + } + println_Msg(""); + } + display_Update(); + + // Switch to write + dataOut(); + controlOut_SNES(); + + // Reset Flash + writeBank_SNES(0xC0, 0x5555L * 2, 0xaa); + writeBank_SNES(0xC0, 0x2AAAL * 2, 0x55); + writeBank_SNES(0xC0, 0x5555L * 2, 0xf0); + + // Reset Flash + writeBank_SNES(0xE0, 0x5555L * 2, 0xaa); + writeBank_SNES(0xE0, 0x2AAAL * 2, 0x55); + writeBank_SNES(0xE0, 0x5555L * 2, 0xf0); + + // Switch to read + dataIn(); + controlIn_SNES(); +} + +// Read the current mapping from the hidden "page buffer" +void readMapping() { + + // Switch to write + dataOut(); + controlOut_SNES(); + + // Reset to defaults + writeBank_SNES(0xC0, 0x0000, 0x38); + writeBank_SNES(0xC0, 0x0000, 0xd0); + // Read Extended Status Register (GSR and PSR) + writeBank_SNES(0xC0, 0x0000, 0x71); + // Page Buffer Swap + writeBank_SNES(0xC0, 0x0000, 0x72); + // Read Page Buffer + writeBank_SNES(0xC0, 0x0000, 0x75); + + // Switch to read + dataIn(); + controlIn_SNES(); + + // Get name, add extension and convert to char array for sd lib + EEPROM_readAnything(10, foldern); + sprintf(fileName, "NP%d", foldern); + strcat(fileName, ".MAP"); + sd.mkdir("NP", true); + sd.chdir("NP"); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + + // Read the mapping info out of the 1st chip + for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { + myFile.write(readBank_SNES(0xC0, currByte)); + } + + // Switch to write + dataOut(); + controlOut_SNES(); + + // Reset to defaults + writeBank_SNES(0xE0, 0x0000, 0x38); + writeBank_SNES(0xE0, 0x0000, 0xd0); + // Read Extended Status Register (GSR and PSR) + writeBank_SNES(0xE0, 0x0000, 0x71); + // Page Buffer Swap + writeBank_SNES(0xE0, 0x0000, 0x72); + // Read Page Buffer + writeBank_SNES(0xE0, 0x0000, 0x75); + + // Switch to read + dataIn(); + controlIn_SNES(); + + // Read the mapping info out of the 1st chip + for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { + myFile.write(readBank_SNES(0xE0, currByte)); + } + + // Close the file: + myFile.close(); + + // Switch to write + dataOut(); + controlOut_SNES(); + + // Reset Flash + writeBank_SNES(0xC0, 0x5555L * 2, 0xaa); + writeBank_SNES(0xC0, 0x2AAAL * 2, 0x55); + writeBank_SNES(0xC0, 0x5555L * 2, 0xf0); + + // Reset Flash + writeBank_SNES(0xE0, 0x5555L * 2, 0xaa); + writeBank_SNES(0xE0, 0x2AAAL * 2, 0x55); + writeBank_SNES(0xE0, 0x5555L * 2, 0xf0); + + // Switch to read + dataIn(); + controlIn_SNES(); + + // Signal end of process + print_Msg(F("Saved to NP/")); + println_Msg(fileName); + display_Update(); +} + +void eraseMapping(byte startBank) { + + if (unlockHirom()) { + // Get ID + idFlash_SFM(startBank); + if (strcmp(flashid, "c2f3") == 0) { + resetFlash_SFM(startBank); + + // Switch to write + dataOut(); + controlOut_SNES(); + + // Prepare to erase/write Page Buffer + writeBank_SNES(startBank, 0x5555L * 2, 0xaa); + writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); + writeBank_SNES(startBank, 0x5555L * 2, 0x77); + // Erase Page Buffer + writeBank_SNES(startBank, 0x5555L * 2, 0xaa); + writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); + writeBank_SNES(startBank, 0x5555L * 2, 0xe0); + + // Wait until complete + busyCheck_SFM(startBank); + + // Switch to read + dataIn(); + controlIn_SNES(); + } + else { + print_Error(F("Error: Wrong Flash ID"), true); + } + } + else { + print_Error(F("Unlock failed"), true); + } +} + +// Check if the current mapping is all 0xFF +byte blankcheckMapping_SFM() { + byte blank = 1; + + // Switch to write + dataOut(); + controlOut_SNES(); + + // Reset to defaults + writeBank_SNES(0xC0, 0x0000, 0x38); + writeBank_SNES(0xC0, 0x0000, 0xd0); + // Read Extended Status Register (GSR and PSR) + writeBank_SNES(0xC0, 0x0000, 0x71); + // Page Buffer Swap + writeBank_SNES(0xC0, 0x0000, 0x72); + // Read Page Buffer + writeBank_SNES(0xC0, 0x0000, 0x75); + + // Switch to read + dataIn(); + controlIn_SNES(); + + // Read the mapping info out of the 1st chip + for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { + if (readBank_SNES(0xC0, currByte) != 0xFF) { + blank = 0; + } + } + + // Switch to write + dataOut(); + controlOut_SNES(); + + // Reset to defaults + writeBank_SNES(0xE0, 0x0000, 0x38); + writeBank_SNES(0xE0, 0x0000, 0xd0); + // Read Extended Status Register (GSR and PSR) + writeBank_SNES(0xE0, 0x0000, 0x71); + // Page Buffer Swap + writeBank_SNES(0xE0, 0x0000, 0x72); + // Read Page Buffer + writeBank_SNES(0xE0, 0x0000, 0x75); + + // Switch to read + dataIn(); + controlIn_SNES(); + + // Read the mapping info out of the 1st chip + for (unsigned long currByte = 0xFF00; currByte <= 0xFFFF; currByte++) { + if (readBank_SNES(0xE0, currByte) != 0xFF) { + blank = 0; + } + } + + // Switch to write + dataOut(); + controlOut_SNES(); + + // Reset Flash + writeBank_SNES(0xC0, 0x5555L * 2, 0xaa); + writeBank_SNES(0xC0, 0x2AAAL * 2, 0x55); + writeBank_SNES(0xC0, 0x5555L * 2, 0xf0); + + // Reset Flash + writeBank_SNES(0xE0, 0x5555L * 2, 0xaa); + writeBank_SNES(0xE0, 0x2AAAL * 2, 0x55); + writeBank_SNES(0xE0, 0x5555L * 2, 0xf0); + + // Switch to read + dataIn(); + controlIn_SNES(); + + return blank; +} + +void writeMapping_SFM(byte startBank, uint32_t pos) { + + if (unlockHirom()) { + // Get ID + idFlash_SFM(startBank); + if (strcmp(flashid, "c2f3") == 0) { + resetFlash_SFM(startBank); + + // Switch to write + dataOut(); + controlOut_SNES(); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Seek to a new position in the file + if (pos != 0) + myFile.seekCur(pos); + + // Write to Page Buffer + for (unsigned long currByte = 0xFF00; currByte < 0xFFFF; currByte += 128) { + // Prepare to erase/write Page Buffer + writeBank_SNES(startBank, 0x5555L * 2, 0xaa); + writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); + writeBank_SNES(startBank, 0x5555L * 2, 0x77); + // Write Page Buffer Command + writeBank_SNES(startBank, 0x5555L * 2, 0xaa); + writeBank_SNES(startBank, 0x2AAAL * 2, 0x55); + writeBank_SNES(startBank, 0x5555L * 2, 0x99); + + myFile.read(sdBuffer, 128); + + for (byte c = 0; c < 128; c++) { + writeBank_SNES(startBank, currByte + c, sdBuffer[c]); + // Write last byte twice + if (c == 127) { + writeBank_SNES(startBank, currByte + c, sdBuffer[c]); + } + } + busyCheck_SFM(startBank); + } + + // Close the file: + myFile.close(); + println_Msg(""); + } + else { + print_Error(F("Can't open file on SD"), false); + } + + // Switch to read + dataIn(); + controlIn_SNES(); + } + else { + print_Error(F("Error: Wrong Flash ID"), true); + } + } + else { + print_Error(F("Unlock failed"), true); + } +} + +/****************************************** + SF Memory functions +*****************************************/ +// Switch to HiRom All and unlock Write Protection +boolean unlockHirom() { + romType = 1; + print_Msg(F("Switch to HiRom...")); + display_Update(); + if (send_SFM(0x04) == 0x2A) { + println_Msg(F("OK")); + display_Update(); + // Unlock Write Protection + print_Msg(F("Enable Write...")); + display_Update(); + send_SFM(0x02); + if (readBank_SNES(0, 0x2401) == 0x4) { + println_Msg(F("OK")); + display_Update(); + return 1; + } + else { + println_Msg(F("failed")); + display_Update(); + return 0; + } + } + else { + println_Msg(F("failed")); + display_Update(); + return 0; + } +} + +// Send a command to the MX15001 chip +byte send_SFM(byte command) { + // Switch to write + dataOut(); + controlOut_SNES(); + + // Write command + writeBank_SNES(0, 0x2400, 0x09); + + // Switch to read + dataIn(); + controlIn_SNES(); + + // Read status + sfmReady = readBank_SNES(0, 0x2400); + + // Switch to write + dataOut(); + controlOut_SNES(); + + writeBank_SNES(0, 0x2401, 0x28); + writeBank_SNES(0, 0x2401, 0x84); + + // NP_CMD_06h, send this only if above read has returned 7Dh, not if it's already returning 2Ah + if (sfmReady == 0x7D) { + writeBank_SNES(0, 0x2400, 0x06); + writeBank_SNES(0, 0x2400, 0x39); + } + + // Write the command + writeBank_SNES(0, 0x2400, command); + + // Switch to read + dataIn(); + controlIn_SNES(); + + // Read status + sfmReady = readBank_SNES(0, 0x2400); + return sfmReady; +} + +// This function will erase and program the NP cart from a 4MB file off the SD card +void write_SFM(int startBank, uint32_t pos) { + // Switch NP cart's mapping + if (unlockHirom()) { + // Get ID + idFlash_SFM(startBank); + if (strcmp(flashid, "c2f3") == 0) { + print_Msg(F("Flash ID: ")); + println_Msg(flashid); + display_Update(); + resetFlash_SFM(startBank); + delay(1000); + // Erase flash + print_Msg(F("Blankcheck...")); + display_Update(); + if (blankcheck_SFM(startBank)) { + println_Msg(F("OK")); + display_Update(); + } + else { + println_Msg(F("Nope")); + display_Clear(); + print_Msg(F("Erasing...")); + display_Update(); + eraseFlash_SFM(startBank); + resetFlash_SFM(startBank); + println_Msg(F("Done")); + print_Msg(F("Blankcheck...")); + display_Update(); + if (blankcheck_SFM(startBank)) { + println_Msg(F("OK")); + display_Update(); + } + else { + print_Error(F("Could not erase flash"), true); + } + } + // Write flash + writeFlash_SFM(startBank, pos); + + // Reset flash + resetFlash_SFM(startBank); + + // Checking for errors + print_Msg(F("Verifying...")); + display_Update(); + writeErrors = verifyFlash_SFM(startBank, pos); + if (writeErrors == 0) { + println_Msg(F("OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), true); + } + } + else { + print_Error(F("Error: Wrong Flash ID"), true); + } + } + else { + print_Error(F("Unlock failed"), true); + } +} + +/****************************************** + GB Memory Cassette +******************************************/ + +/****************************************** + Menu +*****************************************/ +// GBM menu items +static const char gbmMenuItem1[] PROGMEM = "Read ID"; +static const char gbmMenuItem2[] PROGMEM = "Read Flash"; +static const char gbmMenuItem3[] PROGMEM = "Erase Flash"; +static const char gbmMenuItem4[] PROGMEM = "Blankcheck"; +static const char gbmMenuItem5[] PROGMEM = "Write Flash"; +static const char gbmMenuItem6[] PROGMEM = "Read Mapping"; +static const char gbmMenuItem7[] PROGMEM = "Write Mapping"; +static const char* const menuOptionsGBM[] PROGMEM = {gbmMenuItem1, gbmMenuItem2, gbmMenuItem3, gbmMenuItem4, gbmMenuItem5, gbmMenuItem6, gbmMenuItem7}; + +void gbmMenu() { + // create menu with title and 7 options to choose from + unsigned char mainMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsGBM, 7); + mainMenu = question_box("GB Memory Menu", menuOptions, 7, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + // Read Flash ID + case 0: + // Clear screen + display_Clear(); + readFlashID_GBM(); + break; + + // Read Flash + case 1: + // Clear screen + display_Clear(); + // Print warning + println_Msg(F("Attention")); + println_Msg(F("Always power cycle")); + println_Msg(F("cartreader directly")); + println_Msg(F("before reading")); + println_Msg(""); + println_Msg(F("Press Button")); + println_Msg(F("to continue")); + display_Update(); + wait(); + // Clear screen + display_Clear(); + + // Reset to root directory + sd.chdir("/"); + + // Enable access to ports 0120h + send_GBM(0x09); + // Map entire flashrom + send_GBM(0x04); + // Disable ports 0x0120... + send_GBM(0x08); + // Read 1MB rom + readROM_GBM(64); + break; + + // Erase Flash + case 2: + // Clear screen + display_Clear(); + // Print warning + println_Msg(F("Attention")); + println_Msg(F("This will erase your")); + println_Msg(F("NP Cartridge.")); + println_Msg(""); + println_Msg(""); + println_Msg(F("Press Button")); + println_Msg(F("to continue")); + display_Update(); + wait(); + // Clear screen + display_Clear(); + eraseFlash_GBM(); + break; + + // Blankcheck Flash + case 3: + // Clear screen + display_Clear(); + if (blankcheckFlash_GBM()) { + println_Msg(F("OK")); + display_Update(); + } + else { + println_Msg(F("ERROR")); + display_Update(); + } + break; + + // Write Flash + case 4: + // Clear screen + display_Clear(); + + filePath[0] = '\0'; + sd.chdir("/"); + // Launch file browser + fileBrowser("Select 1MB file"); + display_Clear(); + sprintf(filePath, "%s/%s", filePath, fileName); + + // Write rom + writeFlash_GBM(); + break; + + // Read mapping + case 5: + // Clear screen + display_Clear(); + + // Reset to root directory + sd.chdir("/"); + + // Read mapping + readMapping_GBM(); + break; + + // Write mapping + case 6: + // Clear screen + display_Clear(); + + // Print warning + println_Msg(F("Attention")); + println_Msg(F("This will erase your")); + println_Msg(F("NP Cartridge's")); + println_Msg(F("mapping data")); + println_Msg(""); + println_Msg(F("Press Button")); + println_Msg(F("to continue")); + display_Update(); + wait(); + + // Reset to root directory + sd.chdir("/"); + + // Clear screen + display_Clear(); + + // Clear filepath + filePath[0] = '\0'; + + // Reset to root directory + sd.chdir("/"); + + // Launch file browser + fileBrowser("Select MAP file"); + display_Clear(); + sprintf(filePath, "%s/%s", filePath, fileName); + display_Update(); + + // Clear screen + display_Clear(); + + // Erase mapping + eraseMapping_GBM(); + if (blankcheckMapping_GBM()) { + println_Msg(F("OK")); + display_Update(); + } + else { + print_Error(F("Erasing failed"), false); + break; + } + + // Write mapping + writeMapping_GBM(); + break; + } + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +/****************************************** + Setup +*****************************************/ +void setup_GBM() { + // Set RST(PH0) to Input + DDRH &= ~(1 << 0); + // Activate Internal Pullup Resistors + PORTH |= (1 << 0); + + // Set Address Pins to Output + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + + // Set Control Pins to Output RST(PH0) CS(PH3) WR(PH5) RD(PH6) + DDRH |= (1 << 3) | (1 << 5) | (1 << 6); + // Output a high signal on all pins, pins are active low therefore everything is disabled now + PORTH |= (1 << 3) | (1 << 5) | (1 << 6); + + // Set Data Pins (D0-D7) to Input + DDRC = 0x00; + + delay(400); + + // Check for Nintendo Power GB Memory cart + byte timeout = 0; + + // First byte of NP register is always 0x21 + while (readByte_GBM(0x120) != 0x21) { + // Enable ports 0x120h (F2) + send_GBM(0x09); + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + timeout++; + if (timeout > 10) { + println_Msg(F("Error: Time Out")); + print_Error(F("Please power cycle"), true); + } + } +} + +/********************** + LOW LEVEL +**********************/ +// Read one word out of the cartridge +byte readByte_GBM(word myAddress) { + // Set data pins to Input + DDRC = 0x0; + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Switch CS(PH3) and RD(PH6) to LOW + PORTH &= ~(1 << 3); + PORTH &= ~(1 << 6); + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Read + byte tempByte = PINC; + + // Switch CS(PH3) and RD(PH6) to HIGH + PORTH |= (1 << 6); + PORTH |= (1 << 3); + + return tempByte; +} + +// Write one word to data pins of the cartridge +void writeByte_GBM(word myAddress, byte myData) { + // Set data pins to Output + DDRC = 0xFF; + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + PORTC = myData; + + // Pull CS(PH3) and write(PH5) low + PORTH &= ~(1 << 3); + PORTH &= ~(1 << 5); + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Pull CS(PH3) and write(PH5) high + PORTH |= (1 << 5); + PORTH |= (1 << 3); + + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Set data pins to Input (or read errors??!) + DDRC = 0x0; +} + +/********************** + HELPER FUNCTIONS +**********************/ +void printSdBuffer(word startByte, word numBytes) { + for (int currByte = 0; currByte < numBytes; currByte += 10) { + for (byte c = 0; c < 10; c++) { + // Convert to char array so we don't lose leading zeros + char currByteStr[2]; + sprintf(currByteStr, "%02X", sdBuffer[startByte + currByte + c]); + print_Msg(currByteStr); + } + // Add a new line every 10 bytes + println_Msg(""); + } + display_Update(); +} + +void readROM_GBM(word numBanks) { + println_Msg(F("Reading Rom...")); + display_Update(); + + // Get name, add extension and convert to char array for sd lib + EEPROM_readAnything(10, foldern); + sprintf(fileName, "GBM%d", foldern); + strcat(fileName, ".bin"); + sd.mkdir("NP", true); + sd.chdir("NP"); + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + // Open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("Can't create file on SD"), true); + } + else { + // Read rom + word currAddress = 0; + + for (word currBank = 1; currBank < numBanks; currBank++) { + // Set rom bank + writeByte_GBM(0x2100, currBank); + + // Switch bank start address + if (currBank > 1) { + currAddress = 0x4000; + } + + for (; currAddress < 0x7FFF; currAddress += 512) { + for (int currByte = 0; currByte < 512; currByte++) { + sdBuffer[currByte] = readByte_GBM(currAddress + currByte); + } + myFile.write(sdBuffer, 512); + } + } + + // Close the file: + myFile.close(); + + // Signal end of process + print_Msg(F("Saved to NP/")); + println_Msg(fileName); + display_Update(); + } +} + +/********************** + GB Memory Functions +**********************/ +void send_GBM(byte myCommand) { + switch (myCommand) { + case 0x01: + //CMD_01h -> ??? + writeByte_GBM(0x0120, 0x01); + writeByte_GBM(0x013F, 0xA5); + break; + + case 0x02: + //CMD_02h -> Write enable Step 2 + writeByte_GBM(0x0120, 0x02); + writeByte_GBM(0x013F, 0xA5); + break; + + case 0x03: + //CMD_03h -> Undo write Step 2 + writeByte_GBM(0x0120, 0x03); + writeByte_GBM(0x013F, 0xA5); + break; + + case 0x04: + //CMD_04h -> Map entire flashrom (MBC4 mode) + writeByte_GBM(0x0120, 0x04); + writeByte_GBM(0x013F, 0xA5); + break; + + case 0x05: + //CMD_05h -> Map menu (MBC5 mode) + writeByte_GBM(0x0120, 0x05); + writeByte_GBM(0x013F, 0xA5); + break; + + case 0x08: + //CMD_08h -> disable writes/reads to/from special Nintendo Power registers (those at 0120h..013Fh) + writeByte_GBM(0x0120, 0x08); + writeByte_GBM(0x013F, 0xA5); + break; + + case 0x09: + //CMD_09h Wakeup -> re-enable access to ports 0120h..013Fh + writeByte_GBM(0x0120, 0x09); + writeByte_GBM(0x0121, 0xAA); + writeByte_GBM(0x0122, 0x55); + writeByte_GBM(0x013F, 0xA5); + break; + + case 0x0A: + //CMD_0Ah -> Write enable Step 1 + writeByte_GBM(0x0120, 0x0A); + writeByte_GBM(0x0125, 0x62); + writeByte_GBM(0x0126, 0x04); + writeByte_GBM(0x013F, 0xA5); + break; + + case 0x10: + //CMD_10h -> disable writes to normal MBC registers (such like 2100h) + writeByte_GBM(0x0120, 0x10); + writeByte_GBM(0x013F, 0xA5); + break; + + case 0x11: + //CMD_11h -> re-enable access to MBC registers like 0x2100 + writeByte_GBM(0x0120, 0x11); + writeByte_GBM(0x013F, 0xA5); + break; + + default: + print_Error(F("Unknown Command"), true); + break; + } +} + +void send_GBM(byte myCommand, word myAddress, byte myData) { + byte myAddrLow = myAddress & 0xFF; + byte myAddrHigh = (myAddress >> 8) & 0xFF; + + switch (myCommand) { + case 0x0F: + // CMD_0Fh -> Write address/byte to flash + writeByte_GBM(0x0120, 0x0F); + writeByte_GBM(0x0125, myAddrHigh); + writeByte_GBM(0x0126, myAddrLow); + writeByte_GBM(0x0127, myData); + writeByte_GBM(0x013F, 0xA5); + break; + + default: + print_Error(F("Unknown Command"), true); + break; + } +} + +void switchGame_GBM(byte myData) { + // Enable ports 0x0120 (F2) + send_GBM(0x09); + + //CMD_C0h -> map selected game without reset + writeByte_GBM(0x0120, 0xC0 & myData); + writeByte_GBM(0x013F, 0xA5); +} + +void resetFlash_GBM() { + // Enable ports 0x0120 (F2) + send_GBM(0x09); + + // Send reset command + writeByte_GBM(0x2100, 0x01); + send_GBM(0x0F, 0x5555, 0xAA); + send_GBM(0x0F, 0x2AAA, 0x55); + send_GBM(0x0F, 0x5555, 0xF0); + delay(100); +} + +boolean readFlashID_GBM() { + // Enable ports 0x0120 (F2) + send_GBM(0x09); + + writeByte_GBM(0x2100, 0x01); + // Read ID command + send_GBM(0x0F, 0x5555, 0xAA); + send_GBM(0x0F, 0x2AAA, 0x55); + send_GBM(0x0F, 0x5555, 0x90); + + // Read the two id bytes into a string + sprintf(flashid, "%02X%02X", readByte_GBM(0), readByte_GBM(1)); + if (strcmp(flashid, "C289") == 0) { + print_Msg(F("Flash ID: ")); + println_Msg(flashid); + display_Update(); + resetFlash_GBM(); + return 1; + } + else { + print_Msg(F("Flash ID: ")); + println_Msg(flashid); + print_Error(F("Unknown Flash ID"), true); + resetFlash_GBM(); + return 0; + } +} + +void eraseFlash_GBM() { + println_Msg(F("Erasing...")); + display_Update(); + + //enable access to ports 0120h + send_GBM(0x09); + // Enable write + send_GBM(0x0A); + send_GBM(0x2); + + // Unprotect sector 0 + send_GBM(0x0F, 0x5555, 0xAA); + send_GBM(0x0F, 0x2AAA, 0x55); + send_GBM(0x0F, 0x5555, 0x60); + send_GBM(0x0F, 0x5555, 0xAA); + send_GBM(0x0F, 0x2AAA, 0x55); + send_GBM(0x0F, 0x5555, 0x40); + + // Wait for unprotect to complete + while ((readByte_GBM(0) & 0x80) != 0x80) {} + + // Send erase command + send_GBM(0x0F, 0x5555, 0xaa); + send_GBM(0x0F, 0x2AAA, 0x55); + send_GBM(0x0F, 0x5555, 0x80); + send_GBM(0x0F, 0x5555, 0xaa); + send_GBM(0x0F, 0x2AAA, 0x55); + send_GBM(0x0F, 0x5555, 0x10); + + // Wait for erase to complete + while ((readByte_GBM(0) & 0x80) != 0x80) {} + + // Reset flashrom + resetFlash_GBM(); +} + +boolean blankcheckFlash_GBM() { + print_Msg(F("Blankcheck...")); + display_Update(); + + //enable access to ports 0120h (F2) + send_GBM(0x09); + + // Map entire flashrom + send_GBM(0x04); + // Disable ports 0x0120... + send_GBM(0x08); + + // Read rom + word currAddress = 0; + + for (byte currBank = 1; currBank < 64; currBank++) { + // Set rom bank + writeByte_GBM(0x2100, currBank); + + // Switch bank start address + if (currBank > 1) { + currAddress = 0x4000; + } + + for (; currAddress < 0x7FFF; currAddress++) { + if (readByte_GBM(currAddress) != 0xFF) { + return 0; + } + } + } + return 1; +} + +void writeFlash_GBM() { + print_Msg(F("Writing...")); + display_Update(); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + // Get rom size from file + fileSize = myFile.fileSize(); + if ((fileSize / 0x4000) > 64) { + print_Error(F("File is too big."), true); + } + + // Enable access to ports 0120h + send_GBM(0x09); + // Enable write + send_GBM(0x0A); + send_GBM(0x2); + + // Map entire flash rom + send_GBM(0x4); + + // Set bank for unprotect command, writes to 0x5555 need odd bank number + writeByte_GBM(0x2100, 0x1); + + // Disable ports 0x2100 and 0x120 or else those addresses will not be writable + send_GBM(0x10); + send_GBM(0x08); + + // Unprotect sector 0 + writeByte_GBM(0x5555, 0xAA); + writeByte_GBM(0x2AAA, 0x55); + writeByte_GBM(0x5555, 0x60); + writeByte_GBM(0x5555, 0xAA); + writeByte_GBM(0x2AAA, 0x55); + writeByte_GBM(0x5555, 0x40); + + // Check if flashrom is ready for writing or busy + while ((readByte_GBM(0) & 0x80) != 0x80) {} + + // first bank: 0x0000-0x7FFF, + word currAddress = 0x0; + + // Write 63 banks + for (byte currBank = 0x1; currBank < (fileSize / 0x4000); currBank++) { + // Blink led + PORTB ^= (1 << 4); + + // all following banks: 0x4000-0x7FFF + if (currBank > 1) { + currAddress = 0x4000; + } + + // Write single bank in 128 byte steps + for (; currAddress < 0x7FFF; currAddress += 128) { + // Fill SD buffer + myFile.read(sdBuffer, 128); + + // Enable access to ports 0x120 and 0x2100 + send_GBM(0x09); + send_GBM(0x11); + + // Set bank + writeByte_GBM(0x2100, 0x1); + + // Disable ports 0x2100 and 0x120 or else those addresses will not be writable + send_GBM(0x10); + send_GBM(0x08); + + // Write flash buffer command + writeByte_GBM(0x5555, 0xAA); + writeByte_GBM(0x2AAA, 0x55); + writeByte_GBM(0x5555, 0xA0); + + // Wait until flashrom is ready again + while ((readByte_GBM(0) & 0x80) != 0x80) {} + + // Enable access to ports 0x120 and 0x2100 + send_GBM(0x09); + send_GBM(0x11); + + // Set bank + writeByte_GBM(0x2100, currBank); + + // Disable ports 0x2100 and 0x120 or else those addresses will not be writable + send_GBM(0x10); + send_GBM(0x08); + + // Fill flash buffer + for (word currByte = 0; currByte < 128; currByte++) { + writeByte_GBM(currAddress + currByte, sdBuffer[currByte]); + } + // Execute write + writeByte_GBM(currAddress + 127, 0xFF); + + // Wait for write to complete + while ((readByte_GBM(currAddress) & 0x80) != 0x80) {} + } + } + // Close the file: + myFile.close(); + println_Msg(F("Done")); + } + else { + print_Error(F("Can't open file"), false); + } +} + +void readMapping_GBM() { + // Enable ports 0x0120 + send_GBM(0x09); + + // Set WE and WP + send_GBM(0x0A); + send_GBM(0x2); + + // Enable hidden mapping area + writeByte_GBM(0x2100, 0x01); + send_GBM(0x0F, 0x5555, 0xAA); + send_GBM(0x0F, 0x2AAA, 0x55); + send_GBM(0x0F, 0x5555, 0x77); + send_GBM(0x0F, 0x5555, 0xAA); + send_GBM(0x0F, 0x2AAA, 0x55); + send_GBM(0x0F, 0x5555, 0x77); + + // Read mapping + println_Msg(F("Reading Mapping...")); + display_Update(); + + // Get name, add extension and convert to char array for sd lib + EEPROM_readAnything(10, foldern); + sprintf(fileName, "GBM%d", foldern); + strcat(fileName, ".map"); + sd.mkdir("NP", true); + sd.chdir("NP"); + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + // Open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("Can't create file on SD"), true); + } + else { + for (byte currByte = 0; currByte < 128; currByte++) { + sdBuffer[currByte] = readByte_GBM(currByte); + } + myFile.write(sdBuffer, 128); + + // Close the file: + myFile.close(); + + // Signal end of process + printSdBuffer(0, 20); + printSdBuffer(102, 20); + println_Msg(""); + print_Msg(F("Saved to NP/")); + println_Msg(fileName); + display_Update(); + } + + // Reset flash to leave hidden mapping area + resetFlash_GBM(); +} + +void eraseMapping_GBM() { + println_Msg(F("Erasing...")); + display_Update(); + + //enable access to ports 0120h + send_GBM(0x09); + // Enable write + send_GBM(0x0A); + send_GBM(0x2); + + // Unprotect sector 0 + send_GBM(0x0F, 0x5555, 0xAA); + send_GBM(0x0F, 0x2AAA, 0x55); + send_GBM(0x0F, 0x5555, 0x60); + send_GBM(0x0F, 0x5555, 0xAA); + send_GBM(0x0F, 0x2AAA, 0x55); + send_GBM(0x0F, 0x5555, 0x40); + + // Wait for unprotect to complete + while ((readByte_GBM(0) & 0x80) != 0x80) {} + + // Send erase command + send_GBM(0x0F, 0x5555, 0xAA); + send_GBM(0x0F, 0x2AAA, 0x55); + send_GBM(0x0F, 0x5555, 0x60); + send_GBM(0x0F, 0x5555, 0xAA); + send_GBM(0x0F, 0x2AAA, 0x55); + send_GBM(0x0F, 0x5555, 0x04); + + // Wait for erase to complete + while ((readByte_GBM(0) & 0x80) != 0x80) {} + + // Reset flashrom + resetFlash_GBM(); +} + +boolean blankcheckMapping_GBM() { + print_Msg(F("Blankcheck...")); + display_Update(); + + // Enable ports 0x0120 + send_GBM(0x09); + + // Set WE and WP + send_GBM(0x0A); + send_GBM(0x2); + + // Enable hidden mapping area + writeByte_GBM(0x2100, 0x01); + send_GBM(0x0F, 0x5555, 0xAA); + send_GBM(0x0F, 0x2AAA, 0x55); + send_GBM(0x0F, 0x5555, 0x77); + send_GBM(0x0F, 0x5555, 0xAA); + send_GBM(0x0F, 0x2AAA, 0x55); + send_GBM(0x0F, 0x5555, 0x77); + + // Disable ports 0x0120... + send_GBM(0x08); + + // Read rom + for (byte currByte = 0; currByte < 128; currByte++) { + if (readByte_GBM(currByte) != 0xFF) { + return 0; + } + } + return 1; +} + +void writeMapping_GBM() { + print_Msg(F("Writing...")); + display_Update(); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + // Get map file size and check if it exceeds 128KByte + if (myFile.fileSize() > 0x80) { + print_Error(F("File is too big."), true); + } + + // Enable access to ports 0120h + send_GBM(0x09); + + // Enable write + send_GBM(0x0A); + send_GBM(0x2); + + // Map entire flash rom + send_GBM(0x4); + + // Set bank, writes to 0x5555 need odd bank number + writeByte_GBM(0x2100, 0x1); + + // Disable ports 0x2100 and 0x120 or else those addresses will not be writable + send_GBM(0x10); + send_GBM(0x08); + + // Unlock write to map area + writeByte_GBM(0x5555, 0xAA); + writeByte_GBM(0x2AAA, 0x55); + writeByte_GBM(0x5555, 0x60); + writeByte_GBM(0x5555, 0xAA); + writeByte_GBM(0x2AAA, 0x55); + writeByte_GBM(0x5555, 0xE0); + + // Check if flashrom is ready for writing or busy + while ((readByte_GBM(0) & 0x80) != 0x80) {} + + // Fill SD buffer + myFile.read(sdBuffer, 128); + + // Enable access to ports 0x120 and 0x2100 + send_GBM(0x09); + send_GBM(0x11); + + // Set bank + writeByte_GBM(0x2100, 0x1); + + // Disable ports 0x2100 and 0x120 or else those addresses will not be writable + send_GBM(0x10); + send_GBM(0x08); + + // Write flash buffer command + writeByte_GBM(0x5555, 0xAA); + writeByte_GBM(0x2AAA, 0x55); + writeByte_GBM(0x5555, 0xA0); + + // Wait until flashrom is ready again + while ((readByte_GBM(0) & 0x80) != 0x80) {} + + // Enable access to ports 0x120 and 0x2100 + send_GBM(0x09); + send_GBM(0x11); + + // Set bank + writeByte_GBM(0x2100, 0); + + // Disable ports 0x2100 and 0x120 or else those addresses will not be writable + send_GBM(0x10); + send_GBM(0x08); + + // Fill flash buffer + for (word currByte = 0; currByte < 128; currByte++) { + // Blink led + PORTB ^= (1 << 4); + + writeByte_GBM(currByte, sdBuffer[currByte]); + } + // Execute write + writeByte_GBM(127, 0xFF); + + // Close the file: + myFile.close(); + println_Msg(F("Done")); + } + else { + print_Error(F("Can't open file"), false); + } +} + +//****************************************** +// End of File //****************************************** diff --git a/Cart_Reader/PCE.ino b/Cart_Reader/PCE.ino index 6312b29..7384adf 100644 --- a/Cart_Reader/PCE.ino +++ b/Cart_Reader/PCE.ino @@ -1,709 +1,709 @@ -//****************************************** -// PC Engine & TurboGrafx dump code by tamanegi_taro -// Revision 1.0.1 April 18th 2018 -// -// Special thanks -// sanni - Arduino cart reader -// skaman - ROM size detection -// NO-INTRO - CRC list for game name detection -// -//****************************************** - -/****************************************** - Defines - *****************************************/ -#define HUCARD 0 -#define TURBOCHIP 1 - -#define DETECTION_SIZE 64 -#define CHKSUM_SKIP 0 -#define CHKSUM_OK 1 -#define CHKSUM_ERROR 2 - -/****************************************** - Prototype Declarations - *****************************************/ -/* Hoping that sanni will use this progressbar function */ -void draw_progressbar(uint32_t processedsize, uint32_t totalsize); -void pcsMenu(void); -void pceMenu(void); - -/* Several PCE dedicated functions */ -void pin_read_write_PCE(void); -void pin_init_PCE(void); -void setup_cart_PCE(void); -void reset_cart_PCE(void); -uint8_t read_byte_PCE(uint32_t address); -uint8_t write_byte_PCE(uint32_t address, uint8_t data); -uint32_t detect_rom_size_PCE(void); -void read_bank_PCE(uint32_t address_start, uint32_t address_end, uint32_t *processed_size, uint32_t total_size); -void read_rom_PCE(void); - -/****************************************** - Variables - *****************************************/ -uint8_t pce_internal_mode; //0 - HuCARD, 1 - TurboChip - -/****************************************** - Menu -*****************************************/ -// PCE start menu -static const char pceMenuItem1[] PROGMEM = "HuCARD"; -static const char pceMenuItem2[] PROGMEM = "Turbochip"; -static const char* const menuOptionspce[] PROGMEM = {pceMenuItem1, pceMenuItem2}; - -// PCE card menu items -static const char pceCartMenuItem1[] PROGMEM = "Read Rom"; -static const char pceCartMenuItem2[] PROGMEM = "Reset"; -static const char* const menuOptionspceCart[] PROGMEM = {pceCartMenuItem1, pceCartMenuItem2}; - -void draw_progressbar(uint32_t processedsize, uint32_t totalsize) -{ - uint8_t currentstatus, i; - static uint8_t previousstatus; - - //Find progressbar length and draw if processed size is not 0 - if (processedsize != 0) - { - - // Progress bar - if (processedsize >= totalsize) - { - //if processed size is equal to total process size, finish drawing progress bar - currentstatus = 20; - } - else - { - //if processed size did not reach total process size, find how many "*" should be drawn - currentstatus = processedsize / (totalsize / 20); - } - - //Draw "*" if needed - if (currentstatus > previousstatus) - { - for (i = previousstatus; i < currentstatus; i++) - { - if (i == 19) - { - //If end of progress bar, finish progress bar by drawing "]" - print_Msg(F("]")); - } - else - { - print_Msg(F("*")); - } - //Update display - display_Update(); - } - //update previous "*" status - previousstatus = currentstatus; - } - } - else - { - //If processed size is 0, initialize and draw "[" - previousstatus = 0; - print_Msg(F("[")); - display_Update(); - } -} - -// PCE start menu -void pcsMenu(void) { - // create menu with title and 3 options to choose from - unsigned char pceDev; - // Copy menuOptions out of progmem - convertPgm(menuOptionspce, 2); - pceDev = question_box("Select device", menuOptions, 2, 0); - - // wait for user choice to come back from the question box menu - switch (pceDev) - { - case 0: - //Hucard - display_Clear(); - display_Update(); - pce_internal_mode = HUCARD; - setup_cart_PCE(); - mode = mode_PCE; - break; - - case 1: - //Turbografx - display_Clear(); - display_Update(); - pce_internal_mode = TURBOCHIP; - setup_cart_PCE(); - mode = mode_PCE; - break; - } -} - -void pin_read_write_PCE(void) -{ - // Set Address Pins to Output - //A0-A7 - DDRF = 0xFF; - //A8-A15 - DDRK = 0xFF; - //A16-A19 - DDRL = (DDRL & 0xF0) | 0x0F; - - //Set Control Pin to Output CS(PL4) - DDRL |= (1 << 4); - - //Set CS(PL4) to HIGH - PORTL |= (1 << 4); - - // Set Control Pins to Output RST(PH0) RD(PH3) WR(PH5) - DDRH |= (1 << 0) | (1 << 3) | (1 << 5); - // Switch all of above to HIGH - PORTH |= (1 << 0) | (1 << 3) | (1 << 5); - - // Set IRQ(PH4) to Input - DDRH &= ~(1 << 4); - // Activate Internal Pullup Resistors - PORTH |= (1 << 4); - - // Set Data Pins (D0-D7) to Input - DDRC = 0x00; - - // Enable Internal Pullups - PORTC = 0xFF; - - reset_cart_PCE(); -} - -void pin_init_PCE(void) -{ - - //Set Address Pins to input and pull up - DDRF = 0x00; - PORTF = 0xFF; - DDRK = 0x00; - PORTK = 0xFF; - DDRL = 0x00; - PORTL = 0xFF; - DDRH &= ~((1 << 0) | (1 << 3) | (1 << 5) | (1 << 6)); - PORTH = (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); - - // Set IRQ(PH4) to Input - DDRH &= ~(1 << 4); - // Activate Internal Pullup Resistors - PORTH |= (1 << 4); - - // Set Data Pins (D0-D7) to Input - DDRC = 0x00; - // Enable Internal Pullups - PORTC = 0xFF; - -} - -void setup_cart_PCE(void) -{ - // Set cicrstPin(PG1) to Output - DDRG |= (1 << 1); - // Output a high to disable CIC - PORTG |= (1 << 1); - - pin_init_PCE(); - -} - -void reset_cart_PCE(void) -{ - //Set RESET as Low - PORTH &= ~(1 << 0); - delay(200); - //Set RESET as High - PORTH |= (1 << 0); - delay(200); - -} - -void set_address_PCE(uint32_t address) -{ - //Set address - PORTF = address & 0xFF; - PORTK = (address >> 8) & 0xFF; - PORTL = (PORTL & 0xF0) | ((address >> 16) & 0x0F); -} - -uint8_t read_byte_PCE(uint32_t address) -{ - uint8_t ret; - uint8_t address_byte; - - set_address_PCE(address); - - // Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Set CS(PL4) and RD(PH3) as LOW - PORTL &= ~(1 << 4); - PORTH &= ~(1 << 3); - - // Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - //read byte - ret = PINC; - - //Swap bit order for PC Engine HuCARD - if (pce_internal_mode == HUCARD) - { - ret = ((ret & 0x01) << 7) | ((ret & 0x02) << 5) | ((ret & 0x04) << 3) | ((ret & 0x08) << 1) | ((ret & 0x10) >> 1) | ((ret & 0x20) >> 3) | ((ret & 0x40) >> 5) | ((ret & 0x80) >> 7); - } - - // Set CS(PL4) and RD(PH3) as HIGH - PORTL |= (1 << 4); - PORTH |= (1 << 3); - - //return read data - return ret; - -} - -uint8_t write_byte_PCE(uint32_t address, uint8_t data) -{ - uint8_t ret; - uint8_t address_byte; - - set_address_PCE(address); - - // Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - //Swap bit order for PC Engine HuCARD - if (pce_internal_mode == HUCARD) - { - data = ((data & 0x01) << 7) | ((data & 0x02) << 5) | ((data & 0x04) << 3) | ((data & 0x08) << 1) | ((data & 0x10) >> 1) | ((data & 0x20) >> 3) | ((data & 0x40) >> 5) | ((data & 0x80) >> 7); - } - - //write byte - PORTC = data; - - // Set Data Pins (D0-D7) to Output - DDRC = 0xFF; - - // Set CS(PL4) and WR(PH5) as LOW - PORTL &= ~(1 << 4); - PORTH &= ~(1 << 5); - - // Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Set CS(PL4) and WR(PH5) as HIGH - PORTL |= (1 << 4); - PORTH |= (1 << 5); - - // Set Data Pins (D0-D7) to Input - DDRC = 0x00; - - // Enable Internal Pullups - PORTC = 0xFF; - - //return read data - return ret; - -} - -//Confirm the size of ROM - 128Kb, 256Kb, 384Kb, 512Kb, 768Kb or 1024Kb -uint32_t detect_rom_size_PCE(void) -{ - uint32_t rom_size; - uint8_t read_byte; - uint8_t current_byte; - uint8_t detect_128, detect_256, detect_512, detect_768; - - //Initialize variables - detect_128 = 0; - detect_256 = 0; - detect_512 = 0; - detect_768 = 0; - - //Set pins to read PC Engine cart - pin_read_write_PCE(); - - //Confirm where mirror address start from(128KB, 256KB, 512KB, 768, or 1024KB) - for (current_byte = 0; current_byte < DETECTION_SIZE; current_byte++) { - if ((current_byte != detect_128) && (current_byte != detect_256) && (current_byte != detect_512) && (current_byte != detect_768)) - { - //If none matched, it is 1024KB - break; - } - - //read byte for 128KB, 256KB, 512KB detection - read_byte = read_byte_PCE(current_byte); - - //128KB detection - if (current_byte == detect_128) - { - if (read_byte_PCE(current_byte + 128UL * 1024UL) == read_byte) - { - detect_128++; - } - } - - //256KB detection - if (current_byte == detect_256) - { - if (read_byte_PCE(current_byte + 256UL * 1024UL) == read_byte) - { - detect_256++; - } - } - - //512KB detection - if (current_byte == detect_512) - { - if (read_byte_PCE(current_byte + 512UL * 1024UL) == read_byte) - { - detect_512++; - } - } - - //768KB detection - read_byte = read_byte_PCE(current_byte + 512UL * 1024UL); - if (current_byte == detect_768) - { - if (read_byte_PCE(current_byte + 768UL * 1024UL) == read_byte) - { - detect_768++; - } - } - } - - //debug - //sprintf(fileName, "%d %d %d %d", detect_128, detect_256, detect_512, detect_768); //using filename global variable as string. Initialzed in below anyways. - //println_Msg(fileName); - - //ROM size detection by result - if (detect_128 == DETECTION_SIZE) - { - rom_size = 128; - } - else if (detect_256 == DETECTION_SIZE) - { - if (detect_512 == DETECTION_SIZE) - { - rom_size = 256; - } - else - { - //Another confirmation for 384KB because 384KB hucard has data in 0x0--0x40000 and 0x80000--0xA0000(0x40000 is mirror of 0x00000) - rom_size = 384; - } - } - else if (detect_512 == DETECTION_SIZE) - { - rom_size = 512; - } - else if (detect_768 == DETECTION_SIZE) - { - rom_size = 768; - } - else - { - rom_size = 1024; - } - - //If rom size is more than or equal to 512KB, detect Street fighter II' - if (rom_size >= 512) - { - //Look for "NEC HE " - if (read_byte_PCE(0x7FFF9) == 'N' && read_byte_PCE(0x7FFFA) == 'E' && read_byte_PCE(0x7FFFB) == 'C' - && read_byte_PCE(0x7FFFC) == ' ' && read_byte_PCE(0x7FFFD) == 'H' && read_byte_PCE(0x7FFFE) == 'E') - { - rom_size = 2560; - } - } - - return rom_size; -} - -/* Must be address_start and address_end should be 512 byte aligned */ -void read_bank_PCE(uint32_t address_start, uint32_t address_end, uint32_t *processed_size, uint32_t total_size) -{ - uint32_t currByte; - uint16_t c; - - for (currByte = address_start; currByte < address_end; currByte += 512) { - for (c = 0; c < 512; c++) { - sdBuffer[c] = read_byte_PCE(currByte + c); - } - myFile.write(sdBuffer, 512); - *processed_size += 512; - draw_progressbar(*processed_size, total_size); - } -} - -//Get line from file and convert upper case to lower case -void skip_line(SdFile* readfile) -{ - int i = 0; - char str_buf; - - while (readfile->available()) - { - //Read 1 byte from file - str_buf = readfile->read(); - - //if end of file or newline found, execute command - if (str_buf == '\r') - { - readfile->read(); //dispose \n because \r\n - break; - } - i++; - }//End while -} - -//Get line from file and convert upper case to lower case -void get_line(char* str_buf, SdFile* readfile, uint8_t maxi) -{ - int i = 0; - - while (readfile->available()) - { - //If line size is more than maximum array, limit it. - if (i >= maxi) - { - i = maxi - 1; - } - - //Read 1 byte from file - str_buf[i] = readfile->read(); - - //if end of file or newline found, execute command - if (str_buf[i] == '\r') - { - str_buf[i] = '\0'; - readfile->read(); //dispose \n because \r\n - break; - } - i++; - }//End while -} - -uint32_t calculate_crc32(int n, unsigned char c[], uint32_t r) -{ - int i, j; - - for (i = 0; i < n; i++) { - r ^= c[i]; - for (j = 0; j < 8; j++) - if (r & 1) r = (r >> 1) ^ 0xEDB88320UL; - else r >>= 1; - } - return r; -} - -void crc_search(char *file_p, char *folder_p, uint32_t rom_size) -{ - SdFile rom, script; - uint32_t r, crc, processedsize; - char gamename[100]; - char crc_file[9], crc_search[9]; - uint8_t flag; - flag = CHKSUM_SKIP; - - //Open list file. If no list file found, just skip - sd.chdir("/"); //Set read directry to root - if (script.open("PCE_CRC_LIST.txt", O_READ)) - { - //Calculate CRC of ROM file - sd.chdir(folder_p); - if (rom.open(file_p, O_READ)) - { - //Initialize flag as error - flag = CHKSUM_ERROR; - crc = 0xFFFFFFFFUL; //Initialize CRC - display_Clear(); - println_Msg("Calculating chksum..."); - processedsize = 0; - draw_progressbar(0, rom_size * 1024UL); //Initialize progress bar - - while (rom.available()) - { - r = rom.read(sdBuffer, 512); - crc = calculate_crc32(r, sdBuffer, crc); - processedsize += r; - draw_progressbar(processedsize, rom_size * 1024UL); - } - - crc = crc ^ 0xFFFFFFFFUL; //Finish CRC calculation and progress bar - draw_progressbar(rom_size * 1024UL, rom_size * 1024UL); - - //Display calculated CRC - sprintf(crc_file, "%08lX", crc); - - //Search for same CRC in list - while (script.available()) { - //Read 2 lines (game name and CRC) - get_line(gamename, &script, 96); - get_line(crc_search, &script, 9); - skip_line(&script); //Skip every 3rd line - - //if checksum search successful, rename the file and end search - if (strcmp(crc_search, crc_file) == 0) - { - print_Msg("Chksum OK "); - println_Msg(crc_file); - print_Msg(F("Saved to ")); - print_Msg(folder_p); - print_Msg(F("/")); - print_Msg(gamename); - print_Msg(F(".pce")); - flag = CHKSUM_OK; - strcat(gamename, ".pce"); - rom.rename(sd.vwd(), gamename); - break; - } - } - rom.close(); - } - } - - - if (flag == CHKSUM_SKIP) - { - print_Msg(F("Saved to ")); - print_Msg(folder_p); - print_Msg(F("/")); - print_Msg(file_p); - } - else if (flag == CHKSUM_ERROR) - { - print_Msg("Chksum Error "); - println_Msg(crc_file); - print_Msg(F("Saved to ")); - print_Msg(folder_p); - print_Msg(F("/")); - print_Msg(file_p); - } - - script.close(); - -} - - -void read_rom_PCE(void) -{ - uint32_t rom_size; - uint32_t processed_size = 0; - - //clear the screen - display_Clear(); - rom_size = detect_rom_size_PCE(); - sprintf(fileName, "Detected size: %dKB", rom_size); //using filename global variable as string. Initialzed in below anyways. - println_Msg(fileName); - - //debug - //return; - - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, "PCEROM"); - strcat(fileName, ".pce"); - - // create a new folder for the save file - EEPROM_readAnything(10, foldern); - sprintf(folder, "PCE/ROM/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - println_Msg(F("Saving ROM...")); - display_Update(); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - //open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("Can't create file on SD"), true); - } - - pin_read_write_PCE(); - - //Initialize progress bar by setting processed size as 0 - draw_progressbar(0, rom_size * 1024UL); - - if (rom_size == 384) - { - //Read two sections. 0x000000--0x040000 and 0x080000--0x0A0000 for 384KB - read_bank_PCE(0, 0x40000, &processed_size, rom_size * 1024UL); - read_bank_PCE(0x80000, 0xA0000, &processed_size, rom_size * 1024UL); - } - else if (rom_size == 2560) - { - //Dump Street fighter II' Champion Edition - read_bank_PCE(0, 0x80000, &processed_size, rom_size * 1024UL); //Read first bank - write_byte_PCE(0x1FF0, 0xFF); //Display second bank - read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL); //Read second bank - write_byte_PCE(0x1FF1, 0xFF); //Display third bank - read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL); //Read third bank - write_byte_PCE(0x1FF2, 0xFF); //Display forth bank - read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL); //Read forth bank - write_byte_PCE(0x1FF3, 0xFF); //Display fifth bank - read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL); //Read fifth bank - } - else - { - //Read start form 0x000000 and keep reading until end of ROM - read_bank_PCE(0, rom_size * 1024UL, &processed_size, rom_size * 1024UL); - } - - pin_init_PCE(); - - //Close the file: - myFile.close(); - - //CRC search and rename ROM - crc_search(fileName, folder, rom_size); - -} - - - - -// SNES Menu -void pceMenu() { - // create menu with title and 7 options to choose from - unsigned char mainMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionspceCart, 2); - - if (pce_internal_mode == HUCARD) - { - mainMenu = question_box("PCE HuCARD menu", menuOptions, 2, 0); - } - else - { - mainMenu = question_box("TG TurboChip menu", menuOptions, 2, 0); - } - - // wait for user choice to come back from the question box menu - switch (mainMenu) - { - case 0: - display_Clear(); - // Change working dir to root - sd.chdir("/"); - read_rom_PCE(); - break; - - case 1: - asm volatile (" jmp 0"); - break; - } - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); -} - - -//****************************************** -// End of File +//****************************************** +// PC Engine & TurboGrafx dump code by tamanegi_taro +// Revision 1.0.1 April 18th 2018 +// +// Special thanks +// sanni - Arduino cart reader +// skaman - ROM size detection +// NO-INTRO - CRC list for game name detection +// +//****************************************** + +/****************************************** + Defines + *****************************************/ +#define HUCARD 0 +#define TURBOCHIP 1 + +#define DETECTION_SIZE 64 +#define CHKSUM_SKIP 0 +#define CHKSUM_OK 1 +#define CHKSUM_ERROR 2 + +/****************************************** + Prototype Declarations + *****************************************/ +/* Hoping that sanni will use this progressbar function */ +void draw_progressbar(uint32_t processedsize, uint32_t totalsize); +void pcsMenu(void); +void pceMenu(void); + +/* Several PCE dedicated functions */ +void pin_read_write_PCE(void); +void pin_init_PCE(void); +void setup_cart_PCE(void); +void reset_cart_PCE(void); +uint8_t read_byte_PCE(uint32_t address); +uint8_t write_byte_PCE(uint32_t address, uint8_t data); +uint32_t detect_rom_size_PCE(void); +void read_bank_PCE(uint32_t address_start, uint32_t address_end, uint32_t *processed_size, uint32_t total_size); +void read_rom_PCE(void); + +/****************************************** + Variables + *****************************************/ +uint8_t pce_internal_mode; //0 - HuCARD, 1 - TurboChip + +/****************************************** + Menu +*****************************************/ +// PCE start menu +static const char pceMenuItem1[] PROGMEM = "HuCARD"; +static const char pceMenuItem2[] PROGMEM = "Turbochip"; +static const char* const menuOptionspce[] PROGMEM = {pceMenuItem1, pceMenuItem2}; + +// PCE card menu items +static const char pceCartMenuItem1[] PROGMEM = "Read Rom"; +static const char pceCartMenuItem2[] PROGMEM = "Reset"; +static const char* const menuOptionspceCart[] PROGMEM = {pceCartMenuItem1, pceCartMenuItem2}; + +void draw_progressbar(uint32_t processedsize, uint32_t totalsize) +{ + uint8_t currentstatus, i; + static uint8_t previousstatus; + + //Find progressbar length and draw if processed size is not 0 + if (processedsize != 0) + { + + // Progress bar + if (processedsize >= totalsize) + { + //if processed size is equal to total process size, finish drawing progress bar + currentstatus = 20; + } + else + { + //if processed size did not reach total process size, find how many "*" should be drawn + currentstatus = processedsize / (totalsize / 20); + } + + //Draw "*" if needed + if (currentstatus > previousstatus) + { + for (i = previousstatus; i < currentstatus; i++) + { + if (i == 19) + { + //If end of progress bar, finish progress bar by drawing "]" + print_Msg(F("]")); + } + else + { + print_Msg(F("*")); + } + //Update display + display_Update(); + } + //update previous "*" status + previousstatus = currentstatus; + } + } + else + { + //If processed size is 0, initialize and draw "[" + previousstatus = 0; + print_Msg(F("[")); + display_Update(); + } +} + +// PCE start menu +void pcsMenu(void) { + // create menu with title and 3 options to choose from + unsigned char pceDev; + // Copy menuOptions out of progmem + convertPgm(menuOptionspce, 2); + pceDev = question_box("Select device", menuOptions, 2, 0); + + // wait for user choice to come back from the question box menu + switch (pceDev) + { + case 0: + //Hucard + display_Clear(); + display_Update(); + pce_internal_mode = HUCARD; + setup_cart_PCE(); + mode = mode_PCE; + break; + + case 1: + //Turbografx + display_Clear(); + display_Update(); + pce_internal_mode = TURBOCHIP; + setup_cart_PCE(); + mode = mode_PCE; + break; + } +} + +void pin_read_write_PCE(void) +{ + // Set Address Pins to Output + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A19 + DDRL = (DDRL & 0xF0) | 0x0F; + + //Set Control Pin to Output CS(PL4) + DDRL |= (1 << 4); + + //Set CS(PL4) to HIGH + PORTL |= (1 << 4); + + // Set Control Pins to Output RST(PH0) RD(PH3) WR(PH5) + DDRH |= (1 << 0) | (1 << 3) | (1 << 5); + // Switch all of above to HIGH + PORTH |= (1 << 0) | (1 << 3) | (1 << 5); + + // Set IRQ(PH4) to Input + DDRH &= ~(1 << 4); + // Activate Internal Pullup Resistors + PORTH |= (1 << 4); + + // Set Data Pins (D0-D7) to Input + DDRC = 0x00; + + // Enable Internal Pullups + PORTC = 0xFF; + + reset_cart_PCE(); +} + +void pin_init_PCE(void) +{ + + //Set Address Pins to input and pull up + DDRF = 0x00; + PORTF = 0xFF; + DDRK = 0x00; + PORTK = 0xFF; + DDRL = 0x00; + PORTL = 0xFF; + DDRH &= ~((1 << 0) | (1 << 3) | (1 << 5) | (1 << 6)); + PORTH = (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); + + // Set IRQ(PH4) to Input + DDRH &= ~(1 << 4); + // Activate Internal Pullup Resistors + PORTH |= (1 << 4); + + // Set Data Pins (D0-D7) to Input + DDRC = 0x00; + // Enable Internal Pullups + PORTC = 0xFF; + +} + +void setup_cart_PCE(void) +{ + // Set cicrstPin(PG1) to Output + DDRG |= (1 << 1); + // Output a high to disable CIC + PORTG |= (1 << 1); + + pin_init_PCE(); + +} + +void reset_cart_PCE(void) +{ + //Set RESET as Low + PORTH &= ~(1 << 0); + delay(200); + //Set RESET as High + PORTH |= (1 << 0); + delay(200); + +} + +void set_address_PCE(uint32_t address) +{ + //Set address + PORTF = address & 0xFF; + PORTK = (address >> 8) & 0xFF; + PORTL = (PORTL & 0xF0) | ((address >> 16) & 0x0F); +} + +uint8_t read_byte_PCE(uint32_t address) +{ + uint8_t ret; + uint8_t address_byte; + + set_address_PCE(address); + + // Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Set CS(PL4) and RD(PH3) as LOW + PORTL &= ~(1 << 4); + PORTH &= ~(1 << 3); + + // Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + //read byte + ret = PINC; + + //Swap bit order for PC Engine HuCARD + if (pce_internal_mode == HUCARD) + { + ret = ((ret & 0x01) << 7) | ((ret & 0x02) << 5) | ((ret & 0x04) << 3) | ((ret & 0x08) << 1) | ((ret & 0x10) >> 1) | ((ret & 0x20) >> 3) | ((ret & 0x40) >> 5) | ((ret & 0x80) >> 7); + } + + // Set CS(PL4) and RD(PH3) as HIGH + PORTL |= (1 << 4); + PORTH |= (1 << 3); + + //return read data + return ret; + +} + +uint8_t write_byte_PCE(uint32_t address, uint8_t data) +{ + uint8_t ret; + uint8_t address_byte; + + set_address_PCE(address); + + // Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + //Swap bit order for PC Engine HuCARD + if (pce_internal_mode == HUCARD) + { + data = ((data & 0x01) << 7) | ((data & 0x02) << 5) | ((data & 0x04) << 3) | ((data & 0x08) << 1) | ((data & 0x10) >> 1) | ((data & 0x20) >> 3) | ((data & 0x40) >> 5) | ((data & 0x80) >> 7); + } + + //write byte + PORTC = data; + + // Set Data Pins (D0-D7) to Output + DDRC = 0xFF; + + // Set CS(PL4) and WR(PH5) as LOW + PORTL &= ~(1 << 4); + PORTH &= ~(1 << 5); + + // Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Set CS(PL4) and WR(PH5) as HIGH + PORTL |= (1 << 4); + PORTH |= (1 << 5); + + // Set Data Pins (D0-D7) to Input + DDRC = 0x00; + + // Enable Internal Pullups + PORTC = 0xFF; + + //return read data + return ret; + +} + +//Confirm the size of ROM - 128Kb, 256Kb, 384Kb, 512Kb, 768Kb or 1024Kb +uint32_t detect_rom_size_PCE(void) +{ + uint32_t rom_size; + uint8_t read_byte; + uint8_t current_byte; + uint8_t detect_128, detect_256, detect_512, detect_768; + + //Initialize variables + detect_128 = 0; + detect_256 = 0; + detect_512 = 0; + detect_768 = 0; + + //Set pins to read PC Engine cart + pin_read_write_PCE(); + + //Confirm where mirror address start from(128KB, 256KB, 512KB, 768, or 1024KB) + for (current_byte = 0; current_byte < DETECTION_SIZE; current_byte++) { + if ((current_byte != detect_128) && (current_byte != detect_256) && (current_byte != detect_512) && (current_byte != detect_768)) + { + //If none matched, it is 1024KB + break; + } + + //read byte for 128KB, 256KB, 512KB detection + read_byte = read_byte_PCE(current_byte); + + //128KB detection + if (current_byte == detect_128) + { + if (read_byte_PCE(current_byte + 128UL * 1024UL) == read_byte) + { + detect_128++; + } + } + + //256KB detection + if (current_byte == detect_256) + { + if (read_byte_PCE(current_byte + 256UL * 1024UL) == read_byte) + { + detect_256++; + } + } + + //512KB detection + if (current_byte == detect_512) + { + if (read_byte_PCE(current_byte + 512UL * 1024UL) == read_byte) + { + detect_512++; + } + } + + //768KB detection + read_byte = read_byte_PCE(current_byte + 512UL * 1024UL); + if (current_byte == detect_768) + { + if (read_byte_PCE(current_byte + 768UL * 1024UL) == read_byte) + { + detect_768++; + } + } + } + + //debug + //sprintf(fileName, "%d %d %d %d", detect_128, detect_256, detect_512, detect_768); //using filename global variable as string. Initialzed in below anyways. + //println_Msg(fileName); + + //ROM size detection by result + if (detect_128 == DETECTION_SIZE) + { + rom_size = 128; + } + else if (detect_256 == DETECTION_SIZE) + { + if (detect_512 == DETECTION_SIZE) + { + rom_size = 256; + } + else + { + //Another confirmation for 384KB because 384KB hucard has data in 0x0--0x40000 and 0x80000--0xA0000(0x40000 is mirror of 0x00000) + rom_size = 384; + } + } + else if (detect_512 == DETECTION_SIZE) + { + rom_size = 512; + } + else if (detect_768 == DETECTION_SIZE) + { + rom_size = 768; + } + else + { + rom_size = 1024; + } + + //If rom size is more than or equal to 512KB, detect Street fighter II' + if (rom_size >= 512) + { + //Look for "NEC HE " + if (read_byte_PCE(0x7FFF9) == 'N' && read_byte_PCE(0x7FFFA) == 'E' && read_byte_PCE(0x7FFFB) == 'C' + && read_byte_PCE(0x7FFFC) == ' ' && read_byte_PCE(0x7FFFD) == 'H' && read_byte_PCE(0x7FFFE) == 'E') + { + rom_size = 2560; + } + } + + return rom_size; +} + +/* Must be address_start and address_end should be 512 byte aligned */ +void read_bank_PCE(uint32_t address_start, uint32_t address_end, uint32_t *processed_size, uint32_t total_size) +{ + uint32_t currByte; + uint16_t c; + + for (currByte = address_start; currByte < address_end; currByte += 512) { + for (c = 0; c < 512; c++) { + sdBuffer[c] = read_byte_PCE(currByte + c); + } + myFile.write(sdBuffer, 512); + *processed_size += 512; + draw_progressbar(*processed_size, total_size); + } +} + +//Get line from file and convert upper case to lower case +void skip_line(SdFile* readfile) +{ + int i = 0; + char str_buf; + + while (readfile->available()) + { + //Read 1 byte from file + str_buf = readfile->read(); + + //if end of file or newline found, execute command + if (str_buf == '\r') + { + readfile->read(); //dispose \n because \r\n + break; + } + i++; + }//End while +} + +//Get line from file and convert upper case to lower case +void get_line(char* str_buf, SdFile* readfile, uint8_t maxi) +{ + int i = 0; + + while (readfile->available()) + { + //If line size is more than maximum array, limit it. + if (i >= maxi) + { + i = maxi - 1; + } + + //Read 1 byte from file + str_buf[i] = readfile->read(); + + //if end of file or newline found, execute command + if (str_buf[i] == '\r') + { + str_buf[i] = '\0'; + readfile->read(); //dispose \n because \r\n + break; + } + i++; + }//End while +} + +uint32_t calculate_crc32(int n, unsigned char c[], uint32_t r) +{ + int i, j; + + for (i = 0; i < n; i++) { + r ^= c[i]; + for (j = 0; j < 8; j++) + if (r & 1) r = (r >> 1) ^ 0xEDB88320UL; + else r >>= 1; + } + return r; +} + +void crc_search(char *file_p, char *folder_p, uint32_t rom_size) +{ + SdFile rom, script; + uint32_t r, crc, processedsize; + char gamename[100]; + char crc_file[9], crc_search[9]; + uint8_t flag; + flag = CHKSUM_SKIP; + + //Open list file. If no list file found, just skip + sd.chdir("/"); //Set read directry to root + if (script.open("PCE_CRC_LIST.txt", O_READ)) + { + //Calculate CRC of ROM file + sd.chdir(folder_p); + if (rom.open(file_p, O_READ)) + { + //Initialize flag as error + flag = CHKSUM_ERROR; + crc = 0xFFFFFFFFUL; //Initialize CRC + display_Clear(); + println_Msg("Calculating chksum..."); + processedsize = 0; + draw_progressbar(0, rom_size * 1024UL); //Initialize progress bar + + while (rom.available()) + { + r = rom.read(sdBuffer, 512); + crc = calculate_crc32(r, sdBuffer, crc); + processedsize += r; + draw_progressbar(processedsize, rom_size * 1024UL); + } + + crc = crc ^ 0xFFFFFFFFUL; //Finish CRC calculation and progress bar + draw_progressbar(rom_size * 1024UL, rom_size * 1024UL); + + //Display calculated CRC + sprintf(crc_file, "%08lX", crc); + + //Search for same CRC in list + while (script.available()) { + //Read 2 lines (game name and CRC) + get_line(gamename, &script, 96); + get_line(crc_search, &script, 9); + skip_line(&script); //Skip every 3rd line + + //if checksum search successful, rename the file and end search + if (strcmp(crc_search, crc_file) == 0) + { + print_Msg("Chksum OK "); + println_Msg(crc_file); + print_Msg(F("Saved to ")); + print_Msg(folder_p); + print_Msg(F("/")); + print_Msg(gamename); + print_Msg(F(".pce")); + flag = CHKSUM_OK; + strcat(gamename, ".pce"); + rom.rename(sd.vwd(), gamename); + break; + } + } + rom.close(); + } + } + + + if (flag == CHKSUM_SKIP) + { + print_Msg(F("Saved to ")); + print_Msg(folder_p); + print_Msg(F("/")); + print_Msg(file_p); + } + else if (flag == CHKSUM_ERROR) + { + print_Msg("Chksum Error "); + println_Msg(crc_file); + print_Msg(F("Saved to ")); + print_Msg(folder_p); + print_Msg(F("/")); + print_Msg(file_p); + } + + script.close(); + +} + + +void read_rom_PCE(void) +{ + uint32_t rom_size; + uint32_t processed_size = 0; + + //clear the screen + display_Clear(); + rom_size = detect_rom_size_PCE(); + sprintf(fileName, "Detected size: %dKB", rom_size); //using filename global variable as string. Initialzed in below anyways. + println_Msg(fileName); + + //debug + //return; + + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, "PCEROM"); + strcat(fileName, ".pce"); + + // create a new folder for the save file + EEPROM_readAnything(10, foldern); + sprintf(folder, "PCE/ROM/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + println_Msg(F("Saving ROM...")); + display_Update(); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("Can't create file on SD"), true); + } + + pin_read_write_PCE(); + + //Initialize progress bar by setting processed size as 0 + draw_progressbar(0, rom_size * 1024UL); + + if (rom_size == 384) + { + //Read two sections. 0x000000--0x040000 and 0x080000--0x0A0000 for 384KB + read_bank_PCE(0, 0x40000, &processed_size, rom_size * 1024UL); + read_bank_PCE(0x80000, 0xA0000, &processed_size, rom_size * 1024UL); + } + else if (rom_size == 2560) + { + //Dump Street fighter II' Champion Edition + read_bank_PCE(0, 0x80000, &processed_size, rom_size * 1024UL); //Read first bank + write_byte_PCE(0x1FF0, 0xFF); //Display second bank + read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL); //Read second bank + write_byte_PCE(0x1FF1, 0xFF); //Display third bank + read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL); //Read third bank + write_byte_PCE(0x1FF2, 0xFF); //Display forth bank + read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL); //Read forth bank + write_byte_PCE(0x1FF3, 0xFF); //Display fifth bank + read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL); //Read fifth bank + } + else + { + //Read start form 0x000000 and keep reading until end of ROM + read_bank_PCE(0, rom_size * 1024UL, &processed_size, rom_size * 1024UL); + } + + pin_init_PCE(); + + //Close the file: + myFile.close(); + + //CRC search and rename ROM + crc_search(fileName, folder, rom_size); + +} + + + + +// SNES Menu +void pceMenu() { + // create menu with title and 7 options to choose from + unsigned char mainMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionspceCart, 2); + + if (pce_internal_mode == HUCARD) + { + mainMenu = question_box("PCE HuCARD menu", menuOptions, 2, 0); + } + else + { + mainMenu = question_box("TG TurboChip menu", menuOptions, 2, 0); + } + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + case 0: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + read_rom_PCE(); + break; + + case 1: + asm volatile (" jmp 0"); + break; + } + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + + +//****************************************** +// End of File //****************************************** diff --git a/Cart_Reader/SNES.ino b/Cart_Reader/SNES.ino index 7bba293..64288bd 100644 --- a/Cart_Reader/SNES.ino +++ b/Cart_Reader/SNES.ino @@ -1,1927 +1,1927 @@ -//****************************************** -// SUPER NINTENDO MODULE -//****************************************** - -/****************************************** - Defines - *****************************************/ -// SNES Hi and LoRom, SA is HI with different Sram dumping -#define EX 4 -#define SA 3 -#define HI 1 -#define LO 0 - -/****************************************** - Variables - *****************************************/ -// Define SNES Cart Reader Variables -int romSpeed = 0; // 0 = SlowROM, 3 = FastROM -int romChips = 0; // 0 = ROM only, 1 = ROM & RAM, 2 = ROM & Save RAM, 3 = ROM & DSP1, 4 = ROM & RAM & DSP1, 5 = ROM & Save RAM & DSP1, 19 = ROM & SFX -// 227 = ROM & RAM & GameBoy data, 243 = CX4, 246 = ROM & DSP2 -byte romSizeExp = 0; // ROM-Size Exponent -int cartCountry = 255; -boolean NP = false; -byte cx4Type = 0; -byte cx4Map = 0; - -/****************************************** - Menu -*****************************************/ -// SNES/Nintendo Power SF Memory start menu -static const char snsMenuItem1[] PROGMEM = "Super Nintendo"; -static const char snsMenuItem2[] PROGMEM = "NPower SF Memory"; -static const char snsMenuItem3[] PROGMEM = "HiROM repro"; -static const char snsMenuItem4[] PROGMEM = "LoROM repro"; -static const char* const menuOptionsSNS[] PROGMEM = {snsMenuItem1, snsMenuItem2, snsMenuItem3, snsMenuItem4}; - -// SNES menu items -static const char SnesMenuItem1[] PROGMEM = "Read Rom"; -static const char SnesMenuItem2[] PROGMEM = "Read Save"; -static const char SnesMenuItem3[] PROGMEM = "Write Save"; -static const char SnesMenuItem4[] PROGMEM = "Test SRAM"; -static const char SnesMenuItem5[] PROGMEM = "Cycle cart"; -static const char SnesMenuItem6[] PROGMEM = "Reset"; -static const char* const menuOptionsSNES[] PROGMEM = {SnesMenuItem1, SnesMenuItem2, SnesMenuItem3, SnesMenuItem4, SnesMenuItem5, SnesMenuItem6}; - -// Manual config menu items -static const char confMenuItem1[] PROGMEM = "Use header info"; -static const char confMenuItem2[] PROGMEM = "4MB LoRom 256K Sram"; -static const char confMenuItem3[] PROGMEM = "4MB HiRom 64K Sram"; -static const char confMenuItem4[] PROGMEM = "6MB ExRom 256K Sram"; -static const char confMenuItem5[] PROGMEM = "Reset"; -static const char* const menuOptionsConf[] PROGMEM = {confMenuItem1, confMenuItem2, confMenuItem3, confMenuItem4, confMenuItem5}; - -// SNES start menu -void snsMenu() { - // create menu with title and 4 options to choose from - unsigned char snsCart; - // Copy menuOptions out of progmem - convertPgm(menuOptionsSNS, 4); - snsCart = question_box("Select Cart Type", menuOptions, 4, 0); - - // wait for user choice to come back from the question box menu - switch (snsCart) - { - case 0: - display_Clear(); - display_Update(); - setup_Snes(); - mode = mode_SNES; - break; - - case 1: - display_Clear(); - display_Update(); - setup_SFM(); - mode = mode_SFM; - break; - - case 2: - display_Clear(); - display_Update(); - hiROM = 1; - setup_Flash8(); - id_Flash8(); - wait(); - mode = mode_FLASH8; - break; - - case 3: - display_Clear(); - display_Update(); - hiROM = 0; - setup_Flash8(); - id_Flash8(); - wait(); - mode = mode_FLASH8; - break; - } -} - -// SNES Menu -void snesMenu() { - // create menu with title and 7 options to choose from - unsigned char mainMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsSNES, 6); - mainMenu = question_box("SNES Cart Reader", menuOptions, 6, 0); - - // wait for user choice to come back from the question box menu - switch (mainMenu) - { - case 0: - display_Clear(); - // Change working dir to root - sd.chdir("/"); - readROM_SNES(); - compare_checksum(); - break; - - case 1: - if (sramSize > 0) { - display_Clear(); - // Change working dir to root - sd.chdir("/"); - readSRAM(); - } - else { - display_Clear(); - print_Error(F("Does not have SRAM"), false); - } - break; - - case 2: - if (sramSize > 0) { - display_Clear(); - // Change working dir to root - sd.chdir("/"); - writeSRAM(1); - unsigned long wrErrors; - wrErrors = verifySRAM(); - if (wrErrors == 0) { - println_Msg(F("Verified OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(wrErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - } - else { - display_Clear(); - print_Error(F("Does not have SRAM"), false); - } - break; - - case 3: - if (sramSize > 0) { - display_Clear(); - println_Msg(F("Warning:")); - println_Msg(F("This can erase")); - println_Msg(F("your save games")); - println_Msg(F("")); - println_Msg(F("")); - println_Msg(F("Press any button to")); - println_Msg(F("start sram testing")); - display_Update(); - wait(); - display_Clear(); - // Change working dir to root - sd.chdir("/"); - readSRAM(); - eraseSRAM(0x00); - eraseSRAM(0xFF); - writeSRAM(0); - unsigned long wrErrors = verifySRAM(); - if (wrErrors == 0) { - println_Msg(F("Restored OK")); - display_Update(); - } - else { - print_Msg(F("Error: ")); - print_Msg(wrErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - } - else { - display_Clear(); - print_Error(F("Does not have SRAM"), false); - } - break; - - case 4: - // For arcademaster1 (Markfrizb) multi-game carts - // Set reset pin to output (PH0) - DDRH |= (1 << 0); - // Switch RST(PH0) to LOW - PORTH &= ~(1 << 0); - display_Clear(); - print_Msg("Resetting..."); - display_Update(); - delay(3000); // wait 3 secs to switch to next game - asm volatile (" jmp 0"); - break; - - case 5: - asm volatile (" jmp 0"); - break; - } - println_Msg(F("")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); -} - -// Menu for manual configuration -void confMenu() { - // create menu with title and 5 options to choose from - unsigned char subMenu; - // Copy menuOptions out of progmem - convertPgm(menuOptionsConf, 5); - subMenu = question_box("Choose mapping", menuOptions, 5, 0); - - // wait for user choice to come back from the question box menu - switch (subMenu) - { - case 0: - break; - - case 1: - romType = LO; - numBanks = 128; - sramSize = 256; - strcpy(romName, "LOROM"); - break; - - case 2: - romType = HI; - numBanks = 64; - sramSize = 64; - strcpy(romName, "HIROM"); - break; - - case 3: - romType = EX; - numBanks = 96; - sramSize = 256; - strcpy(romName, "EXROM"); - break; - - case 4: - // Reset - asm volatile (" jmp 0"); - break; - } -} - -/****************************************** - Setup - *****************************************/ -void setup_Snes() { - // Set cicrstPin(PG1) to Output - DDRG |= (1 << 1); - // Output a high signal until we're ready to start - PORTG |= (1 << 1); - // Set cichstPin(PG0) to Input - DDRG &= ~(1 << 0); - - // Adafruit Clock Generator - clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0); - clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); - clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); - clockgen.set_freq(2147727200ULL, SI5351_CLK0); - clockgen.set_freq(307200000ULL, SI5351_CLK2); - clockgen.output_enable(SI5351_CLK0, 1); - clockgen.output_enable(SI5351_CLK1, 0); - clockgen.output_enable(SI5351_CLK2, 1); - - // Set Address Pins to Output - //A0-A7 - DDRF = 0xFF; - //A8-A15 - DDRK = 0xFF; - //BA0-BA7 - DDRL = 0xFF; - //PA0-PA7 - DDRA = 0xFF; - - // Set Control Pins to Output RST(PH0) CS(PH3) WR(PH5) RD(PH6) - DDRH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); - // Switch RST(PH0) and WR(PH5) to HIGH - PORTH |= (1 << 0) | (1 << 5); - // Switch CS(PH3) and RD(PH6) to LOW - PORTH &= ~((1 << 3) | (1 << 6)); - - // Set Refresh(PE5) to Output - DDRE |= (1 << 5); - // Switch Refresh(PE5) to LOW (needed for SA-1) - PORTE &= ~(1 << 5); - - // Set CPU Clock(PH1) to Output - DDRH |= (1 << 1); - //PORTH &= ~(1 << 1); - - // Set IRQ(PH4) to Input - DDRH &= ~(1 << 4); - // Activate Internal Pullup Resistors - //PORTH |= (1 << 4); - - // Set expand(PG5) to Imput - DDRG &= ~(1 << 5); - // Activate Internal Pullup Resistors - //PORTG |= (1 << 5); - - // Set Data Pins (D0-D7) to Input - DDRC = 0x00; - // Enable Internal Pullups - //PORTC = 0xFF; - - // Unused pins - // Set wram(PE4) to Output - DDRE |= (1 << 4); - //PORTE &= ~(1 << 4); - // Set pawr(PJ1) to Output - DDRJ |= (1 << 1); - //PORTJ &= ~(1 << 1); - // Set pard(PJ0) to Output - DDRJ |= (1 << 0); - //PORTJ &= ~(1 << 0); - - // Start CIC by outputting a low signal to cicrstPin(PG1) - PORTG &= ~(1 << 1); - - // Wait for CIC reset - delay(1000); - - // Print all the info - getCartInfo_SNES(); -} - -/****************************************** - I/O Functions - *****************************************/ -// Switch control pins to write -void controlOut_SNES() { - // Switch RD(PH6) and WR(PH5) to HIGH - PORTH |= (1 << 6) | (1 << 5); - // Switch CS(PH3) to LOW - PORTH &= ~(1 << 3); -} - -// Switch control pins to read -void controlIn_SNES() { - // Switch WR(PH5) to HIGH - PORTH |= (1 << 5); - // Switch CS(PH3) and RD(PH6) to LOW - PORTH &= ~((1 << 3) | (1 << 6)); -} - -/****************************************** - Low level functions - *****************************************/ -// Write one byte of data to a location specified by bank and address, 00:0000 -void writeBank_SNES(byte myBank, word myAddress, byte myData) { - PORTL = myBank; - PORTF = myAddress & 0xFF; - PORTK = (myAddress >> 8) & 0xFF; - PORTC = myData; - - // Arduino running at 16Mhz -> one nop = 62.5ns - // Wait till output is stable - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Switch WR(PH5) to LOW - PORTH &= ~(1 << 5); - - // Leave WR low for at least 60ns - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Switch WR(PH5) to HIGH - PORTH |= (1 << 5); - - // Leave WR high for at least 50ns - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); -} - -// Read one byte of data from a location specified by bank and address, 00:0000 -byte readBank_SNES(byte myBank, word myAddress) { - PORTL = myBank; - PORTF = myAddress & 0xFF; - PORTK = (myAddress >> 8) & 0xFF; - - // Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total - __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); - - // Read - byte tempByte = PINC; - return tempByte; -} - -void readLoRomBanks( unsigned int start, unsigned int total, SdFile *file) -{ - byte buffer[512]; - - for (int currBank = start; currBank < total; currBank++) { - // Dump the bytes to SD 512B at a time - for (long currByte = 32768; currByte < 65536; currByte += 512) { - for (int c = 0; c < 512; c++) { - buffer[c] = readBank_SNES(currBank, currByte + c); - } - file->write(buffer, 512); - } - } -} - -void readHiRomBanks( unsigned int start, unsigned int total, SdFile *file) -{ - byte buffer[512]; - - for (int currBank = start; currBank < total; currBank++) { - for (long currByte = 0; currByte < 65536; currByte += 512) { - for (int c = 0; c < 512; c++) { - buffer[c] = readBank_SNES(currBank, currByte + c); - } - file->write(buffer, 512); - } - } -} - -/****************************************** - SNES ROM Functions -******************************************/ -void getCartInfo_SNES() { - boolean manualConfig = 0; - - // Print start page - if (checkcart_SNES() == 0) { - // Checksum either corrupt or 0000 - manualConfig = 1; - errorLvl = 1; - rgb.setColor(255, 0, 0); - - display_Clear(); - println_Msg(F("ERROR")); - println_Msg(F("Rom header corrupt")); - println_Msg(F("or missing")); - println_Msg(F("")); - println_Msg(F("")); - println_Msg(F("Press button for")); - println_Msg(F("manual configuration")); - println_Msg(F("or powercycle if SA1")); - display_Update(); - wait(); - // Wait() clears errors but in this case we still have an error - errorLvl = 1; - } - - display_Clear(); - print_Msg(F("Name: ")); - println_Msg(romName); - - print_Msg(F("Type: ")); - if (romType == HI) - print_Msg(F("HiROM")); - else if (romType == LO) - print_Msg(F("LoROM")); - else if (romType == EX) - print_Msg(F("ExHiRom")); - else - print_Msg(romType); - print_Msg(F(" ")); - if (romSpeed == 0) - println_Msg(F("SlowROM")); - else if (romSpeed == 2) - println_Msg(F("SlowROM")); - else if (romSpeed == 3) - println_Msg(F("FastROM")); - else - println_Msg(romSpeed); - - print_Msg(F("ICs: ROM ")); - if (romChips == 0) - println_Msg(F("ONLY")); - else if (romChips == 1) - println_Msg(F("RAM")); - else if (romChips == 2) - println_Msg(F("SAVE")); - else if (romChips == 3) - println_Msg(F("DSP1")); - else if (romChips == 4) - println_Msg(F("DSP1 RAM")); - else if (romChips == 5) - println_Msg(F("DSP1 SAVE")); - else if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) - println_Msg(F("SuperFX")); - else if (romChips == 52) { - println_Msg(F("SA1 RAM")); - romType = SA; - } - else if (romChips == 53) { - println_Msg(F("SA1 RAM BATT")); - romType = SA; - } - else if (romChips == 69) { - println_Msg(F("SDD1 BATT")); - } - else if (romChips == 227) - println_Msg(F("RAM GBoy")); - else if (romChips == 243) - println_Msg(F("CX4")); - else if (romChips == 246) - println_Msg(F("DSP2")); - else if (romChips == 245) - println_Msg(F("SPC RAM BATT")); - else if (romChips == 249) - println_Msg(F("SPC RAM RTC")); - else - println_Msg(F("")); - - print_Msg(F("Rom Size: ")); - print_Msg(romSize); - println_Msg(F("Mbit")); - - print_Msg(F("Banks: ")); - print_Msg(numBanks); - print_Msg(F(" Chips: ")); - println_Msg(romChips); - - print_Msg(F("Sram Size: ")); - print_Msg(sramSize); - println_Msg(F("Kbit")); - - print_Msg(F("ROM Version: 1.")); - println_Msg(romVersion); - - print_Msg(F("Checksum: ")); - println_Msg(checksumStr); - display_Update(); - - // Wait for user input - if (enable_OLED) { - println_Msg(F(" ")); - println_Msg(F(" ")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - } - - else if (enable_Serial) { - println_Msg(F(" ")); - } - - // Start manual config - if (manualConfig == 1) { - confMenu(); - } -} - -void checkAltConf() { - char tempStr1[2]; - char tempStr2[5]; - char sizeStr[3]; - char bankStr[3]; - - if (myFile.open("snes.txt", O_READ)) { - while (myFile.available()) { - // Read 4 bytes into String, do it one at a time so byte order doesn't get mixed up - sprintf(tempStr1, "%c", myFile.read()); - strcpy(tempStr2, tempStr1); - sprintf(tempStr1, "%c", myFile.read()); - strcat(tempStr2, tempStr1); - sprintf(tempStr1, "%c", myFile.read()); - strcat(tempStr2, tempStr1); - sprintf(tempStr1, "%c", myFile.read()); - strcat(tempStr2, tempStr1); - - // Check if string is a match - if (strcmp(tempStr2, checksumStr) == 0) { - - // Skip the , in the file - myFile.seekSet(myFile.curPosition() + 1); - - // Read next two bytes into a string - romSize = myFile.read() - 48; - romSize = romSize * 10 + myFile.read() - 48; - - // Skip the , in the file - myFile.seekSet(myFile.curPosition() + 1); - - // Add next two bytes to the string - numBanks = myFile.read() - 48; - numBanks = numBanks * 10 + myFile.read() - 48; - } - // If no match empty string advance by 8 and try again - else { - myFile.seekSet(myFile.curPosition() + 8); - } - } - } - // Close the file: - myFile.close(); -} - -// Read header information -boolean checkcart_SNES() { - // set control to read - dataIn(); - - // Get Checksum as string - sprintf(checksumStr, "%02X%02X", readBank_SNES(0, 65503), readBank_SNES(0, 65502)); - - romType = readBank_SNES(0, 0xFFD5); - if (romType == 0x35) { - romType = EX; // Check if ExHiROM - } - else if (romType == 0x3A) { - romType = HI; // Check if SPC7110 - } - else { - romType &= 1; // Must be LoROM or HiROM - } - - // Check RomSpeed - romSpeed = (readBank_SNES(0, 65493) >> 4); - - // Check RomChips - romChips = readBank_SNES(0, 65494); - - if (romChips == 69) { - romSize = 48; - numBanks = 96; - romType = HI; - } - else if (romChips == 243) { - cx4Type = readBank_SNES(0, 65481) & 0xF; - if (cx4Type == 2) { // X2 - romSize = 12; - numBanks = 48; - } - else if (cx4Type == 3) { // X3 - romSize = 16; - numBanks = 64; - } - } - else if ((romChips == 245) && (romType == HI)) { - romSize = 24; - numBanks = 48; - } - else if ((romChips == 249) && (romType == HI)) { - romSize = 40; - numBanks = 80; - } - else { - // Check RomSize - byte romSizeExp = readBank_SNES(0, 65495) - 7; - romSize = 1; - while (romSizeExp--) - romSize *= 2; - - if ((romType == EX) || (romType == SA)) { - numBanks = long(romSize) * 2; - } - else { - numBanks = (long(romSize) * 1024 * 1024 / 8) / (32768 + (long(romType) * 32768)); - } - } - - //Check SD card for alt config - checkAltConf(); - - // Get name - byte myByte = 0; - byte myLength = 0; - for (unsigned int i = 65472; i < 65492; i++) { - myByte = readBank_SNES(0, i); - if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 15) { - romName[myLength] = char(myByte); - myLength++; - } - } - // If name consists out of all japanese characters use game code - if (myLength == 0) { - // Get rom code - romName[0] = 'S'; - romName[1] = 'H'; - romName[2] = 'V'; - romName[3] = 'C'; - romName[4] = '-'; - for (unsigned int i = 0; i < 4; i++) { - myByte = readBank_SNES(0, 0xFFB2 + i); - if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 4) { - romName[myLength + 5] = char(myByte); - myLength++; - } - } - if (myLength == 0) { - // Rom code unknown - romName[0] = 'U'; - romName[1] = 'N'; - romName[2] = 'K'; - romName[3] = 'N'; - romName[4] = 'O'; - romName[5] = 'W'; - romName[6] = 'N'; - } - } - - // Read sramSizeExp - byte sramSizeExp; - if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { - // SuperFX - if (readBank_SNES(0, 0x7FDA) == 0x33) { - sramSizeExp = readBank_SNES(0, 0x7FBD); - } - else { - if (strncmp(romName, "STARFOX2", 8) == 0) { - sramSizeExp = 6; - } - else { - sramSizeExp = 5; - } - } - } - else { - // No SuperFX - sramSizeExp = readBank_SNES(0, 0xFFD8); - } - - // Calculate sramSize - if (sramSizeExp != 0) { - sramSizeExp = sramSizeExp + 3; - sramSize = 1; - while (sramSizeExp--) - sramSize *= 2; - } - else { - sramSize = 0; - } - - // Check Cart Country - cartCountry = readBank_SNES(0, 65497); - - // ROM Version - romVersion = readBank_SNES(0, 65499); - - // Test if checksum is equal to reverse checksum - if (((word(readBank_SNES(0, 65500)) + (word(readBank_SNES(0, 65501)) * 256)) + (word(readBank_SNES(0, 65502)) + (word(readBank_SNES(0, 65503)) * 256))) == 65535 ) { - if (strcmp("0000", checksumStr) == 0) { - return 0; - } - else { - return 1; - } - } - // Either rom checksum is wrong or no cart is inserted - else { - return 0; - } -} - -unsigned int calc_checksum (char* fileName, char* folder) { - unsigned int calcChecksum = 0; - unsigned int calcChecksumChunk = 0; - int calcFilesize = 0; - unsigned int c = 0; - unsigned long i = 0; - unsigned long j = 0; - - if (strcmp(folder, "root") != 0) - sd.chdir(folder); - - // If file exists - if (myFile.open(fileName, O_READ)) { - calcFilesize = myFile.fileSize() * 8 / 1024 / 1024; - - // Nintendo Power (SF Memory Cassette) - // Read up to 0x60000 then add FFs to 0x80000 - if (NP == true) { - for (i = 0; i < (0x60000 / 512); i++) { - myFile.read(sdBuffer, 512); - for (c = 0; c < 512; c++) { - calcChecksumChunk += sdBuffer[c]; - } - calcChecksum = calcChecksumChunk; - } - calcChecksum += 0xF47C; // FFs from 0x60000-0x80000 - } - else if ((calcFilesize == 10) || (calcFilesize == 12) || (calcFilesize == 20) || (calcFilesize == 24)) { - unsigned long calcBase = 0; - unsigned long calcMirror = 0; - byte calcMirrorCount = 0; - if (calcFilesize > 16) - calcBase = 2097152; - else - calcBase = 1048576; - calcMirror = myFile.fileSize() - calcBase; - calcMirrorCount = calcBase / calcMirror; - - // Momotarou Dentetsu Happy Fix 3MB (24Mbit) - if ((calcFilesize == 24) && (romChips == 245)) { - for (i = 0; i < (myFile.fileSize() / 512); i++) { - myFile.read(sdBuffer, 512); - for (c = 0; c < 512; c++) { - calcChecksumChunk += sdBuffer[c]; - } - } - calcChecksum = 2 * calcChecksumChunk; - } - else { - // Base 8/16 Mbit chunk - for (j = 0; j < (calcBase / 512); j++) { - myFile.read(sdBuffer, 512); - for (c = 0; c < 512; c++) { - calcChecksumChunk += sdBuffer[c]; - } - } - calcChecksum = calcChecksumChunk; - calcChecksumChunk = 0; - // Add the mirrored chunk - for (j = 0; j < (calcMirror / 512); j++) { - myFile.read(sdBuffer, 512); - for (c = 0; c < 512; c++) { - calcChecksumChunk += sdBuffer[c]; - } - } - calcChecksum += calcMirrorCount * calcChecksumChunk; - } - } - else if ((calcFilesize == 40) && (romChips == 85)) { - // Daikaijuu Monogatari 2 Fix 5MB (40Mbit) - // Add the 4MB (32Mbit) start - for (j = 0; j < (4194304 / 512); j++) { - myFile.read(sdBuffer, 512); - for (c = 0; c < 512; c++) { - calcChecksumChunk += sdBuffer[c]; - } - calcChecksum = calcChecksumChunk; - } - calcChecksumChunk = 0; - // Add the 1MB (8Mbit) end - for (j = 0; j < (1048576 / 512); j++) { - myFile.read(sdBuffer, 512); - for (c = 0; c < 512; c++) { - calcChecksumChunk += sdBuffer[c]; - } - } - calcChecksum += 4 * calcChecksumChunk; - } - else if (calcFilesize == 48) { - // Star Ocean/Tales of Phantasia Fix 6MB (48Mbit) - // Add the 4MB (32Mbit) start - for (j = 0; j < (4194304 / 512); j++) { - myFile.read(sdBuffer, 512); - for (c = 0; c < 512; c++) { - calcChecksumChunk += sdBuffer[c]; - } - calcChecksum = calcChecksumChunk; - } - calcChecksumChunk = 0; - // Add the 2MB (16Mbit) end - for (j = 0; j < (2097152 / 512); j++) { - myFile.read(sdBuffer, 512); - for (c = 0; c < 512; c++) { - calcChecksumChunk += sdBuffer[c]; - } - } - calcChecksum += 2 * calcChecksumChunk; - } - else { - //calcFilesize == 2 || 4 || 8 || 16 || 32 || 40 || etc - for (i = 0; i < (myFile.fileSize() / 512); i++) { - myFile.read(sdBuffer, 512); - for (c = 0; c < 512; c++) { - calcChecksumChunk += sdBuffer[c]; - } - calcChecksum = calcChecksumChunk; - } - } - myFile.close(); - sd.chdir(); - return (calcChecksum); - } - else { - // Else show error - print_Error(F("DUMP ROM 1ST"), false); - return 0; - } -} - -boolean compare_checksum() { - - println_Msg(F("Calculating Checksum")); - display_Update(); - - strcpy(fileName, romName); - strcat(fileName, ".sfc"); - - // last used rom folder - EEPROM_readAnything(10, foldern); - sprintf(folder, "SNES/ROM/%s/%d", romName, foldern - 1); - - char calcsumStr[5]; - sprintf(calcsumStr, "%04X", calc_checksum(fileName, folder)); - - if (strcmp(calcsumStr, checksumStr) == 0) { - print_Msg(F("Result: ")); - println_Msg(calcsumStr); - println_Msg(F("Checksum matches")); - display_Update(); - return 1; - } - else { - print_Msg(F("Result: ")); - println_Msg(calcsumStr); - print_Error(F("Checksum Error"), false); - display_Update(); - return 0; - } -} - -// Read rom to SD card -void readROM_SNES() { - // Set control - dataIn(); - controlIn_SNES(); - - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".sfc"); - - // create a new folder for the save file - EEPROM_readAnything(10, foldern); - sprintf(folder, "SNES/ROM/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - //clear the screen - display_Clear(); - print_Msg(F("Saving to ")); - print_Msg(folder); - println_Msg(F("/...")); - display_Update(); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - //open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("Can't create file on SD"), true); - } - - //Dump Derby Stallion '96 (Japan) Actual Size is 24Mb - if ((romType == LO) && (numBanks == 128) && (strcmp("CC86", checksumStr) == 0)) { - // Read Banks 0x00-0x3F for the 1st/2nd MB - for (int currBank = 0; currBank < 64; currBank++) { - // Dump the bytes to SD 512B at a time - for (long currByte = 32768; currByte < 65536; currByte += 512) { - for (int c = 0; c < 512; c++) { - sdBuffer[c] = readBank_SNES(currBank, currByte + c); - } - myFile.write(sdBuffer, 512); - } - } - //Read Bank 0x80-9F for the 3rd MB - for (int currBank = 128; currBank < 160; currBank++) { - // Dump the bytes to SD 512B at a time - for (long currByte = 32768; currByte < 65536; currByte += 512) { - for (int c = 0; c < 512; c++) { - sdBuffer[c] = readBank_SNES(currBank, currByte + c); - } - myFile.write(sdBuffer, 512); - } - } - } - - //Dump Low-type ROM - else if (romType == LO) { - if (romChips == 243) { //0xF3 - cx4Map = readBank_SNES(0, 32594); //0x7F52 - if ((cx4Type == 2) && (cx4Map != 0)) { //X2 - dataOut(); - controlOut_SNES(); - writeBank_SNES(0, 32594, 0); // Set 0x7F52 to 0 - dataIn(); - controlIn_SNES(); - } - else if ((cx4Type == 3) && (cx4Map == 0)) { //X3 - dataOut(); - controlOut_SNES(); - writeBank_SNES(0, 32594, 1); // Set 0x7F52 to 1 - dataIn(); - controlIn_SNES(); - } - } - if (romSize > 24) { - // ROM > 96 banks (up to 128 banks) - readLoRomBanks( 0x80, numBanks + 0x80, &myFile ); - } else { - // Read up to 96 banks starting at bank 0×00. - readLoRomBanks( 0, numBanks, &myFile ); - } - if (romChips == 243) { //0xF3 - // Restore CX4 Mapping Register - dataOut(); - controlOut_SNES(); - writeBank_SNES(0, 32594, cx4Map); // 0x7F52 - dataIn(); - controlIn_SNES(); - } - } - - // Dump SDD1 High-type ROM - else if ((romType == HI) && (romChips == 69)) { - println_Msg(F("Dumping SDD1 HiRom")); - display_Update(); - controlIn_SNES(); - byte initialSOMap = readBank_SNES(0, 18439); - - for (int currMemmap = 0; currMemmap < (numBanks / 16); currMemmap++) { - - dataOut(); - controlOut_SNES(); - - writeBank_SNES(0, 18439, currMemmap); - - dataIn(); - controlIn_SNES(); - - readHiRomBanks( 240, 256, &myFile ); - } - - dataOut(); - controlOut_SNES(); - - writeBank_SNES(0, 18439, initialSOMap); - - dataIn(); - controlIn_SNES(); - } - - // Dump SPC7110 High-type ROM - else if ((romType == HI) && ((romChips == 245) || (romChips == 249))) { - println_Msg(F("Dumping SPC7110 HiRom")); - display_Update(); - - // 0xC00000-0xDFFFFF - print_Msg(F("Part 1")); - display_Update(); - readHiRomBanks( 192, 224, &myFile ); - - if (numBanks > 32) { - dataOut(); - controlOut_SNES(); - // Set 0x4834 to 0xFF - writeBank_SNES( 0, 0x4834, 0xFF ); - - dataIn(); - controlIn_SNES(); - - // 0xE00000-0xEFFFFF - print_Msg(F(" 2")); - display_Update(); - readHiRomBanks( 224, 240, &myFile ); - - if (numBanks > 48) { - // 0xF00000-0xFFFFFF - print_Msg(F(" 3")); - display_Update(); - readHiRomBanks( 240, 256, &myFile ); - - dataOut(); - controlOut_SNES(); - - // Set 0x4833 to 3 - writeBank_SNES( 0, 0x4833, 3 ); - - dataIn(); - controlIn_SNES(); - - // 0xF00000-0xFFFFFF - print_Msg(F(" 4")); - display_Update(); - readHiRomBanks( 240, 256, &myFile ); - } - println_Msg(F("")); - - // Return mapping registers to initial settings... - dataOut(); - controlOut_SNES(); - - writeBank_SNES( 0, 0x4833, 2 ); - writeBank_SNES( 0, 0x4834, 0 ); - - dataIn(); - controlIn_SNES(); - } - } - - // Dump standard High-type ROM - else if ((romType == HI) || (romType == SA) || (romType == EX)) { - println_Msg(F("Dumping HiRom...")); - display_Update(); - - readHiRomBanks( 192, numBanks + 192, &myFile ); - } - - // Close the file: - myFile.close(); -} - -/****************************************** - SNES SRAM Functions -*****************************************/ -// Write file to SRAM -void writeSRAM (boolean browseFile) { - if (browseFile) { - filePath[0] = '\0'; - sd.chdir("/"); - fileBrowser("Select srm file"); - // Create filepath - sprintf(filePath, "%s/%s", filePath, fileName); - display_Clear(); - } - else - sprintf(filePath, "%s", fileName); - - //open file on sd card - if (myFile.open(filePath, O_READ)) { - - // Set pins to output - dataOut(); - - // Set RST RD WR to High and CS to Low - controlOut_SNES(); - - // LoRom - if (romType == LO) { - // Sram size - long lastByte = (long(sramSize) * 128); - - if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX - if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) - sramBanks = lastByte / 0x10000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0x0000; currByte < 0x10000; currByte++) { - writeBank_SNES(currBank, currByte, myFile.read()); - } - } - } - else { // SuperFX SRAM - for (long currByte = 0; currByte < lastByte; currByte++) { - writeBank_SNES(0x70, currByte, myFile.read()); - } - } - } - else if (lastByte > 0x8000) { // Large SRAM Fix - sramBanks = lastByte / 0x8000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0x0000; currByte < 0x8000; currByte++) { - writeBank_SNES(currBank, currByte, myFile.read()); - } - } - } - else { - for (long currByte = 0; currByte < lastByte; currByte++) { - writeBank_SNES(0x70, currByte, myFile.read()); - } - } - } - // HiRom - else if (romType == HI) { - if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM - // Configure SPC7110 SRAM Register - // Set 0x4830 to 0x80 - writeBank_SNES(0, 0x4830, 0x80); - // Sram size - long lastByte = (long(sramSize) * 128) + 0x6000; - // Write to sram bank - for (long currByte = 0x6000; currByte < lastByte; currByte++) { - writeBank_SNES(0x30, currByte, myFile.read()); - } - // Reset SPC7110 SRAM Register - dataOut(); - // Reset 0x4830 to 0x0 - writeBank_SNES(0, 0x4830, 0); - dataIn(); - } - else { - // Writing SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128); - if (lastByte > 0x2000) { // Large SRAM Fix - sramBanks = lastByte / 0x2000; - for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { - for (long currByte = 0x6000; currByte < 0x8000; currByte++) { - writeBank_SNES(currBank, currByte, myFile.read()); - } - } - } - else { - lastByte += 0x6000; - // Write to sram bank - for (long currByte = 0x6000; currByte < lastByte; currByte++) { - writeBank_SNES(0x30, currByte, myFile.read()); - } - } - } - } - // ExHiRom - else if (romType == EX) { - // Writing SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128) + 0x6000; - for (long currByte = 0x6000; currByte < lastByte; currByte++) { - writeBank_SNES(0xB0, currByte, myFile.read()); - } - } - // SA1 - else if (romType == SA) { - long lastByte = (long(sramSize) * 128); - // Enable CPU Clock - clockgen.set_freq(357954500ULL, SI5351_CLK1); - clockgen.output_enable(SI5351_CLK1, 1); - - // Direct writes to BW-RAM (SRAM) in banks 0x40-0x43 don't work - // Break BW-RAM (SRAM) into 0x2000 blocks - byte lastBlock = 0; - lastBlock = lastByte / 0x2000; - - // Writing SRAM on SA1 needs CS(PH3) to be high - // PORTH |= (1 << 3); - - // Setup BW-RAM - // Set 0x2224 (SNES BMAPS) to map SRAM Block 0 to 0x6000-0x7FFF - writeBank_SNES(0, 0x2224, 0); - // Set 0x2226 (SNES SBWE) to 0x80 Write Enable - writeBank_SNES(0, 0x2226, 0x80); - // Set 0x2228 (SNES BWPA) to 0x00 BW-RAM Write-Protected Area - writeBank_SNES(0, 0x2228, 0); - delay(1000); - - // Use $2224 (SNES) to map BW-RAM block to 0x6000-0x7FFF - // Use $2226 (SNES) to write enable the BW-RAM - byte firstByte = 0; - for (byte currBlock = 0; currBlock < lastBlock; currBlock++) { - // Set 0x2224 (SNES BMAPS) to map SRAM Block to 0x6000-0x7FFF - writeBank_SNES(0, 0x2224, currBlock); - // Set 0x2226 (SNES SBWE) to 0x80 Write Enable - writeBank_SNES(0, 0x2226, 0x80); - for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { - myFile.read(sdBuffer, 512); - if ((currBlock == 0) && (currByte == 0x6000)) { - firstByte = sdBuffer[0]; - } - for (int c = 0; c < 512; c++) { - writeBank_SNES(0, currByte + c, sdBuffer[c]); - } - } - } - // Rewrite First Byte - writeBank_SNES(0, 0x2224, 0); - writeBank_SNES(0, 0x2226, 0x80); - writeBank_SNES(0, 0x6000, firstByte); - // Disable CPU clock - clockgen.output_enable(SI5351_CLK1, 0); - } - - // Set pins to input - dataIn(); - - // Close the file: - myFile.close(); - println_Msg(F("SRAM writing finished")); - display_Update(); - - } - else { - print_Error(F("File doesnt exist"), false); - } -} - -void readSRAM () { - // set control - controlIn_SNES(); - - // Get name, add extension and convert to char array for sd lib - strcpy(fileName, romName); - strcat(fileName, ".srm"); - - // create a new folder for the save file - EEPROM_readAnything(10, foldern); - sprintf(folder, "SNES/SAVE/%s/%d", romName, foldern); - sd.mkdir(folder, true); - sd.chdir(folder); - - // write new folder number back to eeprom - foldern = foldern + 1; - EEPROM_writeAnything(10, foldern); - - //open file on sd card - if (!myFile.open(fileName, O_RDWR | O_CREAT)) { - print_Error(F("SD Error"), true); - } - int sramBanks = 0; - if (romType == LO) { - // Sram size - long lastByte = (long(sramSize) * 128); - if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX - if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) - sramBanks = lastByte / 0x10000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0x0000; currByte < 0x10000; currByte++) { - myFile.write(readBank_SNES(currBank, currByte)); - } - } - } - else { // SuperFX SRAM - for (long currByte = 0; currByte < lastByte; currByte++) { - myFile.write(readBank_SNES(0x70, currByte)); - } - } - } - else if (lastByte > 0x8000) { // Large SRAM Fix - sramBanks = lastByte / 0x8000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0x0000; currByte < 0x8000; currByte++) { - myFile.write(readBank_SNES(currBank, currByte)); - } - } - } - else { - for (long currByte = 0; currByte < lastByte; currByte++) { - myFile.write(readBank_SNES(0x70, currByte)); - } - } - } - else if (romType == HI) { - if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM - // Configure SPC7110 SRAM Register - dataOut(); - // Set 0x4830 to 0x80 - writeBank_SNES(0, 0x4830, 0x80); - dataIn(); - // Sram size - long lastByte = (long(sramSize) * 128) + 0x6000; - for (long currByte = 0x6000; currByte < lastByte; currByte++) { - myFile.write(readBank_SNES(0x30, currByte)); - } - dataOut(); - // Reset 0x4830 to 0x0 - writeBank_SNES(0, 0x4830, 0); - dataIn(); - } - else { - // Dumping SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128); - if (lastByte > 0x2000) { // Large SRAM Fix - sramBanks = lastByte / 0x2000; - for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { - for (long currByte = 0x6000; currByte < 0x8000; currByte++) { - myFile.write(readBank_SNES(currBank, currByte)); - } - } - } - else { - lastByte += 0x6000; - for (long currByte = 0x6000; currByte < lastByte; currByte++) { - myFile.write(readBank_SNES(0x30, currByte)); - } - } - } - } - else if (romType == EX) { - // Dumping SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128) + 0x6000; - for (long currByte = 0x6000; currByte < lastByte; currByte++) { - myFile.write(readBank_SNES(0xB0, currByte)); - } - } - else if (romType == SA) { - // Dumping SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128); - if (lastByte > 0x10000) { - sramBanks = lastByte / 0x10000; - for (int currBank = 0x40; currBank < sramBanks + 0x40; currBank++) { - for (long currByte = 0; currByte < 0x10000; currByte++) { - myFile.write(readBank_SNES(currBank, currByte)); - } - } - } - else { - for (long currByte = 0x0; currByte < lastByte; currByte++) { - myFile.write(readBank_SNES(0x40, currByte)); - } - } - } - - // Close the file: - myFile.close(); - - // Signal end of process - display_Clear(); - print_Msg(F("Saved to ")); - print_Msg(folder); - println_Msg(F("/...")); - display_Update(); -} - -// Check if the SRAM was written without any error -unsigned long verifySRAM() { - //open file on sd card - if (myFile.open(filePath, O_READ)) { - - // Variable for errors - writeErrors = 0; - - // Set control - controlIn_SNES(); - - int sramBanks = 0; - if (romType == LO) { - // Sram size - long lastByte = (long(sramSize) * 128); - if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX - if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) - sramBanks = lastByte / 0x10000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0; currByte < 0x10000; currByte += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { - writeErrors++; - } - } - } - } - } - else { // SuperFX SRAM - for (long currByte = 0; currByte < lastByte; currByte += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(0x70, currByte + c)) != sdBuffer[c]) { - writeErrors++; - } - } - } - } - } - else if (lastByte > 0x8000) { // Large SRAM Fix - sramBanks = lastByte / 0x8000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0; currByte < 0x8000; currByte += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { - writeErrors++; - } - } - } - } - } - else { - for (long currByte = 0; currByte < lastByte; currByte += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(0x70, currByte + c)) != sdBuffer[c]) { - writeErrors++; - } - } - } - } - } - else if (romType == HI) { - if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM - // Configure SPC7110 SRAM Register - dataOut(); - // Set 0x4830 to 0x80 - writeBank_SNES(0, 0x4830, 0x80); - dataIn(); - // Sram size - long lastByte = (long(sramSize) * 128) + 0x6000; - for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(0x30, currByte + c)) != sdBuffer[c]) { - writeErrors++; - } - } - } - dataOut(); - // Reset 0x4830 to 0x0 - writeBank_SNES(0, 0x4830, 0); - dataIn(); - } - else { - // Dumping SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128); - if (lastByte > 0x2000) { // Large SRAM Fix - sramBanks = lastByte / 0x2000; - for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { - for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { - writeErrors++; - } - } - } - } - } - else { - lastByte += 0x6000; - for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(0x30, currByte + c)) != sdBuffer[c]) { - writeErrors++; - } - } - } - } - } - } - else if (romType == EX) { - // Dumping SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128) + 0x6000; - for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(0xB0, currByte + c)) != sdBuffer[c]) { - writeErrors++; - } - } - } - } - else if (romType == SA) { - // Dumping SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128); - - if (lastByte > 0x10000) { - sramBanks = lastByte / 0x10000; - for (int currBank = 0x40; currBank < sramBanks + 0x40; currBank++) { - for (long currByte = 0x0; currByte < 0x10000; currByte += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { - writeErrors++; - } - } - } - } - } - else { - for (long currByte = 0x0; currByte < lastByte; currByte += 512) { - //fill sdBuffer - myFile.read(sdBuffer, 512); - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(0x40, currByte + c)) != sdBuffer[c]) { - writeErrors++; - } - } - } - } - // Reset SA1 - // Set pins to input - dataIn(); - // Close the file: - myFile.close(); - if (writeErrors == 0) { - println_Msg(F("Verified OK")); - } - else { - print_Msg(F("Error: ")); - print_Msg(writeErrors); - println_Msg(F(" bytes ")); - print_Error(F("did not verify."), false); - } - display_Update(); - wait(); - // Set reset pin to output (PH0) - DDRH |= (1 << 0); - // Switch RST(PH0) to LOW - PORTH &= ~(1 << 0); - display_Clear(); - print_Msg("Resetting..."); - display_Update(); - delay(3000); // wait 3 secs - asm volatile (" jmp 0"); - } - // Close the file: - myFile.close(); - return writeErrors; - } - else { - print_Error(F("Can't open file"), false); - } -} - -// Overwrite the entire SRAM -boolean eraseSRAM (byte b) { - print_Msg(F("0x")); - print_Msg(b, HEX); - print_Msg(F(": ")); - display_Update(); - - // Set pins to output - dataOut(); - - // Set control pins - controlOut_SNES(); - - int sramBanks = 0; - if (romType == LO) { - // Sram size - long lastByte = (long(sramSize) * 128); - - if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX - if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) - sramBanks = lastByte / 0x10000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0x0000; currByte < 0x10000; currByte++) { - writeBank_SNES(currBank, currByte, b); - } - } - } - else { // SuperFX SRAM - for (long currByte = 0; currByte < lastByte; currByte++) { - writeBank_SNES(0x70, currByte, b); - } - } - } - else if (lastByte > 0x8000) { // Large SRAM Fix - sramBanks = lastByte / 0x8000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0x0000; currByte < 0x8000; currByte++) { - writeBank_SNES(currBank, currByte, b); - } - } - } - else { - for (long currByte = 0; currByte < lastByte; currByte++) { - writeBank_SNES(0x70, currByte, b); - } - } - } - else if (romType == HI) { - if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM - // Configure SPC7110 SRAM Register - // Set 0x4830 to 0x80 - writeBank_SNES(0, 0x4830, 0x80); - // Sram size - long lastByte = (long(sramSize) * 128) + 0x6000; - // Write to sram bank - for (long currByte = 0x6000; currByte < lastByte; currByte++) { - writeBank_SNES(0x30, currByte, b); - } - // Reset SPC7110 SRAM Register - dataOut(); - // Reset 0x4830 to 0x0 - writeBank_SNES(0, 0x4830, 0); - dataIn(); - } - else { - // Writing SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128); - if (lastByte > 0x2000) { // Large SRAM Fix - sramBanks = lastByte / 0x2000; - for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { - for (long currByte = 0x6000; currByte < 0x8000; currByte++) { - writeBank_SNES(currBank, currByte, b); - } - } - } - else { - lastByte += 0x6000; - // Write to sram bank - for (long currByte = 0x6000; currByte < lastByte; currByte++) { - writeBank_SNES(0x30, currByte, b); - } - } - } - } - // ExHiRom - else if (romType == EX) { - // Writing SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128) + 0x6000; - for (long currByte = 0x6000; currByte < lastByte; currByte++) { - writeBank_SNES(0xB0, currByte, b); - } - } - // SA1 - else if (romType == SA) { - long lastByte = (long(sramSize) * 128); - // Enable CPU Clock - clockgen.set_freq(357954500ULL, SI5351_CLK1); - clockgen.output_enable(SI5351_CLK1, 1); - - // Direct writes to BW-RAM (SRAM) in banks 0x40-0x43 don't work - // Break BW-RAM (SRAM) into 0x2000 blocks - // Use $2224 to map BW-RAM block to 0x6000-0x7FFF - byte lastBlock = 0; - lastBlock = lastByte / 0x2000; - - // Writing SRAM on SA1 needs CS(PH3) to be high - // PORTH |= (1 << 3); - - // Setup BW-RAM - // Set 0x2224 (SNES BMAPS) to map SRAM Block 0 to 0x6000-0x7FFF - writeBank_SNES(0, 0x2224, 0); - // Set 0x2226 (SNES SBWE) to 0x80 Write Enable - writeBank_SNES(0, 0x2226, 0x80); - // Set 0x2228 (SNES BWPA) to 0x00 BW-RAM Write-Protected Area - writeBank_SNES(0, 0x2228, 0); - delay(1000); - - // Use $2224 (SNES) to map BW-RAM block to 0x6000-0x7FFF - // Use $2226 (SNES) to write enable the BW-RAM - for (byte currBlock = 0; currBlock < lastBlock; currBlock++) { - // Set 0x2224 (SNES BMAPS) to map SRAM Block to 0x6000-0x7FFF - writeBank_SNES(0, 0x2224, currBlock); - // Set 0x2226 (SNES SBWE) to 0x80 Write Enable - writeBank_SNES(0, 0x2226, 0x80); - for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { - for (int c = 0; c < 512; c++) { - writeBank_SNES(0, currByte + c, b); - } - } - } - // Rewrite First Byte - writeBank_SNES(0, 0x2224, 0); - writeBank_SNES(0, 0x2226, 0x80); - writeBank_SNES(0, 0x6000, b); - // Disable CPU clock - clockgen.output_enable(SI5351_CLK1, 0); - } - - dataIn(); - - // Variable for errors - writeErrors = 0; - - // Set control - controlIn_SNES(); - - sramBanks = 0; - if (romType == LO) { - // Sram size - long lastByte = (long(sramSize) * 128); - if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX - if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) - sramBanks = lastByte / 0x10000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0; currByte < 0x10000; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != b) { - writeErrors++; - } - } - } - } - } - else { // SuperFX SRAM - for (long currByte = 0; currByte < lastByte; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(0x70, currByte + c)) != b) { - writeErrors++; - } - } - } - } - } - else if (lastByte > 0x8000) { // Large SRAM Fix - sramBanks = lastByte / 0x8000; - for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { - for (long currByte = 0; currByte < 0x8000; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != b) { - writeErrors++; - } - } - } - } - } - else { - for (long currByte = 0; currByte < lastByte; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(0x70, currByte + c)) != b) { - writeErrors++; - } - } - } - } - } - else if (romType == HI) { - if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM - // Configure SPC7110 SRAM Register - dataOut(); - // Set 0x4830 to 0x80 - writeBank_SNES(0, 0x4830, 0x80); - dataIn(); - // Sram size - long lastByte = (long(sramSize) * 128) + 0x6000; - for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(0x30, currByte + c)) != b) { - writeErrors++; - } - } - } - dataOut(); - // Reset 0x4830 to 0x0 - writeBank_SNES(0, 0x4830, 0); - dataIn(); - } - else { - // Dumping SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128); - if (lastByte > 0x2000) { // Large SRAM Fix - sramBanks = lastByte / 0x2000; - for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { - for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != b) { - writeErrors++; - } - } - } - } - } - else { - lastByte += 0x6000; - for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(0x30, currByte + c)) != b) { - writeErrors++; - } - } - } - } - } - } - else if (romType == EX) { - // Dumping SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128) + 0x6000; - for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(0xB0, currByte + c)) != b) { - writeErrors++; - } - } - } - } - else if (romType == SA) { - // Dumping SRAM on HiRom needs CS(PH3) to be high - PORTH |= (1 << 3); - // Sram size - long lastByte = (long(sramSize) * 128); - if (lastByte > 0x10000) { - sramBanks = lastByte / 0x10000; - for (int currBank = 0x40; currBank < sramBanks + 0x40; currBank++) { - for (long currByte = 0x0; currByte < 0x10000; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(currBank, currByte + c)) != b) { - writeErrors++; - } - } - } - } - } - else { - for (long currByte = 0x0; currByte < lastByte; currByte += 512) { - for (int c = 0; c < 512; c++) { - if ((readBank_SNES(0x40, currByte + c)) != b) { - writeErrors++; - } - } - } - } - } - if (writeErrors == 0) { - println_Msg(F("OK")); - return 1; - } - else { - println_Msg(F("ERROR")); - return 0; - } - display_Update(); -} - -//****************************************** -// End of File +//****************************************** +// SUPER NINTENDO MODULE +//****************************************** + +/****************************************** + Defines + *****************************************/ +// SNES Hi and LoRom, SA is HI with different Sram dumping +#define EX 4 +#define SA 3 +#define HI 1 +#define LO 0 + +/****************************************** + Variables + *****************************************/ +// Define SNES Cart Reader Variables +int romSpeed = 0; // 0 = SlowROM, 3 = FastROM +int romChips = 0; // 0 = ROM only, 1 = ROM & RAM, 2 = ROM & Save RAM, 3 = ROM & DSP1, 4 = ROM & RAM & DSP1, 5 = ROM & Save RAM & DSP1, 19 = ROM & SFX +// 227 = ROM & RAM & GameBoy data, 243 = CX4, 246 = ROM & DSP2 +byte romSizeExp = 0; // ROM-Size Exponent +int cartCountry = 255; +boolean NP = false; +byte cx4Type = 0; +byte cx4Map = 0; + +/****************************************** + Menu +*****************************************/ +// SNES/Nintendo Power SF Memory start menu +static const char snsMenuItem1[] PROGMEM = "Super Nintendo"; +static const char snsMenuItem2[] PROGMEM = "NPower SF Memory"; +static const char snsMenuItem3[] PROGMEM = "HiROM repro"; +static const char snsMenuItem4[] PROGMEM = "LoROM repro"; +static const char* const menuOptionsSNS[] PROGMEM = {snsMenuItem1, snsMenuItem2, snsMenuItem3, snsMenuItem4}; + +// SNES menu items +static const char SnesMenuItem1[] PROGMEM = "Read Rom"; +static const char SnesMenuItem2[] PROGMEM = "Read Save"; +static const char SnesMenuItem3[] PROGMEM = "Write Save"; +static const char SnesMenuItem4[] PROGMEM = "Test SRAM"; +static const char SnesMenuItem5[] PROGMEM = "Cycle cart"; +static const char SnesMenuItem6[] PROGMEM = "Reset"; +static const char* const menuOptionsSNES[] PROGMEM = {SnesMenuItem1, SnesMenuItem2, SnesMenuItem3, SnesMenuItem4, SnesMenuItem5, SnesMenuItem6}; + +// Manual config menu items +static const char confMenuItem1[] PROGMEM = "Use header info"; +static const char confMenuItem2[] PROGMEM = "4MB LoRom 256K Sram"; +static const char confMenuItem3[] PROGMEM = "4MB HiRom 64K Sram"; +static const char confMenuItem4[] PROGMEM = "6MB ExRom 256K Sram"; +static const char confMenuItem5[] PROGMEM = "Reset"; +static const char* const menuOptionsConf[] PROGMEM = {confMenuItem1, confMenuItem2, confMenuItem3, confMenuItem4, confMenuItem5}; + +// SNES start menu +void snsMenu() { + // create menu with title and 4 options to choose from + unsigned char snsCart; + // Copy menuOptions out of progmem + convertPgm(menuOptionsSNS, 4); + snsCart = question_box("Select Cart Type", menuOptions, 4, 0); + + // wait for user choice to come back from the question box menu + switch (snsCart) + { + case 0: + display_Clear(); + display_Update(); + setup_Snes(); + mode = mode_SNES; + break; + + case 1: + display_Clear(); + display_Update(); + setup_SFM(); + mode = mode_SFM; + break; + + case 2: + display_Clear(); + display_Update(); + hiROM = 1; + setup_Flash8(); + id_Flash8(); + wait(); + mode = mode_FLASH8; + break; + + case 3: + display_Clear(); + display_Update(); + hiROM = 0; + setup_Flash8(); + id_Flash8(); + wait(); + mode = mode_FLASH8; + break; + } +} + +// SNES Menu +void snesMenu() { + // create menu with title and 7 options to choose from + unsigned char mainMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsSNES, 6); + mainMenu = question_box("SNES Cart Reader", menuOptions, 6, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) + { + case 0: + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readROM_SNES(); + compare_checksum(); + break; + + case 1: + if (sramSize > 0) { + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readSRAM(); + } + else { + display_Clear(); + print_Error(F("Does not have SRAM"), false); + } + break; + + case 2: + if (sramSize > 0) { + display_Clear(); + // Change working dir to root + sd.chdir("/"); + writeSRAM(1); + unsigned long wrErrors; + wrErrors = verifySRAM(); + if (wrErrors == 0) { + println_Msg(F("Verified OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(wrErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + } + else { + display_Clear(); + print_Error(F("Does not have SRAM"), false); + } + break; + + case 3: + if (sramSize > 0) { + display_Clear(); + println_Msg(F("Warning:")); + println_Msg(F("This can erase")); + println_Msg(F("your save games")); + println_Msg(F("")); + println_Msg(F("")); + println_Msg(F("Press any button to")); + println_Msg(F("start sram testing")); + display_Update(); + wait(); + display_Clear(); + // Change working dir to root + sd.chdir("/"); + readSRAM(); + eraseSRAM(0x00); + eraseSRAM(0xFF); + writeSRAM(0); + unsigned long wrErrors = verifySRAM(); + if (wrErrors == 0) { + println_Msg(F("Restored OK")); + display_Update(); + } + else { + print_Msg(F("Error: ")); + print_Msg(wrErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + } + else { + display_Clear(); + print_Error(F("Does not have SRAM"), false); + } + break; + + case 4: + // For arcademaster1 (Markfrizb) multi-game carts + // Set reset pin to output (PH0) + DDRH |= (1 << 0); + // Switch RST(PH0) to LOW + PORTH &= ~(1 << 0); + display_Clear(); + print_Msg("Resetting..."); + display_Update(); + delay(3000); // wait 3 secs to switch to next game + asm volatile (" jmp 0"); + break; + + case 5: + asm volatile (" jmp 0"); + break; + } + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +// Menu for manual configuration +void confMenu() { + // create menu with title and 5 options to choose from + unsigned char subMenu; + // Copy menuOptions out of progmem + convertPgm(menuOptionsConf, 5); + subMenu = question_box("Choose mapping", menuOptions, 5, 0); + + // wait for user choice to come back from the question box menu + switch (subMenu) + { + case 0: + break; + + case 1: + romType = LO; + numBanks = 128; + sramSize = 256; + strcpy(romName, "LOROM"); + break; + + case 2: + romType = HI; + numBanks = 64; + sramSize = 64; + strcpy(romName, "HIROM"); + break; + + case 3: + romType = EX; + numBanks = 96; + sramSize = 256; + strcpy(romName, "EXROM"); + break; + + case 4: + // Reset + asm volatile (" jmp 0"); + break; + } +} + +/****************************************** + Setup + *****************************************/ +void setup_Snes() { + // Set cicrstPin(PG1) to Output + DDRG |= (1 << 1); + // Output a high signal until we're ready to start + PORTG |= (1 << 1); + // Set cichstPin(PG0) to Input + DDRG &= ~(1 << 0); + + // Adafruit Clock Generator + clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0); + clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); + clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); + clockgen.set_freq(2147727200ULL, SI5351_CLK0); + clockgen.set_freq(307200000ULL, SI5351_CLK2); + clockgen.output_enable(SI5351_CLK0, 1); + clockgen.output_enable(SI5351_CLK1, 0); + clockgen.output_enable(SI5351_CLK2, 1); + + // Set Address Pins to Output + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //BA0-BA7 + DDRL = 0xFF; + //PA0-PA7 + DDRA = 0xFF; + + // Set Control Pins to Output RST(PH0) CS(PH3) WR(PH5) RD(PH6) + DDRH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); + // Switch RST(PH0) and WR(PH5) to HIGH + PORTH |= (1 << 0) | (1 << 5); + // Switch CS(PH3) and RD(PH6) to LOW + PORTH &= ~((1 << 3) | (1 << 6)); + + // Set Refresh(PE5) to Output + DDRE |= (1 << 5); + // Switch Refresh(PE5) to LOW (needed for SA-1) + PORTE &= ~(1 << 5); + + // Set CPU Clock(PH1) to Output + DDRH |= (1 << 1); + //PORTH &= ~(1 << 1); + + // Set IRQ(PH4) to Input + DDRH &= ~(1 << 4); + // Activate Internal Pullup Resistors + //PORTH |= (1 << 4); + + // Set expand(PG5) to Imput + DDRG &= ~(1 << 5); + // Activate Internal Pullup Resistors + //PORTG |= (1 << 5); + + // Set Data Pins (D0-D7) to Input + DDRC = 0x00; + // Enable Internal Pullups + //PORTC = 0xFF; + + // Unused pins + // Set wram(PE4) to Output + DDRE |= (1 << 4); + //PORTE &= ~(1 << 4); + // Set pawr(PJ1) to Output + DDRJ |= (1 << 1); + //PORTJ &= ~(1 << 1); + // Set pard(PJ0) to Output + DDRJ |= (1 << 0); + //PORTJ &= ~(1 << 0); + + // Start CIC by outputting a low signal to cicrstPin(PG1) + PORTG &= ~(1 << 1); + + // Wait for CIC reset + delay(1000); + + // Print all the info + getCartInfo_SNES(); +} + +/****************************************** + I/O Functions + *****************************************/ +// Switch control pins to write +void controlOut_SNES() { + // Switch RD(PH6) and WR(PH5) to HIGH + PORTH |= (1 << 6) | (1 << 5); + // Switch CS(PH3) to LOW + PORTH &= ~(1 << 3); +} + +// Switch control pins to read +void controlIn_SNES() { + // Switch WR(PH5) to HIGH + PORTH |= (1 << 5); + // Switch CS(PH3) and RD(PH6) to LOW + PORTH &= ~((1 << 3) | (1 << 6)); +} + +/****************************************** + Low level functions + *****************************************/ +// Write one byte of data to a location specified by bank and address, 00:0000 +void writeBank_SNES(byte myBank, word myAddress, byte myData) { + PORTL = myBank; + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + PORTC = myData; + + // Arduino running at 16Mhz -> one nop = 62.5ns + // Wait till output is stable + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Switch WR(PH5) to LOW + PORTH &= ~(1 << 5); + + // Leave WR low for at least 60ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Switch WR(PH5) to HIGH + PORTH |= (1 << 5); + + // Leave WR high for at least 50ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); +} + +// Read one byte of data from a location specified by bank and address, 00:0000 +byte readBank_SNES(byte myBank, word myAddress) { + PORTL = myBank; + PORTF = myAddress & 0xFF; + PORTK = (myAddress >> 8) & 0xFF; + + // Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Read + byte tempByte = PINC; + return tempByte; +} + +void readLoRomBanks( unsigned int start, unsigned int total, SdFile *file) +{ + byte buffer[512]; + + for (int currBank = start; currBank < total; currBank++) { + // Dump the bytes to SD 512B at a time + for (long currByte = 32768; currByte < 65536; currByte += 512) { + for (int c = 0; c < 512; c++) { + buffer[c] = readBank_SNES(currBank, currByte + c); + } + file->write(buffer, 512); + } + } +} + +void readHiRomBanks( unsigned int start, unsigned int total, SdFile *file) +{ + byte buffer[512]; + + for (int currBank = start; currBank < total; currBank++) { + for (long currByte = 0; currByte < 65536; currByte += 512) { + for (int c = 0; c < 512; c++) { + buffer[c] = readBank_SNES(currBank, currByte + c); + } + file->write(buffer, 512); + } + } +} + +/****************************************** + SNES ROM Functions +******************************************/ +void getCartInfo_SNES() { + boolean manualConfig = 0; + + // Print start page + if (checkcart_SNES() == 0) { + // Checksum either corrupt or 0000 + manualConfig = 1; + errorLvl = 1; + rgb.setColor(255, 0, 0); + + display_Clear(); + println_Msg(F("ERROR")); + println_Msg(F("Rom header corrupt")); + println_Msg(F("or missing")); + println_Msg(F("")); + println_Msg(F("")); + println_Msg(F("Press button for")); + println_Msg(F("manual configuration")); + println_Msg(F("or powercycle if SA1")); + display_Update(); + wait(); + // Wait() clears errors but in this case we still have an error + errorLvl = 1; + } + + display_Clear(); + print_Msg(F("Name: ")); + println_Msg(romName); + + print_Msg(F("Type: ")); + if (romType == HI) + print_Msg(F("HiROM")); + else if (romType == LO) + print_Msg(F("LoROM")); + else if (romType == EX) + print_Msg(F("ExHiRom")); + else + print_Msg(romType); + print_Msg(F(" ")); + if (romSpeed == 0) + println_Msg(F("SlowROM")); + else if (romSpeed == 2) + println_Msg(F("SlowROM")); + else if (romSpeed == 3) + println_Msg(F("FastROM")); + else + println_Msg(romSpeed); + + print_Msg(F("ICs: ROM ")); + if (romChips == 0) + println_Msg(F("ONLY")); + else if (romChips == 1) + println_Msg(F("RAM")); + else if (romChips == 2) + println_Msg(F("SAVE")); + else if (romChips == 3) + println_Msg(F("DSP1")); + else if (romChips == 4) + println_Msg(F("DSP1 RAM")); + else if (romChips == 5) + println_Msg(F("DSP1 SAVE")); + else if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) + println_Msg(F("SuperFX")); + else if (romChips == 52) { + println_Msg(F("SA1 RAM")); + romType = SA; + } + else if (romChips == 53) { + println_Msg(F("SA1 RAM BATT")); + romType = SA; + } + else if (romChips == 69) { + println_Msg(F("SDD1 BATT")); + } + else if (romChips == 227) + println_Msg(F("RAM GBoy")); + else if (romChips == 243) + println_Msg(F("CX4")); + else if (romChips == 246) + println_Msg(F("DSP2")); + else if (romChips == 245) + println_Msg(F("SPC RAM BATT")); + else if (romChips == 249) + println_Msg(F("SPC RAM RTC")); + else + println_Msg(F("")); + + print_Msg(F("Rom Size: ")); + print_Msg(romSize); + println_Msg(F("Mbit")); + + print_Msg(F("Banks: ")); + print_Msg(numBanks); + print_Msg(F(" Chips: ")); + println_Msg(romChips); + + print_Msg(F("Sram Size: ")); + print_Msg(sramSize); + println_Msg(F("Kbit")); + + print_Msg(F("ROM Version: 1.")); + println_Msg(romVersion); + + print_Msg(F("Checksum: ")); + println_Msg(checksumStr); + display_Update(); + + // Wait for user input + if (enable_OLED) { + println_Msg(F(" ")); + println_Msg(F(" ")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + } + + else if (enable_Serial) { + println_Msg(F(" ")); + } + + // Start manual config + if (manualConfig == 1) { + confMenu(); + } +} + +void checkAltConf() { + char tempStr1[2]; + char tempStr2[5]; + char sizeStr[3]; + char bankStr[3]; + + if (myFile.open("snes.txt", O_READ)) { + while (myFile.available()) { + // Read 4 bytes into String, do it one at a time so byte order doesn't get mixed up + sprintf(tempStr1, "%c", myFile.read()); + strcpy(tempStr2, tempStr1); + sprintf(tempStr1, "%c", myFile.read()); + strcat(tempStr2, tempStr1); + sprintf(tempStr1, "%c", myFile.read()); + strcat(tempStr2, tempStr1); + sprintf(tempStr1, "%c", myFile.read()); + strcat(tempStr2, tempStr1); + + // Check if string is a match + if (strcmp(tempStr2, checksumStr) == 0) { + + // Skip the , in the file + myFile.seekSet(myFile.curPosition() + 1); + + // Read next two bytes into a string + romSize = myFile.read() - 48; + romSize = romSize * 10 + myFile.read() - 48; + + // Skip the , in the file + myFile.seekSet(myFile.curPosition() + 1); + + // Add next two bytes to the string + numBanks = myFile.read() - 48; + numBanks = numBanks * 10 + myFile.read() - 48; + } + // If no match empty string advance by 8 and try again + else { + myFile.seekSet(myFile.curPosition() + 8); + } + } + } + // Close the file: + myFile.close(); +} + +// Read header information +boolean checkcart_SNES() { + // set control to read + dataIn(); + + // Get Checksum as string + sprintf(checksumStr, "%02X%02X", readBank_SNES(0, 65503), readBank_SNES(0, 65502)); + + romType = readBank_SNES(0, 0xFFD5); + if (romType == 0x35) { + romType = EX; // Check if ExHiROM + } + else if (romType == 0x3A) { + romType = HI; // Check if SPC7110 + } + else { + romType &= 1; // Must be LoROM or HiROM + } + + // Check RomSpeed + romSpeed = (readBank_SNES(0, 65493) >> 4); + + // Check RomChips + romChips = readBank_SNES(0, 65494); + + if (romChips == 69) { + romSize = 48; + numBanks = 96; + romType = HI; + } + else if (romChips == 243) { + cx4Type = readBank_SNES(0, 65481) & 0xF; + if (cx4Type == 2) { // X2 + romSize = 12; + numBanks = 48; + } + else if (cx4Type == 3) { // X3 + romSize = 16; + numBanks = 64; + } + } + else if ((romChips == 245) && (romType == HI)) { + romSize = 24; + numBanks = 48; + } + else if ((romChips == 249) && (romType == HI)) { + romSize = 40; + numBanks = 80; + } + else { + // Check RomSize + byte romSizeExp = readBank_SNES(0, 65495) - 7; + romSize = 1; + while (romSizeExp--) + romSize *= 2; + + if ((romType == EX) || (romType == SA)) { + numBanks = long(romSize) * 2; + } + else { + numBanks = (long(romSize) * 1024 * 1024 / 8) / (32768 + (long(romType) * 32768)); + } + } + + //Check SD card for alt config + checkAltConf(); + + // Get name + byte myByte = 0; + byte myLength = 0; + for (unsigned int i = 65472; i < 65492; i++) { + myByte = readBank_SNES(0, i); + if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 15) { + romName[myLength] = char(myByte); + myLength++; + } + } + // If name consists out of all japanese characters use game code + if (myLength == 0) { + // Get rom code + romName[0] = 'S'; + romName[1] = 'H'; + romName[2] = 'V'; + romName[3] = 'C'; + romName[4] = '-'; + for (unsigned int i = 0; i < 4; i++) { + myByte = readBank_SNES(0, 0xFFB2 + i); + if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 4) { + romName[myLength + 5] = char(myByte); + myLength++; + } + } + if (myLength == 0) { + // Rom code unknown + romName[0] = 'U'; + romName[1] = 'N'; + romName[2] = 'K'; + romName[3] = 'N'; + romName[4] = 'O'; + romName[5] = 'W'; + romName[6] = 'N'; + } + } + + // Read sramSizeExp + byte sramSizeExp; + if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { + // SuperFX + if (readBank_SNES(0, 0x7FDA) == 0x33) { + sramSizeExp = readBank_SNES(0, 0x7FBD); + } + else { + if (strncmp(romName, "STARFOX2", 8) == 0) { + sramSizeExp = 6; + } + else { + sramSizeExp = 5; + } + } + } + else { + // No SuperFX + sramSizeExp = readBank_SNES(0, 0xFFD8); + } + + // Calculate sramSize + if (sramSizeExp != 0) { + sramSizeExp = sramSizeExp + 3; + sramSize = 1; + while (sramSizeExp--) + sramSize *= 2; + } + else { + sramSize = 0; + } + + // Check Cart Country + cartCountry = readBank_SNES(0, 65497); + + // ROM Version + romVersion = readBank_SNES(0, 65499); + + // Test if checksum is equal to reverse checksum + if (((word(readBank_SNES(0, 65500)) + (word(readBank_SNES(0, 65501)) * 256)) + (word(readBank_SNES(0, 65502)) + (word(readBank_SNES(0, 65503)) * 256))) == 65535 ) { + if (strcmp("0000", checksumStr) == 0) { + return 0; + } + else { + return 1; + } + } + // Either rom checksum is wrong or no cart is inserted + else { + return 0; + } +} + +unsigned int calc_checksum (char* fileName, char* folder) { + unsigned int calcChecksum = 0; + unsigned int calcChecksumChunk = 0; + int calcFilesize = 0; + unsigned int c = 0; + unsigned long i = 0; + unsigned long j = 0; + + if (strcmp(folder, "root") != 0) + sd.chdir(folder); + + // If file exists + if (myFile.open(fileName, O_READ)) { + calcFilesize = myFile.fileSize() * 8 / 1024 / 1024; + + // Nintendo Power (SF Memory Cassette) + // Read up to 0x60000 then add FFs to 0x80000 + if (NP == true) { + for (i = 0; i < (0x60000 / 512); i++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + calcChecksum = calcChecksumChunk; + } + calcChecksum += 0xF47C; // FFs from 0x60000-0x80000 + } + else if ((calcFilesize == 10) || (calcFilesize == 12) || (calcFilesize == 20) || (calcFilesize == 24)) { + unsigned long calcBase = 0; + unsigned long calcMirror = 0; + byte calcMirrorCount = 0; + if (calcFilesize > 16) + calcBase = 2097152; + else + calcBase = 1048576; + calcMirror = myFile.fileSize() - calcBase; + calcMirrorCount = calcBase / calcMirror; + + // Momotarou Dentetsu Happy Fix 3MB (24Mbit) + if ((calcFilesize == 24) && (romChips == 245)) { + for (i = 0; i < (myFile.fileSize() / 512); i++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + } + calcChecksum = 2 * calcChecksumChunk; + } + else { + // Base 8/16 Mbit chunk + for (j = 0; j < (calcBase / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + } + calcChecksum = calcChecksumChunk; + calcChecksumChunk = 0; + // Add the mirrored chunk + for (j = 0; j < (calcMirror / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + } + calcChecksum += calcMirrorCount * calcChecksumChunk; + } + } + else if ((calcFilesize == 40) && (romChips == 85)) { + // Daikaijuu Monogatari 2 Fix 5MB (40Mbit) + // Add the 4MB (32Mbit) start + for (j = 0; j < (4194304 / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + calcChecksum = calcChecksumChunk; + } + calcChecksumChunk = 0; + // Add the 1MB (8Mbit) end + for (j = 0; j < (1048576 / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + } + calcChecksum += 4 * calcChecksumChunk; + } + else if (calcFilesize == 48) { + // Star Ocean/Tales of Phantasia Fix 6MB (48Mbit) + // Add the 4MB (32Mbit) start + for (j = 0; j < (4194304 / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + calcChecksum = calcChecksumChunk; + } + calcChecksumChunk = 0; + // Add the 2MB (16Mbit) end + for (j = 0; j < (2097152 / 512); j++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + } + calcChecksum += 2 * calcChecksumChunk; + } + else { + //calcFilesize == 2 || 4 || 8 || 16 || 32 || 40 || etc + for (i = 0; i < (myFile.fileSize() / 512); i++) { + myFile.read(sdBuffer, 512); + for (c = 0; c < 512; c++) { + calcChecksumChunk += sdBuffer[c]; + } + calcChecksum = calcChecksumChunk; + } + } + myFile.close(); + sd.chdir(); + return (calcChecksum); + } + else { + // Else show error + print_Error(F("DUMP ROM 1ST"), false); + return 0; + } +} + +boolean compare_checksum() { + + println_Msg(F("Calculating Checksum")); + display_Update(); + + strcpy(fileName, romName); + strcat(fileName, ".sfc"); + + // last used rom folder + EEPROM_readAnything(10, foldern); + sprintf(folder, "SNES/ROM/%s/%d", romName, foldern - 1); + + char calcsumStr[5]; + sprintf(calcsumStr, "%04X", calc_checksum(fileName, folder)); + + if (strcmp(calcsumStr, checksumStr) == 0) { + print_Msg(F("Result: ")); + println_Msg(calcsumStr); + println_Msg(F("Checksum matches")); + display_Update(); + return 1; + } + else { + print_Msg(F("Result: ")); + println_Msg(calcsumStr); + print_Error(F("Checksum Error"), false); + display_Update(); + return 0; + } +} + +// Read rom to SD card +void readROM_SNES() { + // Set control + dataIn(); + controlIn_SNES(); + + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".sfc"); + + // create a new folder for the save file + EEPROM_readAnything(10, foldern); + sprintf(folder, "SNES/ROM/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + //clear the screen + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("Can't create file on SD"), true); + } + + //Dump Derby Stallion '96 (Japan) Actual Size is 24Mb + if ((romType == LO) && (numBanks == 128) && (strcmp("CC86", checksumStr) == 0)) { + // Read Banks 0x00-0x3F for the 1st/2nd MB + for (int currBank = 0; currBank < 64; currBank++) { + // Dump the bytes to SD 512B at a time + for (long currByte = 32768; currByte < 65536; currByte += 512) { + for (int c = 0; c < 512; c++) { + sdBuffer[c] = readBank_SNES(currBank, currByte + c); + } + myFile.write(sdBuffer, 512); + } + } + //Read Bank 0x80-9F for the 3rd MB + for (int currBank = 128; currBank < 160; currBank++) { + // Dump the bytes to SD 512B at a time + for (long currByte = 32768; currByte < 65536; currByte += 512) { + for (int c = 0; c < 512; c++) { + sdBuffer[c] = readBank_SNES(currBank, currByte + c); + } + myFile.write(sdBuffer, 512); + } + } + } + + //Dump Low-type ROM + else if (romType == LO) { + if (romChips == 243) { //0xF3 + cx4Map = readBank_SNES(0, 32594); //0x7F52 + if ((cx4Type == 2) && (cx4Map != 0)) { //X2 + dataOut(); + controlOut_SNES(); + writeBank_SNES(0, 32594, 0); // Set 0x7F52 to 0 + dataIn(); + controlIn_SNES(); + } + else if ((cx4Type == 3) && (cx4Map == 0)) { //X3 + dataOut(); + controlOut_SNES(); + writeBank_SNES(0, 32594, 1); // Set 0x7F52 to 1 + dataIn(); + controlIn_SNES(); + } + } + if (romSize > 24) { + // ROM > 96 banks (up to 128 banks) + readLoRomBanks( 0x80, numBanks + 0x80, &myFile ); + } else { + // Read up to 96 banks starting at bank 0×00. + readLoRomBanks( 0, numBanks, &myFile ); + } + if (romChips == 243) { //0xF3 + // Restore CX4 Mapping Register + dataOut(); + controlOut_SNES(); + writeBank_SNES(0, 32594, cx4Map); // 0x7F52 + dataIn(); + controlIn_SNES(); + } + } + + // Dump SDD1 High-type ROM + else if ((romType == HI) && (romChips == 69)) { + println_Msg(F("Dumping SDD1 HiRom")); + display_Update(); + controlIn_SNES(); + byte initialSOMap = readBank_SNES(0, 18439); + + for (int currMemmap = 0; currMemmap < (numBanks / 16); currMemmap++) { + + dataOut(); + controlOut_SNES(); + + writeBank_SNES(0, 18439, currMemmap); + + dataIn(); + controlIn_SNES(); + + readHiRomBanks( 240, 256, &myFile ); + } + + dataOut(); + controlOut_SNES(); + + writeBank_SNES(0, 18439, initialSOMap); + + dataIn(); + controlIn_SNES(); + } + + // Dump SPC7110 High-type ROM + else if ((romType == HI) && ((romChips == 245) || (romChips == 249))) { + println_Msg(F("Dumping SPC7110 HiRom")); + display_Update(); + + // 0xC00000-0xDFFFFF + print_Msg(F("Part 1")); + display_Update(); + readHiRomBanks( 192, 224, &myFile ); + + if (numBanks > 32) { + dataOut(); + controlOut_SNES(); + // Set 0x4834 to 0xFF + writeBank_SNES( 0, 0x4834, 0xFF ); + + dataIn(); + controlIn_SNES(); + + // 0xE00000-0xEFFFFF + print_Msg(F(" 2")); + display_Update(); + readHiRomBanks( 224, 240, &myFile ); + + if (numBanks > 48) { + // 0xF00000-0xFFFFFF + print_Msg(F(" 3")); + display_Update(); + readHiRomBanks( 240, 256, &myFile ); + + dataOut(); + controlOut_SNES(); + + // Set 0x4833 to 3 + writeBank_SNES( 0, 0x4833, 3 ); + + dataIn(); + controlIn_SNES(); + + // 0xF00000-0xFFFFFF + print_Msg(F(" 4")); + display_Update(); + readHiRomBanks( 240, 256, &myFile ); + } + println_Msg(F("")); + + // Return mapping registers to initial settings... + dataOut(); + controlOut_SNES(); + + writeBank_SNES( 0, 0x4833, 2 ); + writeBank_SNES( 0, 0x4834, 0 ); + + dataIn(); + controlIn_SNES(); + } + } + + // Dump standard High-type ROM + else if ((romType == HI) || (romType == SA) || (romType == EX)) { + println_Msg(F("Dumping HiRom...")); + display_Update(); + + readHiRomBanks( 192, numBanks + 192, &myFile ); + } + + // Close the file: + myFile.close(); +} + +/****************************************** + SNES SRAM Functions +*****************************************/ +// Write file to SRAM +void writeSRAM (boolean browseFile) { + if (browseFile) { + filePath[0] = '\0'; + sd.chdir("/"); + fileBrowser("Select srm file"); + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + display_Clear(); + } + else + sprintf(filePath, "%s", fileName); + + //open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Set pins to output + dataOut(); + + // Set RST RD WR to High and CS to Low + controlOut_SNES(); + + // LoRom + if (romType == LO) { + // Sram size + long lastByte = (long(sramSize) * 128); + + if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX + if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) + sramBanks = lastByte / 0x10000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0x0000; currByte < 0x10000; currByte++) { + writeBank_SNES(currBank, currByte, myFile.read()); + } + } + } + else { // SuperFX SRAM + for (long currByte = 0; currByte < lastByte; currByte++) { + writeBank_SNES(0x70, currByte, myFile.read()); + } + } + } + else if (lastByte > 0x8000) { // Large SRAM Fix + sramBanks = lastByte / 0x8000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0x0000; currByte < 0x8000; currByte++) { + writeBank_SNES(currBank, currByte, myFile.read()); + } + } + } + else { + for (long currByte = 0; currByte < lastByte; currByte++) { + writeBank_SNES(0x70, currByte, myFile.read()); + } + } + } + // HiRom + else if (romType == HI) { + if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM + // Configure SPC7110 SRAM Register + // Set 0x4830 to 0x80 + writeBank_SNES(0, 0x4830, 0x80); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + // Write to sram bank + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + writeBank_SNES(0x30, currByte, myFile.read()); + } + // Reset SPC7110 SRAM Register + dataOut(); + // Reset 0x4830 to 0x0 + writeBank_SNES(0, 0x4830, 0); + dataIn(); + } + else { + // Writing SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + if (lastByte > 0x2000) { // Large SRAM Fix + sramBanks = lastByte / 0x2000; + for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { + for (long currByte = 0x6000; currByte < 0x8000; currByte++) { + writeBank_SNES(currBank, currByte, myFile.read()); + } + } + } + else { + lastByte += 0x6000; + // Write to sram bank + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + writeBank_SNES(0x30, currByte, myFile.read()); + } + } + } + } + // ExHiRom + else if (romType == EX) { + // Writing SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + writeBank_SNES(0xB0, currByte, myFile.read()); + } + } + // SA1 + else if (romType == SA) { + long lastByte = (long(sramSize) * 128); + // Enable CPU Clock + clockgen.set_freq(357954500ULL, SI5351_CLK1); + clockgen.output_enable(SI5351_CLK1, 1); + + // Direct writes to BW-RAM (SRAM) in banks 0x40-0x43 don't work + // Break BW-RAM (SRAM) into 0x2000 blocks + byte lastBlock = 0; + lastBlock = lastByte / 0x2000; + + // Writing SRAM on SA1 needs CS(PH3) to be high + // PORTH |= (1 << 3); + + // Setup BW-RAM + // Set 0x2224 (SNES BMAPS) to map SRAM Block 0 to 0x6000-0x7FFF + writeBank_SNES(0, 0x2224, 0); + // Set 0x2226 (SNES SBWE) to 0x80 Write Enable + writeBank_SNES(0, 0x2226, 0x80); + // Set 0x2228 (SNES BWPA) to 0x00 BW-RAM Write-Protected Area + writeBank_SNES(0, 0x2228, 0); + delay(1000); + + // Use $2224 (SNES) to map BW-RAM block to 0x6000-0x7FFF + // Use $2226 (SNES) to write enable the BW-RAM + byte firstByte = 0; + for (byte currBlock = 0; currBlock < lastBlock; currBlock++) { + // Set 0x2224 (SNES BMAPS) to map SRAM Block to 0x6000-0x7FFF + writeBank_SNES(0, 0x2224, currBlock); + // Set 0x2226 (SNES SBWE) to 0x80 Write Enable + writeBank_SNES(0, 0x2226, 0x80); + for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { + myFile.read(sdBuffer, 512); + if ((currBlock == 0) && (currByte == 0x6000)) { + firstByte = sdBuffer[0]; + } + for (int c = 0; c < 512; c++) { + writeBank_SNES(0, currByte + c, sdBuffer[c]); + } + } + } + // Rewrite First Byte + writeBank_SNES(0, 0x2224, 0); + writeBank_SNES(0, 0x2226, 0x80); + writeBank_SNES(0, 0x6000, firstByte); + // Disable CPU clock + clockgen.output_enable(SI5351_CLK1, 0); + } + + // Set pins to input + dataIn(); + + // Close the file: + myFile.close(); + println_Msg(F("SRAM writing finished")); + display_Update(); + + } + else { + print_Error(F("File doesnt exist"), false); + } +} + +void readSRAM () { + // set control + controlIn_SNES(); + + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".srm"); + + // create a new folder for the save file + EEPROM_readAnything(10, foldern); + sprintf(folder, "SNES/SAVE/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(10, foldern); + + //open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("SD Error"), true); + } + int sramBanks = 0; + if (romType == LO) { + // Sram size + long lastByte = (long(sramSize) * 128); + if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX + if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) + sramBanks = lastByte / 0x10000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0x0000; currByte < 0x10000; currByte++) { + myFile.write(readBank_SNES(currBank, currByte)); + } + } + } + else { // SuperFX SRAM + for (long currByte = 0; currByte < lastByte; currByte++) { + myFile.write(readBank_SNES(0x70, currByte)); + } + } + } + else if (lastByte > 0x8000) { // Large SRAM Fix + sramBanks = lastByte / 0x8000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0x0000; currByte < 0x8000; currByte++) { + myFile.write(readBank_SNES(currBank, currByte)); + } + } + } + else { + for (long currByte = 0; currByte < lastByte; currByte++) { + myFile.write(readBank_SNES(0x70, currByte)); + } + } + } + else if (romType == HI) { + if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM + // Configure SPC7110 SRAM Register + dataOut(); + // Set 0x4830 to 0x80 + writeBank_SNES(0, 0x4830, 0x80); + dataIn(); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + myFile.write(readBank_SNES(0x30, currByte)); + } + dataOut(); + // Reset 0x4830 to 0x0 + writeBank_SNES(0, 0x4830, 0); + dataIn(); + } + else { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + if (lastByte > 0x2000) { // Large SRAM Fix + sramBanks = lastByte / 0x2000; + for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { + for (long currByte = 0x6000; currByte < 0x8000; currByte++) { + myFile.write(readBank_SNES(currBank, currByte)); + } + } + } + else { + lastByte += 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + myFile.write(readBank_SNES(0x30, currByte)); + } + } + } + } + else if (romType == EX) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + myFile.write(readBank_SNES(0xB0, currByte)); + } + } + else if (romType == SA) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + if (lastByte > 0x10000) { + sramBanks = lastByte / 0x10000; + for (int currBank = 0x40; currBank < sramBanks + 0x40; currBank++) { + for (long currByte = 0; currByte < 0x10000; currByte++) { + myFile.write(readBank_SNES(currBank, currByte)); + } + } + } + else { + for (long currByte = 0x0; currByte < lastByte; currByte++) { + myFile.write(readBank_SNES(0x40, currByte)); + } + } + } + + // Close the file: + myFile.close(); + + // Signal end of process + display_Clear(); + print_Msg(F("Saved to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); +} + +// Check if the SRAM was written without any error +unsigned long verifySRAM() { + //open file on sd card + if (myFile.open(filePath, O_READ)) { + + // Variable for errors + writeErrors = 0; + + // Set control + controlIn_SNES(); + + int sramBanks = 0; + if (romType == LO) { + // Sram size + long lastByte = (long(sramSize) * 128); + if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX + if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) + sramBanks = lastByte / 0x10000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0; currByte < 0x10000; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + } + else { // SuperFX SRAM + for (long currByte = 0; currByte < lastByte; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x70, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + } + else if (lastByte > 0x8000) { // Large SRAM Fix + sramBanks = lastByte / 0x8000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0; currByte < 0x8000; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + } + else { + for (long currByte = 0; currByte < lastByte; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x70, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + } + else if (romType == HI) { + if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM + // Configure SPC7110 SRAM Register + dataOut(); + // Set 0x4830 to 0x80 + writeBank_SNES(0, 0x4830, 0x80); + dataIn(); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x30, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + dataOut(); + // Reset 0x4830 to 0x0 + writeBank_SNES(0, 0x4830, 0); + dataIn(); + } + else { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + if (lastByte > 0x2000) { // Large SRAM Fix + sramBanks = lastByte / 0x2000; + for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { + for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + } + else { + lastByte += 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x30, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + } + } + else if (romType == EX) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0xB0, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + else if (romType == SA) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + + if (lastByte > 0x10000) { + sramBanks = lastByte / 0x10000; + for (int currBank = 0x40; currBank < sramBanks + 0x40; currBank++) { + for (long currByte = 0x0; currByte < 0x10000; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + } + else { + for (long currByte = 0x0; currByte < lastByte; currByte += 512) { + //fill sdBuffer + myFile.read(sdBuffer, 512); + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x40, currByte + c)) != sdBuffer[c]) { + writeErrors++; + } + } + } + } + // Reset SA1 + // Set pins to input + dataIn(); + // Close the file: + myFile.close(); + if (writeErrors == 0) { + println_Msg(F("Verified OK")); + } + else { + print_Msg(F("Error: ")); + print_Msg(writeErrors); + println_Msg(F(" bytes ")); + print_Error(F("did not verify."), false); + } + display_Update(); + wait(); + // Set reset pin to output (PH0) + DDRH |= (1 << 0); + // Switch RST(PH0) to LOW + PORTH &= ~(1 << 0); + display_Clear(); + print_Msg("Resetting..."); + display_Update(); + delay(3000); // wait 3 secs + asm volatile (" jmp 0"); + } + // Close the file: + myFile.close(); + return writeErrors; + } + else { + print_Error(F("Can't open file"), false); + } +} + +// Overwrite the entire SRAM +boolean eraseSRAM (byte b) { + print_Msg(F("0x")); + print_Msg(b, HEX); + print_Msg(F(": ")); + display_Update(); + + // Set pins to output + dataOut(); + + // Set control pins + controlOut_SNES(); + + int sramBanks = 0; + if (romType == LO) { + // Sram size + long lastByte = (long(sramSize) * 128); + + if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX + if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) + sramBanks = lastByte / 0x10000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0x0000; currByte < 0x10000; currByte++) { + writeBank_SNES(currBank, currByte, b); + } + } + } + else { // SuperFX SRAM + for (long currByte = 0; currByte < lastByte; currByte++) { + writeBank_SNES(0x70, currByte, b); + } + } + } + else if (lastByte > 0x8000) { // Large SRAM Fix + sramBanks = lastByte / 0x8000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0x0000; currByte < 0x8000; currByte++) { + writeBank_SNES(currBank, currByte, b); + } + } + } + else { + for (long currByte = 0; currByte < lastByte; currByte++) { + writeBank_SNES(0x70, currByte, b); + } + } + } + else if (romType == HI) { + if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM + // Configure SPC7110 SRAM Register + // Set 0x4830 to 0x80 + writeBank_SNES(0, 0x4830, 0x80); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + // Write to sram bank + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + writeBank_SNES(0x30, currByte, b); + } + // Reset SPC7110 SRAM Register + dataOut(); + // Reset 0x4830 to 0x0 + writeBank_SNES(0, 0x4830, 0); + dataIn(); + } + else { + // Writing SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + if (lastByte > 0x2000) { // Large SRAM Fix + sramBanks = lastByte / 0x2000; + for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { + for (long currByte = 0x6000; currByte < 0x8000; currByte++) { + writeBank_SNES(currBank, currByte, b); + } + } + } + else { + lastByte += 0x6000; + // Write to sram bank + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + writeBank_SNES(0x30, currByte, b); + } + } + } + } + // ExHiRom + else if (romType == EX) { + // Writing SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte++) { + writeBank_SNES(0xB0, currByte, b); + } + } + // SA1 + else if (romType == SA) { + long lastByte = (long(sramSize) * 128); + // Enable CPU Clock + clockgen.set_freq(357954500ULL, SI5351_CLK1); + clockgen.output_enable(SI5351_CLK1, 1); + + // Direct writes to BW-RAM (SRAM) in banks 0x40-0x43 don't work + // Break BW-RAM (SRAM) into 0x2000 blocks + // Use $2224 to map BW-RAM block to 0x6000-0x7FFF + byte lastBlock = 0; + lastBlock = lastByte / 0x2000; + + // Writing SRAM on SA1 needs CS(PH3) to be high + // PORTH |= (1 << 3); + + // Setup BW-RAM + // Set 0x2224 (SNES BMAPS) to map SRAM Block 0 to 0x6000-0x7FFF + writeBank_SNES(0, 0x2224, 0); + // Set 0x2226 (SNES SBWE) to 0x80 Write Enable + writeBank_SNES(0, 0x2226, 0x80); + // Set 0x2228 (SNES BWPA) to 0x00 BW-RAM Write-Protected Area + writeBank_SNES(0, 0x2228, 0); + delay(1000); + + // Use $2224 (SNES) to map BW-RAM block to 0x6000-0x7FFF + // Use $2226 (SNES) to write enable the BW-RAM + for (byte currBlock = 0; currBlock < lastBlock; currBlock++) { + // Set 0x2224 (SNES BMAPS) to map SRAM Block to 0x6000-0x7FFF + writeBank_SNES(0, 0x2224, currBlock); + // Set 0x2226 (SNES SBWE) to 0x80 Write Enable + writeBank_SNES(0, 0x2226, 0x80); + for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { + for (int c = 0; c < 512; c++) { + writeBank_SNES(0, currByte + c, b); + } + } + } + // Rewrite First Byte + writeBank_SNES(0, 0x2224, 0); + writeBank_SNES(0, 0x2226, 0x80); + writeBank_SNES(0, 0x6000, b); + // Disable CPU clock + clockgen.output_enable(SI5351_CLK1, 0); + } + + dataIn(); + + // Variable for errors + writeErrors = 0; + + // Set control + controlIn_SNES(); + + sramBanks = 0; + if (romType == LO) { + // Sram size + long lastByte = (long(sramSize) * 128); + if ((romChips == 19) || (romChips == 20) || (romChips == 21) || (romChips == 26)) { // SuperFX + if (lastByte > 0x10000) { // Large SuperFX SRAM (no known carts) + sramBanks = lastByte / 0x10000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0; currByte < 0x10000; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != b) { + writeErrors++; + } + } + } + } + } + else { // SuperFX SRAM + for (long currByte = 0; currByte < lastByte; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x70, currByte + c)) != b) { + writeErrors++; + } + } + } + } + } + else if (lastByte > 0x8000) { // Large SRAM Fix + sramBanks = lastByte / 0x8000; + for (int currBank = 0x70; currBank < sramBanks + 0x70; currBank++) { + for (long currByte = 0; currByte < 0x8000; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != b) { + writeErrors++; + } + } + } + } + } + else { + for (long currByte = 0; currByte < lastByte; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x70, currByte + c)) != b) { + writeErrors++; + } + } + } + } + } + else if (romType == HI) { + if ((romChips == 245) || (romChips == 249)) { // SPC7110 SRAM + // Configure SPC7110 SRAM Register + dataOut(); + // Set 0x4830 to 0x80 + writeBank_SNES(0, 0x4830, 0x80); + dataIn(); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x30, currByte + c)) != b) { + writeErrors++; + } + } + } + dataOut(); + // Reset 0x4830 to 0x0 + writeBank_SNES(0, 0x4830, 0); + dataIn(); + } + else { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + if (lastByte > 0x2000) { // Large SRAM Fix + sramBanks = lastByte / 0x2000; + for (int currBank = 0x30; currBank < sramBanks + 0x30; currBank++) { + for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != b) { + writeErrors++; + } + } + } + } + } + else { + lastByte += 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x30, currByte + c)) != b) { + writeErrors++; + } + } + } + } + } + } + else if (romType == EX) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128) + 0x6000; + for (long currByte = 0x6000; currByte < lastByte; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0xB0, currByte + c)) != b) { + writeErrors++; + } + } + } + } + else if (romType == SA) { + // Dumping SRAM on HiRom needs CS(PH3) to be high + PORTH |= (1 << 3); + // Sram size + long lastByte = (long(sramSize) * 128); + if (lastByte > 0x10000) { + sramBanks = lastByte / 0x10000; + for (int currBank = 0x40; currBank < sramBanks + 0x40; currBank++) { + for (long currByte = 0x0; currByte < 0x10000; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(currBank, currByte + c)) != b) { + writeErrors++; + } + } + } + } + } + else { + for (long currByte = 0x0; currByte < lastByte; currByte += 512) { + for (int c = 0; c < 512; c++) { + if ((readBank_SNES(0x40, currByte + c)) != b) { + writeErrors++; + } + } + } + } + } + if (writeErrors == 0) { + println_Msg(F("OK")); + return 1; + } + else { + println_Msg(F("ERROR")); + return 0; + } + display_Update(); +} + +//****************************************** +// End of File //******************************************