SMS Updates

1. SMS cart size detection algorithm is updated based on bank count instead of multiples of detected cart size from the header.
2. SMS cart size detection is only invoked if the "TMR SEGA" header is parsed for non-JP carts.
3. Manual selection of ROM size is now present in the SMS/GG menus to allow for manual override as detection algorithm does not work correctly for Cloud Master and Penguin Land US SMS carts.
4. SG-1000 Operation menu now allows for reset instead of relying on a power cycle.
This commit is contained in:
Andy Miles 2024-07-29 13:08:59 -07:00
parent 7c9a1b6c01
commit 644e1dc944
No known key found for this signature in database
GPG Key ID: 4F416ABAAC32750B

View File

@ -16,7 +16,11 @@ static const char SMSAdapterItem6[] PROGMEM = "SG-1000 raphnet";
static const char* const SMSAdapterMenu[] PROGMEM = { SMSAdapterItem1, SMSAdapterItem2, SMSAdapterItem3, SMSAdapterItem4, SMSAdapterItem5, SMSAdapterItem6 }; static const char* const SMSAdapterMenu[] PROGMEM = { SMSAdapterItem1, SMSAdapterItem2, SMSAdapterItem3, SMSAdapterItem4, SMSAdapterItem5, SMSAdapterItem6 };
// Operations menu // Operations menu
static const char* const SMSOperationMenu[] PROGMEM = { FSTRING_READ_ROM, FSTRING_READ_SAVE, FSTRING_WRITE_SAVE, FSTRING_RESET }; static const char SMSOperationMenuItem4[] PROGMEM = "Set ROM Size";
static const char* const SMSOperationMenu[] PROGMEM = { FSTRING_READ_ROM, FSTRING_READ_SAVE, FSTRING_WRITE_SAVE, SMSOperationMenuItem4, FSTRING_RESET };
// SG Operations menu
static const char* const SGOperationsMenu[] PROGMEM = { FSTRING_READ_ROM, FSTRING_RESET };
// Rom sizes menu // Rom sizes menu
static const char SMSRomSizeItem1[] PROGMEM = "8 KB"; static const char SMSRomSizeItem1[] PROGMEM = "8 KB";
@ -43,6 +47,9 @@ static bool adapter_raphnet = false; // raphet adapater (SMS-to-MD or MIII-to-M
static bool adapter_retrode = false; // Retrode adapter (SMS-to-MD or GG-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) static bool adapter_retron = false; // Retron 3in1 adapter (SMS-to-MD or GG-to-MD)
// Manual ROM Size Selection Flag
bool manRomSizeSelected = false;
//********************************************************* //*********************************************************
// Main menu with systems/adapters setups to choose from // Main menu with systems/adapters setups to choose from
//********************************************************* //*********************************************************
@ -57,92 +64,100 @@ void smsMenu() {
system_sms = true; system_sms = true;
adapter_raphnet = true; adapter_raphnet = true;
break; break;
case 1: case 1:
// SMS with Retrode adapter // SMS with Retrode adapter
system_sms = true; system_sms = true;
adapter_retrode = true; adapter_retrode = true;
break; break;
case 2: case 2:
// SMS with Retron 3in1 adapter // SMS with Retron 3in1 adapter
system_sms = true; system_sms = true;
adapter_retron = true; adapter_retron = true;
break; break;
case 3: case 3:
// GameGear with Retrode adapter // GameGear with Retrode adapter
system_gg = true; system_gg = true;
adapter_retrode = true; adapter_retrode = true;
break; break;
case 4: case 4:
// GameGear with Retron 3in1 adapter // GameGear with Retron 3in1 adapter
system_gg = true; system_gg = true;
adapter_retron = true; adapter_retron = true;
break; break;
case 5: case 5:
// SG-1000 with raphnet adapter // SG-1000 with raphnet adapter
system_sg1000 = true; system_sg1000 = true;
adapter_raphnet = true; adapter_raphnet = true;
break; break;
} }
for (;;) smsOperations(); for (;;) operationsMenu();
} }
//**************************************************** //****************************************************
// Create custom menu depending on selected setup // Create custom menu depending on selected setup
//**************************************************** //****************************************************
void smsOperations() { void operationsMenu() {
unsigned char SMSOperation = '3'; unsigned char SMSOperation = '3';
convertPgm(SMSOperationMenu, 4);
if (system_sms) {
if (adapter_raphnet) {
SMSOperation = question_box(FS(SMSAdapterItem1), menuOptions, 4, 0);
} else if (adapter_retrode) {
SMSOperation = question_box(FS(SMSAdapterItem2), menuOptions, 4, 0);
} else if (adapter_retron) {
SMSOperation = question_box(FS(SMSAdapterItem3), menuOptions, 4, 0);
}
} else if (system_gg) {
if (adapter_retrode) {
SMSOperation = question_box(FS(SMSAdapterItem4), menuOptions, 4, 0);
} else if (adapter_retron) {
SMSOperation = question_box(FS(SMSAdapterItem5), menuOptions, 4, 0);
}
} else if (system_sg1000) {
SMSOperation = question_box(FS(SMSAdapterItem6), menuOptions, 1, 0);
}
if (system_sg1000) {
convertPgm(SGOperationsMenu, 2);
SMSOperation = question_box(FS(SMSAdapterItem6), menuOptions, 2, 0);
switch (SMSOperation) { switch (SMSOperation) {
case 0: case 0: // Read ROM
// Read ROM
mode = CORE_SMS; mode = CORE_SMS;
setup_SMS(); setup_SMS();
readROM_SMS(); readROM_SMS();
break; break;
default:
case 1: case 1:
// Read SRAM // Reset
resetArduino();
break;
}
} else {
convertPgm(SMSOperationMenu, 5);
if (system_sms) {
if (adapter_raphnet) {
SMSOperation = question_box(FS(SMSAdapterItem1), menuOptions, 5, 0);
} else if (adapter_retrode) {
SMSOperation = question_box(FS(SMSAdapterItem2), menuOptions, 5, 0);
} else if (adapter_retron) {
SMSOperation = question_box(FS(SMSAdapterItem3), menuOptions, 5, 0);
}
} else if (system_gg) {
if (adapter_retrode) {
SMSOperation = question_box(FS(SMSAdapterItem4), menuOptions, 5, 0);
} else if (adapter_retron) {
SMSOperation = question_box(FS(SMSAdapterItem5), menuOptions, 5, 0);
}
}
switch (SMSOperation) {
case 0: // Read ROM
mode = CORE_SMS;
setup_SMS();
readROM_SMS();
break;
case 1: // Read SRAM
mode = CORE_SMS; mode = CORE_SMS;
setup_SMS(); setup_SMS();
readSRAM_SMS(); readSRAM_SMS();
break; break;
case 2: // Write SRAM
case 2:
// Write SRAM
mode = CORE_SMS; mode = CORE_SMS;
setup_SMS(); setup_SMS();
writeSRAM_SMS(); writeSRAM_SMS();
break; break;
case 3: // Select ROM Size
case 3: manual_selectRomSize();
break;
default:
case 4:
// Reset // Reset
resetArduino(); resetArduino();
break; break;
} }
}
display_Update(); display_Update();
wait(); wait();
@ -209,8 +224,10 @@ void setup_SMS() {
delay(400); delay(400);
// Read and print cart info // Read and print cart info only if ROM size not manually selected
if (manRomSizeSelected == false) {
getCartInfo_SMS(); getCartInfo_SMS();
}
} }
//***************************************** //*****************************************
@ -354,42 +371,43 @@ byte readNibble(byte data, byte number) {
// Cartridges functions // Cartridges functions
//***************************************** //*****************************************
void getCartInfo_SMS() { void getCartInfo_SMS() {
// Get rom size byte cartNib = readNibble(readByte_SMS(0x7FFF), 0);
switch (readNibble(readByte_SMS(0x7FFF), 0)) {
// Adding UL gets rid of integer overflow compiler warning // Get rom size from header
// Note: Common for this value to be smaller than the actual value.
// Normally used for BIOS checksum calculations on export hardware (non JP).
switch (cartNib) {
case 0xa: case 0xa:
cartSize = 8 * 1024UL; cartSize = 8192; // 8 KiB
break; break;
case 0xb: case 0xb:
cartSize = 16 * 1024UL; cartSize = 16384; // 16 KiB
break; break;
case 0xc: case 0xc:
cartSize = 32 * 1024UL; cartSize = 32768; // 32 KiB
break; break;
case 0xd: case 0xd:
cartSize = 48 * 1024UL; cartSize = 49152; // 48 KiB
break; break;
case 0xe: case 0xe:
cartSize = 64 * 1024UL; cartSize = 65536; // 64 KiB
break; break;
case 0xf: case 0xf:
cartSize = 128 * 1024UL; cartSize = 131072; // 128 KiB
break; break;
case 0x0: case 0x0:
cartSize = 256 * 1024UL; cartSize = 262144; // 256 KiB
break; break;
case 0x1: case 0x1:
cartSize = 512 * 1024UL;
break;
case 0x2: case 0x2:
cartSize = 512 * 1024UL; cartSize = 524288; // 512 KiB
break; break;
case 0x3: case 0x3:
// 0x3 is (only?) used in The Pro Yakyuu '91 (Game Gear) // 0x3 is (only?) used in The Pro Yakyuu '91 (Game Gear)
cartSize = 128 * 1024UL; cartSize = 131072; // 128 KiB
break; break;
default: default:
cartSize = 48 * 1024UL; cartSize = 49152; // 48 KiB
// LED Error // LED Error
rgbLed(blue_color); rgbLed(blue_color);
break; break;
@ -402,38 +420,50 @@ void getCartInfo_SMS() {
romName[8] = '\0'; romName[8] = '\0';
// Attempt to detect cart size by checking if "TMR SEGA" is mirrored // Attempt to detect cart size by checking if "TMR SEGA" is mirrored
unsigned long mirror_offset = cartSize; // Based on https://www.raphnet.net/electronique/sms_cartridge_programmer/index_en.php#6
char romName2[9]; // Note: Logic does not work on US CloudMaster (256K) and Penquin Land (128K) SMS carts.
while (mirror_offset < 1024 * 1024UL) { // Both detect as 512 KB based on the logic below.
byte bank = 1 + (mirror_offset / (16 * 1024UL)); if (strcmp(romName, "TMR SEGA") == 0) {
writeByte_SMS(0xfffe, bank); byte bank = 1;
char romNameBuf[9];
while (bank < 64) { // Total number of possible banks: 1 MiB / 16 (KiB/Bank)
bank++; // Increment the bank
writeByte_SMS(0xfffe, bank); // Load bank into slot 1
for (byte i = 0; i < 8; i++) { for (byte i = 0; i < 8; i++) {
romName2[i] = char(readByte_SMS(0x7FF0 + i)); romNameBuf[i] = char(readByte_SMS(0x7FF0 + i));
} }
romName2[8] = '\0'; romNameBuf[8] = '\0';
// print_Msg(F("Name2: ")); // Debug info:
// println_Msg(romName2); // print_Msg(F("Bank: "));
// print_Msg(F("from bank ")); // println_Msg(bank);
// print_Msg(bank); // print_Msg(F("Parsed ROM name: "));
// print_Msg(F(" offset ")); // println_Msg(romNameBuf);
// print_Msg_PaddedHex32(mirror_offset + 0x7FF0);
// println_Msg(FS(FSTRING_EMPTY)); // println_Msg(FS(FSTRING_EMPTY));
if (strcmp(romName2, romName) == 0) { if (strcmp(romNameBuf, romName) == 0) {
break; break;
} }
if (cartSize == 48 * 1024UL) {
cartSize = 64 * 1024UL;
} else {
cartSize *= 2;
} }
mirror_offset = cartSize; if (bank > 2) { // 32 KiB is the smallest SMS/GG ROM size
cartSize = (bank - 1) * 16384UL;
} }
// Debug info:
// print_Msg(F("Calculated ROM Size: "));
// println_Msg(cartSize);
// Reset Bank Slot 1
writeByte_SMS(0xFFFE, 1); writeByte_SMS(0xFFFE, 1);
// Display header info
display_Clear();
if (system_sms) {
println_Msg(F("SMS Header:"));
} else {
println_Msg(F("GG Header:"));
}
} else { // romName != "TMR SEGA"
// Fix for "Fantasy Zone (J) (V1.0)" that has not the normal header, but "COPYRIGHT SEGAPRG. BY T.ASAI". // Fix for "Fantasy Zone (J) (V1.0)" that has not the normal header, but "COPYRIGHT SEGAPRG. BY T.ASAI".
char headerFZ[29]; char headerFZ[29];
if (strcmp(romName, "G. BY T.A") != 0) { if (strcmp(romName, "G. BY T.A") != 0) {
@ -444,94 +474,23 @@ void getCartInfo_SMS() {
if (strcmp(headerFZ, "COPYRIGHT SEGAPRG. BY T.ASAI") == 0) { if (strcmp(headerFZ, "COPYRIGHT SEGAPRG. BY T.ASAI") == 0) {
strcpy(romName, "TMR SEGA"); strcpy(romName, "TMR SEGA");
cartSize = 128 * 1024UL; cartSize = 131072; // 128 KiB
} }
} }
// If "TMR SEGA" header is not found manual_selectRomSize();
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 cart info
display_Clear(); display_Clear();
println_Msg(F("SMS/GG header not found")); println_Msg(F("SMS/GG header not found"));
println_Msg(FS(FSTRING_SPACE));
print_Msg(FS(FSTRING_NAME));
println_Msg(romName);
print_Msg(F("Selected Size: "));
print_Msg(cartSize / 1024);
println_Msg(F("KB"));
println_Msg(FS(FSTRING_SPACE));
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(FS(FSTRING_SPACE)); println_Msg(FS(FSTRING_SPACE));
print_Msg(FS(FSTRING_NAME)); print_Msg(FS(FSTRING_NAME));
println_Msg(romName); println_Msg(romName);
print_Msg(FS(FSTRING_SIZE));
print_Msg(cartSize / 1024); print_Msg(cartSize / 1024);
println_Msg(F("KB")); println_Msg(F("KB"));
println_Msg(FS(FSTRING_SPACE)); println_Msg(FS(FSTRING_SPACE));
}
// Wait for user input // Wait for user input
#if (defined(ENABLE_LCD) || defined(ENABLE_OLED)) #if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
@ -545,6 +504,71 @@ void getCartInfo_SMS() {
rgbLed(black_color); rgbLed(black_color);
} }
void manual_selectRomSize() {
// 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 = 8192; // 8 KiB
break;
case 1:
cartSize = 16384; // 16 KiB
break;
case 2:
cartSize = 24576; // 24 KiB
break;
case 3:
cartSize = 32768; // 32 KiB
break;
//case 4:
// cartSize = 40960; // 40KB
// break;
//case 5:
// cartSize = 49152; // 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 = 32768; // 32 KiB
break;
case 1:
cartSize = 65536; // 64 KiB
break;
case 2:
cartSize = 131072; // 128 KiB
break;
case 3:
cartSize = 262144; // 256 KiB
break;
case 4:
cartSize = 524288; // 512 KiB
break;
case 5:
cartSize = 1048576; // 1 MiB
break;
}
}
strcpy(romName, "UNKNOWN"); // Use default UNKNOWN rom name upon manual selection
manRomSizeSelected = true; // This ensures manually selected value is used upon read from the menu
// Display info
display_Clear();
print_Msg(F("Selected Size: "));
print_Msg(cartSize / 1024);
println_Msg(F("KB"));
print_STR(press_button_STR, 1);
}
//****************************************** //******************************************
// Read ROM and save it to the SD card // Read ROM and save it to the SD card
//****************************************** //******************************************
@ -560,16 +584,20 @@ void readROM_SMS() {
printAndIncrementFolder(true); printAndIncrementFolder(true);
print_Msg(F("ROM Size: "));
print_Msg(cartSize / 1024);
println_Msg(F("KB"));
// Open file on sd card // Open file on sd card
if (!myFile.open(fileName, O_RDWR | O_CREAT)) { if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
print_FatalError(sd_error_STR); print_FatalError(sd_error_STR);
} }
// Set default bank size to 16KB // Set default bank size to 16 KiB
word bankSize = 16 * 1024UL; word bankSize = 16384;
// For carts not using mappers (SG1000 or SMS/GG 32KB) // For carts not using mappers (SG1000 or SMS/GG 32 KiB)
if ((system_sg1000) || (cartSize == 32 * 1024UL)) { if ((system_sg1000) || (cartSize == 32768)) {
bankSize = cartSize; bankSize = cartSize;
} }
@ -591,7 +619,7 @@ void readROM_SMS() {
for (word currBuffer = 0; currBuffer < bankSize; currBuffer += 512) { for (word currBuffer = 0; currBuffer < bankSize; currBuffer += 512) {
// Fill SD buffer // Fill SD buffer
for (int currByte = 0; currByte < 512; currByte++) { for (int currByte = 0; currByte < 512; currByte++) {
sdBuffer[currByte] = readByte_SMS(((system_sg1000) || (cartSize == 32 * 1024UL) ? 0 : 0x8000) + currBuffer + currByte); sdBuffer[currByte] = readByte_SMS(((system_sg1000) || (cartSize == 32768) ? 0 : 0x8000) + currBuffer + currByte);
} }
// hexdump for debugging: // hexdump for debugging:
// if (currBank == 0 && currBuffer == 0) { // if (currBank == 0 && currBuffer == 0) {
@ -649,16 +677,16 @@ void readSRAM_SMS() {
} }
createFolderAndOpenFile(system, "SAVE", romName, "sav"); createFolderAndOpenFile(system, "SAVE", romName, "sav");
// Write the whole 32KB // Write the whole 32 KiB
// When there is only 8KB of SRAM, the contents should be duplicated // When there is only 8 KiB of SRAM, the contents should be duplicated
word bankSize = 16 * 1024UL; word bankSize = 16384;
for (byte currBank = 0x0; currBank < 2; currBank++) { for (byte currBank = 0x0; currBank < 2; currBank++) {
writeByte_SMS(0xFFFC, 0x08 | (currBank << 2)); writeByte_SMS(0xFFFC, 0x08 | (currBank << 2));
// Blink led // Blink led
blinkLED(); blinkLED();
// Read 16KB from slot 2 which starts at 0x8000 // Read 16 KiB from slot 2 which starts at 0x8000
for (word currBuffer = 0; currBuffer < bankSize; currBuffer += 512) { for (word currBuffer = 0; currBuffer < bankSize; currBuffer += 512) {
// Fill SD buffer // Fill SD buffer
for (int currByte = 0; currByte < 512; currByte++) { for (int currByte = 0; currByte < 512; currByte++) {
@ -699,7 +727,7 @@ void writeSRAM_SMS() {
print_Msg(F("sramSize: ")); print_Msg(F("sramSize: "));
print_Msg(sramSize); print_Msg(sramSize);
println_Msg(FS(FSTRING_EMPTY)); println_Msg(FS(FSTRING_EMPTY));
word bankSize = 16 * 1024; word bankSize = 16384;
for (word address = 0x0; address < sramSize; address += 512) { for (word address = 0x0; address < sramSize; address += 512) {
byte currBank = address >= bankSize ? 1 : 0; byte currBank = address >= bankSize ? 1 : 0;
word page_address = address - (currBank * bankSize); word page_address = address - (currBank * bankSize);