mirror of
https://github.com/sanni/cartreader.git
synced 2024-12-27 05:21:53 +01:00
2cf7f5dbe7
The `setVoltage()` function should be called even when `ENABLE_VSELECT` is disabled because `ENABLE_3V3FIX` also uses it. There is no resource cost to do this as when both options are disabled the compiler will optimize this function out. This just "future proofs" the code so if that function ever does more it doesn't need updated everywhere. This applies to `setup_FlashVoltage()` as well. The changes to OSCR.cpp are just for code formatting and additional comments to clarify this.
738 lines
18 KiB
C++
738 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() {
|
|
// Request 5V
|
|
setVoltage(VOLTS_SET_5V);
|
|
|
|
// 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() {
|
|
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
|
|
//******************************************
|