mirror of
https://github.com/sanni/cartreader.git
synced 2024-11-10 23:15:08 +01:00
010b7e7525
Lots of changes/additions. Added: * Firmware Updater support: Supports the Firmware Updater app (release to follow soon). Enabled by default, can be disabled in the config. * 3.3V Fix (3V3FIX): Enable if you have stability issues when using 3.3V, works best with VSELECT. Disabled by default, can be enabled in the config. * `DynamicClockSerial`: Class that extends and modifies HardwareSerial to be compatible with a dynamically changing clock speed. Used through the `ClockedSerial` object/variable. * `OSCR.cpp` & `OSCR.h`: New files for storing globals. Only contains these new additions for now. More code cleanup to come. Changed: * Moved configuration flags to `Config.h` and documented them better. * Removed `vselect()` function. Now uses `setVoltage()` with the params `VOLTS_SET_3V3` and `VOLTS_SET_5V`. Known Issues: * Rarely the LCD backlight turns white when using 3V3FIX. Resetting fixes it. Doesn't affect functionality/usability; it's just weird.
736 lines
18 KiB
C++
736 lines
18 KiB
C++
//******************************************
|
|
// GB SMART MODULE
|
|
// Supports 32M cart with LH28F016SUT flash
|
|
//******************************************
|
|
#ifdef enable_GBX
|
|
#define GB_SMART_GAMES_PER_PAGE 6
|
|
|
|
/******************************************
|
|
Menu
|
|
*****************************************/
|
|
// GB Smart menu items
|
|
static const char gbSmartMenuItem1[] PROGMEM = "Game Menu";
|
|
static const char gbSmartMenuItem2[] PROGMEM = "Flash Menu";
|
|
//static const char gbSmartMenuItem3[] PROGMEM = "Reset"; (stored in common strings array)
|
|
static const char *const menuOptionsGBSmart[] PROGMEM = { gbSmartMenuItem1, gbSmartMenuItem2, string_reset2 };
|
|
|
|
static const char gbSmartFlashMenuItem1[] PROGMEM = "Read Flash";
|
|
static const char gbSmartFlashMenuItem2[] PROGMEM = "Write Flash";
|
|
static const char gbSmartFlashMenuItem3[] PROGMEM = "Back";
|
|
static const char *const menuOptionsGBSmartFlash[] PROGMEM = { gbSmartFlashMenuItem1, gbSmartFlashMenuItem2, gbSmartFlashMenuItem3 };
|
|
|
|
static const char gbSmartGameMenuItem1[] PROGMEM = "Read Game";
|
|
static const char gbSmartGameMenuItem2[] PROGMEM = "Read SRAM";
|
|
static const char gbSmartGameMenuItem3[] PROGMEM = "Write SRAM";
|
|
static const char gbSmartGameMenuItem4[] PROGMEM = "Switch Game";
|
|
//static const char gbSmartGameMenuItem5[] PROGMEM = "Reset"; (stored in common strings array)
|
|
static const char *const menuOptionsGBSmartGame[] PROGMEM = { gbSmartGameMenuItem1, gbSmartGameMenuItem2, gbSmartGameMenuItem3, gbSmartGameMenuItem4, string_reset2 };
|
|
|
|
struct GBSmartGameInfo {
|
|
uint8_t start_bank;
|
|
uint8_t rom_type;
|
|
uint8_t rom_size;
|
|
uint8_t sram_size;
|
|
char title[16];
|
|
};
|
|
|
|
uint32_t gbSmartSize = 32 * 131072;
|
|
uint16_t gbSmartBanks = 256;
|
|
|
|
uint8_t gbSmartBanksPerFlashChip = 128;
|
|
uint8_t gbSmartBanksPerFlashBlock = 4;
|
|
uint32_t gbSmartFlashBlockSize = (gbSmartBanksPerFlashBlock << 14);
|
|
|
|
uint8_t gbSmartRomSizeGB = 0x07;
|
|
uint8_t gbSmartSramSizeGB = 0x04;
|
|
uint8_t gbSmartFlashSizeGB = 0x06;
|
|
|
|
byte signature[48];
|
|
uint16_t gameMenuStartBank;
|
|
|
|
// 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));
|
|
|
|
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"));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
byte readByte_GBS(word myAddress) {
|
|
PORTF = myAddress & 0xFF;
|
|
PORTK = (myAddress >> 8) & 0xFF;
|
|
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
|
|
// Switch CS(PH3) and RD(PH6) to LOW
|
|
PORTH &= ~((1 << 3) | (1 << 6));
|
|
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
|
|
// Read
|
|
byte tempByte = PINC;
|
|
|
|
// Switch CS(PH3) and RD(PH6) to HIGH
|
|
PORTH |= (1 << 3) | (1 << 6);
|
|
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
|
|
return tempByte;
|
|
}
|
|
|
|
void setup_GBSmart() {
|
|
// take from setup_GB
|
|
// Set RST(PH0) to Input
|
|
DDRH &= ~(1 << 0);
|
|
// Activate Internal Pullup Resistors
|
|
PORTH |= (1 << 0);
|
|
|
|
// Set Address Pins to Output
|
|
//A0-A7
|
|
DDRF = 0xFF;
|
|
//A8-A15
|
|
DDRK = 0xFF;
|
|
|
|
// Set Control Pins to Output CS(PH3) WR(PH5) RD(PH6) AUDIOIN(PH4) RESET(PH0)
|
|
DDRH |= (1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
|
|
// Output a high signal on all pins, pins are active low therefore everything is disabled now
|
|
PORTH |= (1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
|
|
|
|
// Set Data Pins (D0-D7) to Input
|
|
DDRC = 0x00;
|
|
|
|
delay(400);
|
|
|
|
gbSmartRemapStartBank(0x00, 0x00, 0x00);
|
|
|
|
getCartInfo_GB();
|
|
|
|
for (byte i = 0; i < 0x30; i++)
|
|
signature[i] = readByte_GBS(0x0104 + i);
|
|
|
|
gameMenuStartBank = 0x02;
|
|
|
|
display_Clear();
|
|
display_Update();
|
|
}
|
|
|
|
void gbSmartMenu() {
|
|
setVoltage(VOLTS_SET_5V);
|
|
uint8_t mainMenu;
|
|
|
|
// Copy menuOptions out of progmem
|
|
convertPgm(menuOptionsGBSmart, 3);
|
|
mainMenu = question_box(F("GB Smart"), menuOptions, 3, 0);
|
|
|
|
// wait for user choice to come back from the question box menu
|
|
switch (mainMenu) {
|
|
case 0:
|
|
{
|
|
gbSmartGameMenu();
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
mode = mode_GB_GBSmart_Flash;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
asm volatile(" jmp 0");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void gbSmartGameOptions() {
|
|
uint8_t gameSubMenu;
|
|
|
|
convertPgm(menuOptionsGBSmartGame, 5);
|
|
gameSubMenu = question_box(F("GB Smart Game Menu"), menuOptions, 5, 0);
|
|
|
|
switch (gameSubMenu) {
|
|
case 0: // Read Game
|
|
{
|
|
display_Clear();
|
|
sd.chdir("/");
|
|
readROM_GB();
|
|
compare_checksum_GBS();
|
|
break;
|
|
}
|
|
case 1: // Read SRAM
|
|
{
|
|
display_Clear();
|
|
sd.chdir("/");
|
|
readSRAM_GB();
|
|
break;
|
|
}
|
|
case 2: // Write SRAM
|
|
{
|
|
display_Clear();
|
|
sd.chdir("/");
|
|
writeSRAM_GB();
|
|
uint32_t wrErrors = verifySRAM_GB();
|
|
if (wrErrors == 0) {
|
|
println_Msg(F("Verified OK"));
|
|
display_Update();
|
|
} else {
|
|
print_STR(error_STR, 0);
|
|
print_Msg(wrErrors);
|
|
println_Msg(F(" bytes"));
|
|
print_Error(did_not_verify_STR);
|
|
}
|
|
break;
|
|
}
|
|
case 3: // Switch Game
|
|
{
|
|
gameMenuStartBank = 0x02;
|
|
gbSmartGameMenu();
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
asm volatile(" jmp 0");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (gameSubMenu != 3) {
|
|
println_Msg(F(""));
|
|
// Prints string out of the common strings array either with or without newline
|
|
print_STR(press_button_STR, 1);
|
|
display_Update();
|
|
wait();
|
|
}
|
|
}
|
|
|
|
void gbSmartGameMenu() {
|
|
boolean hasMenu;
|
|
byte numGames;
|
|
struct GBSmartGameInfo gbSmartGames[GB_SMART_GAMES_PER_PAGE];
|
|
uint8_t gameSubMenu = 0;
|
|
gb_smart_load_more_games:
|
|
if (gameMenuStartBank > 0xfe)
|
|
gameMenuStartBank = 0x02;
|
|
|
|
gbSmartGetGames(gbSmartGames, &hasMenu, &numGames);
|
|
|
|
if (hasMenu) {
|
|
char menuOptionsGBSmartGames[7][20];
|
|
int i = 0;
|
|
for (; i < numGames; i++)
|
|
strncpy(menuOptionsGBSmartGames[i], gbSmartGames[i].title, 16);
|
|
|
|
strncpy(menuOptionsGBSmartGames[i], "...", 16);
|
|
gameSubMenu = question_box(F("Select Game"), menuOptionsGBSmartGames, i + 1, 0);
|
|
|
|
if (gameSubMenu >= i)
|
|
goto gb_smart_load_more_games;
|
|
} else {
|
|
gameSubMenu = 0;
|
|
}
|
|
|
|
// copy romname
|
|
strcpy(romName, gbSmartGames[gameSubMenu].title);
|
|
|
|
// select a game
|
|
gbSmartRemapStartBank(gbSmartGames[gameSubMenu].start_bank, gbSmartGames[gameSubMenu].rom_size, gbSmartGames[gameSubMenu].sram_size);
|
|
getCartInfo_GB();
|
|
showCartInfo_GB();
|
|
|
|
mode = mode_GB_GBSmart_Game;
|
|
}
|
|
|
|
void gbSmartFlashMenu() {
|
|
uint8_t flashSubMenu;
|
|
|
|
convertPgm(menuOptionsGBSmartFlash, 3);
|
|
flashSubMenu = question_box(F("GB Smart Flash Menu"), menuOptions, 3, 0);
|
|
|
|
switch (flashSubMenu) {
|
|
case 0:
|
|
{
|
|
// read flash
|
|
display_Clear();
|
|
sd.chdir("/");
|
|
|
|
EEPROM_readAnything(0, foldern);
|
|
sprintf(fileName, "GBS%d.bin", foldern);
|
|
sd.mkdir("GB/GBS", true);
|
|
sd.chdir("GB/GBS");
|
|
foldern = foldern + 1;
|
|
EEPROM_writeAnything(0, foldern);
|
|
|
|
gbSmartReadFlash();
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
// write flash
|
|
display_Clear();
|
|
|
|
println_Msg(F("Attention"));
|
|
println_Msg(F("This will erase your"));
|
|
println_Msg(F("GB Smart Cartridge."));
|
|
println_Msg(F(""));
|
|
// Prints string out of the common strings array either with or without newline
|
|
print_STR(press_button_STR, 1);
|
|
display_Update();
|
|
wait();
|
|
|
|
display_Clear();
|
|
filePath[0] = '\0';
|
|
sd.chdir("/");
|
|
fileBrowser(F("Select 4MB file"));
|
|
|
|
sprintf(filePath, "%s/%s", filePath, fileName);
|
|
gbSmartWriteFlash();
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
mode = mode_GB_GBSmart;
|
|
return;
|
|
}
|
|
}
|
|
|
|
println_Msg(F(""));
|
|
// Prints string out of the common strings array either with or without newline
|
|
print_STR(press_button_STR, 1);
|
|
display_Update();
|
|
wait();
|
|
}
|
|
|
|
void gbSmartGetOneGame(struct GBSmartGameInfo *gbSmartGames, byte bank, word base) {
|
|
uint8_t myByte, myLength = 0;
|
|
word title_address = base + 0x0134;
|
|
|
|
for (uint8_t j = 0; j < 15; j++) {
|
|
myByte = readByte_GBS(title_address++);
|
|
|
|
if (((myByte >= '0' && myByte <= '9') || (myByte >= 'A' && myByte <= 'z')))
|
|
gbSmartGames->title[myLength++] = myByte;
|
|
}
|
|
|
|
gbSmartGames->title[myLength] = 0x00;
|
|
gbSmartGames->start_bank = bank;
|
|
gbSmartGames->rom_type = readByte_GBS(base + 0x0147);
|
|
gbSmartGames->rom_size = readByte_GBS(base + 0x0148);
|
|
gbSmartGames->sram_size = readByte_GBS(base + 0x0149);
|
|
}
|
|
|
|
void gbSmartGetGames(struct GBSmartGameInfo *gbSmartGames, boolean *hasMenu, byte *numGames) {
|
|
static const byte menu_title[] = { 0x47, 0x42, 0x31, 0x36, 0x4d };
|
|
|
|
// reset remap setting
|
|
gbSmartRemapStartBank(0x00, gbSmartRomSizeGB, gbSmartSramSizeGB);
|
|
|
|
uint16_t i;
|
|
|
|
// check if contain menu
|
|
*hasMenu = true;
|
|
dataIn();
|
|
for (i = 0; i < 5; i++) {
|
|
if (readByte_GBS(0x0134 + i) != menu_title[i]) {
|
|
*hasMenu = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (*hasMenu) {
|
|
for (i = gameMenuStartBank, *numGames = 0; i < gbSmartBanks && *numGames < GB_SMART_GAMES_PER_PAGE;) {
|
|
// switch bank
|
|
dataOut();
|
|
writeByte_GB(0x2100, i);
|
|
|
|
dataIn();
|
|
// read signature
|
|
for (uint8_t j = 0x00; j < 0x30; j++) {
|
|
if (readByte_GBS(0x4104 + j) != signature[j]) {
|
|
i += 0x02;
|
|
continue;
|
|
}
|
|
}
|
|
gbSmartGetOneGame(&gbSmartGames[*numGames], i, 0x4000);
|
|
i += (2 << gbSmartGames[(*numGames)++].rom_size);
|
|
}
|
|
|
|
gameMenuStartBank = i;
|
|
} else {
|
|
dataIn();
|
|
gbSmartGetOneGame(&gbSmartGames[0], 0, 0);
|
|
|
|
*numGames = 1;
|
|
gameMenuStartBank = 0xfe;
|
|
}
|
|
}
|
|
|
|
void gbSmartReadFlash() {
|
|
print_Msg(F("Saving as GB/GBS/"));
|
|
print_Msg(fileName);
|
|
println_Msg(F("..."));
|
|
display_Update();
|
|
|
|
if (!myFile.open(fileName, O_RDWR | O_CREAT))
|
|
print_FatalError(create_file_STR);
|
|
|
|
// reset flash to read array state
|
|
for (uint16_t i = 0x00; i < gbSmartBanks; i += gbSmartBanksPerFlashChip)
|
|
gbSmartResetFlash(i);
|
|
|
|
// remaps mmc to full access
|
|
gbSmartRemapStartBank(0x00, gbSmartRomSizeGB, gbSmartSramSizeGB);
|
|
|
|
// dump fixed bank 0x00
|
|
dataIn();
|
|
for (uint16_t addr = 0x0000; addr <= 0x3fff; addr += 512) {
|
|
for (uint16_t c = 0; c < 512; c++)
|
|
sdBuffer[c] = readByte_GBS(addr + c);
|
|
|
|
myFile.write(sdBuffer, 512);
|
|
}
|
|
|
|
// read rest banks
|
|
for (uint16_t bank = 0x01; bank < gbSmartBanks; bank++) {
|
|
dataOut();
|
|
writeByte_GB(0x2100, bank);
|
|
|
|
dataIn();
|
|
for (uint16_t addr = 0x4000; addr <= 0x7fff; addr += 512) {
|
|
for (uint16_t c = 0; c < 512; c++)
|
|
sdBuffer[c] = readByte_GBS(addr + c);
|
|
|
|
myFile.write(sdBuffer, 512);
|
|
}
|
|
}
|
|
|
|
// back to initial state
|
|
writeByte_GB(0x2100, 0x01);
|
|
|
|
myFile.close();
|
|
println_Msg("");
|
|
println_Msg(F("Finished reading"));
|
|
display_Update();
|
|
}
|
|
|
|
void gbSmartWriteFlash() {
|
|
for (uint16_t bank = 0x00; bank < gbSmartBanks; bank += gbSmartBanksPerFlashChip) {
|
|
display_Clear();
|
|
|
|
print_Msg(F("Erasing..."));
|
|
display_Update();
|
|
|
|
gbSmartEraseFlash(bank);
|
|
gbSmartResetFlash(bank);
|
|
|
|
print_STR(done_STR, 1);
|
|
print_Msg(F("Blankcheck..."));
|
|
display_Update();
|
|
|
|
if (!gbSmartBlankCheckingFlash(bank))
|
|
print_FatalError(F("Could not erase flash"));
|
|
|
|
println_Msg(F("Passed"));
|
|
display_Update();
|
|
|
|
// write full chip
|
|
gbSmartWriteFlash(bank);
|
|
|
|
// reset chip
|
|
gbSmartWriteFlashByte(0x0000, 0xff);
|
|
}
|
|
|
|
print_STR(verifying_STR, 0);
|
|
display_Update();
|
|
|
|
writeErrors = gbSmartVerifyFlash();
|
|
if (writeErrors == 0) {
|
|
println_Msg(F("OK"));
|
|
display_Update();
|
|
} else {
|
|
print_STR(error_STR, 0);
|
|
print_Msg(writeErrors);
|
|
print_STR(_bytes_STR, 1);
|
|
print_FatalError(did_not_verify_STR);
|
|
}
|
|
}
|
|
|
|
void gbSmartWriteFlash(uint32_t start_bank) {
|
|
if (!myFile.open(filePath, O_READ))
|
|
print_FatalError(open_file_STR);
|
|
|
|
// switch to flash base bank
|
|
gbSmartRemapStartBank(start_bank, gbSmartFlashSizeGB, gbSmartSramSizeGB);
|
|
|
|
myFile.seekCur((start_bank << 14));
|
|
|
|
print_Msg(F("Writing Bank 0x"));
|
|
print_Msg(start_bank, HEX);
|
|
print_Msg(F("..."));
|
|
display_Update();
|
|
|
|
// handle bank 0x00 on 0x0000
|
|
gbSmartWriteFlashFromMyFile(0x0000);
|
|
|
|
// handle rest banks on 0x4000
|
|
for (uint8_t bank = 0x01; bank < gbSmartBanksPerFlashChip; bank++) {
|
|
dataOut();
|
|
writeByte_GB(0x2100, bank);
|
|
|
|
gbSmartWriteFlashFromMyFile(0x4000);
|
|
}
|
|
|
|
myFile.close();
|
|
println_Msg("");
|
|
}
|
|
|
|
void gbSmartWriteFlashFromMyFile(uint32_t addr) {
|
|
for (uint16_t i = 0; i < 16384; i += 256) {
|
|
myFile.read(sdBuffer, 256);
|
|
|
|
// sequence load to page
|
|
dataOut();
|
|
gbSmartWriteFlashByte(addr, 0xe0);
|
|
gbSmartWriteFlashByte(addr, 0xff);
|
|
gbSmartWriteFlashByte(addr, 0x00); // BCH should be 0x00
|
|
|
|
// fill page buffer
|
|
for (int d = 0; d < 256; d++)
|
|
gbSmartWriteFlashByte(d, sdBuffer[d]);
|
|
|
|
// start flashing page
|
|
gbSmartWriteFlashByte(addr, 0x0c);
|
|
gbSmartWriteFlashByte(addr, 0xff);
|
|
gbSmartWriteFlashByte(addr + i, 0x00); // BCH should be 0x00
|
|
|
|
// waiting for finishing
|
|
dataIn();
|
|
while ((readByte_GBS(addr + i) & 0x80) == 0x00)
|
|
;
|
|
}
|
|
|
|
// blink LED
|
|
blinkLED();
|
|
}
|
|
|
|
uint32_t gbSmartVerifyFlash() {
|
|
uint32_t verified = 0;
|
|
|
|
if (!myFile.open(filePath, O_READ)) {
|
|
verified = 0xffffffff;
|
|
print_Error(F("Can't open file on SD"));
|
|
} else {
|
|
// remaps mmc to full access
|
|
gbSmartRemapStartBank(0x00, gbSmartRomSizeGB, gbSmartSramSizeGB);
|
|
|
|
// verify bank 0x00
|
|
dataIn();
|
|
for (uint16_t addr = 0x0000; addr <= 0x3fff; addr += 512) {
|
|
myFile.read(sdBuffer, 512);
|
|
|
|
for (uint16_t c = 0; c < 512; c++) {
|
|
if (readByte_GBS(addr + c) != sdBuffer[c])
|
|
verified++;
|
|
}
|
|
}
|
|
|
|
// verify rest banks
|
|
for (uint16_t bank = 0x01; bank < gbSmartBanks; bank++) {
|
|
dataOut();
|
|
writeByte_GB(0x2100, bank);
|
|
|
|
dataIn();
|
|
for (uint16_t addr = 0x4000; addr <= 0x7fff; addr += 512) {
|
|
myFile.read(sdBuffer, 512);
|
|
|
|
for (uint16_t c = 0; c < 512; c++) {
|
|
if (readByte_GBS(addr + c) != sdBuffer[c])
|
|
verified++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// back to initial state
|
|
writeByte_GB(0x2100, 0x01);
|
|
|
|
myFile.close();
|
|
}
|
|
|
|
return verified;
|
|
}
|
|
|
|
byte gbSmartBlankCheckingFlash(uint8_t flash_start_bank) {
|
|
gbSmartRemapStartBank(flash_start_bank, gbSmartFlashSizeGB, gbSmartSramSizeGB);
|
|
|
|
// check first bank
|
|
dataIn();
|
|
for (uint16_t addr = 0x0000; addr <= 0x3fff; addr++) {
|
|
if (readByte_GBS(addr) != 0xff)
|
|
return 0;
|
|
}
|
|
|
|
// check rest banks
|
|
for (uint16_t bank = 0x01; bank < gbSmartBanksPerFlashChip; bank++) {
|
|
dataOut();
|
|
writeByte_GB(0x2100, bank);
|
|
|
|
dataIn();
|
|
for (uint16_t addr = 0x4000; addr <= 0x7fff; addr++) {
|
|
if (readByte_GBS(addr) != 0xff)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void gbSmartResetFlash(uint8_t flash_start_bank) {
|
|
gbSmartRemapStartBank(flash_start_bank, gbSmartFlashSizeGB, gbSmartSramSizeGB);
|
|
|
|
dataOut();
|
|
gbSmartWriteFlashByte(0x0, 0xff);
|
|
}
|
|
|
|
void gbSmartEraseFlash(uint8_t flash_start_bank) {
|
|
gbSmartRemapStartBank(flash_start_bank, gbSmartFlashSizeGB, gbSmartSramSizeGB);
|
|
|
|
// handling first flash block
|
|
dataOut();
|
|
gbSmartWriteFlashByte(0x0000, 0x20);
|
|
gbSmartWriteFlashByte(0x0000, 0xd0);
|
|
|
|
dataIn();
|
|
while ((readByte_GBS(0x0000) & 0x80) == 0x00)
|
|
;
|
|
|
|
// blink LED
|
|
blinkLED();
|
|
|
|
// rest of flash block
|
|
for (uint32_t ba = gbSmartBanksPerFlashBlock; ba < gbSmartBanksPerFlashChip; ba += gbSmartBanksPerFlashBlock) {
|
|
dataOut();
|
|
writeByte_GB(0x2100, ba);
|
|
|
|
gbSmartWriteFlashByte(0x4000, 0x20);
|
|
gbSmartWriteFlashByte(0x4000, 0xd0);
|
|
|
|
dataIn();
|
|
while ((readByte_GBS(0x4000) & 0x80) == 0x00)
|
|
;
|
|
|
|
// blink LED
|
|
blinkLED();
|
|
}
|
|
}
|
|
|
|
void gbSmartWriteFlashByte(uint32_t myAddress, uint8_t myData) {
|
|
PORTF = myAddress & 0xff;
|
|
PORTK = (myAddress >> 8) & 0xff;
|
|
PORTC = myData;
|
|
|
|
// wait for 62.5 x 4 = 250ns
|
|
__asm__("nop\n\tnop\n\tnop\n\tnop\n\t");
|
|
|
|
// Pull FLASH_WE (PH4) low
|
|
PORTH &= ~(1 << 4);
|
|
|
|
// pull low for another 250ns
|
|
__asm__("nop\n\tnop\n\tnop\n\tnop\n\t");
|
|
|
|
// Pull FLASH_WE (PH4) high
|
|
PORTH |= (1 << 4);
|
|
|
|
// pull high for another 250ns
|
|
__asm__("nop\n\tnop\n\tnop\n\tnop\n\t");
|
|
}
|
|
|
|
// rom_start_bank = 0x00 means back to original state
|
|
void gbSmartRemapStartBank(uint8_t rom_start_bank, uint8_t rom_size, uint8_t sram_size) {
|
|
rom_start_bank &= 0xfe;
|
|
|
|
dataOut();
|
|
|
|
// clear base bank setting
|
|
writeByte_GB(0x1000, 0xa5);
|
|
writeByte_GB(0x7000, 0x00);
|
|
writeByte_GB(0x1000, 0x98);
|
|
writeByte_GB(0x2000, rom_start_bank);
|
|
|
|
if (rom_start_bank > 1) {
|
|
// start set new base bank
|
|
writeByte_GB(0x1000, 0xa5);
|
|
|
|
dataIn();
|
|
rom_start_bank = gbSmartGetResizeParam(rom_size, sram_size);
|
|
|
|
dataOut();
|
|
writeByte_GB(0x7000, rom_start_bank);
|
|
writeByte_GB(0x1000, 0x98);
|
|
|
|
writeByte_GB(0x2100, 0x01);
|
|
}
|
|
|
|
dataIn();
|
|
}
|
|
|
|
// Get magic number for 0x7000 register.
|
|
// Use for setting correct rom and sram size
|
|
// Code logic is take from SmartCard32M V1.3 menu code,
|
|
// see 0x2db2 to 0x2e51 (0xa0 bytes)
|
|
uint8_t gbSmartGetResizeParam(uint8_t rom_size, uint8_t sram_size) {
|
|
if (rom_size < 0x0f) {
|
|
rom_size &= 0x07;
|
|
rom_size ^= 0x07;
|
|
} else {
|
|
rom_size = 0x01;
|
|
}
|
|
|
|
if (sram_size > 0) {
|
|
if (sram_size > 1) {
|
|
sram_size--;
|
|
sram_size ^= 0x03;
|
|
sram_size <<= 4;
|
|
sram_size &= 0x30;
|
|
} else {
|
|
sram_size = 0x20; // 2KiB treat as 8KiB
|
|
}
|
|
} else {
|
|
sram_size = 0x30; // no sram
|
|
}
|
|
|
|
return (sram_size | rom_size);
|
|
}
|
|
|
|
#endif
|
|
//******************************************
|
|
// End of File
|
|
//******************************************
|