diff --git a/Cart_Reader/Cart_Reader.ino b/Cart_Reader/Cart_Reader.ino index c6e4b71..debf1f3 100644 --- a/Cart_Reader/Cart_Reader.ino +++ b/Cart_Reader/Cart_Reader.ino @@ -4,7 +4,7 @@ This project represents a community-driven effort to provide an easy to build and easy to modify cartridge dumper. - Date: 06.10.2022 + Date: 07.10.2022 Version: 10.0 SD lib: https://github.com/greiman/SdFat @@ -737,18 +737,26 @@ byte starting_letter() { line--; selection = 6; } + else if ((selection == 0) && (line == 0)) { + line = 3; + selection = 6; + } else if (selection > 0) { selection--; } #if defined(enable_LCD) display.setDrawColor(0); - display.drawLine(0, 10 + (line + 1) * 16, 128, 10 + (line + 1) * 16); - display.drawLine(0, 10 + line * 16, 128, 10 + line * 16); + display.drawLine(0, 10 + 0 * 16, 128, 10 + 0 * 16); + display.drawLine(0, 10 + 1 * 16, 128, 10 + 1 * 16); + display.drawLine(0, 10 + 2 * 16, 128, 10 + 2 * 16); + display.drawLine(0, 10 + 3 * 16, 128, 10 + 3 * 16); display.setDrawColor(1); display.drawLine(4 + selection * 16, 10 + line * 16, 9 + selection * 16, 10 + line * 16); #elif defined(enable_OLED) - display.drawLine(0, 10 + (line + 1) * 16, 128, 10 + (line + 1) * 16, BLACK); - display.drawLine(0, 10 + line * 16, 128, 10 + line * 16, BLACK); + display.drawLine(0, 10 + 0 * 16, 128, 10 + 0 * 16, BLACK); + display.drawLine(0, 10 + 1 * 16, 128, 10 + 1 * 16, BLACK); + display.drawLine(0, 10 + 2 * 16, 128, 10 + 2 * 16, BLACK); + display.drawLine(0, 10 + 3 * 16, 128, 10 + 3 * 16, BLACK); display.drawLine(selection * 18, 10 + line * 16, 5 + selection * 18, 10 + line * 16, WHITE); #endif display_Update(); @@ -760,18 +768,26 @@ byte starting_letter() { line++; selection = 0; } + else if ((selection == 6) && (line == 3)) { + line = 0; + selection = 0; + } else if (selection < 6) { selection++; } #if defined(enable_LCD) display.setDrawColor(0); - display.drawLine(0, 10 + (line - 1) * 16, 128, 10 + (line - 1) * 16); - display.drawLine(0, 10 + line * 16, 128, 10 + line * 16); + display.drawLine(0, 10 + 0 * 16, 128, 10 + 0 * 16); + display.drawLine(0, 10 + 1 * 16, 128, 10 + 1 * 16); + display.drawLine(0, 10 + 2 * 16, 128, 10 + 2 * 16); + display.drawLine(0, 10 + 3 * 16, 128, 10 + 3 * 16); display.setDrawColor(1); display.drawLine(4 + selection * 16, 10 + line * 16, 9 + selection * 16, 10 + line * 16); #elif defined(enable_OLED) - display.drawLine(0, 10 + (line - 1) * 16, 128, 10 + (line - 1) * 16, BLACK); - display.drawLine(0, 10 + line * 16, 128, 10 + line * 16, BLACK); + display.drawLine(0, 10 + 0 * 16, 128, 10 + 0 * 16, BLACK); + display.drawLine(0, 10 + 1 * 16, 128, 10 + 1 * 16, BLACK); + display.drawLine(0, 10 + 2 * 16, 128, 10 + 2 * 16, BLACK); + display.drawLine(0, 10 + 3 * 16, 128, 10 + 3 * 16, BLACK); display.drawLine(selection * 18, 10 + line * 16, 5 + selection * 18, 10 + line * 16, WHITE); #endif display_Update(); diff --git a/Cart_Reader/NES.ino b/Cart_Reader/NES.ino index 20fa96e..cdd5315 100644 --- a/Cart_Reader/NES.ino +++ b/Cart_Reader/NES.ino @@ -197,11 +197,11 @@ int b = 0; Menus *****************************************/ // NES start menu -static const char nesMenuItem1[] PROGMEM = "Change Mapper"; -static const char nesMenuItem2[] PROGMEM = "Read iNES Rom"; -static const char nesMenuItem3[] PROGMEM = "Read PRG/CHR"; -static const char nesMenuItem4[] PROGMEM = "Read Sram"; -static const char nesMenuItem5[] PROGMEM = "Write Sram"; +static const char nesMenuItem1[] PROGMEM = "Read iNES Rom"; +static const char nesMenuItem2[] PROGMEM = "Read PRG/CHR"; +static const char nesMenuItem3[] PROGMEM = "Read Sram"; +static const char nesMenuItem4[] PROGMEM = "Write Sram"; +static const char nesMenuItem5[] PROGMEM = "Change Mapper"; static const char nesMenuItem6[] PROGMEM = "Flash NESMaker"; static const char nesMenuItem7[] PROGMEM = "Reset"; static const char* const menuOptionsNES[] PROGMEM = {nesMenuItem1, nesMenuItem2, nesMenuItem3, nesMenuItem4, nesMenuItem5, nesMenuItem6, nesMenuItem7}; @@ -225,23 +225,8 @@ void nesMenu() { // wait for user choice to come back from the question box menu switch (answer) { - // Change Mapper - case 0: - romName[0] = 'C'; - romName[1] = 'A'; - romName[2] = 'R'; - romName[3] = 'T'; - romName[4] = '\0'; - setMapper(); - checkMapperSize(); - setPRGSize(); - setCHRSize(); - setRAMSize(); - checkStatus_NES(); - break; - // Read Rom - case 1: + case 0: #ifndef no-intro CartStart(); readPRG(false); @@ -270,12 +255,12 @@ void nesMenu() { break; // Read single chip - case 2: + case 1: nesChipMenu(); break; // Read RAM - case 3: + case 2: CreateROMFolderInSD(); readRAM(); resetROM(); @@ -286,7 +271,7 @@ void nesMenu() { break; // Write RAM - case 4: + case 3: writeRAM(); resetROM(); println_Msg(F("")); @@ -295,6 +280,21 @@ void nesMenu() { wait(); break; + // Change Mapper + case 4: + romName[0] = 'C'; + romName[1] = 'A'; + romName[2] = 'R'; + romName[3] = 'T'; + romName[4] = '\0'; + setMapper(); + checkMapperSize(); + setPRGSize(); + setCHRSize(); + setRAMSize(); + checkStatus_NES(); + break; + // Write FLASH case 5: if (mapper == 30) { @@ -431,15 +431,16 @@ uint32_t uppow2(uint32_t n) { return n; } -void printPRG() { +void printPRG(unsigned long myOffset) { display_Clear(); - println_Msg(F("Printing PRG at 0x8000")); + print_Msg(F("Printing PRG at ")); + println_Msg(myOffset); char myBuffer[3]; for (word currLine = 0; currLine < 512; currLine += 16) { for (byte currByte = 0; currByte < 16; currByte++) { - itoa (read_prg_byte(0x8000 + currLine + currByte), myBuffer, 16); + itoa (read_prg_byte(myOffset + currLine + currByte), myBuffer, 16); for (word i = 0; i < 2 - strlen(myBuffer); i++) { print_Msg(F("0")); } @@ -494,14 +495,14 @@ boolean getMapping() { println_Msg(F("...")); display_Update(); - // Filter out 0xFF checksum - if (strcmp(crcStr, "BD7BC39F") == 0) { - delay(500); + // Filter out all 0xFF checksums at 0x8000 and 0xE000 + if ((strcmp(crcStr, "BD7BC39F") == 0) && (strcmp(crcStrMMC3, "BD7BC39F") == 0)) { + delay(200); println_Msg(F("")); - println_Msg(F("No data found at 0x8000")); + println_Msg(F("No data found.")); println_Msg(F("Using manual selection")); display_Update(); - delay(1000); + delay(500); romName[0] = 'C'; romName[1] = 'A'; romName[2] = 'R'; @@ -548,8 +549,8 @@ boolean getMapping() { //Skip every 3rd line skip_line(&myFile); - //if checksum search successful set mapper and end search - if ((strcmp(crc_search, crcStr) == 0) || (strcmp(crc_search, crcStrMMC3) == 0)) { + //if checksum search was successful set mapper and end search, also filter out 0xFF checksum + if (((strcmp(crc_search, crcStr) == 0) || (strcmp(crc_search, crcStrMMC3) == 0)) && (strcmp(crc_search, "BD7BC39F") != 0)) { // Rewind to start of entry for (byte count_newline = 0; count_newline < 4; count_newline++) { @@ -789,20 +790,22 @@ boolean getMapping() { } } // File searched until end but nothing found - if (strcmp(crc_search, crcStr) != 0) { - println_Msg(F("")); - println_Msg(F("CRC not found in database")); - println_Msg(F("Using manual selection")); - display_Update(); - delay(1000); - printPRG(); - romName[0] = 'C'; - romName[1] = 'A'; - romName[2] = 'R'; - romName[3] = 'T'; - romName[4] = '\0'; - return 0; - } + println_Msg(F("")); + println_Msg(F("CRC not found in database")); + println_Msg(F("Using manual selection")); + display_Update(); + delay(1000); + // Print debug + printPRG(0x8000); + printPRG(0xE000); + + // Change ROM name to CART + romName[0] = 'C'; + romName[1] = 'A'; + romName[2] = 'R'; + romName[3] = 'T'; + romName[4] = '\0'; + return 0; } else { println_Msg(F("Database file not found")); @@ -823,270 +826,281 @@ void selectMapping() { // Select starting letter byte myLetter = starting_letter(); - // Open database - if (myFile.open("nes.txt", O_READ)) { + if (myLetter == 27) { + // Change Mapper + setMapper(); + checkMapperSize(); + setPRGSize(); + setCHRSize(); + setRAMSize(); + checkStatus_NES(); + } + else { + // Open database + if (myFile.open("nes.txt", O_READ)) { #ifdef global_log - // Disable log to prevent unnecessary logging - dont_log = true; + // Disable log to prevent unnecessary logging + dont_log = true; #endif - // Skip ahead to selected starting letter - if ((myLetter > 0) && (myLetter <= 26)) { + // Skip ahead to selected starting letter + if ((myLetter > 0) && (myLetter <= 26)) { + while (myFile.available()) { + // Read current name +#if defined(enable_OLED) + get_line(gamename, &myFile, 42); +#else + get_line(gamename, &myFile, 96); +#endif + + // Compare selected letter with first letter of current name until match + while (gamename[0] != 64 + myLetter) { + skip_line(&myFile); + skip_line(&myFile); +#if defined(enable_OLED) + get_line(gamename, &myFile, 42); +#else + get_line(gamename, &myFile, 96); +#endif + } + break; + } + + // Rewind one line + for (byte count_newline = 0; count_newline < 2; count_newline++) { + while (1) { + if (myFile.curPosition() == 0) { + break; + } + else if (myFile.peek() == '\n') { + myFile.seekSet(myFile.curPosition() - 1); + break; + } + else { + myFile.seekSet(myFile.curPosition() - 1); + } + } + } + if (myFile.curPosition() != 0) + myFile.seekSet(myFile.curPosition() + 2); + } + + // Display database while (myFile.available()) { - // Read current name + display_Clear(); + + // Read game name #if defined(enable_OLED) get_line(gamename, &myFile, 42); #else get_line(gamename, &myFile, 96); #endif - // Compare selected letter with first letter of current name until match - while (gamename[0] != 64 + myLetter) { - skip_line(&myFile); - skip_line(&myFile); -#if defined(enable_OLED) - get_line(gamename, &myFile, 42); -#else - get_line(gamename, &myFile, 96); -#endif + // Read CRC32 checksum + sprintf(checksumStr, "%c", myFile.read()); + for (byte i = 0; i < 7; i++) { + sprintf(tempStr2, "%c", myFile.read()); + strcat(checksumStr, tempStr2); } - break; - } - // Rewind one line - for (byte count_newline = 0; count_newline < 2; count_newline++) { - while (1) { - if (myFile.curPosition() == 0) { - break; - } - else if (myFile.peek() == '\n') { - myFile.seekSet(myFile.curPosition() - 1); - break; - } - else { - myFile.seekSet(myFile.curPosition() - 1); - } + // Skip over semicolon + myFile.seekSet(myFile.curPosition() + 1); + + // Read CRC32 of first 512 bytes + sprintf(crc_search, "%c", myFile.read()); + for (byte i = 0; i < 7; i++) { + sprintf(tempStr2, "%c", myFile.read()); + strcat(crc_search, tempStr2); } - } - if (myFile.curPosition() != 0) - myFile.seekSet(myFile.curPosition() + 2); - } - // Display database - while (myFile.available()) { - display_Clear(); + // Skip over semicolon + myFile.seekSet(myFile.curPosition() + 1); - // Read game name -#if defined(enable_OLED) - get_line(gamename, &myFile, 42); -#else - get_line(gamename, &myFile, 96); -#endif + // Read iNES header + get_line(iNES_STR, &myFile, 33); - // Read CRC32 checksum - sprintf(checksumStr, "%c", myFile.read()); - for (byte i = 0; i < 7; i++) { - sprintf(tempStr2, "%c", myFile.read()); - strcat(checksumStr, tempStr2); - } + // Skip every 3rd line + skip_line(&myFile); - // Skip over semicolon - myFile.seekSet(myFile.curPosition() + 1); + // Convert "4E4553" to (0x4E, 0x45, 0x53) + byte iNES_BUF[2]; + for (byte j = 0; j < 16; j++) { + sscanf(iNES_STR + j * 2, "%2X", iNES_BUF); + iNES_HEADER[j] = iNES_BUF[0]; + } - // Read CRC32 of first 512 bytes - sprintf(crc_search, "%c", myFile.read()); - for (byte i = 0; i < 7; i++) { - sprintf(tempStr2, "%c", myFile.read()); - strcat(crc_search, tempStr2); - } + // Convert iNES garbage to useful info (thx to fceux) + mapper = (iNES_HEADER[6] >> 4); + mapper |= (iNES_HEADER[7] & 0xF0); + mapper |= ((iNES_HEADER[8] & 0x0F) << 8); - // Skip over semicolon - myFile.seekSet(myFile.curPosition() + 1); + // PRG size + if ((iNES_HEADER[9] & 0x0F) != 0x0F) { + // simple notation + prgsize = (iNES_HEADER[4] | ((iNES_HEADER[9] & 0x0F) << 8)); //*16 + } + else { + // exponent-multiplier notation + prgsize = (((1 << (iNES_HEADER[4] >> 2)) * ((iNES_HEADER[4] & 0b11) * 2 + 1)) >> 14); //*16 + } + if (prgsize != 0) + prgsize = (int(log(prgsize) / log(2))); - // Read iNES header - get_line(iNES_STR, &myFile, 33); + // CHR size + if ((iNES_HEADER[9] & 0xF0) != 0xF0) { + // simple notation + chrsize = (uppow2(iNES_HEADER[5] | ((iNES_HEADER[9] & 0xF0) << 4))) * 2; //*4 + } + else { + chrsize = (((1 << (iNES_HEADER[5] >> 2)) * ((iNES_HEADER[5] & 0b11) * 2 + 1)) >> 13) * 2; //*4 + } + if (chrsize != 0) + chrsize = (int(log(chrsize) / log(2))); - // Skip every 3rd line - skip_line(&myFile); + // RAM size + ramsize = ((iNES_HEADER[10] & 0xF0) ? (64 << ((iNES_HEADER[10] & 0xF0) >> 4)) : 0) / 4096; //*4 + if (ramsize != 0) + ramsize = (int(log(ramsize) / log(2))); - // Convert "4E4553" to (0x4E, 0x45, 0x53) - byte iNES_BUF[2]; - for (byte j = 0; j < 16; j++) { - sscanf(iNES_STR + j * 2, "%2X", iNES_BUF); - iNES_HEADER[j] = iNES_BUF[0]; - } - - // Convert iNES garbage to useful info (thx to fceux) - mapper = (iNES_HEADER[6] >> 4); - mapper |= (iNES_HEADER[7] & 0xF0); - mapper |= ((iNES_HEADER[8] & 0x0F) << 8); - - // PRG size - if ((iNES_HEADER[9] & 0x0F) != 0x0F) { - // simple notation - prgsize = (iNES_HEADER[4] | ((iNES_HEADER[9] & 0x0F) << 8)); //*16 - } - else { - // exponent-multiplier notation - prgsize = (((1 << (iNES_HEADER[4] >> 2)) * ((iNES_HEADER[4] & 0b11) * 2 + 1)) >> 14); //*16 - } - if (prgsize != 0) - prgsize = (int(log(prgsize) / log(2))); - - // CHR size - if ((iNES_HEADER[9] & 0xF0) != 0xF0) { - // simple notation - chrsize = (uppow2(iNES_HEADER[5] | ((iNES_HEADER[9] & 0xF0) << 4))) * 2; //*4 - } - else { - chrsize = (((1 << (iNES_HEADER[5] >> 2)) * ((iNES_HEADER[5] & 0b11) * 2 + 1)) >> 13) * 2; //*4 - } - if (chrsize != 0) - chrsize = (int(log(chrsize) / log(2))); - - // RAM size - ramsize = ((iNES_HEADER[10] & 0xF0) ? (64 << ((iNES_HEADER[10] & 0xF0) >> 4)) : 0) / 4096; //*4 - if (ramsize != 0) - ramsize = (int(log(ramsize) / log(2))); - - prg = (int_pow(2, prgsize)) * 16; - if (chrsize == 0) - chr = 0; // 0K - else - chr = (int_pow(2, chrsize)) * 4; - if (ramsize == 0) - ram = 0; // 0K - else if (mapper == 82) - ram = 5; // 5K - else - ram = (int_pow(2, ramsize)) * 4; - - // Mapper Variants - // Identify variant for use across multiple functions - if (mapper == 4) { // Check for MMC6/MMC3 - checkMMC6(); - if (mmc6) - ram = 1; // 1K - } - - println_Msg(gamename); - print_Msg(F("MAPPER: ")); - println_Msg(mapper); - print_Msg(F("PRG SIZE: ")); - print_Msg(prg); - println_Msg(F("K")); - print_Msg(F("CHR SIZE: ")); - print_Msg(chr); - println_Msg(F("K")); - print_Msg(F("RAM SIZE: ")); - if (mapper == 0) { - print_Msg(ram / 4); - println_Msg(F("K")); - } - else if ((mapper == 16) || (mapper == 80) || (mapper == 159)) { - if (mapper == 16) - print_Msg(ram * 32); + prg = (int_pow(2, prgsize)) * 16; + if (chrsize == 0) + chr = 0; // 0K else - print_Msg(ram * 16); - println_Msg(F("B")); - } - else if (mapper == 19) { - if (ramsize == 2) - println_Msg(F("128B")); + chr = (int_pow(2, chrsize)) * 4; + if (ramsize == 0) + ram = 0; // 0K + else if (mapper == 82) + ram = 5; // 5K + else + ram = (int_pow(2, ramsize)) * 4; + + // Mapper Variants + // Identify variant for use across multiple functions + if (mapper == 4) { // Check for MMC6/MMC3 + checkMMC6(); + if (mmc6) + ram = 1; // 1K + } + + println_Msg(gamename); + print_Msg(F("MAPPER: ")); + println_Msg(mapper); + print_Msg(F("PRG SIZE: ")); + print_Msg(prg); + println_Msg(F("K")); + print_Msg(F("CHR SIZE: ")); + print_Msg(chr); + println_Msg(F("K")); + print_Msg(F("RAM SIZE: ")); + if (mapper == 0) { + print_Msg(ram / 4); + println_Msg(F("K")); + } + else if ((mapper == 16) || (mapper == 80) || (mapper == 159)) { + if (mapper == 16) + print_Msg(ram * 32); + else + print_Msg(ram * 16); + println_Msg(F("B")); + } + else if (mapper == 19) { + if (ramsize == 2) + println_Msg(F("128B")); + else { + print_Msg(ram); + println_Msg(F("K")); + } + } else { print_Msg(ram); println_Msg(F("K")); } - } - else { - print_Msg(ram); - println_Msg(F("K")); - } #if defined(enable_OLED) - println_Msg(F("Press left to Change")); - println_Msg(F("and right to Select")); + println_Msg(F("Press left to Change")); + println_Msg(F("and right to Select")); #elif defined(enable_LCD) - println_Msg(F("Rotate to Change")); - println_Msg(F("Press to Select")); + println_Msg(F("Rotate to Change")); + println_Msg(F("Press to Select")); #elif defined(SERIAL_MONITOR) - println_Msg(F("U/D to Change")); - println_Msg(F("Space to Select")); + println_Msg(F("U/D to Change")); + println_Msg(F("Space to Select")); #endif - display_Update(); + display_Update(); - int b = 0; - while (1) { - // Check button input - b = checkButton(); + int b = 0; + while (1) { + // Check button input + b = checkButton(); - // Next - if (b == 1) { - break; - } + // Next + if (b == 1) { + break; + } - // Previous - else if (b == 2) { - for (byte count_newline = 0; count_newline < 7; count_newline++) { - while (1) { - if (myFile.curPosition() == 0) { + // Previous + else if (b == 2) { + for (byte count_newline = 0; count_newline < 7; count_newline++) { + while (1) { + if (myFile.curPosition() == 0) { + break; + } + else if (myFile.peek() == '\n') { + myFile.seekSet(myFile.curPosition() - 1); + break; + } + else { + myFile.seekSet(myFile.curPosition() - 1); + } + } + } + if (myFile.curPosition() != 0) + myFile.seekSet(myFile.curPosition() + 2); + break; + } + + // Selection + else if (b == 3) { + // Get name + byte myLength = 0; + for (unsigned int i = 0; i < 20; i++) { + // Stop at first "(" to remove "(Country)" + if (char(gamename[i]) == 40) { break; } - else if (myFile.peek() == '\n') { - myFile.seekSet(myFile.curPosition() - 1); - break; - } - else { - myFile.seekSet(myFile.curPosition() - 1); + if (((char(gamename[i]) >= 48 && char(gamename[i]) <= 57) || (char(gamename[i]) >= 65 && char(gamename[i]) <= 90) || (char(gamename[i]) >= 97 && char(gamename[i]) <= 122)) && (myLength < 15)) { + romName[myLength] = char(gamename[i]); + myLength++; } } - } - if (myFile.curPosition() != 0) - myFile.seekSet(myFile.curPosition() + 2); - break; - } - // Selection - else if (b == 3) { - // Get name - byte myLength = 0; - for (unsigned int i = 0; i < 20; i++) { - // Stop at first "(" to remove "(Country)" - if (char(gamename[i]) == 40) { - break; + // If name consists out of all japanese characters use CART as name + if (myLength == 0) { + romName[0] = 'C'; + romName[1] = 'A'; + romName[2] = 'R'; + romName[3] = 'T'; + romName[4] = '\0'; } - if (((char(gamename[i]) >= 48 && char(gamename[i]) <= 57) || (char(gamename[i]) >= 65 && char(gamename[i]) <= 90) || (char(gamename[i]) >= 97 && char(gamename[i]) <= 122)) && (myLength < 15)) { - romName[myLength] = char(gamename[i]); - myLength++; - } - } - // If name consists out of all japanese characters use CART as name - if (myLength == 0) { - romName[0] = 'C'; - romName[1] = 'A'; - romName[2] = 'R'; - romName[3] = 'T'; - romName[4] = '\0'; + // Save Mapper + EEPROM_writeAnything(7, mapper); + EEPROM_writeAnything(8, prgsize); + EEPROM_writeAnything(9, chrsize); + EEPROM_writeAnything(10, ramsize); + myFile.close(); + break; } - - // Save Mapper - EEPROM_writeAnything(7, mapper); - EEPROM_writeAnything(8, prgsize); - EEPROM_writeAnything(9, chrsize); - EEPROM_writeAnything(10, ramsize); - myFile.close(); - break; } } - } #ifdef global_log - // Enable log again - dont_log = false; + // Enable log again + dont_log = false; #endif - } - else { - print_Error(F("Database file not found"), true); + } + else { + print_Error(F("Database file not found"), true); + } } }