mirror of
https://github.com/sanni/cartreader.git
synced 2024-09-21 15:49:45 +02:00
580 lines
14 KiB
C++
580 lines
14 KiB
C++
//******************************************
|
|
// CASIO LOOPY MODULE
|
|
//******************************************
|
|
#ifdef enable_LOOPY
|
|
|
|
// SH-1 memory map locations, ROM starts here
|
|
const uint32_t LOOPY_MAP_ROM_ZERO = 0x0E000000;
|
|
const uint32_t LOOPY_MAP_SRAM_ZERO = 0x02000000;
|
|
const uint32_t LOOPY_SRAM_SIZE = 0x2000;
|
|
|
|
//******************************************
|
|
// SETUP
|
|
//******************************************
|
|
|
|
void setup_LOOPY() {
|
|
// Request 5V
|
|
setVoltage(VOLTS_SET_5V);
|
|
|
|
// Set Address Pins to Output
|
|
// PK1-PK7, PA1-PA7, PC0-PC3, PL0-PL3 // Take whole port and unset the exceptions later
|
|
DDRK = DDRA = DDRC = DDRL = 0xFF;
|
|
|
|
// Set Control Pins to Output
|
|
// /RAMWE(PH6)/RAMCS2(PH4)
|
|
DDRH |= (1 << 6) | (1 << 4);
|
|
// /CE(PL7) /OE(PL6)
|
|
DDRL |= (1 << 7) | (1 << 6);
|
|
|
|
// Set Pins (D0-D15) to Input
|
|
dataIn_LOOPY();
|
|
|
|
// Reset HIGH (PF7)
|
|
DDRF |= (1 << 7);
|
|
PORTF |= (1 << 7);
|
|
|
|
strcpy(romName, "LOOPY");
|
|
getCartInfo_LOOPY();
|
|
|
|
mode = mode_LOOPY;
|
|
}
|
|
|
|
//******************************************
|
|
// MENU
|
|
//******************************************
|
|
|
|
// Base Menu
|
|
static const char loopyMenuItem1[] PROGMEM = "Read ROM";
|
|
static const char loopyMenuItem2[] PROGMEM = "Read SRAM";
|
|
static const char loopyMenuItem3[] PROGMEM = "Write SRAM";
|
|
//static const char loopyMenuItem4[] PROGMEM = "Reset"; (stored in common strings array)
|
|
static const char* const menuOptionsLOOPY[] PROGMEM = { loopyMenuItem1, loopyMenuItem2, loopyMenuItem3, string_reset2 };
|
|
|
|
void loopyMenu() {
|
|
convertPgm(menuOptionsLOOPY, 4);
|
|
uint8_t mainMenu = question_box(F("CASIO LOOPY MENU"), menuOptions, 4, 0);
|
|
|
|
switch (mainMenu) {
|
|
case 0:
|
|
// Read ROM
|
|
sd.chdir("/");
|
|
readROM_LOOPY();
|
|
sd.chdir("/");
|
|
break;
|
|
|
|
case 1:
|
|
// Read SRAM
|
|
if (sramSize) {
|
|
sd.chdir("/");
|
|
display_Clear();
|
|
println_Msg(F("Reading SRAM..."));
|
|
display_Update();
|
|
readSRAM_LOOPY();
|
|
sd.chdir("/");
|
|
} else {
|
|
print_Error(F("Cart has no SRAM"));
|
|
}
|
|
#if (defined(enable_OLED) || defined(enable_LCD))
|
|
// Wait for user input
|
|
// Prints string out of the common strings array either with or without newline
|
|
print_STR(press_button_STR, 1);
|
|
display_Update();
|
|
wait();
|
|
#endif
|
|
break;
|
|
|
|
case 2:
|
|
// Write SRAM
|
|
if (sramSize) {
|
|
// Change working dir to root
|
|
sd.chdir("/");
|
|
fileBrowser(F("Select SAV file"));
|
|
display_Clear();
|
|
writeSRAM_LOOPY();
|
|
writeErrors = verifySRAM_LOOPY();
|
|
if (writeErrors == 0) {
|
|
println_Msg(F("SRAM verified OK"));
|
|
display_Update();
|
|
} else {
|
|
print_STR(error_STR, 0);
|
|
print_Msg(writeErrors);
|
|
print_STR(_bytes_STR, 1);
|
|
print_Error(did_not_verify_STR);
|
|
}
|
|
} else {
|
|
print_Error(F("Cart has no SRAM"));
|
|
}
|
|
#if (defined(enable_OLED) || defined(enable_LCD))
|
|
// Wait for user input
|
|
// Prints string out of the common strings array either with or without newline
|
|
print_STR(press_button_STR, 1);
|
|
display_Update();
|
|
wait();
|
|
#endif
|
|
break;
|
|
|
|
case 3:
|
|
// reset
|
|
resetArduino();
|
|
break;
|
|
}
|
|
}
|
|
|
|
//******************************************
|
|
// LOW LEVEL FUNCTIONS
|
|
//******************************************
|
|
|
|
void setAddress_LOOPY(unsigned long A) {
|
|
// PK1 A0
|
|
// PK2 A1
|
|
// PK3 A21
|
|
// PK4 A3
|
|
// PK5 A20
|
|
// PK6 A15
|
|
// PK7 A13
|
|
PORTK = (bitRead(A, 0) << 1)
|
|
| (bitRead(A, 1) << 2)
|
|
| (bitRead(A, 21) << 3)
|
|
| (bitRead(A, 3) << 4)
|
|
| (bitRead(A, 20) << 5)
|
|
| (bitRead(A, 15) << 6)
|
|
| (bitRead(A, 13) << 7);
|
|
// PA1 A2
|
|
// PA2 A4
|
|
// PA3 A19
|
|
// PA4 A18
|
|
// PA5 A16
|
|
// PA6 A17
|
|
// PA7 A14
|
|
PORTA = (bitRead(A, 2) << 1)
|
|
| (bitRead(A, 4) << 2)
|
|
| (bitRead(A, 19) << 3)
|
|
| (bitRead(A, 18) << 4)
|
|
| (bitRead(A, 16) << 5)
|
|
| (bitRead(A, 17) << 6)
|
|
| (bitRead(A, 14) << 7);
|
|
// PC0 A6
|
|
// PC1 A8
|
|
// PC2 A10
|
|
// PC3 A12
|
|
PORTC = (bitRead(A, 6))
|
|
| (bitRead(A, 8) << 1)
|
|
| (bitRead(A, 10) << 2)
|
|
| (bitRead(A, 12) << 3);
|
|
// PL0 A5
|
|
// PL1 A7
|
|
// PL2 A9
|
|
// PL3 A11
|
|
PORTL = (bitRead(A, 5))
|
|
| (bitRead(A, 7) << 1)
|
|
| (bitRead(A, 9) << 2)
|
|
| (bitRead(A, 11) << 3);
|
|
}
|
|
|
|
uint16_t getWord_LOOPY() {
|
|
return digitalRead(A8)
|
|
| (digitalRead(22) << 1)
|
|
| (digitalRead(A6) << 2)
|
|
| (digitalRead(A5) << 3)
|
|
| (digitalRead(A3) << 4)
|
|
| (digitalRead(40) << 5)
|
|
| (digitalRead(A2) << 6)
|
|
| (digitalRead(41) << 7)
|
|
| (digitalRead(A1) << 8)
|
|
| (digitalRead(3) << 9)
|
|
| (digitalRead(A0) << 10)
|
|
| (digitalRead(2) << 11)
|
|
| (digitalRead(14) << 12)
|
|
| (digitalRead(15) << 13)
|
|
| (digitalRead(A4) << 14)
|
|
| (digitalRead(4) << 15);
|
|
}
|
|
|
|
uint8_t getByte_LOOPY() {
|
|
return digitalRead(A8)
|
|
| (digitalRead(22) << 1)
|
|
| (digitalRead(A6) << 2)
|
|
| (digitalRead(A5) << 3)
|
|
| (digitalRead(A3) << 4)
|
|
| (digitalRead(40) << 5)
|
|
| (digitalRead(A2) << 6)
|
|
| (digitalRead(41) << 7);
|
|
}
|
|
|
|
void setByte_LOOPY(uint8_t D) {
|
|
digitalWrite(A8, bitRead(D, 0));
|
|
digitalWrite(22, bitRead(D, 1));
|
|
digitalWrite(A6, bitRead(D, 2));
|
|
digitalWrite(A5, bitRead(D, 3));
|
|
digitalWrite(A3, bitRead(D, 4));
|
|
digitalWrite(40, bitRead(D, 5));
|
|
digitalWrite(A2, bitRead(D, 6));
|
|
digitalWrite(41, bitRead(D, 7));
|
|
}
|
|
|
|
void writeByte_LOOPY(unsigned long myAddress, byte myData) {
|
|
setAddress_LOOPY(myAddress);
|
|
|
|
__asm__("nop\n\t");
|
|
|
|
PORTA = myData;
|
|
|
|
// Set CS2(PH0), /CE(PH3), /OE(PH6) to HIGH
|
|
PORTH |= (1 << 0) | (1 << 3) | (1 << 6);
|
|
// Set /CS1(PH4), /WE0(PH5) to LOW
|
|
PORTH &= ~(1 << 4) & ~(1 << 5);
|
|
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
|
|
// Set CS2(PH0), /CS1(PH4), /WE0(PH5) to HIGH
|
|
PORTH |= (1 << 0) | (1 << 4) | (1 << 5);
|
|
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
}
|
|
|
|
word readWord_LOOPY(unsigned long myAddress) {
|
|
setAddress_LOOPY(myAddress);
|
|
|
|
__asm__("nop\n\t");
|
|
|
|
// Set CS2(PH0), /CS1(PH4), /WE0(PH5) to HIGH
|
|
PORTH |= (1 << 0) | (1 << 4) | (1 << 5);
|
|
// Set /CE(PH3), /OE(PH6) to LOW
|
|
PORTH &= ~(1 << 3) & ~(1 << 6);
|
|
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
|
|
word tempWord = ((PINA & 0xFF) << 8) | (PINC & 0xFF);
|
|
|
|
// Set /CE(PH3), /OE(PH6) to HIGH
|
|
PORTH |= (1 << 3) | (1 << 6);
|
|
// Setting CS2(PH0) LOW
|
|
PORTH &= ~(1 << 0);
|
|
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
|
|
return tempWord;
|
|
}
|
|
|
|
byte readByte_LOOPY(unsigned long myAddress) { // SRAM BYTE
|
|
setAddress_LOOPY(myAddress);
|
|
|
|
__asm__("nop\n\t");
|
|
|
|
// Set CS2(PH0), /CE(PH3), /WE0(PH5) to HIGH
|
|
PORTH |= (1 << 0) | (1 << 3) | (1 << 5);
|
|
// Set /CS1(PH4), /OE(PH6) to LOW
|
|
PORTH &= ~(1 << 4) & ~(1 << 6);
|
|
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
|
|
byte tempByte = PINA;
|
|
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
|
|
// Set /CS1(PH4), /OE(PH6) to HIGH
|
|
PORTH |= (1 << 3) | (1 << 6);
|
|
// Setting CS2(PH0) LOW
|
|
PORTH &= ~(1 << 0);
|
|
|
|
__asm__("nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t"
|
|
"nop\n\t");
|
|
|
|
return tempByte;
|
|
}
|
|
|
|
// Switch data pins to write
|
|
void dataOut_LOOPY() {
|
|
// // PA0
|
|
// DDRA |= 0x01;
|
|
// // PK0
|
|
// DDRK |= 0x01;
|
|
// // PG0, PG1, PG5 (rest unused?)
|
|
// DDRG = 0xFF;
|
|
// // PJ0-1 (rest unused?)
|
|
// DDRJ = 0xFF;
|
|
// // PE4-PE5 (rest unused?)
|
|
// DDRE = 0xFF;
|
|
// // PF0-PF6
|
|
// DDRF |= 0b0111111;
|
|
|
|
pinMode(A8, OUTPUT);
|
|
pinMode(22, OUTPUT);
|
|
pinMode(A6, OUTPUT);
|
|
pinMode(A5, OUTPUT);
|
|
pinMode(A3, OUTPUT);
|
|
pinMode(40, OUTPUT);
|
|
pinMode(A2, OUTPUT);
|
|
pinMode(41, OUTPUT);
|
|
pinMode(A1, OUTPUT);
|
|
pinMode(3, OUTPUT);
|
|
pinMode(A0, OUTPUT);
|
|
pinMode(2, OUTPUT);
|
|
pinMode(14, OUTPUT);
|
|
pinMode(15, OUTPUT);
|
|
pinMode(A4, OUTPUT);
|
|
pinMode(4, OUTPUT);
|
|
}
|
|
|
|
// Switch data pins to read
|
|
void dataIn_LOOPY() {
|
|
// // PA0
|
|
// DDRA &= ~0x01;
|
|
// // PK0
|
|
// DDRK &= ~0x01;
|
|
// // PG0, PG1, PG5 (rest unused?)
|
|
// DDRG = 0x00;
|
|
// // PJ0-1 (rest unused?)
|
|
// DDRJ = 0x00;
|
|
// // PE4-PE5 (rest unused?)
|
|
// DDRE = 0x00;
|
|
// // PF0-PF6
|
|
// DDRF &= ~0b0111111;
|
|
pinMode(A8, INPUT);
|
|
pinMode(22, INPUT);
|
|
pinMode(A6, INPUT);
|
|
pinMode(A5, INPUT);
|
|
pinMode(A3, INPUT);
|
|
pinMode(40, INPUT);
|
|
pinMode(A2, INPUT);
|
|
pinMode(41, INPUT);
|
|
pinMode(A1, INPUT);
|
|
pinMode(3, INPUT);
|
|
pinMode(A0, INPUT);
|
|
pinMode(2, INPUT);
|
|
pinMode(14, INPUT);
|
|
pinMode(15, INPUT);
|
|
pinMode(A4, INPUT);
|
|
pinMode(4, INPUT);
|
|
}
|
|
|
|
//******************************************
|
|
// CART INFO
|
|
//******************************************
|
|
|
|
void getCartInfo_LOOPY() {
|
|
// Set control
|
|
dataIn_LOOPY();
|
|
|
|
// Last word of ROM stored as 32-bit pointer at 000004h
|
|
// TODO make sure you have endianness right when interpreting this
|
|
uint32_t headerRomSize = ((uint32_t)readWord_LOOPY(0x4) << 16 | (uint32_t)readWord_LOOPY(0x6));
|
|
cartSize = headerRomSize - LOOPY_MAP_ROM_ZERO + 2;
|
|
|
|
// Get internal CRC32 from header
|
|
uint32_t cartHeaderCrc = (uint32_t)readWord_LOOPY(0x8) << 16 | (uint32_t)readWord_LOOPY(0xA);
|
|
snprintf(checksumStr, 8, "%08X", cartHeaderCrc);
|
|
|
|
// Look up in database
|
|
// compareCRC("loopy.txt", cartId, false, 0);
|
|
|
|
// SRAM size can be calculated from subtracting 32bit pointers 000014h (last byte of sram, memory mapped) - 000010h (first byte of sram, memory mapped)
|
|
// But this is fine
|
|
sramSize = LOOPY_SRAM_SIZE;
|
|
|
|
display_Clear();
|
|
println_Msg(F("Cart Info"));
|
|
println_Msg(F(" "));
|
|
// print_Msg(F("Name: "));
|
|
// println_Msg(romName);
|
|
print_Msg(F("CRC32: "));
|
|
println_Msg(checksumStr);
|
|
print_Msg(F("Size: "));
|
|
print_Msg(cartSize * 8 / 1024 / 1024);
|
|
println_Msg(F(" MBit"));
|
|
print_Msg(F("Sram: "));
|
|
if (sramSize > 0) {
|
|
print_Msg(sramSize * 8 / 1024);
|
|
println_Msg(F(" KBit"));
|
|
} else
|
|
println_Msg(F("None"));
|
|
println_Msg(F(" "));
|
|
|
|
#if (defined(enable_OLED) || defined(enable_LCD))
|
|
// Wait for user input
|
|
// Prints string out of the common strings array either with or without newline
|
|
print_STR(press_button_STR, 1);
|
|
display_Update();
|
|
wait();
|
|
#endif
|
|
}
|
|
|
|
//******************************************
|
|
// READ CODE
|
|
//******************************************
|
|
|
|
void readROM_LOOPY() {
|
|
dataIn_LOOPY();
|
|
|
|
strcpy(fileName, romName);
|
|
strcat(fileName, ".bin");
|
|
|
|
EEPROM_readAnything(0, foldern);
|
|
sprintf(folder, "LOOPY/ROM/%s/%d", romName, foldern);
|
|
sd.mkdir(folder, true);
|
|
sd.chdir(folder);
|
|
|
|
display_Clear();
|
|
print_STR(saving_to_STR, 0);
|
|
print_Msg(folder);
|
|
println_Msg(F("/..."));
|
|
display_Update();
|
|
|
|
foldern = foldern + 1;
|
|
EEPROM_writeAnything(0, foldern);
|
|
|
|
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
|
|
print_FatalError(sd_error_STR);
|
|
}
|
|
|
|
word d = 0;
|
|
uint32_t progress = 0;
|
|
draw_progressbar(0, cartSize);
|
|
|
|
for (unsigned long currBuffer = 0; currBuffer < cartSize / 2; currBuffer += 256) {
|
|
for (int currWord = 0; currWord < 256; currWord++) {
|
|
word myWord = readWord_LOOPY(currBuffer + currWord);
|
|
// Split word into two bytes
|
|
sdBuffer[d] = ((myWord >> 8) & 0xFF);
|
|
sdBuffer[d + 1] = (myWord & 0xFF);
|
|
d += 2;
|
|
}
|
|
myFile.write(sdBuffer, 512);
|
|
d = 0;
|
|
progress += 512;
|
|
draw_progressbar(progress, cartSize);
|
|
}
|
|
myFile.close();
|
|
|
|
// Compare CRC32 to database and rename ROM if found
|
|
// Arguments: database name, precalculated crc string or 0 to calculate, rename rom or not, starting offset
|
|
compareCRC("loopy.txt", 0, 1, 0);
|
|
|
|
#if (defined(enable_OLED) || defined(enable_LCD))
|
|
// Wait for user input
|
|
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();
|
|
#endif
|
|
}
|
|
|
|
//******************************************
|
|
// SRAM
|
|
//******************************************
|
|
|
|
void writeSRAM_LOOPY() {
|
|
dataOut_LOOPY();
|
|
|
|
sprintf(filePath, "%s/%s", filePath, fileName);
|
|
println_Msg(F("Writing..."));
|
|
println_Msg(filePath);
|
|
display_Update();
|
|
|
|
if (myFile.open(filePath, O_READ)) {
|
|
for (unsigned long currByte = 0; currByte < sramSize; currByte++) {
|
|
// writeWord_LOOPY(currByte, ((myFile.read() << 8 ) & 0xFF));
|
|
writeByte_LOOPY(currByte, (myFile.read()));
|
|
}
|
|
myFile.close();
|
|
print_STR(done_STR, 1);
|
|
display_Update();
|
|
} else {
|
|
print_FatalError(sd_error_STR);
|
|
}
|
|
dataIn_LOOPY();
|
|
}
|
|
|
|
void readSRAM_LOOPY() {
|
|
dataIn_LOOPY();
|
|
|
|
strcpy(fileName, romName);
|
|
strcat(fileName, ".sav");
|
|
|
|
EEPROM_readAnything(0, foldern);
|
|
sprintf(folder, "LOOPY/SAVE/%s/%d", romName, foldern);
|
|
sd.mkdir(folder, true);
|
|
sd.chdir(folder);
|
|
|
|
foldern = foldern + 1;
|
|
EEPROM_writeAnything(0, foldern);
|
|
|
|
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
|
|
print_FatalError(sd_error_STR);
|
|
}
|
|
for (unsigned long currBuffer = 0; currBuffer < sramSize; currBuffer += 512) {
|
|
for (int currByte = 0; currByte < 512; currByte++) {
|
|
byte myByte = readByte_LOOPY(currBuffer + currByte);
|
|
sdBuffer[currByte] = myByte;
|
|
}
|
|
myFile.write(sdBuffer, 512);
|
|
}
|
|
myFile.close();
|
|
print_Msg(F("Saved to "));
|
|
print_Msg(folder);
|
|
println_Msg(F("/"));
|
|
display_Update();
|
|
}
|
|
|
|
unsigned long verifySRAM_LOOPY() {
|
|
dataIn_LOOPY();
|
|
writeErrors = 0;
|
|
|
|
if (myFile.open(filePath, O_READ)) {
|
|
for (unsigned long currBuffer = 0; currBuffer < sramSize; currBuffer += 512) {
|
|
for (int currByte = 0; currByte < 512; currByte++) {
|
|
byte myByte = readByte_LOOPY(currBuffer + currByte);
|
|
sdBuffer[currByte] = myByte;
|
|
}
|
|
for (int i = 0; i < 512; i++) {
|
|
if (myFile.read() != sdBuffer[i]) {
|
|
writeErrors++;
|
|
}
|
|
}
|
|
}
|
|
myFile.close();
|
|
} else {
|
|
print_FatalError(sd_error_STR);
|
|
}
|
|
|
|
return writeErrors;
|
|
}
|
|
#endif
|
|
//******************************************
|
|
// End of File
|
|
//******************************************
|