diff --git a/Cart_Reader/Cart_Reader.ino b/Cart_Reader/Cart_Reader.ino index a9a1c41..b57cf1a 100644 --- a/Cart_Reader/Cart_Reader.ino +++ b/Cart_Reader/Cart_Reader.ino @@ -146,6 +146,9 @@ char ver[5] = "12.3"; // WonderSwan //#define enable_WS +// Super A'can +#define enable_SUPRACAN + //****************************************** // HW CONFIGS //****************************************** @@ -409,6 +412,8 @@ void print_STR(byte string_number, boolean newline) { #define mode_ARC 30 #define mode_FAIRCHILD 31 +#define mode_SUPRACAN 99 + // optimization-safe nop delay #define NOP __asm__ __volatile__("nop\n\t") @@ -1002,8 +1007,9 @@ static const char modeItem16[] PROGMEM = "Magnavox Odyssey 2"; static const char modeItem17[] PROGMEM = "Arcadia 2001"; static const char modeItem18[] PROGMEM = "Fairchild Channel F"; static const char modeItem19[] PROGMEM = "Flashrom Programmer"; +static const char modeItem99[] PROGMEM = "Super A'can"; static const char modeItem20[] PROGMEM = "About"; -static const char* const modeOptions[] PROGMEM = { modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7, modeItem8, modeItem9, modeItem10, modeItem11, modeItem12, modeItem13, modeItem14, modeItem15, modeItem16, modeItem17, modeItem18, modeItem19, modeItem20 }; +static const char* const modeOptions[] PROGMEM = { modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7, modeItem8, modeItem9, modeItem10, modeItem11, modeItem12, modeItem13, modeItem14, modeItem15, modeItem16, modeItem17, modeItem18, modeItem99, modeItem19, modeItem20 }; // All included slots void mainMenu() { @@ -1171,6 +1177,11 @@ void mainMenu() { case 17: setup_FAIRCHILD(); fairchildMenu(); + +#ifdef enable_SUPRACAN + case 99: + setup_SuprAcan(); + mode = mode_SUPRACAN; break; #endif @@ -1236,6 +1247,10 @@ static const char consoles80Item3[] PROGMEM = "SMS/GG/MIII/SG-1000"; //static const char consoles80Item4[] PROGMEM = "Reset"; (stored in common strings array) static const char* const consoles80Options[] PROGMEM = { consoles80Item1, consoles80Item2, consoles80Item3, string_reset2 }; +// 90s Consoles submenu +static const char consoles90Item1[] PROGMEM = "Super A'can"; +static const char* const consoles90Options[] PROGMEM = { consoles90Item1, string_reset2 }; + // Handhelds submenu static const char handheldsItem1[] PROGMEM = "Virtual Boy"; static const char handheldsItem2[] PROGMEM = "WonderSwan"; @@ -1347,7 +1362,6 @@ void consoles70Menu() { // Copy menuOptions out of progmem convertPgm(consoles70Options, 7); consoles70Menu = question_box(F("Choose Adapter"), menuOptions, 7, 0); - // wait for user choice to come back from the question box menu switch (consoles70Menu) { #ifdef enable_ATARI diff --git a/Cart_Reader/SUPRACAN.ino b/Cart_Reader/SUPRACAN.ino new file mode 100644 index 0000000..4cc8a8d --- /dev/null +++ b/Cart_Reader/SUPRACAN.ino @@ -0,0 +1,562 @@ +//****************************************** +// Super A'can MODULE +//****************************************** +#ifdef enable_SUPRACAN + +/****************************************** + Menu +*****************************************/ +static const char acanMenuItem1[] PROGMEM = "Read Rom"; +static const char acanMenuItem2[] PROGMEM = "Read Save"; +static const char acanMenuItem3[] PROGMEM = "Write Save"; +static const char acanMenuItem4[] PROGMEM = "Read UM6650"; +static const char acanMenuItem5[] PROGMEM = "Write UM6650"; + +static const char* const menuOptionsAcan[] PROGMEM = {acanMenuItem1, acanMenuItem2, acanMenuItem3, acanMenuItem4, acanMenuItem5, string_reset2}; + +void setup_SuprAcan() +{ + // addr as output + DDRF = 0xff; // A0 - A7 + DDRK = 0xff; // A8 - A15 + DDRL = 0xff; // A16 - A23 + + // data as input + DDRC = 0xff; + DDRA = 0xff; + PORTC = 0x00; // disable internal pull-up + PORTA = 0x00; + DDRC = 0x00; // D0 - D7 + DDRA = 0x00; // D8 - D15 + + // set /RST(PH0), /CS(PH3), C27(PH4), R/W(PH5), /AS(PH6) output + DDRH |= ((1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6)); + PORTH |= ((1 << 3) | (1 << 5) | (1 << 6)); + PORTH &= ~((1 << 0) | (1 << 4)); + + // set /CARTIN(PG5) input + DDRG &= ~(1 << 5); + // set 6619_124(PE4) input + DDRE &= ~(1 << 4); + + display_Clear(); + initializeClockOffset(); + + // clockgen + if (i2c_found) + { + clockgen.set_freq(1073863500ULL, SI5351_CLK1); // cpu + clockgen.set_freq(357954500ULL, SI5351_CLK2); // subcarrier + clockgen.set_freq(5369317500ULL, SI5351_CLK0); // master clock + + clockgen.output_enable(SI5351_CLK1, 1); + clockgen.output_enable(SI5351_CLK2, 1); + clockgen.output_enable(SI5351_CLK0, 0); + + // Wait for clock generator + clockgen.update_status(); + delay(500); + } +#ifdef clockgen_installed + else + { + print_FatalError(F("Clock Generator not found")); + } +#endif + + // /RST to 1 + PORTH |= (1 << 0); + + cartSize = checkRomSize_Acan(); + romSize = cartSize >> 17; + mode = mode_SUPRACAN; + + if (cartSize == 0) + print_Error(F("Unable to find rom signature...")); + else + { + print_Msg(F("ROM Size: ")); + print_Msg(romSize); + println_Msg(F(" Mb")); + } + + display_Update(); + wait(); +} + +void suprAcanMenu() +{ + uint8_t mainMenu; + + convertPgm(menuOptionsAcan, 6); + mainMenu = question_box(F("Super A'can Menu"), menuOptions, 6, 0); + + switch (mainMenu) + { + case 0: + { + readROM_Acan(); + break; + } + case 1: + { + readSRAM_Acan(); + break; + } + case 2: + { + writeSRAM_Acan(); + verifySRAM_Acan(); + break; + } + case 3: + { + readUM6650(); + break; + } + case 4: + { + writeUM6650(); + verifyUM6650(); + break; + } + default: + { + resetCart_Acan(); + resetArduino(); + break; + } + } + + println_Msg(F("")); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +static void readROM_Acan() +{ + uint32_t crc32 = 0xffffffff; + + EEPROM_readAnything(0, foldern); + snprintf(folder, FILEPATH_LENGTH, "ACAN/ROM/%d", foldern); + + display_Clear(); + print_STR(saving_to_STR, 0); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + sd.mkdir(folder, true); + sd.chdir(folder); + + if (!myFile.open("rom.bin", O_RDWR | O_CREAT)) + print_FatalError(create_file_STR); + + foldern++; + EEPROM_writeAnything(0, foldern); + + draw_progressbar(0, cartSize); + + dataIn_MD(); + for (uint32_t addr = 0; addr < cartSize; addr += 512, draw_progressbar(addr, cartSize)) + { + for (uint32_t i = 0; i < 512; i += 2) + { + *((uint16_t*)(sdBuffer + i)) = readWord_Acan(addr + i); + UPDATE_CRC(crc32, sdBuffer[i]); + UPDATE_CRC(crc32, sdBuffer[i + 1]); + } + + myFile.write(sdBuffer, 512); + + if ((addr & ((1 << 14) - 1)) == 0) + blinkLED(); + } + + crc32 = ~crc32; + myFile.close(); + + print_Msg(F("CRC32: ")); + print_Msg_PaddedHex32(crc32); + println_Msg(F("")); + print_STR(done_STR, 1); +} + +static void readSRAM_Acan() +{ + // create a new folder for storing rom file + EEPROM_readAnything(0, foldern); + snprintf(folder, FILEPATH_LENGTH, "ACAN/SAVE/%d", foldern); + + display_Clear(); + print_STR(saving_to_STR, 0); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + sd.mkdir(folder, true); + sd.chdir(folder); + + if (!myFile.open("save.bin", O_RDWR | O_CREAT)) + print_FatalError(create_file_STR); + + foldern++; + EEPROM_writeAnything(0, foldern); + + dataIn_MD(); + for (uint32_t i = 0; i < 0x10000; i += 1024) + { + for (uint32_t j = 0; j < 1024; j += 2) + sdBuffer[(j >> 1)] = readWord_Acan(0xec0000 + i + j); + + myFile.write(sdBuffer, 512); + } + + myFile.close(); + print_STR(done_STR, 1); +} + +static void writeSRAM_Acan() +{ + filePath[0] = 0; + sd.chdir("/"); + fileBrowser(F("Select a file")); + snprintf(filePath, FILEPATH_LENGTH, "%s/%s", filePath, fileName); + + display_Clear(); + + if (!myFile.open(filePath, O_READ)) + { + print_Error(F("File doesn't exist")); + return; + } + + print_Msg(F("Writing ")); + print_Msg(filePath); + println_Msg(F("...")); + display_Update(); + + dataOut_MD(); + for (uint32_t i = 0; i < 0x10000 && myFile.available(); i += 1024) + { + myFile.read(sdBuffer, 512); + + for (uint32_t j = 0; j < 1024; j += 2) + writeWord_Acan(0xec0000 + i + j, sdBuffer[(j >> 1)]); + } + + myFile.close(); + + dataIn_MD(); + print_STR(done_STR, 1); +} + +static void verifySRAM_Acan() +{ + print_STR(verifying_STR, 0); + display_Update(); + + if (!myFile.open(filePath, O_READ)) + { + print_Error(F("File doesn't exist")); + return; + } + + uint16_t write_errors = 0; + + dataIn_MD(); + for (uint32_t i = 0; i < 0x10000 && myFile.available(); i += 1024) + { + myFile.read(sdBuffer, 512); + + for (uint32_t j = 0; j < 1024; j += 2) + { + if (readWord_Acan(0xec0000 + i + j) != sdBuffer[(j >> 1)]) + write_errors++; + } + } + + myFile.close(); + + if (write_errors == 0) + { + println_Msg(F("passed")); + } + else + { + println_Msg(F("failed")); + print_Msg(F("Error: ")); + print_Msg(write_errors); + println_Msg(F(" bytes ")); + print_Error(did_not_verify_STR); + } +} + +static void readUM6650() +{ + // create a new folder for storing rom file + EEPROM_readAnything(0, foldern); + snprintf(folder, sizeof(folder), "ACAN/UM6650/%d", foldern); + + display_Clear(); + print_STR(saving_to_STR, 0); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + sd.mkdir(folder, true); + sd.chdir(folder); + + if (!myFile.open("UM6650.bin", O_RDWR | O_CREAT)) + print_FatalError(create_file_STR); + + foldern++; + EEPROM_writeAnything(0, foldern); + + for (uint16_t i = 0; i < 256; i++) + { + dataOut_MD(); + writeWord_Acan(0xeb0d03, i); + + dataIn_MD(); + sdBuffer[i] = readWord_Acan(0xeb0d01); + } + + myFile.write(sdBuffer, 256); + myFile.close(); + + print_STR(done_STR, 1); +} + +static void verifyUM6650() +{ + print_STR(verifying_STR, 0); + display_Update(); + + if (!myFile.open(filePath, O_READ)) + { + print_Error(F("File doesn't exist")); + return; + } + + uint16_t write_errors = 0; + uint16_t len = myFile.read(sdBuffer, 256); + myFile.close(); + + for (uint16_t i = 0; i < len; i++) + { + dataOut_MD(); + writeWord_Acan(0xeb0d03, i); + + dataIn_MD(); + if (readWord_Acan(0xeb0d01) != sdBuffer[i]) + write_errors++; + } + + if (write_errors) + { + println_Msg(F("failed")); + print_Msg(F("Error: ")); + print_Msg(write_errors); + println_Msg(F(" bytes ")); + print_Error(did_not_verify_STR); + } + else + { + println_Msg(F("passed")); + } +} + +static void writeUM6650() +{ + filePath[0] = 0; + sd.chdir("/"); + fileBrowser(F("Select a file")); + snprintf(filePath, FILEPATH_LENGTH, "%s/%s", filePath, fileName); + + display_Clear(); + + if (!myFile.open(filePath, O_READ)) + { + print_Error(F("File doesn't exist")); + return; + } + + uint16_t len = myFile.read(sdBuffer, 256); + myFile.close(); + + print_Msg(F("Writing ")); + print_Msg(filePath); + println_Msg(F("...")); + display_Update(); + + dataOut_MD(); + for (uint16_t i = 0; i < len; i++) + { + writeWord_Acan(0xeb0d03, i); + writeWord_Acan(0xeb0d01, sdBuffer[i]); + + delay(10); // for AT28C64B write + } + + dataIn_MD(); + print_STR(done_STR, 1); +} + +static uint32_t checkRomSize_Acan() +{ + uint32_t addr = 0; + uint32_t crc32; + + do + { + // check if there is rom chip exists + // pull-up enable + DDRC = 0xff; + DDRA = 0xff; + PORTC = 0xff; + PORTA = 0xff; + DDRC = 0x00; + DDRA = 0x00; + *((uint16_t*)sdBuffer) = readWord_Acan(addr); + + // pull-up disable + DDRC = 0xff; + DDRA = 0xff; + PORTC = 0x00; + PORTA = 0x00; + DDRC = 0x00; + DDRA = 0x00; + *((uint16_t*)(sdBuffer + 2)) = readWord_Acan(addr); + + // should be them same if chip exists + if (sdBuffer[0] != sdBuffer[2] || sdBuffer[1] != sdBuffer[3]) + break; + + crc32 = 0xffffffff; + + for (uint32_t i = 0x2000; i < 0x2080; i += 2) + { + *((uint16_t*)sdBuffer) = readWord_Acan(addr + i); + UPDATE_CRC(crc32, sdBuffer[0]); + UPDATE_CRC(crc32, sdBuffer[1]); + } + + crc32 = ~crc32; + + if (crc32 == 0xa2bc9d7a) + { + if (addr > 0) + break; + } + else + { + if (addr == 0) + break; + } + + addr += 0x80000; + + } while (addr < 0x800000); + + return addr; +} + +static void resetCart_Acan() +{ + // set /CS(PH3), R/W(PH5), /AS(PH6) high + // /RST(PH0) and C27(PH4) low + PORTH |= ((1 << 3) | (1 << 5) | (1 << 6)); + PORTH &= ~((1 << 0) | (1 << 4)); + + if (i2c_found) + { + clockgen.output_enable(SI5351_CLK1, 0); // CPU clock + clockgen.output_enable(SI5351_CLK2, 0); // CIC clock + clockgen.output_enable(SI5351_CLK0, 0); // master clock + } +} + +static void writeWord_Acan(uint32_t addr, uint16_t data) +{ + uint8_t *ptr = (uint8_t*)&addr; + + PORTF = *ptr++; + PORTK = *ptr++; + PORTL = *ptr; + + if (*ptr < 0xe0) + { + // ROM area + // /CS(PH3), C27(PH4), R/W(PH5), /AS(PH6) to L + PORTH &= ~((1 << 3) | (1 << 4) | (1 << 5) | (1 << 6)); + } + else if (*ptr == 0xec) + { + // save area + // /CS(PH3) to H, C27(PH4), R/W(PH5), /AS(PH6) to L + PORTH |= (1 << 3); + PORTH &= ~((1 << 4) | (1 << 5) | (1 << 6)); + } + else if (addr == 0x00eb0d03 || addr == 0x00eb0d01) + { + // UM6650 area + // /CS(PH3), C27(PH4) to H, R/W(PH5), /AS(PH6) to L + PORTH |= ((1 << 3) | (1 << 4)); + PORTH &= ~((1 << 5) | (1 << 6)); + } + + ptr = (uint8_t*)&data; + PORTC = *ptr++; + PORTA = *ptr; + + NOP; NOP; NOP; + + PORTH &= ~(1 << 4); + PORTH |= ((1 << 3) | (1 << 5) | (1 << 6)); +} + +static uint16_t readWord_Acan(uint32_t addr) +{ + uint8_t *ptr = (uint8_t*)&addr; + uint16_t data; + + PORTF = *ptr++; + PORTK = *ptr++; + PORTL = *ptr; + + if (*ptr < 0xe0) + { + // ROM area + // /CS(PH3), C27(PH4), /AS(PH6) to L + PORTH &= ~((1 << 3) | (1 << 4) | (1 << 6)); + } + else if (*ptr == 0xec) + { + // save area + // /CS(PH3) to H, C27(PH4), /AS(PH6) to L + PORTH |= (1 << 3); + PORTH &= ~((1 << 4) | (1 << 6)); + } + else if (addr == 0x00eb0d03 || addr == 0x00eb0d01) + { + // UM6650 area + // /CS(PH3), C27(PH4) to H, /AS(PH6) to L + PORTH |= ((1 << 3) | (1 << 4)); + PORTH &= ~(1 << 6); + } + + ptr = (uint8_t*)&data; + NOP; NOP; NOP; + + *ptr++ = PINC; + *ptr = PINA; + + PORTH &= ~(1 << 4); + PORTH |= ((1 << 3) | (1 << 5) | (1 << 6)); + + return data; +} + +#endif