cartreader/Cart_Reader/GBA.ino
sanni 1562ed1ff8 V28A: Rewrite GBA repros with 16MB MX29GL128E flashrom (the ones with switched data pins)
The whole GBA code is currently a little bit buggy, so for dumping roms you propbably should stay on the last somewhat stable release V27D. 
Reflashing only seems to work with 5V. Probably out of the same reason why some real carts don't work with V28 ... bad read/write timings.

I also might have broken support for the Intel flashrom I added in the last commit...
2017-09-08 01:09:42 +02:00

2453 lines
62 KiB
C++

//******************************************
// GAME BOY ADVANCE
//******************************************
/******************************************
Variables
*****************************************/
char calcChecksumStr[5];
boolean readType;
static const int nintendoLogo[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x24, 0xFF, 0xAE, 0x51, 0x69, 0x9A, 0xA2, 0x21, 0x3D, 0x84, 0x82, 0x0A,
0x84, 0xE4, 0x09, 0xAD, 0x11, 0x24, 0x8B, 0x98, 0xC0, 0x81, 0x7F, 0x21, 0xA3, 0x52, 0xBE, 0x19,
0x93, 0x09, 0xCE, 0x20, 0x10, 0x46, 0x4A, 0x4A, 0xF8, 0x27, 0x31, 0xEC, 0x58, 0xC7, 0xE8, 0x33,
0x82, 0xE3, 0xCE, 0xBF, 0x85, 0xF4, 0xDF, 0x94, 0xCE, 0x4B, 0x09, 0xC1, 0x94, 0x56, 0x8A, 0xC0,
0x13, 0x72, 0xA7, 0xFC, 0x9F, 0x84, 0x4D, 0x73, 0xA3, 0xCA, 0x9A, 0x61, 0x58, 0x97, 0xA3, 0x27,
0xFC, 0x03, 0x98, 0x76, 0x23, 0x1D, 0xC7, 0x61, 0x03, 0x04, 0xAE, 0x56, 0xBF, 0x38, 0x84, 0x00,
0x40, 0xA7, 0x0E, 0xFD, 0xFF, 0x52, 0xFE, 0x03, 0x6F, 0x95, 0x30, 0xF1, 0x97, 0xFB, 0xC0, 0x85,
0x60, 0xD6, 0x80, 0x25, 0xA9, 0x63, 0xBE, 0x03, 0x01, 0x4E, 0x38, 0xE2, 0xF9, 0xA2, 0x34, 0xFF,
0xBB, 0x3E, 0x03, 0x44, 0x78, 0x00, 0x90, 0xCB, 0x88, 0x11, 0x3A, 0x94, 0x65, 0xC0, 0x7C, 0x63,
0x87, 0xF0, 0x3C, 0xAF, 0xD6, 0x25, 0xE4, 0x8B, 0x38, 0x0A, 0xAC, 0x72, 0x21, 0xD4, 0xF8, 0x07
};
/******************************************
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
*****************************************/
// Test known Nintendo header for errors and sets read method to either sequential or random access
int testHeader() {
// 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, 156 bytes starting at 0x04
int errors = 0;
for (int currByte = 0x4; currByte < 0xA0; currByte++) {
if (pgm_read_byte(&nintendoLogo[currByte]) != sdBuffer[currByte]) {
errors++;
}
}
return errors;
}
// Read info out of rom header
void getCartInfo_GBA() {
// Test rom header for errors
int logoErrors = testHeader();
if (logoErrors != 0) {
print_Error(F("CARTRIDGE ERROR"), false);
cartSize = 32;
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) {
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) {
cartSize = 0x2000000;
}
else {
// Send swapped MX29GL128E 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
if (strcmp(flashid, "227E") == 0) {
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 32Mbit 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 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 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, "227E") == 0)) {
print_Msg(F("ID: "));
print_Msg(flashid);
print_Msg(F(" Size: "));
print_Msg(cartSize / 0x100000);
println_Msg(F("MB"));
println_Msg("");
println_Msg(F("This will erase your"));
println_Msg(F("Repro Cartridge."));
println_Msg("");
println_Msg(F("Press Button"));
println_Msg(F("to continue"));
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();
}
else if (strcmp(flashid, "227E") == 0) {
//if (sectorCheckMX29GL128E_GBA()) {
//print_Error(F("Sector Protected"), true);
//}
//else {
println_Msg(F("Erasing..."));
display_Update();
sectorEraseMX29GL128E_GBA();
//}
}
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) {
writeIntel4000_GBA();
}
else if (strcmp(flashid, "227E") == 0) {
writeMX29GL128E_GBA();
}
// Close the file:
myFile.close();
// Verify
print_Msg(F("Verifying..."));
display_Update();
if (strcmp(flashid, "8802") == 0) {
resetIntel_GBA(0x8000);
delay(1000);
resetIntel_GBA(0x100000);
delay(1000);
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);
}
}
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
//******************************************