mirror of
https://github.com/sanni/cartreader.git
synced 2024-11-27 15:04:15 +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.
773 lines
21 KiB
C++
773 lines
21 KiB
C++
//***********************************************************
|
|
// SEGA MASTER SYSTEM / MARK III / GAME GEAR / SG-1000 MODULE
|
|
//***********************************************************
|
|
#ifdef enable_SMS
|
|
|
|
//******************************************
|
|
// Menus
|
|
//******************************************
|
|
// Adapters menu
|
|
static const char SMSAdapterItem1[] PROGMEM = "SMS/MarkIII raphnet";
|
|
static const char SMSAdapterItem2[] PROGMEM = "SMS Retrode";
|
|
static const char SMSAdapterItem3[] PROGMEM = "SMS Retron3in1";
|
|
static const char SMSAdapterItem4[] PROGMEM = "GameGear Retrode";
|
|
static const char SMSAdapterItem5[] PROGMEM = "GameGear Retron3in1";
|
|
static const char SMSAdapterItem6[] PROGMEM = "SG-1000 raphnet";
|
|
static const char* const SMSAdapterMenu[] PROGMEM = { SMSAdapterItem1, SMSAdapterItem2, SMSAdapterItem3, SMSAdapterItem4, SMSAdapterItem5, SMSAdapterItem6 };
|
|
|
|
// Operations menu
|
|
static const char SMSOperationItem1[] PROGMEM = "Read Rom";
|
|
static const char SMSOperationItem2[] PROGMEM = "Read from SRAM";
|
|
static const char SMSOperationItem3[] PROGMEM = "Write to SRAM";
|
|
static const char* const SMSOperationMenu[] PROGMEM = { SMSOperationItem1, SMSOperationItem2, SMSOperationItem3, string_reset2 };
|
|
|
|
// Rom sizes menu
|
|
static const char SMSRomSizeItem1[] PROGMEM = "8 KB";
|
|
static const char SMSRomSizeItem2[] PROGMEM = "16 KB";
|
|
static const char SMSRomSizeItem3[] PROGMEM = "24 KB";
|
|
static const char SMSRomSizeItem4[] PROGMEM = "32 KB";
|
|
static const char SMSRomSizeItem5[] PROGMEM = "40 KB"; //SG-1000 40k mapping not yet supported
|
|
static const char SMSRomSizeItem6[] PROGMEM = "48 KB"; //SG-1000 40k mapping not yet supported
|
|
static const char SMSRomSizeItem7[] PROGMEM = "64 KB";
|
|
static const char SMSRomSizeItem8[] PROGMEM = "128 KB";
|
|
static const char SMSRomSizeItem9[] PROGMEM = "256 KB";
|
|
static const char SMSRomSizeItem10[] PROGMEM = "512 KB";
|
|
static const char SMSRomSizeItem11[] PROGMEM = "1024 KB";
|
|
static const char* const SG1RomSizeMenu[] PROGMEM = { SMSRomSizeItem1, SMSRomSizeItem2, SMSRomSizeItem3, SMSRomSizeItem4 }; // Rom sizes for SG-1000
|
|
static const char* const SMSRomSizeMenu[] PROGMEM = { SMSRomSizeItem4, SMSRomSizeItem7, SMSRomSizeItem8, SMSRomSizeItem9, SMSRomSizeItem10, SMSRomSizeItem11 }; // Rom sizes for SMS and GG
|
|
|
|
// Init systems
|
|
static bool system_sms = false; // SMS or MarkIII
|
|
static bool system_gg = false; // GameGear
|
|
static bool system_sg1000 = false; // SG-1000
|
|
|
|
// Init adapters
|
|
static bool adapter_raphnet = false; // raphet adapater (SMS-to-MD or MIII-to-MD)
|
|
static bool adapter_retrode = false; // Retrode adapter (SMS-to-MD or GG-to-MD)
|
|
static bool adapter_retron = false; // Retron 3in1 adapter (SMS-to-MD or GG-to-MD)
|
|
|
|
//*********************************************************
|
|
// Main menu with systems/adapters setups to choose from
|
|
//*********************************************************
|
|
void smsMenu() {
|
|
unsigned char SMSSetup;
|
|
convertPgm(SMSAdapterMenu, 6);
|
|
SMSSetup = question_box(F("Select your setup"), menuOptions, 6, 0);
|
|
|
|
switch (SMSSetup) {
|
|
case 0:
|
|
// SMS or MarkIII with raphnet adapter
|
|
system_sms = true;
|
|
adapter_raphnet = true;
|
|
break;
|
|
|
|
case 1:
|
|
// SMS with Retrode adapter
|
|
system_sms = true;
|
|
adapter_retrode = true;
|
|
break;
|
|
|
|
case 2:
|
|
// SMS with Retron 3in1 adapter
|
|
system_sms = true;
|
|
adapter_retron = true;
|
|
break;
|
|
|
|
case 3:
|
|
// GameGear with Retrode adapter
|
|
system_gg = true;
|
|
adapter_retrode = true;
|
|
break;
|
|
|
|
case 4:
|
|
// GameGear with Retron 3in1 adapter
|
|
system_gg = true;
|
|
adapter_retron = true;
|
|
break;
|
|
|
|
case 5:
|
|
// SG-1000 with raphnet adapter
|
|
system_sg1000 = true;
|
|
adapter_raphnet = true;
|
|
break;
|
|
}
|
|
for (;;) smsOperations();
|
|
}
|
|
|
|
//****************************************************
|
|
// Create custom menu depending on selected setup
|
|
//****************************************************
|
|
void smsOperations() {
|
|
unsigned char SMSOperation = '3';
|
|
convertPgm(SMSOperationMenu, 4);
|
|
|
|
if (system_sms) {
|
|
if (adapter_raphnet) {
|
|
SMSOperation = question_box(F("SMS/MarkIII raphnet"), menuOptions, 4, 0);
|
|
} else if (adapter_retrode) {
|
|
SMSOperation = question_box(F("SMS Retrode"), menuOptions, 4, 0);
|
|
} else if (adapter_retron) {
|
|
SMSOperation = question_box(F("SMS Retron 3in1"), menuOptions, 4, 0);
|
|
}
|
|
} else if (system_gg) {
|
|
if (adapter_retrode) {
|
|
SMSOperation = question_box(F("GameGear Retrode"), menuOptions, 4, 0);
|
|
} else if (adapter_retron) {
|
|
SMSOperation = question_box(F("GameGear Retron 3in1"), menuOptions, 4, 0);
|
|
}
|
|
} else if (system_sg1000) {
|
|
SMSOperation = question_box(F("SG-1000 raphnet"), menuOptions, 1, 0);
|
|
}
|
|
|
|
switch (SMSOperation) {
|
|
case 0:
|
|
// Read ROM
|
|
mode = mode_SMS;
|
|
setup_SMS();
|
|
readROM_SMS();
|
|
break;
|
|
|
|
case 1:
|
|
// Read SRAM
|
|
mode = mode_SMS;
|
|
setup_SMS();
|
|
readSRAM_SMS();
|
|
break;
|
|
|
|
case 2:
|
|
// Write SRAM
|
|
mode = mode_SMS;
|
|
setup_SMS();
|
|
writeSRAM_SMS();
|
|
break;
|
|
|
|
case 3:
|
|
// Reset
|
|
resetArduino();
|
|
break;
|
|
}
|
|
|
|
display_Update();
|
|
wait();
|
|
}
|
|
|
|
//********************************
|
|
// Setup I/O
|
|
//********************************
|
|
void setup_SMS() {
|
|
// Request 5V
|
|
setVoltage(VOLTS_SET_5V);
|
|
|
|
// Set Address Pins to Output
|
|
//A0-A7
|
|
DDRF = 0xFF;
|
|
//A8-A14
|
|
DDRK = 0xFF;
|
|
//A15
|
|
DDRH |= (1 << 3);
|
|
|
|
//For Retrode adapter
|
|
if (adapter_retrode) {
|
|
PORTH &= ~((1 << 0) | (1 << 3) | (1 << 5));
|
|
PORTL &= ~(1 << 1);
|
|
DDRH &= ~((1 << 0) | (1 << 5));
|
|
DDRL &= ~((1 << 1));
|
|
// Set Control Pins to Output OE(PH6)
|
|
DDRH |= (1 << 6);
|
|
// WR(PL5) and RD(PL6)
|
|
DDRL |= (1 << 5) | (1 << 6);
|
|
// Setting OE(PH6) HIGH
|
|
PORTH |= (1 << 6);
|
|
// Setting WR(PL5) and RD(PL6) HIGH
|
|
PORTL |= (1 << 5) | (1 << 6);
|
|
}
|
|
|
|
// For Raphnet and Retron adapters
|
|
else {
|
|
// Set Control Pins to Output RST(PH0) WR(PH5) OE(PH6)
|
|
DDRH |= (1 << 0) | (1 << 5) | (1 << 6);
|
|
// CE(PL1)
|
|
DDRL |= (1 << 1);
|
|
// Setting RST(PH0) WR(PH5) OE(PH6) HIGH
|
|
PORTH |= (1 << 0) | (1 << 5) | (1 << 6);
|
|
// CE(PL1)
|
|
PORTL |= (1 << 1);
|
|
}
|
|
|
|
if (!system_sg1000) {
|
|
// SMS/GG ROM has 16KB banks which can be mapped to one of three slots via register writes
|
|
// Register Slot Address space
|
|
// $fffd 0 $0000-$3fff
|
|
// $fffe 1 $4000-$7fff
|
|
// $ffff 2 $8000-$bfff
|
|
|
|
// Disable sram
|
|
writeByte_SMS(0xFFFC, 0);
|
|
|
|
// Map first 3 banks so we can read-out the header info
|
|
writeByte_SMS(0xFFFD, 0);
|
|
writeByte_SMS(0xFFFE, 1);
|
|
writeByte_SMS(0xFFFF, 2);
|
|
}
|
|
|
|
delay(400);
|
|
|
|
// Read and print cart info
|
|
getCartInfo_SMS();
|
|
}
|
|
|
|
//*****************************************
|
|
// Low level functions
|
|
//*****************************************
|
|
void writeByte_SMS(word myAddress, byte myData) {
|
|
if (adapter_retrode && system_gg) {
|
|
// Set Data Pins (D8-D15) to Output
|
|
DDRA = 0xFF;
|
|
} else {
|
|
// Set Data Pins (D0-D7) to Output
|
|
DDRC = 0xFF;
|
|
}
|
|
|
|
// Set address
|
|
PORTF = myAddress & 0xFF;
|
|
PORTK = (myAddress >> 8) & 0xFF;
|
|
if (!adapter_retrode) {
|
|
// CE(PH3) and OE(PH6) are connected
|
|
PORTH = (PORTH & 0b11110111) | ((myAddress >> 12) & 0b00001000);
|
|
}
|
|
|
|
// Output data
|
|
if (adapter_retrode && system_gg) {
|
|
PORTA = myData;
|
|
} else {
|
|
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");
|
|
|
|
if (adapter_retrode) {
|
|
// Switch WR(PL5) and OE/CE(PH6) to LOW
|
|
PORTL &= ~(1 << 5);
|
|
PORTH &= ~(1 << 6);
|
|
} else {
|
|
// Switch CE(PL1) and WR(PH5) to LOW
|
|
PORTL &= ~(1 << 1);
|
|
PORTH &= ~(1 << 5);
|
|
}
|
|
|
|
// Leave WR low for at least 60ns
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
|
|
if (adapter_retrode) {
|
|
// Switch WR(PL5) and OE/CE(PH6) to HIGH
|
|
PORTH |= (1 << 6);
|
|
PORTL |= (1 << 5);
|
|
} else {
|
|
// Switch CE(PL1) and WR(PH5) to HIGH
|
|
PORTH |= (1 << 5);
|
|
PORTL |= (1 << 1);
|
|
}
|
|
|
|
// Leave WR high for at least 50ns
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
|
|
if (adapter_retrode && system_gg) {
|
|
// Set Data Pins (D8-D15) to Input
|
|
DDRA = 0x00;
|
|
} else {
|
|
// Set Data Pins (D0-D7) to Input
|
|
DDRC = 0x00;
|
|
}
|
|
}
|
|
|
|
byte readByte_SMS(word myAddress) {
|
|
if (adapter_retrode && system_gg) {
|
|
// Set Data Pins (D8-D15) to Input
|
|
DDRA = 0x00;
|
|
} else {
|
|
// Set Data Pins (D0-D7) to Input
|
|
DDRC = 0x00;
|
|
}
|
|
|
|
// Set Address
|
|
PORTF = myAddress & 0xFF;
|
|
PORTK = (myAddress >> 8) & 0xFF;
|
|
if (!adapter_retrode) {
|
|
// CE(PH3) and OE(PH6) are connected
|
|
PORTH = (PORTH & 0b11110111) | ((myAddress >> 12) & 0b00001000);
|
|
}
|
|
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
|
|
if (adapter_retrode) {
|
|
// Switch RD(PL6) and OE(PH6) to LOW
|
|
PORTL &= ~(1 << 6);
|
|
PORTH &= ~(1 << 6);
|
|
} else {
|
|
// Switch CE(PL1) and OE(PH6) to LOW
|
|
PORTL &= ~(1 << 1);
|
|
PORTH &= ~(1 << 6);
|
|
}
|
|
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
|
|
// Read
|
|
byte tempByte = (adapter_retrode && system_gg) ? PINA : PINC;
|
|
|
|
if (adapter_retrode) {
|
|
// Switch RD(PL6) and OE(PH6) to HIGH
|
|
PORTH |= (1 << 6);
|
|
PORTL |= (1 << 6);
|
|
} else {
|
|
// Switch CE(PL1) and OE(PH6) to HIGH
|
|
PORTH |= (1 << 6);
|
|
PORTL |= (1 << 1);
|
|
}
|
|
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
|
|
return tempByte;
|
|
}
|
|
|
|
byte readNibble(byte data, byte number) {
|
|
return ((data >> (number * 4)) & 0xF);
|
|
}
|
|
|
|
//*****************************************
|
|
// Cartridges functions
|
|
//*****************************************
|
|
void getCartInfo_SMS() {
|
|
// Get rom size
|
|
switch (readNibble(readByte_SMS(0x7FFF), 0)) {
|
|
// Adding UL gets rid of integer overflow compiler warning
|
|
case 0xa:
|
|
cartSize = 8 * 1024UL;
|
|
break;
|
|
case 0xb:
|
|
cartSize = 16 * 1024UL;
|
|
break;
|
|
case 0xc:
|
|
cartSize = 32 * 1024UL;
|
|
break;
|
|
case 0xd:
|
|
cartSize = 48 * 1024UL;
|
|
break;
|
|
case 0xe:
|
|
cartSize = 64 * 1024UL;
|
|
break;
|
|
case 0xf:
|
|
cartSize = 128 * 1024UL;
|
|
break;
|
|
case 0x0:
|
|
cartSize = 256 * 1024UL;
|
|
break;
|
|
case 0x1:
|
|
cartSize = 512 * 1024UL;
|
|
break;
|
|
case 0x2:
|
|
cartSize = 512 * 1024UL;
|
|
break;
|
|
case 0x3:
|
|
// 0x3 is (only?) used in The Pro Yakyuu '91 (Game Gear)
|
|
cartSize = 128 * 1024UL;
|
|
break;
|
|
default:
|
|
cartSize = 48 * 1024UL;
|
|
// LED Error
|
|
setColor_RGB(0, 0, 255);
|
|
break;
|
|
}
|
|
|
|
// Get rom name (expecting "TMR SEGA" string)
|
|
for (byte i = 0; i < 8; i++) {
|
|
romName[i] = char(readByte_SMS(0x7FF0 + i));
|
|
}
|
|
romName[8] = '\0';
|
|
|
|
// Attempt to detect cart size by checking if "TMR SEGA" is mirrored
|
|
unsigned long mirror_offset = cartSize;
|
|
char romName2[9];
|
|
while (mirror_offset < 1024 * 1024UL) {
|
|
byte bank = 1 + (mirror_offset / (16 * 1024UL));
|
|
writeByte_SMS(0xfffe, bank);
|
|
for (byte i = 0; i < 8; i++) {
|
|
romName2[i] = char(readByte_SMS(0x7FF0 + i));
|
|
}
|
|
romName2[8] = '\0';
|
|
|
|
// print_Msg(F("Name2: "));
|
|
// println_Msg(romName2);
|
|
// print_Msg(F("from bank "));
|
|
// print_Msg(bank);
|
|
// print_Msg(F(" offset "));
|
|
// print_Msg_PaddedHex32(mirror_offset + 0x7FF0);
|
|
// println_Msg(F(""));
|
|
|
|
if (strcmp(romName2, romName) == 0) {
|
|
break;
|
|
}
|
|
|
|
if (cartSize == 48 * 1024UL) {
|
|
cartSize = 64 * 1024UL;
|
|
} else {
|
|
cartSize *= 2;
|
|
}
|
|
mirror_offset = cartSize;
|
|
}
|
|
|
|
writeByte_SMS(0xFFFE, 1);
|
|
|
|
// Fix for "Fantasy Zone (J) (V1.0)" that has not the normal header, but "COPYRIGHT SEGAPRG. BY T.ASAI".
|
|
char headerFZ[29];
|
|
if (strcmp(romName, "G. BY T.A") != 0) {
|
|
for (byte i = 0; i < 28; i++) {
|
|
headerFZ[i] = char(readByte_SMS(0x7FE0 + i));
|
|
}
|
|
headerFZ[28] = '\0';
|
|
|
|
if (strcmp(headerFZ, "COPYRIGHT SEGAPRG. BY T.ASAI") == 0) {
|
|
strcpy(romName, "TMR SEGA");
|
|
cartSize = 128 * 1024UL;
|
|
}
|
|
}
|
|
|
|
// If "TMR SEGA" header is not found
|
|
if (strcmp(romName, "TMR SEGA") != 0) {
|
|
// Set rom size manually
|
|
unsigned char SMSRomSize;
|
|
|
|
if (system_sg1000) {
|
|
// Rom sizes for SG-1000
|
|
convertPgm(SG1RomSizeMenu, 4);
|
|
SMSRomSize = question_box(F("Select ROM size"), menuOptions, 4, 0);
|
|
switch (SMSRomSize) {
|
|
case 0:
|
|
cartSize = 8 * 1024UL; // 8KB
|
|
break;
|
|
case 1:
|
|
cartSize = 16 * 1024UL; // 16KB
|
|
break;
|
|
case 2:
|
|
cartSize = 24 * 1024UL; // 24KB
|
|
break;
|
|
case 3:
|
|
cartSize = 32 * 1024UL; // 32KB
|
|
break;
|
|
//case 4:
|
|
// cartSize = 40 * 1024UL; // 40KB
|
|
// break;
|
|
//case 5:
|
|
// cartSize = 48 * 1024UL; // 48KB
|
|
// break;
|
|
}
|
|
} else {
|
|
// Rom sizes for SMS and GG
|
|
convertPgm(SMSRomSizeMenu, 6);
|
|
SMSRomSize = question_box(F("Select ROM size"), menuOptions, 6, 0);
|
|
switch (SMSRomSize) {
|
|
case 0:
|
|
cartSize = 32 * 1024UL; // 32KB
|
|
break;
|
|
case 1:
|
|
cartSize = 64 * 1024UL; // 64KB
|
|
break;
|
|
case 2:
|
|
cartSize = 128 * 1024UL; // 128KB
|
|
break;
|
|
case 3:
|
|
cartSize = 256 * 1024UL; // 256KB
|
|
break;
|
|
case 4:
|
|
cartSize = 512 * 1024UL; // 512KB
|
|
break;
|
|
case 5:
|
|
cartSize = 1024 * 1024UL; // 1MB
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Display cart info
|
|
display_Clear();
|
|
println_Msg(F("SMS/GG header not found"));
|
|
println_Msg(F(" "));
|
|
print_Msg(F("Name: "));
|
|
println_Msg(romName);
|
|
print_Msg(F("Selected Size: "));
|
|
print_Msg(cartSize / 1024);
|
|
println_Msg(F("KB"));
|
|
println_Msg(F(" "));
|
|
sprintf(romName, "UNKNOWN");
|
|
}
|
|
|
|
// If "TMR SEGA" header is found
|
|
else {
|
|
display_Clear();
|
|
if (system_sms) {
|
|
println_Msg(F("SMS header info"));
|
|
} else {
|
|
println_Msg(F("GG header info"));
|
|
}
|
|
println_Msg(F(" "));
|
|
print_Msg(F("Name: "));
|
|
println_Msg(romName);
|
|
print_Msg(F("Size: "));
|
|
print_Msg(cartSize / 1024);
|
|
println_Msg(F("KB"));
|
|
println_Msg(F(" "));
|
|
}
|
|
|
|
// Wait for user input
|
|
#if (defined(enable_LCD) || defined(enable_OLED))
|
|
// Prints string out of the common strings array either with or without newline
|
|
print_STR(press_button_STR, 1);
|
|
display_Update();
|
|
wait();
|
|
#endif
|
|
|
|
// Turn off LED
|
|
setColor_RGB(0, 0, 0);
|
|
}
|
|
|
|
//******************************************
|
|
// Read ROM and save it to the SD card
|
|
//******************************************
|
|
void readROM_SMS() {
|
|
// Get name, add extension depending on the system and convert to char array for sd lib
|
|
EEPROM_readAnything(0, foldern);
|
|
strcpy(fileName, romName);
|
|
if (system_sms) {
|
|
strcat(fileName, ".sms");
|
|
sprintf(folder, "SMS/ROM/%s/%d", romName, foldern);
|
|
} else if (system_gg) {
|
|
strcat(fileName, ".gg");
|
|
sprintf(folder, "GG/ROM/%s/%d", romName, foldern);
|
|
} else {
|
|
strcat(fileName, ".sg");
|
|
sprintf(folder, "SG1000/ROM/%s/%d", romName, foldern);
|
|
}
|
|
|
|
// Create a new folder
|
|
sd.chdir("/");
|
|
sd.mkdir(folder, true);
|
|
sd.chdir(folder);
|
|
|
|
display_Clear();
|
|
print_STR(saving_to_STR, 0);
|
|
print_Msg(folder);
|
|
println_Msg(F("/..."));
|
|
display_Update();
|
|
|
|
// Write new folder number back to eeprom
|
|
foldern = foldern + 1;
|
|
EEPROM_writeAnything(0, foldern);
|
|
|
|
// Open file on sd card
|
|
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
|
|
print_FatalError(sd_error_STR);
|
|
}
|
|
|
|
// Set default bank size to 16KB
|
|
word bankSize = 16 * 1024UL;
|
|
|
|
// For carts not using mappers (SG1000 or SMS/GG 32KB)
|
|
if ((system_sg1000) || (cartSize == 32 * 1024UL)) {
|
|
bankSize = cartSize;
|
|
}
|
|
|
|
// Initialize progress bar
|
|
uint32_t processedProgressBar = 0;
|
|
uint32_t totalProgressBar = (uint32_t)(cartSize);
|
|
draw_progressbar(0, totalProgressBar);
|
|
|
|
for (byte currBank = 0x0; currBank < (cartSize / bankSize); currBank++) {
|
|
// Write current 16KB bank to slot 2 register 0xFFFF
|
|
if (!system_sg1000) {
|
|
writeByte_SMS(0xFFFF, currBank);
|
|
}
|
|
|
|
// Blink led
|
|
blinkLED();
|
|
|
|
// Read 16KB from slot 2 which starts at 0x8000
|
|
for (word currBuffer = 0; currBuffer < bankSize; currBuffer += 512) {
|
|
// Fill SD buffer
|
|
for (int currByte = 0; currByte < 512; currByte++) {
|
|
sdBuffer[currByte] = readByte_SMS(((system_sg1000) || (cartSize == 32 * 1024UL) ? 0 : 0x8000) + currBuffer + currByte);
|
|
}
|
|
// hexdump for debugging:
|
|
// if (currBank == 0 && currBuffer == 0) {
|
|
// for (word xi = 0; xi < 0x100; xi++) {
|
|
// if (xi%16==0) {
|
|
// print_Msg_PaddedHex16(xi);
|
|
// print_Msg(F(" "));
|
|
// }
|
|
// print_Msg_PaddedHexByte(sdBuffer[xi]);
|
|
// if (xi>0&&((xi+1)%16)==0) {
|
|
// println_Msg(F(""));
|
|
// } else {
|
|
// print_Msg(F(" "));
|
|
// }
|
|
// }
|
|
// }
|
|
myFile.write(sdBuffer, 512);
|
|
}
|
|
|
|
// Update progress bar
|
|
processedProgressBar += bankSize;
|
|
draw_progressbar(processedProgressBar, totalProgressBar);
|
|
}
|
|
|
|
// Close file
|
|
myFile.close();
|
|
|
|
// Compare dump checksum with database values
|
|
if (system_sms) {
|
|
compareCRC("sms.txt", 0, 1, 0);
|
|
} else if (system_gg) {
|
|
compareCRC("gg.txt", 0, 1, 0);
|
|
} else {
|
|
compareCRC("sg1000.txt", 0, 1, 0);
|
|
}
|
|
|
|
#ifdef global_log
|
|
save_log();
|
|
#endif
|
|
|
|
print_STR(press_button_STR, 1);
|
|
}
|
|
|
|
//******************************************
|
|
// Read SRAM and save to the SD card
|
|
///*****************************************
|
|
void readSRAM_SMS() {
|
|
// Get name, add extension and convert to char array for sd lib
|
|
strcpy(fileName, romName);
|
|
strcat(fileName, ".sav");
|
|
|
|
EEPROM_readAnything(0, foldern);
|
|
if (system_gg) {
|
|
sprintf(folder, "GG/SAVE/%s/%d", romName, foldern);
|
|
} else {
|
|
sprintf(folder, "SMS/SAVE/%s/%d", romName, foldern);
|
|
}
|
|
|
|
// Create a new folder
|
|
sd.chdir("/");
|
|
sd.mkdir(folder, true);
|
|
sd.chdir(folder);
|
|
|
|
display_Clear();
|
|
print_STR(saving_to_STR, 0);
|
|
print_Msg(folder);
|
|
println_Msg(F("/..."));
|
|
display_Update();
|
|
|
|
// write new folder number back to eeprom
|
|
foldern = foldern + 1;
|
|
EEPROM_writeAnything(0, foldern);
|
|
|
|
// Create file on sd card
|
|
if (myFile.open(fileName, O_RDWR | O_CREAT)) {
|
|
// Write the whole 32KB
|
|
// When there is only 8KB of SRAM, the contents should be duplicated
|
|
word bankSize = 16 * 1024UL;
|
|
for (byte currBank = 0x0; currBank < 2; currBank++) {
|
|
writeByte_SMS(0xFFFC, 0x08 | (currBank << 2));
|
|
|
|
// Blink led
|
|
blinkLED();
|
|
|
|
// Read 16KB from slot 2 which starts at 0x8000
|
|
for (word currBuffer = 0; currBuffer < bankSize; currBuffer += 512) {
|
|
// Fill SD buffer
|
|
for (int currByte = 0; currByte < 512; currByte++) {
|
|
sdBuffer[currByte] = readByte_SMS(0x8000 + currBuffer + currByte);
|
|
}
|
|
myFile.write(sdBuffer, 512);
|
|
}
|
|
}
|
|
// Close file
|
|
myFile.close();
|
|
print_STR(press_button_STR, 1);
|
|
display_Update();
|
|
} else {
|
|
print_FatalError(sd_error_STR);
|
|
}
|
|
}
|
|
|
|
//**********************************************
|
|
// Read file from SD card and write it to SRAM
|
|
//**********************************************
|
|
void writeSRAM_SMS() {
|
|
if (false) {
|
|
print_Error(F("DISABLED"));
|
|
} else {
|
|
fileBrowser(F("Select file"));
|
|
|
|
sd.chdir();
|
|
sprintf(filePath, "%s/%s", filePath, fileName);
|
|
|
|
display_Clear();
|
|
println_Msg(F("Restoring from "));
|
|
println_Msg(filePath);
|
|
display_Update();
|
|
|
|
if (myFile.open(filePath, O_READ)) {
|
|
// Get SRAM size from file, with a maximum of 32KB
|
|
uint32_t sramSize = myFile.fileSize();
|
|
if (sramSize > ((uint32_t)32 * (uint32_t)1024)) {
|
|
sramSize = (uint32_t)32 * (uint32_t)1024;
|
|
}
|
|
print_Msg(F("sramSize: "));
|
|
print_Msg(sramSize);
|
|
println_Msg(F(""));
|
|
word bankSize = 16 * 1024;
|
|
for (word address = 0x0; address < sramSize; address += 512) {
|
|
byte currBank = address >= bankSize ? 1 : 0;
|
|
word page_address = address - (currBank * bankSize);
|
|
writeByte_SMS(0xFFFC, 0x08 | (currBank << 2));
|
|
// Blink led
|
|
blinkLED();
|
|
myFile.read(sdBuffer, 512);
|
|
for (int x = 0; x < 512; x++) {
|
|
writeByte_SMS(0x8000 + page_address + x, sdBuffer[x]);
|
|
}
|
|
}
|
|
|
|
// Close file
|
|
myFile.close();
|
|
print_STR(press_button_STR, 1);
|
|
display_Update();
|
|
} else {
|
|
print_FatalError(sd_error_STR);
|
|
}
|
|
}
|
|
sd.chdir(); // root
|
|
filePath[0] = '\0'; // Reset filePath
|
|
}
|
|
#endif
|
|
|
|
//******************************************
|
|
// End of File
|
|
//******************************************
|