V8.5 BETA: Add global log and GB database

Both are disabled in options.h by default since they push the RAM usage over the limit resulting in corrupted LCD output.

Global log outputs all info to OSCR_LOG.txt in the root of the SD.

no-intro calculates the CRC32 of a Gameboy ROM and if found in the database renames it to no-intro naming scheme.
This commit is contained in:
sanni 2022-06-12 12:30:52 +02:00
parent a237f64470
commit 1c6d277e84
5 changed files with 10710 additions and 166 deletions

View File

@ -4,8 +4,8 @@
This project represents a community-driven effort to provide This project represents a community-driven effort to provide
an easy to build and easy to modify cartridge dumper. an easy to build and easy to modify cartridge dumper.
Date: 09.06.2022 Date: 11.06.2022
Version: 8.4 Version: 8.5 BETA
SD lib: https://github.com/greiman/SdFat SD lib: https://github.com/greiman/SdFat
OLED lib: https://github.com/adafruit/Adafruit_SSD1306 OLED lib: https://github.com/adafruit/Adafruit_SSD1306
@ -19,7 +19,7 @@
RTC lib: https://github.com/adafruit/RTClib RTC lib: https://github.com/adafruit/RTClib
Frequency lib: https://github.com/PaulStoffregen/FreqCount Frequency lib: https://github.com/PaulStoffregen/FreqCount
Compiled with Arduino 1.8.16 Compiled with Arduino 1.8.19
Thanks to: Thanks to:
MichlK - ROM Reader for Super Nintendo MichlK - ROM Reader for Super Nintendo
@ -58,7 +58,7 @@
**********************************************************************************/ **********************************************************************************/
char ver[5] = "8.4"; char ver[5] = "8.5B";
/****************************************** /******************************************
Libraries Libraries
@ -77,6 +77,9 @@ char ver[5] = "8.4";
SdFs sd; SdFs sd;
FsFile myDir; FsFile myDir;
FsFile myFile; FsFile myFile;
#ifdef global_log
FsFile myLog;
#endif
// AVR Eeprom // AVR Eeprom
#include <EEPROM.h> #include <EEPROM.h>
@ -785,6 +788,12 @@ void setup() {
print_Error(F("SD Error"), true); print_Error(F("SD Error"), true);
} }
#ifdef global_log
if (!myLog.open("OSCR_LOG.txt", O_RDWR | O_CREAT | O_APPEND)) {
print_Error(F("SD Error"), true);
}
#endif
#ifdef RTC_installed #ifdef RTC_installed
// Start RTC // Start RTC
RTCStart(); RTCStart();
@ -892,6 +901,9 @@ void print_Msg(const __FlashStringHelper *string) {
#ifdef enable_serial #ifdef enable_serial
Serial.print(string); Serial.print(string);
#endif #endif
#ifdef global_log
myLog.print(string);
#endif
} }
void print_Msg(const char myString[]) { void print_Msg(const char myString[]) {
@ -906,8 +918,8 @@ void print_Msg(const char myString[]) {
} }
// Newline // Newline
display.setCursor(0, display.ty + 8); display.setCursor(0, display.ty + 8);
// Print remaining characters // Print until end of display and ignore remaining characters
while (strPos < strlen(myString)) { while ((strPos < strlen(myString)) && (display.tx < 122)) {
display.print(myString[strPos]); display.print(myString[strPos]);
strPos++; strPos++;
} }
@ -922,6 +934,9 @@ void print_Msg(const char myString[]) {
#ifdef enable_serial #ifdef enable_serial
Serial.print(myString); Serial.print(myString);
#endif #endif
#ifdef global_log
myLog.print(myString);
#endif
} }
void print_Msg(long unsigned int message) { void print_Msg(long unsigned int message) {
@ -934,6 +949,9 @@ void print_Msg(long unsigned int message) {
#ifdef enable_serial #ifdef enable_serial
Serial.print(message); Serial.print(message);
#endif #endif
#ifdef global_log
myLog.print(message);
#endif
} }
void print_Msg(byte message, int outputFormat) { void print_Msg(byte message, int outputFormat) {
@ -946,6 +964,9 @@ void print_Msg(byte message, int outputFormat) {
#ifdef enable_serial #ifdef enable_serial
Serial.print(message, outputFormat); Serial.print(message, outputFormat);
#endif #endif
#ifdef global_log
myLog.print(message, outputFormat);
#endif
} }
void print_Msg(String string) { void print_Msg(String string) {
@ -958,6 +979,9 @@ void print_Msg(String string) {
#ifdef enable_serial #ifdef enable_serial
Serial.print(string); Serial.print(string);
#endif #endif
#ifdef global_log
myLog.print(string);
#endif
} }
void print_Msg_PaddedHexByte(byte message) { void print_Msg_PaddedHexByte(byte message) {
@ -976,10 +1000,9 @@ void print_Msg_PaddedHex32(unsigned long message) {
print_Msg_PaddedHexByte((message >> 8) & 0xFF); print_Msg_PaddedHexByte((message >> 8) & 0xFF);
print_Msg_PaddedHexByte((message >> 0) & 0xFF); print_Msg_PaddedHexByte((message >> 0) & 0xFF);
} }
void println_Msg(String string) { void println_Msg(String string) {
#ifdef enable_LCD #ifdef enable_LCD
print_Msg(string); display.print(string);
display.setCursor(0, display.ty + 8); display.setCursor(0, display.ty + 8);
#endif #endif
#ifdef enable_OLED #ifdef enable_OLED
@ -988,11 +1011,14 @@ void println_Msg(String string) {
#ifdef enable_serial #ifdef enable_serial
Serial.println(string); Serial.println(string);
#endif #endif
#ifdef global_log
myLog.println(string);
#endif
} }
void println_Msg(byte message, int outputFormat) { void println_Msg(byte message, int outputFormat) {
#ifdef enable_LCD #ifdef enable_LCD
print_Msg(message, outputFormat); display.print(message, outputFormat);
display.setCursor(0, display.ty + 8); display.setCursor(0, display.ty + 8);
#endif #endif
#ifdef enable_OLED #ifdef enable_OLED
@ -1001,24 +1027,48 @@ void println_Msg(byte message, int outputFormat) {
#ifdef enable_serial #ifdef enable_serial
Serial.println(message, outputFormat); Serial.println(message, outputFormat);
#endif #endif
#ifdef global_log
myLog.println(message, outputFormat);
#endif
} }
void println_Msg(const char message[]) { void println_Msg(const char myString[]) {
#ifdef enable_LCD #ifdef enable_LCD
print_Msg(message); // test for word wrap
if ((display.tx + strlen(myString) * 6) > 128) {
int strPos = 0;
// Print until end of display
while (display.tx < 122) {
display.print(myString[strPos]);
strPos++;
}
// Newline
display.setCursor(0, display.ty + 8);
// Print until end of display and ignore remaining characters
while ((strPos < strlen(myString)) && (display.tx < 122)) {
display.print(myString[strPos]);
strPos++;
}
}
else {
display.print(myString);
}
display.setCursor(0, display.ty + 8); display.setCursor(0, display.ty + 8);
#endif #endif
#ifdef enable_OLED #ifdef enable_OLED
display.println(message); display.println(myString);
#endif #endif
#ifdef enable_serial #ifdef enable_serial
Serial.println(message); Serial.println(myString);
#endif
#ifdef global_log
myLog.println(myString);
#endif #endif
} }
void println_Msg(const __FlashStringHelper *string) { void println_Msg(const __FlashStringHelper *string) {
#ifdef enable_LCD #ifdef enable_LCD
print_Msg(string); display.print(string);
display.setCursor(0, display.ty + 8); display.setCursor(0, display.ty + 8);
#endif #endif
#ifdef enable_OLED #ifdef enable_OLED
@ -1027,11 +1077,14 @@ void println_Msg(const __FlashStringHelper *string) {
#ifdef enable_serial #ifdef enable_serial
Serial.println(string); Serial.println(string);
#endif #endif
#ifdef global_log
myLog.println(string);
#endif
} }
void println_Msg(long unsigned int message) { void println_Msg(long unsigned int message) {
#ifdef enable_LCD #ifdef enable_LCD
print_Msg(message); display.print(message);
display.setCursor(0, display.ty + 8); display.setCursor(0, display.ty + 8);
#endif #endif
#ifdef enable_OLED #ifdef enable_OLED
@ -1040,6 +1093,9 @@ void println_Msg(long unsigned int message) {
#ifdef enable_serial #ifdef enable_serial
Serial.println(message); Serial.println(message);
#endif #endif
#ifdef global_log
myLog.println(message);
#endif
} }
void display_Update() { void display_Update() {
@ -1052,6 +1108,9 @@ void display_Update() {
#ifdef enable_serial #ifdef enable_serial
delay(100); delay(100);
#endif #endif
#ifdef global_log
myLog.flush();
#endif
} }
void display_Clear() { void display_Clear() {
@ -1063,6 +1122,9 @@ void display_Clear() {
display.clearDisplay(); display.clearDisplay();
display.setCursor(0, 0); display.setCursor(0, 0);
#endif #endif
#ifdef global_log
myLog.println("");
#endif
} }
unsigned char question_box(const __FlashStringHelper* question, char answers[7][20], int num_answers, int default_choice) { unsigned char question_box(const __FlashStringHelper* question, char answers[7][20], int num_answers, int default_choice) {

View File

@ -299,7 +299,7 @@ void gbMenu() {
// Change working dir to root // Change working dir to root
sd.chdir("/"); sd.chdir("/");
readROM_GB(); readROM_GB();
compare_checksum_GB(); compare_checksums_GB();
break; break;
case 1: case 1:
@ -313,6 +313,7 @@ void gbMenu() {
else { else {
print_Error(F("Cart has no Sram"), false); print_Error(F("Cart has no Sram"), false);
} }
println_Msg(F(""));
break; break;
case 2: case 2:
@ -340,13 +341,13 @@ void gbMenu() {
else { else {
print_Error(F("Cart has no Sram"), false); print_Error(F("Cart has no Sram"), false);
} }
println_Msg(F(""));
break; break;
case 3: case 3:
resetArduino(); resetArduino();
break; break;
} }
println_Msg(F(""));
println_Msg(F("Press Button...")); println_Msg(F("Press Button..."));
display_Update(); display_Update();
wait(); wait();
@ -372,8 +373,8 @@ void setup_GB() {
// Set Data Pins (D0-D7) to Input // Set Data Pins (D0-D7) to Input
DDRC = 0x00; DDRC = 0x00;
// Disable Internal Pullups // Enable Internal Pullups
//PORTC = 0x00; PORTC = 0xFF;
delay(400); delay(400);
@ -385,15 +386,12 @@ void setup_GB() {
showCartInfo_GB(); showCartInfo_GB();
// MMM01 initialize // MMM01 initialize
if (romType >= 11 && romType <= 13) if (romType >= 11 && romType <= 13) {
{
dataOut();
writeByte_GB(0x3fff, 0x00); writeByte_GB(0x3fff, 0x00);
writeByte_GB(0x5fff, 0x40); writeByte_GB(0x5fff, 0x40);
writeByte_GB(0x7fff, 0x01); writeByte_GB(0x7fff, 0x01);
writeByte_GB(0x1fff, 0x3a); writeByte_GB(0x1fff, 0x3a);
writeByte_GB(0x1fff, 0x7a); writeByte_GB(0x1fff, 0x7a);
dataIn_GB();
} }
} }
@ -508,17 +506,14 @@ void showCartInfo_GB() {
/****************************************** /******************************************
Low level functions Low level functions
*****************************************/ *****************************************/
// Switch data pins to read
void dataIn_GB() {
// Set to Input
DDRC = 0x00;
// Pullups
//PORTC = 0xFF;
}
byte readByte_GB(word myAddress) { byte readByte_GB(word myAddress) {
// Set address
PORTF = myAddress & 0xFF; PORTF = myAddress & 0xFF;
PORTK = (myAddress >> 8) & 0xFF; PORTK = (myAddress >> 8) & 0xFF;
// Switch data pins to input
DDRC = 0x00;
// Enable pullups
PORTC = 0xFF;
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");
@ -539,11 +534,14 @@ byte readByte_GB(word myAddress) {
} }
void writeByte_GB(int myAddress, byte myData) { void writeByte_GB(int myAddress, byte myData) {
// Set address
PORTF = myAddress & 0xFF; PORTF = myAddress & 0xFF;
PORTK = (myAddress >> 8) & 0xFF; PORTK = (myAddress >> 8) & 0xFF;
// Set data
PORTC = myData; PORTC = myData;
// Switch data pins to output
DDRC = 0xFF;
// Arduino running at 16Mhz -> one nop = 62.5ns
// Wait till output is stable // Wait till output is stable
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");
@ -558,12 +556,21 @@ void writeByte_GB(int myAddress, byte myData) {
// Leave WR high for at least 50ns // Leave WR high for at least 50ns
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");
// Switch data pins to input
DDRC = 0x00;
// Enable pullups
PORTC = 0xFF;
} }
// Triggers CS and CLK pin // Triggers CS and CLK pin
byte readByteSRAM_GB(word myAddress) { byte readByteSRAM_GB(word myAddress) {
PORTF = myAddress & 0xFF; PORTF = myAddress & 0xFF;
PORTK = (myAddress >> 8) & 0xFF; PORTK = (myAddress >> 8) & 0xFF;
// Switch data pins to input
DDRC = 0x00;
// Enable pullups
PORTC = 0xFF;
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");
@ -594,9 +601,13 @@ byte readByteSRAM_GB(word myAddress) {
// Triggers CS and CLK pin // Triggers CS and CLK pin
void writeByteSRAM_GB(int myAddress, byte myData) { void writeByteSRAM_GB(int myAddress, byte myData) {
// Set address
PORTF = myAddress & 0xFF; PORTF = myAddress & 0xFF;
PORTK = (myAddress >> 8) & 0xFF; PORTK = (myAddress >> 8) & 0xFF;
// Set data
PORTC = myData; PORTC = myData;
// Switch data pins to output
DDRC = 0xFF;
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");
@ -635,6 +646,11 @@ void writeByteSRAM_GB(int myAddress, byte myData) {
// Leave WR high for at least 50ns // Leave WR high for at least 50ns
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");
// Switch data pins to input
DDRC = 0x00;
// Enable pullups
PORTC = 0xFF;
} }
/****************************************** /******************************************
@ -642,9 +658,63 @@ void writeByteSRAM_GB(int myAddress, byte myData) {
*****************************************/ *****************************************/
// Read Cartridge Header // Read Cartridge Header
void getCartInfo_GB() { void getCartInfo_GB() {
romType = readByte_GB(0x0147); // Read Header into array
romSize = readByte_GB(0x0148); for (int currByte = 0x100; currByte < 0x150; currByte++) {
sramSize = readByte_GB(0x0149); sdBuffer[currByte] = readByte_GB(currByte);
}
/* Compare Nintendo logo against known checksum, 156 bytes starting at 0x04
word logoChecksum = 0;
for (int currByte = 0x104; currByte < 0x134; currByte++) {
logoChecksum += sdBuffer[currByte];
}
if (logoChecksum != 0x1546) {
print_Error(F("STARTUP LOGO ERROR"), false);
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();
}
*/
// Calculate header checksum
byte headerChecksum = 0;
for (int currByte = 0x134; currByte < 0x14D; currByte++) {
headerChecksum = headerChecksum - sdBuffer[currByte] - 1;
}
if (headerChecksum != sdBuffer[0x14D]) {
// Read Header into array a second time
for (int currByte = 0x100; currByte < 0x150; currByte++) {
sdBuffer[currByte] = readByte_GB(currByte);
}
// Calculate header checksum a second time
headerChecksum = 0;
for (int currByte = 0x134; currByte < 0x14D; currByte++) {
headerChecksum = headerChecksum - sdBuffer[currByte] - 1;
}
}
if (headerChecksum != sdBuffer[0x14D]) {
print_Error(F("HEADER CHECKSUM ERROR"), false);
println_Msg(F(""));
println_Msg(F(""));
println_Msg(F(""));
println_Msg(F("Press Button to"));
println_Msg(F("ignore or clean"));
println_Msg(F("cart and try again"));
display_Update();
wait();
}
romType = sdBuffer[0x147];
romSize = sdBuffer[0x148];
sramSize = sdBuffer[0x149];
// ROM banks // ROM banks
switch (romSize) { switch (romSize) {
@ -710,18 +780,16 @@ void getCartInfo_GB() {
} }
// Get Checksum as string // Get Checksum as string
eepbit[6] = readByte_GB(0x014E); eepbit[6] = sdBuffer[0x14E];
eepbit[7] = readByte_GB(0x014F); eepbit[7] = sdBuffer[0x14F];
sprintf(checksumStr, "%02X%02X", eepbit[6], eepbit[7]); sprintf(checksumStr, "%02X%02X", eepbit[6], eepbit[7]);
// Get name // Get name
byte myByte = 0;
byte myLength = 0; byte myLength = 0;
for (int addr = 0x0134; addr <= 0x13C; addr++) { for (int addr = 0x0134; addr <= 0x13C; addr++) {
myByte = readByte_GB(addr); if (((char(sdBuffer[addr]) >= 48 && char(sdBuffer[addr]) <= 57) || (char(sdBuffer[addr]) >= 65 && char(sdBuffer[addr]) <= 122)) && myLength < 15) {
if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 15) { romName[myLength] = char(sdBuffer[addr]);
romName[myLength] = char(myByte);
myLength++; myLength++;
} }
} }
@ -765,9 +833,6 @@ void readROM_GB() {
draw_progressbar(0, totalProgressBar); draw_progressbar(0, totalProgressBar);
for (word currBank = 1; currBank < romBanks; currBank++) { for (word currBank = 1; currBank < romBanks; currBank++) {
// Switch data pins to output
dataOut();
// Second bank starts at 0x4000 // Second bank starts at 0x4000
if (currBank > 1) { if (currBank > 1) {
romAddress = 0x4000; romAddress = 0x4000;
@ -808,9 +873,6 @@ void readROM_GB() {
writeByte_GB(0x2000, currBank & 0x1F); writeByte_GB(0x2000, currBank & 0x1F);
} }
// Switch data pins to intput
dataIn_GB();
// Read banks and save to SD // Read banks and save to SD
while (romAddress <= 0x7FFF) { while (romAddress <= 0x7FFF) {
for (int i = 0; i < 512; i++) { for (int i = 0; i < 512; i++) {
@ -834,9 +896,6 @@ unsigned int calc_checksum_GB (char* fileName, char* folder) {
unsigned long i = 0; unsigned long i = 0;
int c = 0; int c = 0;
if (strcmp(folder, "root") != 0)
sd.chdir(folder);
// If file exists // If file exists
if (myFile.open(fileName, O_READ)) { if (myFile.open(fileName, O_READ)) {
//calcFilesize = myFile.fileSize() * 8 / 1024 / 1024; // unused //calcFilesize = myFile.fileSize() * 8 / 1024 / 1024; // unused
@ -847,7 +906,6 @@ unsigned int calc_checksum_GB (char* fileName, char* folder) {
} }
} }
myFile.close(); myFile.close();
sd.chdir();
// Subtract checksum bytes // Subtract checksum bytes
calcChecksum -= eepbit[6]; calcChecksum -= eepbit[6];
calcChecksum -= eepbit[7]; calcChecksum -= eepbit[7];
@ -863,8 +921,8 @@ unsigned int calc_checksum_GB (char* fileName, char* folder) {
} }
// Compare checksum // Compare checksum
boolean compare_checksum_GB() { void compare_checksums_GB() {
println_Msg(F("Calculating Checksum")); println_Msg(F("Calculating Checksum..."));
display_Update(); display_Update();
strcpy(fileName, romName); strcpy(fileName, romName);
@ -874,21 +932,105 @@ boolean compare_checksum_GB() {
EEPROM_readAnything(0, foldern); EEPROM_readAnything(0, foldern);
sprintf(folder, "GB/ROM/%s/%d", romName, foldern - 1); sprintf(folder, "GB/ROM/%s/%d", romName, foldern - 1);
if (strcmp(folder, "root") != 0)
sd.chdir(folder);
// Internal ROM checksum
char calcsumStr[5]; char calcsumStr[5];
sprintf(calcsumStr, "%04X", calc_checksum_GB(fileName, folder)); sprintf(calcsumStr, "%04X", calc_checksum_GB(fileName, folder));
if (strcmp(calcsumStr, checksumStr) == 0) { if (strcmp(calcsumStr, checksumStr) == 0) {
print_Msg(F("Result: ")); print_Msg(F("Internal: "));
println_Msg(calcsumStr); print_Msg(calcsumStr);
println_Msg(F("Checksum matches")); println_Msg(" -> OK");
display_Update();
return 1;
} }
else { else {
print_Msg(F("Result: ")); print_Msg(F("Internal: "));
println_Msg(calcsumStr); println_Msg(calcsumStr);
print_Error(F("Checksum Error"), false); print_Error(F("Checksum Error"), false);
return 0; }
#ifdef no-intro
//CRC32
char crcStr[9];
sprintf(crcStr, "%08lX", crcGB(fileName, folder));
// Print checksum
print_Msg("CRC32: ");
print_Msg(crcStr);
//Search for CRC32 in file
char gamename[50];
char crc_search[9];
//go to root
sd.chdir();
if (myFile.open("gb.txt", O_READ)) {
//Search for same CRC in list
while (myFile.available()) {
//Read 2 lines (game name and CRC)
get_line(gamename, &myFile, 46);
get_line(crc_search, &myFile, 9);
skip_line(&myFile); //Skip every 3rd line
//if checksum search successful, rename the file and end search
if (strcmp(crc_search, crcStr) == 0)
{
// Close the file:
myFile.close();
print_Msg(" -> ");
println_Msg(gamename);
// Rename file to no-intro
sd.chdir(folder);
if (myFile.open(fileName, O_READ)) {
myFile.rename(gamename);
// Close the file:
myFile.close();
}
break;
}
}
if (strcmp(crc_search, crcStr) != 0)
{
println_Msg(" -> Not found");
}
}
else {
println_Msg("gb.txt not found");
}
#else
println_Msg("");
#endif
display_Update();
//go to root
sd.chdir();
}
inline uint32_t updateCRC_GB(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);
}
// Calculate rom's CRC32 from SD
uint32_t crcGB(char* fileName, char* folder) {
if (myFile.open(fileName, O_READ)) {
uint32_t oldcrc32 = 0xFFFFFFFF;
for (unsigned long currByte = 0; currByte < (myFile.fileSize() / 512); currByte++) {
myFile.read(sdBuffer, 512);
for (int c = 0; c < 512; c++) {
oldcrc32 = updateCRC_GB(sdBuffer[c], oldcrc32);
}
}
// Close the file:
myFile.close();
return ~oldcrc32;
}
else {
print_Error(F("File not found"), true);
} }
} }
@ -919,12 +1061,9 @@ void readSRAM_GB() {
print_Error(F("SD Error"), true); print_Error(F("SD Error"), true);
} }
dataIn_GB();
// MBC2 Fix // MBC2 Fix
readByte_GB(0x0134); readByte_GB(0x0134);
dataOut();
if (romType <= 4 || (romType >= 11 && romType <= 13)) { if (romType <= 4 || (romType >= 11 && romType <= 13)) {
writeByte_GB(0x6000, 1); writeByte_GB(0x6000, 1);
} }
@ -934,11 +1073,9 @@ void readSRAM_GB() {
// Switch SRAM banks // Switch SRAM banks
for (byte currBank = 0; currBank < sramBanks; currBank++) { for (byte currBank = 0; currBank < sramBanks; currBank++) {
dataOut();
writeByte_GB(0x4000, currBank); writeByte_GB(0x4000, currBank);
// Read SRAM // Read SRAM
dataIn_GB();
for (word sramAddress = 0xA000; sramAddress <= lastByte; sramAddress += 64) { for (word sramAddress = 0xA000; sramAddress <= lastByte; sramAddress += 64) {
for (byte i = 0; i < 64; i++) { for (byte i = 0; i < 64; i++) {
sdBuffer[i] = readByteSRAM_GB(sramAddress + i); sdBuffer[i] = readByteSRAM_GB(sramAddress + i);
@ -948,9 +1085,7 @@ void readSRAM_GB() {
} }
// Disable SRAM // Disable SRAM
dataOut();
writeByte_GB(0x0000, 0x00); writeByte_GB(0x0000, 0x00);
dataIn_GB();
// Close the file: // Close the file:
myFile.close(); myFile.close();
@ -975,14 +1110,9 @@ void writeSRAM_GB() {
//open file on sd card //open file on sd card
if (myFile.open(filePath, O_READ)) { if (myFile.open(filePath, O_READ)) {
// Set pins to input
dataIn_GB();
// MBC2 Fix // MBC2 Fix
readByte_GB(0x0134); readByte_GB(0x0134);
dataOut();
// Enable SRAM for MBC1 // Enable SRAM for MBC1
if (romType <= 4 || (romType >= 11 && romType <= 13)) { if (romType <= 4 || (romType >= 11 && romType <= 13)) {
writeByte_GB(0x6000, 1); writeByte_GB(0x6000, 1);
@ -1003,9 +1133,6 @@ void writeSRAM_GB() {
// Disable SRAM // Disable SRAM
writeByte_GB(0x0000, 0x00); writeByte_GB(0x0000, 0x00);
// Set pins to input
dataIn_GB();
// Close the file: // Close the file:
myFile.close(); myFile.close();
display_Clear(); display_Clear();
@ -1031,14 +1158,11 @@ unsigned long verifySRAM_GB() {
// Variable for errors // Variable for errors
writeErrors = 0; writeErrors = 0;
dataIn_GB();
// MBC2 Fix // MBC2 Fix
readByte_GB(0x0134); readByte_GB(0x0134);
// Check SRAM size // Check SRAM size
if (lastByte > 0) { if (lastByte > 0) {
dataOut();
if (romType <= 4) { // MBC1 if (romType <= 4) { // MBC1
writeByte_GB(0x6000, 1); // Set RAM Mode writeByte_GB(0x6000, 1); // Set RAM Mode
} }
@ -1048,11 +1172,9 @@ unsigned long verifySRAM_GB() {
// Switch SRAM banks // Switch SRAM banks
for (byte currBank = 0; currBank < sramBanks; currBank++) { for (byte currBank = 0; currBank < sramBanks; currBank++) {
dataOut();
writeByte_GB(0x4000, currBank); writeByte_GB(0x4000, currBank);
// Read SRAM // Read SRAM
dataIn_GB();
for (word sramAddress = 0xA000; sramAddress <= lastByte; sramAddress += 64) { for (word sramAddress = 0xA000; sramAddress <= lastByte; sramAddress += 64) {
//fill sdBuffer //fill sdBuffer
myFile.read(sdBuffer, 64); myFile.read(sdBuffer, 64);
@ -1063,10 +1185,8 @@ unsigned long verifySRAM_GB() {
} }
} }
} }
dataOut();
// Disable RAM // Disable RAM
writeByte_GB(0x0000, 0x00); writeByte_GB(0x0000, 0x00);
dataIn_GB();
} }
// Close the file: // Close the file:
myFile.close(); myFile.close();
@ -1132,9 +1252,6 @@ void writeFlash29F_GB(byte MBC, boolean flashErase) {
romBanks = 2; romBanks = 2;
} }
// Set data pins to output
dataOut();
// Set ROM bank hi 0 // Set ROM bank hi 0
writeByte_GB(0x3000, 0); writeByte_GB(0x3000, 0);
// Set ROM bank low 0 // Set ROM bank low 0
@ -1150,8 +1267,6 @@ void writeFlash29F_GB(byte MBC, boolean flashErase) {
writeByte_GB(0x2aa, 0x55); writeByte_GB(0x2aa, 0x55);
writeByte_GB(0x555, 0x90); writeByte_GB(0x555, 0x90);
dataIn_GB();
// Read the two id bytes into a string // Read the two id bytes into a string
sprintf(flashid, "%02X%02X", readByte_GB(0), readByte_GB(1)); sprintf(flashid, "%02X%02X", readByte_GB(0), readByte_GB(1));
@ -1196,7 +1311,6 @@ void writeFlash29F_GB(byte MBC, boolean flashErase) {
display_Update(); display_Update();
print_Error(F("Unknown flashrom"), true); print_Error(F("Unknown flashrom"), true);
} }
dataOut();
// Reset flash // Reset flash
writeByte_GB(0x555, 0xf0); writeByte_GB(0x555, 0xf0);
@ -1215,8 +1329,6 @@ void writeFlash29F_GB(byte MBC, boolean flashErase) {
writeByte_GB(0x2aa, 0x55); writeByte_GB(0x2aa, 0x55);
writeByte_GB(0x555, 0x10); writeByte_GB(0x555, 0x10);
// Set data pins to input
dataIn_GB();
// Read the status register // Read the status register
byte statusReg = readByte_GB(0); byte statusReg = readByte_GB(0);
// After a completed erase D7 will output 1 // After a completed erase D7 will output 1
@ -1234,11 +1346,8 @@ void writeFlash29F_GB(byte MBC, boolean flashErase) {
// Blink led // Blink led
blinkLED(); blinkLED();
dataOut();
// Set ROM bank // Set ROM bank
writeByte_GB(0x2000, currBank); writeByte_GB(0x2000, currBank);
dataIn_GB();
for (unsigned int currAddr = 0x4000; currAddr < 0x7FFF; currAddr += 512) { for (unsigned int currAddr = 0x4000; currAddr < 0x7FFF; currAddr += 512) {
for (int currByte = 0; currByte < 512; currByte++) { for (int currByte = 0; currByte < 512; currByte++) {
@ -1259,8 +1368,6 @@ void writeFlash29F_GB(byte MBC, boolean flashErase) {
display_Update(); display_Update();
// Write flash // Write flash
dataOut();
word currAddr = 0; word currAddr = 0;
word endAddr = 0x3FFF; word endAddr = 0x3FFF;
@ -1292,9 +1399,6 @@ void writeFlash29F_GB(byte MBC, boolean flashErase) {
// Write current byte // Write current byte
writeByte_GB(currAddr + currByte, sdBuffer[currByte]); writeByte_GB(currAddr + currByte, sdBuffer[currByte]);
// Set data pins to input
dataIn_GB();
// Set OE/RD(PH6) LOW // Set OE/RD(PH6) LOW
PORTH &= ~(1 << 6); PORTH &= ~(1 << 6);
@ -1304,9 +1408,6 @@ void writeFlash29F_GB(byte MBC, boolean flashErase) {
// Switch OE/RD(PH6) to HIGH // Switch OE/RD(PH6) to HIGH
PORTH |= (1 << 6); PORTH |= (1 << 6);
// Set data pins to output
dataOut();
} }
currAddr += 512; currAddr += 512;
processedProgressBar += 512; processedProgressBar += 512;
@ -1320,8 +1421,6 @@ void writeFlash29F_GB(byte MBC, boolean flashErase) {
display_Update(); display_Update();
// Write flash // Write flash
dataOut();
//Initialize progress bar //Initialize progress bar
uint32_t processedProgressBar = 0; uint32_t processedProgressBar = 0;
uint32_t totalProgressBar = (uint32_t)(romBanks) * 16384; uint32_t totalProgressBar = (uint32_t)(romBanks) * 16384;
@ -1347,9 +1446,6 @@ void writeFlash29F_GB(byte MBC, boolean flashErase) {
// Write current byte // Write current byte
writeByte_GB(currAddr + currByte, sdBuffer[currByte]); writeByte_GB(currAddr + currByte, sdBuffer[currByte]);
// Set data pins to input
dataIn_GB();
// Set OE/RD(PH6) LOW // Set OE/RD(PH6) LOW
PORTH &= ~(1 << 6); PORTH &= ~(1 << 6);
@ -1359,9 +1455,6 @@ void writeFlash29F_GB(byte MBC, boolean flashErase) {
// Switch OE/RD(PH6) to HIGH // Switch OE/RD(PH6) to HIGH
PORTH |= (1 << 6); PORTH |= (1 << 6);
// Set data pins to output
dataOut();
} }
processedProgressBar += 512; processedProgressBar += 512;
draw_progressbar(processedProgressBar, totalProgressBar); draw_progressbar(processedProgressBar, totalProgressBar);
@ -1369,9 +1462,6 @@ void writeFlash29F_GB(byte MBC, boolean flashErase) {
} }
} }
// Set data pins to input again
dataIn_GB();
print_Msg(F("Verifying...")); print_Msg(F("Verifying..."));
display_Update(); display_Update();
@ -1385,9 +1475,6 @@ void writeFlash29F_GB(byte MBC, boolean flashErase) {
// Read number of banks and switch banks // Read number of banks and switch banks
for (word bank = 1; bank < romBanks; bank++) { for (word bank = 1; bank < romBanks; bank++) {
// Switch data pins to output
dataOut();
if (romType >= 5) { // MBC2 and above if (romType >= 5) { // MBC2 and above
writeByte_GB(0x2100, bank); // Set ROM bank writeByte_GB(0x2100, bank); // Set ROM bank
} }
@ -1397,9 +1484,6 @@ void writeFlash29F_GB(byte MBC, boolean flashErase) {
writeByte_GB(0x2000, bank & 0x1F); // Set bits 0 & 4 (00011111) 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) { if (bank > 1) {
romAddress = 0x4000; romAddress = 0x4000;
} }
@ -1496,14 +1580,12 @@ void startCFIMode(boolean x16Mode) {
void identifyCFI_GB() { void identifyCFI_GB() {
// Reset flash // Reset flash
display_Clear(); display_Clear();
dataOut();
writeByte_GB(0x6000, 0); // Set ROM Mode writeByte_GB(0x6000, 0); // Set ROM Mode
writeByte_GB(0x2000, 0); // Set Bank to 0 writeByte_GB(0x2000, 0); // Set Bank to 0
writeByte_GB(0x3000, 0); writeByte_GB(0x3000, 0);
startCFIMode(false); // Trying x8 mode first startCFIMode(false); // Trying x8 mode first
dataIn_GB();
display_Clear(); display_Clear();
// Try x8 mode first // Try x8 mode first
char cfiQRYx8[7]; char cfiQRYx8[7];
@ -1544,9 +1626,7 @@ void identifyCFI_GB() {
return; return;
} }
} }
dataIn_GB();
flashBanks = 1 << (readByteCompensated(0x4E) - 14); // - flashX16Mode); flashBanks = 1 << (readByteCompensated(0x4E) - 14); // - flashX16Mode);
dataOut();
// Reset flash // Reset flash
writeByteCompensated(0xAAA, 0xf0); writeByteCompensated(0xAAA, 0xf0);
@ -1621,9 +1701,6 @@ bool writeCFI_GB() {
resetArduino(); resetArduino();
} }
// Set data pins to output
dataOut();
// Set ROM bank hi 0 // Set ROM bank hi 0
writeByte_GB(0x3000, 0); writeByte_GB(0x3000, 0);
// Set ROM bank low 0 // Set ROM bank low 0
@ -1633,7 +1710,6 @@ bool writeCFI_GB() {
// Reset flash // Reset flash
writeByteCompensated(0xAAA, 0xf0); writeByteCompensated(0xAAA, 0xf0);
delay(100); delay(100);
dataOut();
// Reset flash // Reset flash
writeByte_GB(0x555, 0xf0); writeByte_GB(0x555, 0xf0);
@ -1650,8 +1726,6 @@ bool writeCFI_GB() {
writeByteCompensated(0x555, 0x55); writeByteCompensated(0x555, 0x55);
writeByteCompensated(0xAAA, 0x10); writeByteCompensated(0xAAA, 0x10);
dataIn_GB();
// Read the status register // Read the status register
byte statusReg = readByte_GB(0); byte statusReg = readByte_GB(0);
@ -1673,11 +1747,8 @@ bool writeCFI_GB() {
// Blink led // Blink led
blinkLED(); blinkLED();
dataOut();
// Set ROM bank // Set ROM bank
writeByte_GB(0x2000, currBank); writeByte_GB(0x2000, currBank);
dataIn_GB();
for (unsigned int currAddr = 0x4000; currAddr < 0x7FFF; currAddr += 512) { for (unsigned int currAddr = 0x4000; currAddr < 0x7FFF; currAddr += 512) {
for (int currByte = 0; currByte < 512; currByte++) { for (int currByte = 0; currByte < 512; currByte++) {
@ -1696,8 +1767,6 @@ bool writeCFI_GB() {
display_Update(); display_Update();
// Write flash // Write flash
dataOut();
word currAddr = 0; word currAddr = 0;
word endAddr = 0x3FFF; word endAddr = 0x3FFF;
@ -1727,9 +1796,6 @@ bool writeCFI_GB() {
// Write current byte // Write current byte
writeByte_GB(currAddr + currByte, sdBuffer[currByte]); writeByte_GB(currAddr + currByte, sdBuffer[currByte]);
// Set data pins to input
dataIn_GB();
// Setting CS(PH3) and OE/RD(PH6) LOW // Setting CS(PH3) and OE/RD(PH6) LOW
PORTH &= ~((1 << 3) | (1 << 6)); PORTH &= ~((1 << 3) | (1 << 6));
@ -1753,16 +1819,11 @@ bool writeCFI_GB() {
PORTH |= (1 << 3) | (1 << 6); PORTH |= (1 << 3) | (1 << 6);
__asm__("nop\n\tnop\n\tnop\n\t"); // Waste a few CPU cycles to remove write errors __asm__("nop\n\tnop\n\tnop\n\t"); // Waste a few CPU cycles to remove write errors
// Set data pins to output
dataOut();
} }
currAddr += 512; currAddr += 512;
} }
} }
// Set data pins to input again
dataIn_GB();
display_Clear(); display_Clear();
println_Msg(F("Verifying")); println_Msg(F("Verifying"));
display_Update(); display_Update();
@ -1777,9 +1838,6 @@ bool writeCFI_GB() {
// Read number of banks and switch banks // Read number of banks and switch banks
for (word bank = 1; bank < romBanks; bank++) { for (word bank = 1; bank < romBanks; bank++) {
// Switch data pins to output
dataOut();
if (romType >= 5) { // MBC2 and above if (romType >= 5) { // MBC2 and above
writeByte_GB(0x2100, bank); // Set ROM bank writeByte_GB(0x2100, bank); // Set ROM bank
} }
@ -1789,9 +1847,6 @@ bool writeCFI_GB() {
writeByte_GB(0x2000, bank & 0x1F); // Set bits 0 & 4 (00011111) 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) { if (bank > 1) {
romAddress = 0x4000; romAddress = 0x4000;
} }

View File

@ -62,6 +62,36 @@ boolean hasMenu;
byte numGames; byte numGames;
#endif #endif
// Compare checksum
boolean compare_checksum_GBS() {
println_Msg(F("Calculating Checksum"));
display_Update();
strcpy(fileName, romName);
strcat(fileName, ".GB");
// last used rom folder
EEPROM_readAnything(0, 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;
}
}
byte readByte_GBS(word myAddress) { byte readByte_GBS(word myAddress) {
PORTF = myAddress & 0xFF; PORTF = myAddress & 0xFF;
PORTK = (myAddress >> 8) & 0xFF; PORTK = (myAddress >> 8) & 0xFF;
@ -166,7 +196,7 @@ void gbSmartGameOptions()
display_Clear(); display_Clear();
sd.chdir("/"); sd.chdir("/");
readROM_GB(); readROM_GB();
compare_checksum_GB(); compare_checksum_GBS();
break; break;
} }
case 1: // Read SRAM case 1: // Read SRAM
@ -329,7 +359,7 @@ void gbSmartGetGames()
// check if contain menu // check if contain menu
hasMenu = true; hasMenu = true;
dataIn_GB(); dataIn();
for (i = 0; i < 5; i++) for (i = 0; i < 5; i++)
{ {
if (readByte_GBS(0x0134 + i) != menu_title[i]) if (readByte_GBS(0x0134 + i) != menu_title[i])
@ -349,7 +379,7 @@ void gbSmartGetGames()
dataOut(); dataOut();
writeByte_GB(0x2100, i); writeByte_GB(0x2100, i);
dataIn_GB(); dataIn();
// read signature // read signature
for (uint8_t j = 0x00; j < 0x30; j++) for (uint8_t j = 0x00; j < 0x30; j++)
{ {
@ -385,7 +415,7 @@ gb_smart_get_game_loop_end:;
} }
else else
{ {
dataIn_GB(); dataIn();
for (uint8_t j = 0; j < 15; j++) for (uint8_t j = 0; j < 15; j++)
{ {
myByte = readByte_GBS(0x0134 + j); myByte = readByte_GBS(0x0134 + j);
@ -424,7 +454,7 @@ void gbSmartReadFlash()
gbSmartRemapStartBank(0x00, gbSmartRomSizeGB, gbSmartSramSizeGB); gbSmartRemapStartBank(0x00, gbSmartRomSizeGB, gbSmartSramSizeGB);
// dump fixed bank 0x00 // dump fixed bank 0x00
dataIn_GB(); dataIn();
for (uint16_t addr = 0x0000; addr <= 0x3fff; addr += 512) for (uint16_t addr = 0x0000; addr <= 0x3fff; addr += 512)
{ {
for (uint16_t c = 0; c < 512; c++) for (uint16_t c = 0; c < 512; c++)
@ -439,7 +469,7 @@ void gbSmartReadFlash()
dataOut(); dataOut();
writeByte_GB(0x2100, bank); writeByte_GB(0x2100, bank);
dataIn_GB(); dataIn();
for (uint16_t addr = 0x4000; addr <= 0x7fff; addr += 512) for (uint16_t addr = 0x4000; addr <= 0x7fff; addr += 512)
{ {
for (uint16_t c = 0; c < 512; c++) for (uint16_t c = 0; c < 512; c++)
@ -558,7 +588,7 @@ void gbSmartWriteFlashFromMyFile(uint32_t addr)
gbSmartWriteFlashByte(addr + i, 0x00); // BCH should be 0x00 gbSmartWriteFlashByte(addr + i, 0x00); // BCH should be 0x00
// waiting for finishing // waiting for finishing
dataIn_GB(); dataIn();
while ((readByte_GBS(addr + i) & 0x80) == 0x00); while ((readByte_GBS(addr + i) & 0x80) == 0x00);
} }
@ -581,7 +611,7 @@ uint32_t gbSmartVerifyFlash()
gbSmartRemapStartBank(0x00, gbSmartRomSizeGB, gbSmartSramSizeGB); gbSmartRemapStartBank(0x00, gbSmartRomSizeGB, gbSmartSramSizeGB);
// verify bank 0x00 // verify bank 0x00
dataIn_GB(); dataIn();
for (uint16_t addr = 0x0000; addr <= 0x3fff; addr += 512) for (uint16_t addr = 0x0000; addr <= 0x3fff; addr += 512)
{ {
myFile.read(sdBuffer, 512); myFile.read(sdBuffer, 512);
@ -599,7 +629,7 @@ uint32_t gbSmartVerifyFlash()
dataOut(); dataOut();
writeByte_GB(0x2100, bank); writeByte_GB(0x2100, bank);
dataIn_GB(); dataIn();
for (uint16_t addr = 0x4000; addr <= 0x7fff; addr += 512) for (uint16_t addr = 0x4000; addr <= 0x7fff; addr += 512)
{ {
myFile.read(sdBuffer, 512); myFile.read(sdBuffer, 512);
@ -626,7 +656,7 @@ byte gbSmartBlankCheckingFlash(uint8_t flash_start_bank)
gbSmartRemapStartBank(flash_start_bank, gbSmartFlashSizeGB, gbSmartSramSizeGB); gbSmartRemapStartBank(flash_start_bank, gbSmartFlashSizeGB, gbSmartSramSizeGB);
// check first bank // check first bank
dataIn_GB(); dataIn();
for (uint16_t addr = 0x0000; addr <= 0x3fff; addr++) for (uint16_t addr = 0x0000; addr <= 0x3fff; addr++)
{ {
if (readByte_GBS(addr) != 0xff) if (readByte_GBS(addr) != 0xff)
@ -639,7 +669,7 @@ byte gbSmartBlankCheckingFlash(uint8_t flash_start_bank)
dataOut(); dataOut();
writeByte_GB(0x2100, bank); writeByte_GB(0x2100, bank);
dataIn_GB(); dataIn();
for (uint16_t addr = 0x4000; addr <= 0x7fff; addr++) for (uint16_t addr = 0x4000; addr <= 0x7fff; addr++)
{ {
if (readByte_GBS(addr) != 0xff) if (readByte_GBS(addr) != 0xff)
@ -667,7 +697,7 @@ void gbSmartEraseFlash(uint8_t flash_start_bank)
gbSmartWriteFlashByte(0x0000, 0x20); gbSmartWriteFlashByte(0x0000, 0x20);
gbSmartWriteFlashByte(0x0000, 0xd0); gbSmartWriteFlashByte(0x0000, 0xd0);
dataIn_GB(); dataIn();
while ((readByte_GBS(0x0000) & 0x80) == 0x00); while ((readByte_GBS(0x0000) & 0x80) == 0x00);
// blink LED // blink LED
@ -682,7 +712,7 @@ void gbSmartEraseFlash(uint8_t flash_start_bank)
gbSmartWriteFlashByte(0x4000, 0x20); gbSmartWriteFlashByte(0x4000, 0x20);
gbSmartWriteFlashByte(0x4000, 0xd0); gbSmartWriteFlashByte(0x4000, 0xd0);
dataIn_GB(); dataIn();
while ((readByte_GBS(0x4000) & 0x80) == 0x00); while ((readByte_GBS(0x4000) & 0x80) == 0x00);
// blink LED // blink LED
@ -730,7 +760,7 @@ void gbSmartRemapStartBank(uint8_t rom_start_bank, uint8_t rom_size, uint8_t sra
// start set new base bank // start set new base bank
writeByte_GB(0x1000, 0xa5); writeByte_GB(0x1000, 0xa5);
dataIn_GB(); dataIn();
rom_start_bank = gbSmartGetResizeParam(rom_size, sram_size); rom_start_bank = gbSmartGetResizeParam(rom_size, sram_size);
dataOut(); dataOut();
@ -740,7 +770,7 @@ void gbSmartRemapStartBank(uint8_t rom_start_bank, uint8_t rom_size, uint8_t sra
writeByte_GB(0x2100, 0x01); writeByte_GB(0x2100, 0x01);
} }
dataIn_GB(); dataIn();
} }
// Get magic number for 0x7000 register. // Get magic number for 0x7000 register.

View File

@ -62,13 +62,21 @@
// Use calibration data from snes_clk.txt // Use calibration data from snes_clk.txt
// #define clockgen_calibration // #define clockgen_calibration
// Write all info to log.txt in root dir
//#define global_log
// Use Adafruit Clock Generator
// #define clockgen_installed
//******************************************
// GB OPTIONS
//******************************************
// Renames ROM if found in database (slow)
//#define no-intro
//****************************************** //******************************************
// N64 OPTIONS // N64 OPTIONS
//****************************************** //******************************************
// Read N64 Eeprom with Adadruit clockgen, CLK1 switch needs to be switch to ON
// add // and disable CLK1 switch if you don't have the clockgen installed or if you want to read a repros save
// #define clockgen_installed
// The CRC for N64 Roms will be calculated during dumping from memory instead of after dumping from SD card, not compatible to all Cart Readers // The CRC for N64 Roms will be calculated during dumping from memory instead of after dumping from SD card, not compatible to all Cart Readers
// #define fastcrc // #define fastcrc

10389
sd/gb.txt Normal file

File diff suppressed because it is too large Load Diff