Merge pull request #256 from scrap-a/lock-on

Add support for Sonic & Knuckles Lock-on games
This commit is contained in:
sanni 2021-09-20 17:17:27 +02:00 committed by GitHub
commit dce2ffe691

View File

@ -89,6 +89,26 @@ boolean realtec = 0;
#define DEFAULT_VALUE_segaSram16bit 0 #define DEFAULT_VALUE_segaSram16bit 0
int segaSram16bit = DEFAULT_VALUE_segaSram16bit; int segaSram16bit = DEFAULT_VALUE_segaSram16bit;
/******************************************
SONIC & KNUCKLES LOCK-ON MODE VARIABLES
// SnKmode :
// 0 = Not Sonic & Knuckles
// 1 = Only Sonic & Knucles
// 2 = Sonic & Knucles + Sonic1
// 3 = Sonic & Knucles + Sonic2
// 4 = Sonic & Knucles + Sonic3
// 5 = Sonic & Knucles + Other game
*****************************************/
static byte SnKmode = 0;
static unsigned long cartSizeLockon;
static unsigned long cartSizeSonic2 = 262144;
static word chksumLockon;
static word chksumSonic2 = 0x0635;
static char romNameLockon[12];
static char id[15];
static char idLockon[15];
static char labelLockon[17];
/****************************************** /******************************************
Configuration Configuration
*****************************************/ *****************************************/
@ -628,6 +648,90 @@ void getCartInfo_MD() {
} }
} }
// Sonic & Knuckles Check
SnKmode = 0;
if (chksum == 0xDFB3) {
// Get ID
for (byte c = 0; c < 14; c += 2) {
// split word
word myWord = readWord_MD((0x180 + c) / 2);
byte loByte = myWord & 0xFF;
byte hiByte = myWord >> 8;
// write to buffer
sdBuffer[c] = hiByte;
sdBuffer[c + 1] = loByte;
}
for(int i=0; i<14; i++){
id[i] = char(sdBuffer[i]);
}
//Sonic & Knuckles ID:GM MK-1563 -00
if (!strcmp("GM MK-1563 -00", id)) {
// Get labelLockon
for (byte c = 0; c < 16; c += 2) {
// split word
word myWord = readWord_MD((0x200100 + c) / 2);
byte loByte = myWord & 0xFF;
byte hiByte = myWord >> 8;
// write to buffer
sdBuffer[c] = hiByte;
sdBuffer[c + 1] = loByte;
}
for(int i=0; i<16; i++){
labelLockon[i] = char(sdBuffer[i]);
}
// check Lock-on game presence
if (!(strcmp("SEGA MEGA DRIVE ", labelLockon) & strcmp("SEGA GENESIS ", labelLockon))) {
// Lock-on cart checksum
chksumLockon = readWord_MD(0x1000C7);
// Lock-on cart size
cartSizeLockon = ((long(readWord_MD(0x1000D2)) << 16) | readWord_MD(0x1000D3)) + 1;
// Get IdLockon
for (byte c = 0; c < 14; c += 2) {
// split word
word myWord = readWord_MD((0x200180 + c) / 2);
byte loByte = myWord & 0xFF;
byte hiByte = myWord >> 8;
// write to buffer
sdBuffer[c] = hiByte;
sdBuffer[c + 1] = loByte;
}
for(int i=0; i<14; i++){
idLockon[i] = char(sdBuffer[i]);
}
if (!(strncmp("GM 00001009-0", idLockon,13) & strncmp("GM 00004049-0", idLockon,13) )) {
//Sonic1 ID:GM 00001009-0? or GM 00004049-0?
SnKmode = 2;
}else if (!(strcmp("GM 00001051-00", idLockon) & strcmp("GM 00001051-01", idLockon) & strcmp("GM 00001051-02", idLockon))) {
//Sonic2 ID:GM 00001051-00 or GM 00001051-01 or GM 00001051-02
SnKmode = 3;
// Prepare Sonic2 Banks
writeSSF2Map(0x509878, 1); // 0xA130F1
}else if (!strcmp("GM MK-1079 -00", idLockon)) {
//Sonic1 ID:GM MK-1079 -00
SnKmode = 4;
}else{
//Other game
SnKmode = 5;
}
}else{
SnKmode = 1;
}
}
}
// Serial EEPROM Check // Serial EEPROM Check
for (int i = 0; i < eepcount; i++) { for (int i = 0; i < eepcount; i++) {
int index = i * 2; int index = i * 2;
@ -821,6 +925,39 @@ void getCartInfo_MD() {
} }
} }
//Get Lock-on cart name
if(SnKmode>=2){
//Change romName
strcpy(romName,"SnK_");
for (byte c = 0; c < 48; c += 2) {
// split word
word myWord = readWord_MD((0x200150 + c) / 2);
byte loByte = myWord & 0xFF;
byte hiByte = myWord >> 8;
// write to buffer
sdBuffer[c] = hiByte;
sdBuffer[c + 1] = loByte;
}
byte myLength = 0;
for (unsigned int i = 0; i < 48; i++) {
if (((char(sdBuffer[i]) >= 48 && char(sdBuffer[i]) <= 57) || (char(sdBuffer[i]) >= 65 && char(sdBuffer[i]) <= 122)) && myLength < 11) {
romNameLockon[myLength] = char(sdBuffer[i]);
myLength++;
}
}
switch(SnKmode){
case 2: strcat(romName,"SONIC1"); break;
case 3: strcat(romName,"SONIC2"); break;
case 4: strcat(romName,"SONIC3"); break;
case 5: strcat(romName,romNameLockon); break;
}
}
// Realtec Mapper Check // Realtec Mapper Check
word realtecCheck1 = readWord_MD(0x3F080); // 0x7E100 "SEGA" (BootROM starts at 0x7E000) word realtecCheck1 = readWord_MD(0x3F080); // 0x7E100 "SEGA" (BootROM starts at 0x7E000)
word realtecCheck2 = readWord_MD(0x3F081); word realtecCheck2 = readWord_MD(0x3F081);
@ -846,10 +983,41 @@ void getCartInfo_MD() {
} }
print_Msg(F("Size: ")); print_Msg(F("Size: "));
print_Msg(cartSize * 8 / 1024 / 1024 ); print_Msg(cartSize * 8 / 1024 / 1024 );
switch(SnKmode){
case 2:
case 4:
case 5:
print_Msg(F("+"));
print_Msg(cartSizeLockon * 8 / 1024 / 1024 );
break;
case 3:
print_Msg(F("+"));
print_Msg(cartSizeLockon * 8 / 1024 / 1024 );
print_Msg(F("+"));
print_Msg(cartSizeSonic2 * 8 / 1024 / 1024 );
break;
}
println_Msg(F(" MBit")); println_Msg(F(" MBit"));
print_Msg(F("ChkS: ")); print_Msg(F("ChkS: "));
print_Msg_PaddedHexByte((chksum >> 8)); print_Msg_PaddedHexByte((chksum >> 8));
print_Msg_PaddedHexByte((chksum & 0x00ff)); print_Msg_PaddedHexByte((chksum & 0x00ff));
switch(SnKmode){
case 2:
case 4:
case 5:
print_Msg(F("+"));
print_Msg_PaddedHexByte((chksumLockon >> 8));
print_Msg_PaddedHexByte((chksumLockon & 0x00ff));
break;
case 3:
print_Msg(F("+"));
print_Msg_PaddedHexByte((chksumLockon >> 8));
print_Msg_PaddedHexByte((chksumLockon & 0x00ff));
print_Msg(F("+"));
print_Msg_PaddedHexByte((chksumSonic2 >> 8));
print_Msg_PaddedHexByte((chksumSonic2 & 0x00ff));
break;
}
println_Msg(F("")); println_Msg(F(""));
if (saveType == 4) { if (saveType == 4) {
print_Msg(F("Serial EEPROM: ")); print_Msg(F("Serial EEPROM: "));
@ -913,6 +1081,8 @@ void writeSSF2Map(unsigned long myAddress, word myData) {
void readROM_MD() { void readROM_MD() {
// Checksum // Checksum
uint16_t calcCKS = 0; uint16_t calcCKS = 0;
uint16_t calcCKSLockon = 0;
uint16_t calcCKSSonic2 = 0;
// Set control // Set control
dataIn_MD(); dataIn_MD();
@ -958,6 +1128,8 @@ void readROM_MD() {
//Initialize progress bar //Initialize progress bar
uint32_t processedProgressBar = 0; uint32_t processedProgressBar = 0;
uint32_t totalProgressBar = (uint32_t)(cartSize); uint32_t totalProgressBar = (uint32_t)(cartSize);
if(SnKmode>=2) totalProgressBar += (uin32_t) cartSizeLockon;
if(SnKmode==3) totalProgressBar += (uin32_t) cartSizeSonic2;
draw_progressbar(0, totalProgressBar); draw_progressbar(0, totalProgressBar);
for (unsigned long currBuffer = 0; currBuffer < cartSize / 2; currBuffer += 512) { for (unsigned long currBuffer = 0; currBuffer < cartSize / 2; currBuffer += 512) {
@ -1012,6 +1184,93 @@ void readROM_MD() {
processedProgressBar += 1024; processedProgressBar += 1024;
draw_progressbar(processedProgressBar, totalProgressBar); draw_progressbar(processedProgressBar, totalProgressBar);
} }
if(SnKmode >= 2){
for (unsigned long currBuffer = 0; currBuffer < cartSizeLockon / 2; currBuffer += 512) {
// Blink led
if (currBuffer % 16384 == 0)
PORTB ^= (1 << 4);
d = 0;
for (int currWord = 0; currWord < 512; currWord++) {
unsigned long myAddress = currBuffer + currWord + cartSize / 2;
PORTF = myAddress & 0xFF;
PORTK = (myAddress >> 8) & 0xFF;
PORTL = (myAddress >> 16) & 0xFF;
// Arduino running at 16Mhz -> one nop = 62.5ns
NOP;
// Setting CS(PH3) LOW
PORTH &= ~(1 << 3);
// Setting OE(PH6) LOW
PORTH &= ~(1 << 6);
// most MD ROMs are 200ns, comparable to SNES > use similar access delay of 6 x 62.5 = 375ns
NOP; NOP; NOP; NOP; NOP; NOP;
// Read
buffer[d] = PINA;
buffer[d + 1] = PINC;
// Setting CS(PH3) HIGH
PORTH |= (1 << 3);
// Setting OE(PH6) HIGH
PORTH |= (1 << 6);
// Skip first 256 words
if (((currBuffer == 0) && (currWord >= 256)) || (currBuffer > 0)) {
calcCKSLockon += ((buffer[d] << 8) | buffer[d + 1]);
}
d += 2;
}
myFile.write(buffer, 1024);
// update progress bar
processedProgressBar += 1024;
draw_progressbar(processedProgressBar, totalProgressBar);
}
}
if(SnKmode == 3){
for (unsigned long currBuffer = 0; currBuffer < cartSizeSonic2 / 2; currBuffer += 512) {
// Blink led
if (currBuffer % 16384 == 0)
PORTB ^= (1 << 4);
d = 0;
for (int currWord = 0; currWord < 512; currWord++) {
unsigned long myAddress = currBuffer + currWord + (cartSize+cartSizeLockon) / 2;
PORTF = myAddress & 0xFF;
PORTK = (myAddress >> 8) & 0xFF;
PORTL = (myAddress >> 16) & 0xFF;
// Arduino running at 16Mhz -> one nop = 62.5ns
NOP;
// Setting CS(PH3) LOW
PORTH &= ~(1 << 3);
// Setting OE(PH6) LOW
PORTH &= ~(1 << 6);
// most MD ROMs are 200ns, comparable to SNES > use similar access delay of 6 x 62.5 = 375ns
NOP; NOP; NOP; NOP; NOP; NOP;
// Read
buffer[d] = PINA;
buffer[d + 1] = PINC;
// Setting CS(PH3) HIGH
PORTH |= (1 << 3);
// Setting OE(PH6) HIGH
PORTH |= (1 << 6);
calcCKSSonic2 += ((buffer[d] << 8) | buffer[d + 1]);
d += 2;
}
myFile.write(buffer, 1024);
// update progress bar
processedProgressBar += 1024;
draw_progressbar(processedProgressBar, totalProgressBar);
}
}
// Close the file: // Close the file:
myFile.close(); myFile.close();
@ -1040,6 +1299,34 @@ void readROM_MD() {
print_Error(F(""), false); print_Error(F(""), false);
display_Update(); display_Update();
} }
if(SnKmode >= 2){
if (chksumLockon == calcCKSLockon) {
println_Msg(F("Checksum2 OK"));
display_Update();
}
else {
print_Msg(F("Checksum2 Error: "));
char calcsumStr[5];
sprintf(calcsumStr, "%04X", calcCKSLockon);
println_Msg(calcsumStr);
print_Error(F(""), false);
display_Update();
}
}
if(SnKmode == 3){
if (chksumSonic2 == calcCKSSonic2) {
println_Msg(F("Checksum3 OK"));
display_Update();
}
else {
print_Msg(F("Checksum3 Error: "));
char calcsumStr[5];
sprintf(calcsumStr, "%04X", calcCKSSonic2);
println_Msg(calcsumStr);
print_Error(F(""), false);
display_Update();
}
}
} }
/****************************************** /******************************************