cartreader/Cart_Reader/SMS.ino

430 lines
10 KiB
Arduino
Raw Normal View History

//******************************************
// SEGA MASTER SYSTEM MODULE
//******************************************
/******************************************
Variables
*****************************************/
/******************************************
Menu
*****************************************/
// MD menu items
static const char SMSMenuItem1[] PROGMEM = "Read Rom";
static const char SMSMenuItem2[] PROGMEM = "Read SRAM";
static const char SMSMenuItem3[] PROGMEM = "Toggle Retrode Mode";
static const char SMSMenuItem4[] PROGMEM = "Reset";
static const char* const menuOptionsSMS[] PROGMEM = {SMSMenuItem1, SMSMenuItem2, SMSMenuItem3, SMSMenuItem4};
2020-07-04 12:16:14 +02:00
// Set retrode_mode to true when using a retrode SMS/GG adapter
static bool retrode_mode = false;
void _smsMenu() {
// create menu with title and 2 options to choose from
unsigned char mainMenu;
// Copy menuOptions out of progmem
2020-07-04 12:16:14 +02:00
convertPgm(menuOptionsSMS, 3);
mainMenu = question_box(retrode_mode ? F("SMS/GG Retrode:YES") : F("SMS/GG Retrode:NO"), menuOptions, 3, 0);
// wait for user choice to come back from the question box menu
switch (mainMenu)
{
case 0:
display_Clear();
2019-09-27 23:01:45 +02:00
mode = mode_SMS;
2019-09-19 13:42:46 +02:00
setup_SMS();
// Change working dir to root
sd.chdir("/");
readROM_SMS();
break;
case 1:
2020-07-04 12:16:14 +02:00
display_Clear();
mode = mode_SMS;
setup_SMS();
// Change working dir to root
sd.chdir("/");
readSRAM_SMS();
2020-07-04 12:16:14 +02:00
break;
case 2:
retrode_mode = !retrode_mode;
break;
case 3:
// Reset
2020-04-12 11:11:10 +02:00
resetArduino();
break;
}
2020-07-04 12:16:14 +02:00
println_Msg(retrode_mode ? F("Retrode Mode On") : F("Retrode Mode Off"));
println_Msg(F(""));
println_Msg(F("Press Button..."));
display_Update();
wait();
}
void smsMenu() {
for (;;) _smsMenu();
}
/******************************************
Setup
*****************************************/
void setup_SMS() {
// Set Address Pins to Output
//A0-A7
DDRF = 0xFF;
2019-09-20 14:30:57 +02:00
//A8-A14
DDRK = 0xFF;
2019-09-20 14:30:57 +02:00
//A15
DDRH |= (1 << 3);
2020-07-04 12:16:14 +02:00
if (retrode_mode) {
// Revert changes from the other mode
PORTH &= ~((1 << 0) | (1 << 3) | (1 << 5));
PORTL &= ~(1 << 1);
DDRH &= ~((1 << 0) | (1 << 5));
DDRL &= ~((1 << 1));
2020-07-04 12:16:14 +02:00
// 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);
} else {
// Revert changes from the other mode
PORTL &= ~((1 << 5) | (1 << 6));
DDRL &= ~((1 << 5) | (1 << 6));
2020-07-04 12:16:14 +02:00
// 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);
}
2019-09-19 13:42:46 +02:00
// 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
2019-09-20 11:16:53 +02:00
// Disable sram
writeByte_SMS(0xFFFC, 0);
2019-09-19 13:42:46 +02:00
// Map first 3 banks so we can read-out the header info
writeByte_SMS(0xFFFD, 0);
writeByte_SMS(0xFFFE, 1);
writeByte_SMS(0xFFFF, 2);
2019-09-19 16:06:19 +02:00
delay(400);
// Print all the info
getCartInfo_SMS();
}
/******************************************
Low level functions
*****************************************/
2019-09-19 16:06:19 +02:00
void writeByte_SMS(word myAddress, byte myData) {
2020-07-04 12:16:14 +02:00
if (retrode_mode) {
// Set Data Pins (D8-D15) to Output
DDRA = 0xFF;
} else {
// Set Data Pins (D0-D7) to Output
DDRC = 0xFF;
}
2019-09-20 11:16:53 +02:00
2019-09-19 13:42:46 +02:00
// Set address
PORTF = myAddress & 0xFF;
PORTK = (myAddress >> 8) & 0xFF;
2020-07-04 12:16:14 +02:00
if (!retrode_mode) {
// CE(PH3) and OE(PH6) are connected
PORTH = (PORTH & 0b11110111) | ((myAddress >> 12) & 0b00001000);
}
2019-09-19 13:42:46 +02:00
// Output data
2020-07-04 12:16:14 +02:00
if (retrode_mode) {
PORTA = myData;
} else {
PORTC = myData;
}
// Arduino running at 16Mhz -> one nop = 62.5ns
// Wait till output is stable
2019-09-19 16:06:19 +02:00
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");
2020-07-04 12:16:14 +02:00
if (retrode_mode) {
// 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);
}
2020-07-04 12:16:14 +02:00
// Leave WR low for at least 60ns
2019-09-19 16:06:19 +02:00
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");
2020-07-04 12:16:14 +02:00
if (retrode_mode) {
// 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);
}
2020-07-04 12:16:14 +02:00
// Leave WR high for at least 50ns
2019-09-19 16:06:19 +02:00
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");
2019-09-19 13:42:46 +02:00
2020-07-04 12:16:14 +02:00
if (retrode_mode) {
// Set Data Pins (D8-D15) to Input
DDRA = 0x00;
} else {
// Set Data Pins (D0-D7) to Input
DDRC = 0x00;
}
}
2019-09-19 16:06:19 +02:00
byte readByte_SMS(word myAddress) {
2020-07-04 12:16:14 +02:00
if (retrode_mode) {
// Set Data Pins (D8-D15) to Input
DDRA = 0x00;
} else {
// Set Data Pins (D0-D7) to Input
DDRC = 0x00;
}
2019-09-19 13:42:46 +02:00
// Set Address
PORTF = myAddress & 0xFF;
PORTK = (myAddress >> 8) & 0xFF;
2020-07-04 12:16:14 +02:00
if (!retrode_mode) {
// CE(PH3) and OE(PH6) are connected
PORTH = (PORTH & 0b11110111) | ((myAddress >> 12) & 0b00001000);
}
2019-09-19 16:06:19 +02:00
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");
2020-07-04 12:16:14 +02:00
if (retrode_mode) {
// 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);
}
2019-09-19 16:06:19 +02:00
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");
// Read
2020-07-04 12:16:14 +02:00
byte tempByte = retrode_mode ? PINA : PINC;
if (retrode_mode) {
// 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);
}
2019-09-19 13:42:46 +02:00
2019-09-19 16:06:19 +02:00
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t");
return tempByte;
}
2019-09-19 16:06:19 +02:00
//unsigned char hex2bcd (unsigned char x) {
// unsigned char y;
// y = (x / 10) << 4;
// y = y | (x % 10);
//return (y);
//}
2019-09-19 13:42:46 +02:00
byte readNibble(byte data, byte number) {
return ((data >> (number * 4)) & 0xf);
}
/******************************************
MASTER SYSTEM functions
*****************************************/
void getCartInfo_SMS() {
// Rom size
2019-09-19 13:42:46 +02:00
switch (readNibble(readByte_SMS(0x7fff), 0)) {
case 0xa:
2019-09-10 17:38:56 +02:00
// Adding UL gets rid of integer overflow compiler warning
cartSize = 8 * 1024UL;
break;
case 0xb:
2019-09-10 17:38:56 +02:00
cartSize = 16 * 1024UL;
break;
case 0xc:
2019-09-10 17:38:56 +02:00
cartSize = 32 * 1024UL;
break;
case 0xd:
2019-09-10 17:38:56 +02:00
cartSize = 48 * 1024UL;
break;
case 0xe:
2019-09-10 17:38:56 +02:00
cartSize = 64 * 1024UL;
break;
case 0xf:
2019-09-10 17:38:56 +02:00
cartSize = 128 * 1024UL;
break;
case 0x0:
2020-07-04 12:16:14 +02:00
if (retrode_mode) {
cartSize = 512 * 1024UL;
} else {
cartSize = 256 * 1024UL;
}
break;
case 0x1:
2019-09-10 17:38:56 +02:00
cartSize = 512 * 1024UL;
break;
case 0x2:
2019-09-10 17:38:56 +02:00
cartSize = 512 * 1024UL;
break;
2019-09-19 16:06:19 +02:00
default:
cartSize = 48 * 1024UL;
// LED Error
rgb.setColor(0, 0, 255);
break;
}
2019-09-19 13:42:46 +02:00
// Read TMR Sega string
for (byte i = 0; i < 8; i++) {
romName[i] = char(readByte_SMS(0x7ff0 + i));
}
romName[8] = '\0';
display_Clear();
println_Msg(F("Cart Info"));
println_Msg(F(" "));
print_Msg(F("Name: "));
println_Msg(romName);
print_Msg(F("Size: "));
2019-09-19 16:06:19 +02:00
print_Msg(cartSize / 1024);
println_Msg(F("KB"));
println_Msg(F(" "));
2019-09-19 16:06:19 +02:00
if (strcmp(romName, "TMR SEGA") != 0) {
print_Error(F("Not working yet"), false);
sprintf(romName, "ERROR");
cartSize = 48 * 1024UL;
}
2019-09-19 13:42:46 +02:00
// Wait for user input
#ifdef enable_OLED
println_Msg(F("Press Button..."));
display_Update();
wait();
#endif
2019-09-27 23:01:45 +02:00
// Turn off LED
rgb.setColor(0, 0, 0);
}
// Read rom and save to the SD card
void readROM_SMS() {
// Get name, add extension and convert to char array for sd lib
strcpy(fileName, romName);
strcat(fileName, ".SMS");
// create a new folder
EEPROM_readAnything(0, foldern);
sprintf(folder, "SMS/ROM/%s/%d", romName, foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
display_Clear();
print_Msg(F("Saving to "));
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_Error(F("SD Error"), true);
}
2019-09-19 16:06:19 +02:00
word bankSize = 16 * 1024UL;
for (byte currBank = 0x0; currBank < (cartSize / bankSize); currBank++) {
2019-09-19 13:42:46 +02:00
// Write current 16KB bank to slot 2 register 0xFFFF
writeByte_SMS(0xFFFF, currBank);
// Blink led
2019-09-19 13:42:46 +02:00
PORTB ^= (1 << 4);
// Read 16KB from slot 2 which starts at 0x8000
2019-09-19 16:06:19 +02:00
for (word currBuffer = 0; currBuffer < bankSize; currBuffer += 512) {
2019-09-19 13:42:46 +02:00
// Fill SD buffer
for (int currByte = 0; currByte < 512; currByte++) {
sdBuffer[currByte] = readByte_SMS(0x8000 + currBuffer + currByte);
}
myFile.write(sdBuffer, 512);
}
}
// Close the file:
myFile.close();
}
// 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");
// create a new folder
EEPROM_readAnything(0, foldern);
sprintf(folder, "SMS/SAVE/%s/%d", romName, foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
display_Clear();
print_Msg(F("Saving to "));
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_Error(F("SD Error"), true);
}
// 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
PORTB ^= (1 << 4);
// 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 the file:
myFile.close();
}
//******************************************
// End of File
//******************************************