diff --git a/Cart_Reader/Cart_Reader.ino b/Cart_Reader/Cart_Reader.ino index a9a1c41..0ef9c2f 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 //****************************************** @@ -408,6 +411,7 @@ void print_STR(byte string_number, boolean newline) { #define mode_ODY2 29 #define mode_ARC 30 #define mode_FAIRCHILD 31 +#define mode_SUPRACAN 32 // optimization-safe nop delay #define NOP __asm__ __volatile__("nop\n\t") @@ -1002,8 +1006,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 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 modeItem20[] PROGMEM = "Super A'can"; +static const char modeItem21[] 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, modeItem21 }; // All included slots void mainMenu() { @@ -1026,7 +1031,7 @@ void mainMenu() { num_answers = 7; } else { // currPage == 3 option_offset = 14; - num_answers = 6; + num_answers = 7; } // Copy menuOptions out of progmem convertPgm(modeOptions + option_offset, num_answers); @@ -1184,7 +1189,13 @@ void mainMenu() { break; #endif +#ifdef enable_SUPRACAN case 19: + setup_SuprAcan(); + break; +#endif + + case 20: aboutScreen(); break; @@ -1214,10 +1225,11 @@ static const char* const modeOptions[] PROGMEM = { modeItem1, modeItem2, modeIte // Add-ons submenu static const char addonsItem1[] PROGMEM = "70s Consoles"; static const char addonsItem2[] PROGMEM = "80s Consoles"; -static const char addonsItem3[] PROGMEM = "Handhelds"; -static const char addonsItem4[] PROGMEM = "Flashrom Programmer"; +static const char addonsItem3[] PROGMEM = "90s Consoles"; +static const char addonsItem4[] PROGMEM = "Handhelds"; +static const char addonsItem5[] PROGMEM = "Flashrom Programmer"; //static const char addonsItem5[] PROGMEM = "Reset"; (stored in common strings array) -static const char* const addonsOptions[] PROGMEM = { addonsItem1, addonsItem2, addonsItem3, addonsItem4, string_reset2 }; +static const char* const addonsOptions[] PROGMEM = { addonsItem1, addonsItem2, addonsItem3, addonsItem4, addonsItem5, string_reset2 }; // 70s Consoles submenu static const char consoles70Item1[] PROGMEM = "Atari 2600"; @@ -1236,6 +1248,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"; @@ -1301,8 +1317,8 @@ void addonMenu() { // create menu with title and 5 options to choose from unsigned char addonsMenu; // Copy menuOptions out of progmem - convertPgm(addonsOptions, 5); - addonsMenu = question_box(F("Type"), menuOptions, 5, 0); + convertPgm(addonsOptions, 6); + addonsMenu = question_box(F("Type"), menuOptions, 6, 0); // wait for user choice to come back from the question box menu switch (addonsMenu) { @@ -1316,13 +1332,17 @@ void addonMenu() { consoles80Menu(); break; - // Handhelds case 2: + consoles90Menu(); + break; + + // Handhelds + case 3: handheldMenu(); break; #ifdef enable_FLASH - case 3: + case 4: #ifdef enable_FLASH16 flashMenu(); #else @@ -1331,7 +1351,7 @@ void addonMenu() { break; #endif - case 4: + case 5: resetArduino(); break; @@ -1347,7 +1367,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 @@ -1446,6 +1465,31 @@ void consoles80Menu() { } } +// Everything that needs an adapter +void consoles90Menu() { + unsigned char consoles90Menu; + // Copy menuOptions out of progmem + convertPgm(consoles90Options, 2); + consoles90Menu = question_box(F("Choose Adapter"), menuOptions, 2, 0); + + // wait for user choice to come back from the question box menu + switch (consoles90Menu) { +#ifdef enable_SUPRACAN + case 0: + setup_SuprAcan(); + break; +#endif + + case 1: + resetArduino(); + break; + + default: + print_MissingModule(); // does not return + break; + } +} + // Everything that needs an adapter void handheldMenu() { // create menu with title and 6 options to choose from @@ -3510,6 +3554,11 @@ void loop() { else if (mode == mode_FAIRCHILD) { fairchildMenu(); } +#endif +#ifdef enable_SUPRACAN + else if (mode == mode_SUPRACAN) { + suprAcanMenu(); + } #endif else { display_Clear(); diff --git a/Cart_Reader/SUPRACAN.ino b/Cart_Reader/SUPRACAN.ino new file mode 100644 index 0000000..329705d --- /dev/null +++ b/Cart_Reader/SUPRACAN.ino @@ -0,0 +1,647 @@ +//****************************************** +// Super A'can MODULE +// Only tested with HW3 and HW5 +//****************************************** +#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 acanMenuItem6[] PROGMEM = "Flash repro"; + +static const char *const menuOptionsAcan[] PROGMEM = { acanMenuItem1, acanMenuItem2, acanMenuItem3, acanMenuItem4, acanMenuItem5, string_reset2, acanMenuItem6 }; + +void setup_SuprAcan() { + vselect(false); + + // 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 6619_124(PE4) input + DDRE &= ~(1 << 4); + + // detect if flash chip exists + PORTG |= (1 << 5); + DDRG |= (1 << 5); + PORTG |= (1 << 5); + + dataOut_MD(); + writeWord_Acan(0xaaaa, 0xaaaa); + writeWord_Acan(0x5555, 0x5555); + writeWord_Acan(0xaaaa, 0x9090); + + dataIn_MD(); + eepbit[0] = readWord_Acan(0x2); + eepbit[1] = readWord_Acan(0x0); + + dataOut_MD(); + writeWord_Acan(0x0, 0xf0f0); + + dataIn_MD(); + // set /CARTIN(PG5) input + PORTG &= ~(1 << 5); + DDRG &= ~(1 << 5); + + *((uint32_t *)(eepbit + 4)) = getFlashChipSize_Acan(*((uint16_t *)eepbit)); + + 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 + + checkRomExist_Acan(); + wait(); +} + +void suprAcanMenu() { + uint8_t mainMenu = 6; + + if (*((uint32_t *)(eepbit + 4)) > 0) + mainMenu = 7; + + convertPgm(menuOptionsAcan, mainMenu); + mainMenu = question_box(F("Super A'can Menu"), menuOptions, mainMenu, 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; + } + case 6: + { + flashCart_Acan(); + 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 void flashCart_Acan() { + uint32_t *flash_size = (uint32_t *)(eepbit + 4); + + 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 + 1); + println_Msg(F("...")); + display_Update(); + + uint32_t i, j, k, file_length = myFile.fileSize(); + uint16_t data; + + DDRG |= (1 << 5); + PORTG |= (1 << 5); + + draw_progressbar(0, file_length); + + for (i = 0; i < file_length; i += *flash_size) { + // erase chip + dataOut_MD(); + writeWord_Acan(i + 0xaaaa, 0xaaaa); + writeWord_Acan(i + 0x5555, 0x5555); + writeWord_Acan(i + 0xaaaa, 0x8080); + writeWord_Acan(i + 0xaaaa, 0xaaaa); + writeWord_Acan(i + 0x5555, 0x5555); + writeWord_Acan(i + 0xaaaa, 0x1010); + + dataIn_MD(); + while (readWord_Acan(i) != 0xffff) + ; + + for (j = 0; j < *flash_size; j += 512) { + myFile.read(sdBuffer, 512); + + for (k = 0; k < 512; k += 2) { + data = *((uint16_t *)(sdBuffer + k)); + + dataOut_MD(); + writeWord_Acan(i + 0xaaaa, 0xaaaa); + writeWord_Acan(i + 0x5555, 0x5555); + writeWord_Acan(i + 0xaaaa, 0xa0a0); + writeWord_Acan(i + j + k, data); + + dataIn_MD(); + while (readWord_Acan(i + j + k) != data) + ; + } + + draw_progressbar(i + j + k, file_length); + + if ((j & 0xfff) == 0) + blinkLED(); + } + } + + PORTG &= ~(1 << 5); + DDRG &= ~(1 << 5); + + myFile.close(); + print_STR(done_STR, 1); + + checkRomExist_Acan(); +} + +static void checkRomExist_Acan() { + // RST to 0 + PORTH &= ~(1 << 0); + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + + // /RST to 1 + PORTH |= (1 << 0); + + cartSize = getRomSize_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(); +} + +static uint32_t getRomSize_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; +} + +static uint32_t getFlashChipSize_Acan(uint16_t chip_id) { + // 0x0458 (8M), 0x01ab (4M), 0x01d8 (16M) + switch (chip_id) { + case 0x01ab: return 524288; + case 0x0458: return 1048576; + case 0x01d8: return 2097152; + } + + return 0; +} + +#endif