diff --git a/Cart_Reader/ARC.ino b/Cart_Reader/ARC.ino new file mode 100644 index 0000000..8efc01b --- /dev/null +++ b/Cart_Reader/ARC.ino @@ -0,0 +1,534 @@ +//****************************************** +// EMERSON ARCADIA 2001 MODULE +//****************************************** +#if defined(enable_ARC) +// Emerson Arcadia 2001 +// Cartridge Pinout +// 30P 2.54mm pitch connector +// +// FRONT BACK +// SIDE SIDE +// +-------+ +// GND -| 2 1 |- A13 (A12) +// VCC (+5V) -| 4 3 |- D3 +// A0 -| 6 5 |- D4 +// A1 -| 8 7 |- D5 +// A2 -| 10 9 |- D6 +// A3 -| 12 11 |- D7 +// A4 -| 14 13 |- D0 +// A5 -| 16 15 |- D2 +// A6 -| 18 17 |- D1 +// A7 -| 20 19 |- NC +// A8 -| 22 21 |- NC +// A9 -| 24 23 |- NC +// A10 -| 26 25 |- GND +// A11 -| 28 27 |- GND +// A12 (/EN) -| 30 29 |- NC +// +-------+ +// +// BACK +// +------------------------------------------------------------+ +// | 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 | +// LEFT | | RIGHT +// | 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 | +// +------------------------------------------------------------+ +// FRONT +// + +byte ARC[] = { 2, 4, 6, 8 }; +byte arclo = 0; // Lowest Entry +byte archi = 3; // Highest Entry + +byte arcsize; +byte newarcsize; + +// EEPROM MAPPING +// 08 ROM SIZE + +//****************************************** +// Menu +//****************************************** +// Base Menu +static const char arcMenuItem1[] PROGMEM = "Select Cart"; +static const char arcMenuItem2[] PROGMEM = "Read ROM"; +static const char arcMenuItem3[] PROGMEM = "Set Size"; +static const char arcMenuItem4[] PROGMEM = "Reset"; +static const char* const menuOptionsARC[] PROGMEM = { arcMenuItem1, arcMenuItem2, arcMenuItem3, arcMenuItem4 }; + +void setup_ARC() { + // Set Address Pins to Output + // Arcadia 2001 uses A0-A13 [A14-A23 UNUSED] + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output + // ---(PH0) ---(PH1) ---(PH3) ---(PH4) ---(PH5) ---(PH6) + DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Set TIME(PJ0) to Output (UNUSED) + DDRJ |= (1 << 0); + + // Set Pins (D0-D7) to Input + DDRC = 0x00; + + // Setting Unused Control Pins to HIGH + // ---(PH0) ---(PH1) ---(PH3) ---(PH4) ---(PH5) ---(PH6) + PORTH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Set Unused Data Pins (PA0-PA7) to Output + DDRA = 0xFF; + + // Set Unused Pins HIGH + PORTA = 0xFF; + PORTL = 0xFF; // A16-A23 + PORTJ |= (1 << 0); // TIME(PJ0) + + checkStatus_ARC(); + strcpy(romName, "ARCADIA"); + + mode = mode_ARC; +} + +void arcMenu() { + convertPgm(menuOptionsARC, 4); + uint8_t mainMenu = question_box(F("ARCADIA 2001 MENU"), menuOptions, 4, 0); + + switch (mainMenu) { + case 0: + // Select Cart + setCart_ARC(); + wait(); + setup_ARC(); + break; + + case 1: + // Read ROM + sd.chdir("/"); + readROM_ARC(); + sd.chdir("/"); + break; + + case 2: + // Set Size + setROMSize_ARC(); + break; + + case 3: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// READ CODE +//****************************************** + +uint8_t readData_ARC(uint16_t addr) { + PORTF = addr & 0xFF; // A0-A7 + PORTK = (addr >> 8) & 0xFF; // A8-A13 + NOP; + NOP; + NOP; + NOP; + NOP; + + uint8_t ret = PINC; + + return ret; +} + +void readSegment_ARC(uint16_t startaddr, uint16_t endaddr) { + for (uint16_t addr = startaddr; addr < endaddr; addr += 512) { + for (int w = 0; w < 512; w++) { + uint8_t temp = readData_ARC(addr + w); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + } +} + +void readROM_ARC() { + strcpy(fileName, romName); + strcat(fileName, ".bin"); + + // create a new folder for storing rom file + EEPROM_readAnything(0, foldern); + sprintf(folder, "ARC/ROM/%d", foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // open file on sdcard + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_FatalError(create_file_STR); + } + // write new folder number back to EEPROM + foldern++; + EEPROM_writeAnything(0, foldern); + + readSegment_ARC(0x0000, 0x0800); // 2K + if (arcsize > 0) { + readSegment_ARC(0x0800, 0x1000); // +2K = 4K + if (arcsize > 1) { + readSegment_ARC(0x2000, 0x2800); // +2K = 6K + if (arcsize > 2) { + readSegment_ARC(0x2800, 0x3000); // +2K = 8K + } + } + } + myFile.close(); + + unsigned long crcsize = ARC[arcsize] * 0x400; + calcCRC(fileName, crcsize, NULL, 0); + + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +void setROMSize_ARC() { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + if (arclo == archi) + newarcsize = arclo; + else { + int b = 0; + int i = arclo; + while (1) { + display_Clear(); + print_Msg(F("ROM Size: ")); + println_Msg(ARC[i]); + println_Msg(F("")); + println_Msg(F("Press to Change")); + println_Msg(F("Hold to Select")); + display_Update(); + b = checkButton(); + if (b == 2) { // Previous (doubleclick) + if (i == arclo) + i = archi; + else + i--; + } + if (b == 1) { // Next (press) + if (i == archi) + i = arclo; + else + i++; + } + if (b == 3) { // Long Press - Execute (hold) + newarcsize = i; + break; + } + } + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(F("ROM SIZE ")); + print_Msg(ARC[newarcsize]); + println_Msg(F("K")); + display_Update(); + delay(1000); +#else + if (arclo == archi) + newarcsize = arclo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (archi - arclo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(ARC[i + arclo]); + Serial.println(F("K")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newarcsize = sizeROM.toInt() + arclo; + if (newarcsize > archi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(F("")); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(ARC[newarcsize]); + Serial.println(F("K")); +#endif + EEPROM_writeAnything(8, newarcsize); + arcsize = newarcsize; +} + +void checkStatus_ARC() { + EEPROM_readAnything(8, arcsize); + if (arcsize > 3) { + arcsize = 0; + EEPROM_writeAnything(8, arcsize); + } + +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("ARCADIA 2001 READER")); + println_Msg(F("CURRENT SETTINGS")); + println_Msg(F("")); + print_Msg(F("ROM SIZE: ")); + print_Msg(ARC[arcsize]); + println_Msg(F("K")); + display_Update(); + wait(); +#else + Serial.print(F("CURRENT ROM SIZE: ")); + Serial.print(ARC[arcsize]); + Serial.println(F("K")); + Serial.println(F("")); +#endif +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +FsFile arccsvFile; +char arcgame[20]; // title +char arcrr[3]; // romsize +char arcll[4]; // linelength (previous line) +unsigned long arccsvpos; // CSV File Position +char arccartCSV[] = "arccart.txt"; // CSV List +char arccsvEND[] = "EOF"; // CSV End Marker for scrolling + +bool readLine_ARC(FsFile& f, char* line, size_t maxLen) { + for (size_t n = 0; n < maxLen; n++) { + int c = f.read(); + if (c < 0 && n == 0) return false; // EOF + if (c < 0 || c == '\n') { + line[n] = 0; + return true; + } + line[n] = c; + } + return false; // line too long +} + +bool readVals_ARC(char* arcgame, char* arcrr, char* arcll) { + char line[26]; + arccsvpos = arccsvFile.position(); + if (!readLine_ARC(arccsvFile, line, sizeof(line))) { + return false; // EOF or too long + } + char* comma = strtok(line, ","); + int x = 0; + while (comma != NULL) { + if (x == 0) + strcpy(arcgame, comma); + else if (x == 1) + strcpy(arcrr, comma); + else if (x == 2) + strcpy(arcll, comma); + comma = strtok(NULL, ","); + x += 1; + } + return true; +} + +bool getCartListInfo_ARC() { + bool buttonreleased = 0; + bool cartselected = 0; +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F(" HOLD TO FAST CYCLE")); + display_Update(); +#else + Serial.println(F("HOLD BUTTON TO FAST CYCLE")); +#endif + delay(2000); +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + boolean buttonVal1 = (PING & (1 << 2)); //PG2 +#endif + if (buttonVal1 == LOW) { // Button Held - Fast Cycle + while (1) { // Scroll Game List + while (readVals_ARC(arcgame, arcrr, arcll)) { + if (strcmp(arccsvEND, arcgame) == 0) { + arccsvFile.seek(0); // Restart + } else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART TITLE:")); + println_Msg(F("")); + println_Msg(arcgame); + display_Update(); +#else + Serial.print(F("CART TITLE:")); + Serial.println(arcgame); +#endif +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + buttonVal1 = (PING & (1 << 2)); //PG2 +#endif + if (buttonVal1 == HIGH) { // Button Released + buttonreleased = 1; + break; + } + if (buttonreleased) { + buttonreleased = 0; // Reset Flag + break; + } + } + } +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + buttonVal1 = (PING & (1 << 2)); //PG2 +#endif + if (buttonVal1 == HIGH) // Button Released + break; + } + } +#if (defined(enable_OLED) || defined(enable_LCD)) + display.setCursor(0, 56); + println_Msg(F("FAST CYCLE OFF")); + display_Update(); +#else + Serial.println(F("")); + Serial.println(F("FAST CYCLE OFF")); + Serial.println(F("PRESS BUTTON TO STEP FORWARD")); + Serial.println(F("DOUBLE CLICK TO STEP BACK")); + Serial.println(F("HOLD TO SELECT")); + Serial.println(F("")); +#endif + while (readVals_ARC(arcgame, arcrr, arcll)) { + if (strcmp(arccsvEND, arcgame) == 0) { + arccsvFile.seek(0); // Restart + } else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART TITLE:")); + println_Msg(F("")); + println_Msg(arcgame); + display.setCursor(0, 48); + println_Msg(F("Press to Change")); + println_Msg(F("Hold to Select")); + display_Update(); +#else + Serial.print(F("CART TITLE:")); + Serial.println(arcgame); +#endif + while (1) { // Single Step + int b = checkButton(); + if (b == 1) { // Continue (press) + break; + } + if (b == 2) { // Reset to Start of List (doubleclick) + byte prevline = strtol(arcll, NULL, 10); + arccsvpos -= prevline; + arccsvFile.seek(arccsvpos); + break; + } + if (b == 3) { // Long Press - Select Cart (hold) + newarcsize = strtol(arcrr, NULL, 10); + EEPROM_writeAnything(8, newarcsize); + cartselected = 1; // SELECTION MADE +#if (defined(enable_OLED) || defined(enable_LCD)) + println_Msg(F("SELECTION MADE")); + display_Update(); +#else + Serial.println(F("SELECTION MADE")); +#endif + break; + } + } + if (cartselected) { + cartselected = 0; // Reset Flag + return true; + } + } + } +#if (defined(enable_OLED) || defined(enable_LCD)) + println_Msg(F("")); + println_Msg(F("END OF FILE")); + display_Update(); +#else + Serial.println(F("END OF FILE")); +#endif + + return false; +} + +void checkCSV_ARC() { + if (getCartListInfo_ARC()) { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART SELECTED")); + println_Msg(F("")); + println_Msg(arcgame); + display_Update(); + // Display Settings + display.setCursor(0, 56); + print_Msg(F("CODE: R")); + println_Msg(newarcsize); + display_Update(); +#else + Serial.println(F("")); + Serial.println(F("CART SELECTED")); + Serial.println(arcgame); + // Display Settings + Serial.print(F("CODE: R")); + Serial.println(newarcsize); + Serial.println(F("")); +#endif + } else { +#ifdef enable_OLED + display.setCursor(0, 56); + println_Msg(F("NO SELECTION")); + display_Update(); +#else + Serial.println(F("NO SELECTION")); +#endif + } +} + +void setCart_ARC() { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(arccartCSV); + display_Update(); +#endif + sd.chdir(); + sprintf(folder, "ARC/CSV"); + sd.chdir(folder); // Switch Folder + arccsvFile = sd.open(arccartCSV, O_READ); + if (!arccsvFile) { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CSV FILE NOT FOUND!")); + display_Update(); +#else + Serial.println(F("CSV FILE NOT FOUND!")); +#endif + while (1) { + if (checkButton() != 0) + setup_ARC(); + } + } + checkCSV_ARC(); + + arccsvFile.close(); +} +#endif \ No newline at end of file diff --git a/Cart_Reader/ATARI.ino b/Cart_Reader/ATARI.ino new file mode 100644 index 0000000..1b92ca2 --- /dev/null +++ b/Cart_Reader/ATARI.ino @@ -0,0 +1,912 @@ +//****************************************** +// ATARI 2600 MODULE +//****************************************** +#if defined(enable_ATARI) +// Atari 2600 +// Cartridge Pinout +// 24P 2.54mm pitch connector +// +// LABEL SIDE +// +// GND +5V A8 A9 A11 A10 A12 D7 D6 D5 D4 D3 +// +--------------------------------------------------+ +// | 24 23 22 21 20 19 18 17 16 15 14 13 | +// LEFT | | RIGHT +// | 1 2 3 4 5 6 7 8 9 10 11 12 | +// +--------------------------------------------------+ +// A7 A6 A5 A4 A3 A2 A1 A0 D0 D1 D2 GND +// +// BOTTOM SIDE + +// Cart Configurations +// Format = {mapper,romsize} +static const byte PROGMEM atarimapsize[] = { + 0x20, 0, // 2K + 0x3F, 2, // Tigervision 8K + 0x40, 1, // 4K [DEFAULT] + 0xC0, 0, // "CV" Commavid 2K + 0xD0, 2, // "DPC" Pitfall II 10K + 0xE0, 2, // Parker Bros 8K + 0xE7, 4, // M-Network 16K + 0xF0, 6, // Megaboy 64K + 0xF4, 5, // Atari 32K + 0xF6, 4, // Atari 16K + 0xF8, 2, // Atari 8K + 0xFA, 3, // CBS RAM Plus 12K + 0xFE, 2, // Activision 8K + 0x0A, 2, // "UA" UA Ltd 8K +}; + +//byte atarimapcount = 14; +byte atarimapcount = (sizeof(atarimapsize) / sizeof(atarimapsize[0])) / 2; + +byte atarimapselect; +int atariindex; + +byte ATARI[] = { 2, 4, 8, 12, 16, 32, 64 }; +byte atarimapper = 0; +byte newatarimapper; +byte atarisize; + +// EEPROM MAPPING +// 07 MAPPER +// 08 ROM SIZE + +//****************************************** +// Menu +//****************************************** +// Base Menu +static const char atariMenuItem1[] PROGMEM = "Select Cart"; +static const char atariMenuItem2[] PROGMEM = "Read ROM"; +static const char atariMenuItem3[] PROGMEM = "Set Mapper"; +static const char atariMenuItem4[] PROGMEM = "Reset"; +static const char* const menuOptionsATARI[] PROGMEM = { atariMenuItem1, atariMenuItem2, atariMenuItem3, atariMenuItem4 }; + +void setup_ATARI() { + // Set Address Pins to Output + // Atari 2600 uses A0-A12 [A13-A23 UNUSED] + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output [UNUSED] + // ---(PH0) ---(PH1) ---(PH3) ---(PH4) ---(PH5) ---(PH6) + DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Set TIME(PJ0) to Output (UNUSED) + DDRJ |= (1 << 0); + + // Set Pins (D0-D7) to Input + DDRC = 0x00; + + // Setting Control Pins to HIGH [UNUSED] + // ---(PH0) ---(PH1) ---(PH3) ---(PH4) ---(PH5) ---(PH6) + PORTH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Set Unused Data Pins (PA0-PA7) to Output + DDRA = 0xFF; + + // Set Unused Pins HIGH + PORTA = 0xFF; + PORTL = 0xFF; // A16-A23 + PORTJ |= (1 << 0); // TIME(PJ0) + + checkStatus_ATARI(); + strcpy(romName, "ATARI"); + + mode = mode_ATARI; +} + +void atariMenu() { + convertPgm(menuOptionsATARI, 4); + uint8_t mainMenu = question_box(F("ATARI 2600 MENU"), menuOptions, 4, 0); + + switch (mainMenu) { + case 0: + // Select Cart + setCart_ATARI(); + wait(); + setup_ATARI(); + break; + + case 1: + // Read ROM + sd.chdir("/"); + readROM_ATARI(); + sd.chdir("/"); + break; + + case 2: + // Set Mapper + setMapper_ATARI(); + checkStatus_ATARI(); + break; + + case 3: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// READ CODE +//****************************************** + +uint8_t readData_ATARI(uint16_t addr) // Add Input Pullup +{ + PORTF = addr & 0xFF; // A0-A7 + PORTK = (addr >> 8) & 0xFF; // A8-A12 + NOP; + NOP; + NOP; + NOP; + NOP; + + DDRC = 0x00; // Set to Input + PORTC = 0xFF; // Input Pullup + NOP; + NOP; + NOP; + NOP; + NOP; + + uint8_t ret = PINC; + NOP; + NOP; + NOP; + NOP; + NOP; + + return ret; +} + +void readSegment_ATARI(uint16_t startaddr, uint16_t endaddr) { + for (uint16_t addr = startaddr; addr < endaddr; addr += 512) { + for (int w = 0; w < 512; w++) { + uint8_t temp = readData_ATARI(addr + w); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + } +} + +void readSegmentF8_ATARI(uint16_t startaddr, uint16_t endaddr, uint16_t bankaddr) { + for (uint16_t addr = startaddr; addr < endaddr; addr += 512) { + for (int w = 0; w < 512; w++) { + if (addr > 0x1FF9) // SET BANK ADDRESS FOR 0x1FFA-0x1FFF + readData_ATARI(bankaddr); + uint8_t temp = readData_ATARI(addr + w); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + } +} + +void writeData_ATARI(uint16_t addr, uint8_t data) { + PORTF = addr & 0xFF; // A0-A7 + PORTK = (addr >> 8) & 0xFF; // A8-A12 + NOP; + NOP; + NOP; + NOP; + NOP; + + DDRC = 0xFF; // Set to Output + NOP; + NOP; + NOP; + NOP; + NOP; + + PORTC = data; + NOP; + NOP; + NOP; + NOP; + NOP; + + DDRC = 0x00; // Reset to Input +} + +void writeData3F_ATARI(uint16_t addr, uint8_t data) { + PORTF = addr & 0xFF; // A0-A7 + PORTK = (addr >> 8) & 0xFF; // A8-A12 + NOP; + NOP; + NOP; + NOP; + NOP; + + DDRC = 0xFF; // Set to Output + NOP; + NOP; + NOP; + NOP; + NOP; + + PORTC = data; + NOP; + NOP; + NOP; + NOP; + NOP; + + // Address (0x1000); + PORTF = 0x00; // A0-A7 + PORTK = 0x10; // A8-A12 + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + + DDRC = 0x00; // Reset to Input +} + +void readROM_ATARI() { + strcpy(fileName, romName); + strcat(fileName, ".a26"); + + // create a new folder for storing rom file + EEPROM_readAnything(0, foldern); + sprintf(folder, "ATARI/ROM/%d", foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // open file on sdcard + if (!myFile.open(fileName, O_RDWR | O_CREAT)) + print_FatalError(create_file_STR); + + // write new folder number back to EEPROM + foldern++; + EEPROM_writeAnything(0, foldern); + + // ROM Start 0xF000 + // Address A12-A0 = 0x1000 = 1 0000 0000 0000 = 4KB + // Read Start 0x1000 + + switch (atarimapper) { + case 0x20: // 2K Standard 2KB + readSegment_ATARI(0x1000, 0x1800); + break; + + case 0x3F: // 3F Mapper 8KB + for (int x = 0; x < 0x3; x++) { + writeData3F_ATARI(0x3F, x); + readSegment_ATARI(0x1000, 0x1800); + } + readSegment_ATARI(0x1800, 0x2000); + break; + + case 0x40: // 4K Default 4KB + readSegment_ATARI(0x1000, 0x2000); + break; + + case 0xC0: // CV Mapper 2KB + readSegment_ATARI(0x1800, 0x2000); + break; + + case 0xD0: // DPC Mapper 10KB + // 8K ROM + for (int x = 0; x < 0x2; x++) { + readData_ATARI(0x1FF8 + x); + // Split Read of 1st 0x200 bytes + // 0x0000-0x0080 are DPC Registers (Random on boot) + for (int y = 0; y < 0x80; y++) { + sdBuffer[y] = 0xFF; // Output 0xFFs for Registers + } + myFile.write(sdBuffer, 128); + for (int z = 0; z < 0x180; z++) { + sdBuffer[z] = readData_ATARI(0x1080 + z); + } + myFile.write(sdBuffer, 384); + // Read Segment + readSegment_ATARI(0x1200, 0x1800); + // 0x1000-0x1080 are DPC Registers (Random on boot) + for (int y = 0; y < 0x80; y++) { + sdBuffer[y] = 0xFF; // Output 0xFFs for Registers + } + myFile.write(sdBuffer, 128); + for (int z = 0; z < 0x180; z++) { + sdBuffer[z] = readData_ATARI(0x1880 + z); + } + myFile.write(sdBuffer, 384); + // Read Segment + readSegment_ATARI(0x1A00, 0x1E00); + // Split Read of Last 0x200 bytes + for (int y = 0; y < 0x1F8; y++) { + sdBuffer[y] = readData_ATARI(0x1E00 + y); + } + myFile.write(sdBuffer, 504); + for (int z = 0; z < 8; z++) { + // Set Bank to ensure 0x1FFA-0x1FFF is correct + readData_ATARI(0x1FF8 + x); + sdBuffer[z] = readData_ATARI(0x1FF8 + z); + } + myFile.write(sdBuffer, 8); + } + + // 2K DPC Internal Graphics ROM + // Read Registers 0x1008-0x100F (Graphics 0x1008-0x100C) + // Write Registers LSB 0x1050-0x1057 AND MSB 0x1058-0x105F + + // Set Data Fetcher 0 Limits + writeData_ATARI(0x1040, 0xFF); // MAX for Data Fetcher 0 + writeData_ATARI(0x1048, 0x00); // MIN for Data Fetcher 0 + // Set Data Fetcher 0 Counter (0x7FF) + writeData_ATARI(0x1050, 0xFF); // LSB for Data Fetcher 0 + writeData_ATARI(0x1058, 0x07); // MSB for Data Fetcher 0 + // Set Data Fetcher 1 Counter (0x7FF) + writeData_ATARI(0x1051, 0xFF); // LSB for Data Fetcher 1 + writeData_ATARI(0x1059, 0x07); // MSB for Data Fetcher 1 + for (int x = 0; x < 0x800; x += 512) { + for (int y = 0; y < 512; y++) { + sdBuffer[y] = readData_ATARI(0x1008); // Data Fetcher 0 + readData_ATARI(0x1009); // Data Fetcher 1 + } + myFile.write(sdBuffer, 512); + } + break; + + case 0xE0: // E0 Mapper 8KB + for (int x = 0; x < 0x7; x++) { + readData_ATARI(0x1FE0 + x); + readSegment_ATARI(0x1000, 0x1400); + } + readSegment_ATARI(0x1C00, 0x2000); + break; + + case 0xE7: // E7 Mapper 16KB + writeData_ATARI(0x1800, 0xFF); + for (int x = 0; x < 0x7; x++) { + readData_ATARI(0x1FE0 + x); + readSegment_ATARI(0x1000, 0x1800); + } + readSegment_ATARI(0x1800, 0x2000); + break; + + case 0xF0: // F0 Mapper 64KB + for (int x = 0; x < 0x10; x++) { + readData_ATARI(0x1FF0); + readSegment_ATARI(0x1000, 0x2000); + } + break; + + case 0xF4: // F4 Mapper 32KB + for (int x = 0; x < 0x8; x++) { + readData_ATARI(0x1FF4 + x); + readSegment_ATARI(0x1000, 0x2000); + } + break; + + case 0xF6: // F6 Mapper 16KB + for (int x = 0; x < 0x4; x++) { + readData_ATARI(0x1FF6 + x); + readSegment_ATARI(0x1000, 0x2000); + } + break; + + case 0xF8: // F8 Mapper 8KB + for (int x = 0; x < 0x2; x++) { + readData_ATARI(0x1FF8 + x); + readSegment_ATARI(0x1000, 0x1E00); + // Split Read of Last 0x200 bytes + for (int y = 0; y < 0x1F8; y++) { + sdBuffer[y] = readData_ATARI(0x1E00 + y); + } + myFile.write(sdBuffer, 504); + for (int z = 0; z < 8; z++) { + // Set Bank to ensure 0x1FFA-0x1FFF is correct + readData_ATARI(0x1FF8 + x); + sdBuffer[z] = readData_ATARI(0x1FF8 + z); + } + myFile.write(sdBuffer, 8); + } + break; + + case 0xFA: // FA Mapper 12KB + for (int x = 0; x < 0x3; x++) { + writeData_ATARI(0x1FF8 + x, 0x1); // Set Bank with D0 HIGH + readSegment_ATARI(0x1000, 0x1E00); + // Split Read of Last 0x200 bytes + for (int y = 0; y < 0x1F8; y++) { + sdBuffer[y] = readData_ATARI(0x1E00 + y); + } + myFile.write(sdBuffer, 504); + for (int z = 0; z < 8; z++) { + // Set Bank to ensure 0x1FFB-0x1FFF is correct + writeData_ATARI(0x1FF8 + x, 0x1); // Set Bank with D0 HIGH + sdBuffer[z] = readData_ATARI(0x1FF8 + z); + } + myFile.write(sdBuffer, 8); + } + break; + + case 0xFE: // FE Mapper 8KB + for (int x = 0; x < 0x2; x++) { + writeData_ATARI(0x01FE, 0xF0 ^ (x << 5)); + writeData_ATARI(0x01FF, 0xF0 ^ (x << 5)); + readSegment_ATARI(0x1000, 0x2000); + } + break; + + case 0x0A: // UA Mapper 8KB + readData_ATARI(0x220); + readSegment_ATARI(0x1000, 0x2000); + readData_ATARI(0x240); + readSegment_ATARI(0x1000, 0x2000); + break; + } + myFile.close(); + + unsigned long crcsize = ATARI[atarisize] * 0x400; + calcCRC(fileName, crcsize, NULL, 0); + + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +void checkStatus_ATARI() { + EEPROM_readAnything(7, atarimapper); + EEPROM_readAnything(8, atarisize); + if (atarisize > 6) { + atarisize = 1; // default 4KB + EEPROM_writeAnything(8, atarisize); + } + +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("ATARI 2600 READER")); + println_Msg(F("CURRENT SETTINGS")); + println_Msg(F("")); + print_Msg(F("MAPPER: ")); + if (atarimapper == 0x20) + println_Msg(F("2K")); + else if (atarimapper == 0x40) + println_Msg(F("4K")); + else if (atarimapper == 0x0A) + println_Msg(F("UA")); + else if (atarimapper == 0xC0) + println_Msg(F("CV")); + else if (atarimapper == 0xD0) + println_Msg(F("DPC")); + else + println_Msg(atarimapper, HEX); + print_Msg(F("ROM SIZE: ")); + if (atarimapper == 0xD0) + print_Msg(F("10")); + else + print_Msg(ATARI[atarisize]); + println_Msg(F("K")); + display_Update(); + wait(); +#else + Serial.print(F("MAPPER: ")); + if (atarimapper == 0x20) + Serial.println(F("2K")); + else if (atarimapper == 0x40) + Serial.println(F("4K")); + else if (atarimapper == 0x0A) + Serial.println(F("UA")); + else if (atarimapper == 0xC0) + Serial.println(F("CV")); + else if (atarimapper == 0xD0) + Serial.println(F("DPC")); + else + Serial.println(atarimapper, HEX); + Serial.print(F("ROM SIZE: ")); + if (atarimapper == 0xD0) + Serial.print(F("10")); + else + Serial.print(ATARI[atarisize]); + Serial.println(F("K")); + Serial.println(F("")); +#endif +} + +//****************************************** +// SET MAPPER +//****************************************** + +void setMapper_ATARI() { +#if (defined(enable_OLED) || defined(enable_LCD)) + int b = 0; + int i = 0; + // Check Button Status +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + boolean buttonVal1 = (PING & (1 << 2)); //PG2 +#endif + if (buttonVal1 == LOW) { // Button Pressed + while (1) { // Scroll Mapper List +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + buttonVal1 = (PING & (1 << 2)); //PG2 +#endif + if (buttonVal1 == HIGH) { // Button Released + // Correct Overshoot + if (i == 0) + i = atarimapcount - 1; + else + i--; + break; + } + display_Clear(); + print_Msg(F("Mapper: ")); + atariindex = i * 2; + atarimapselect = pgm_read_byte(atarimapsize + atariindex); + if (atarimapselect == 0x20) + println_Msg(F("2K")); + else if (atarimapselect == 0x40) + println_Msg(F("4K")); + else if (atarimapselect == 0x0A) + println_Msg(F("UA")); + else if (atarimapselect == 0xC0) + println_Msg(F("CV")); + else if (atarimapselect == 0xD0) + println_Msg(F("DPC")); + else + println_Msg(atarimapselect, HEX); + display_Update(); + if (i == (atarimapcount - 1)) + i = 0; + else + i++; + delay(250); + } + } + while (1) { + display_Clear(); + print_Msg(F("Mapper: ")); + atariindex = i * 2; + atarimapselect = pgm_read_byte(atarimapsize + atariindex); + if (atarimapselect == 0x20) + println_Msg(F("2K")); + else if (atarimapselect == 0x40) + println_Msg(F("4K")); + else if (atarimapselect == 0x0A) + println_Msg(F("UA")); + else if (atarimapselect == 0xC0) + println_Msg(F("CV")); + else if (atarimapselect == 0xD0) + println_Msg(F("DPC")); + else + println_Msg(atarimapselect, HEX); + println_Msg(F("")); + println_Msg(F("Press to Change")); + println_Msg(F("Hold to Select")); + display_Update(); + b = checkButton(); + if (b == 2) { // Previous Mapper (doubleclick) + if (i == 0) + i = atarimapcount - 1; + else + i--; + } + if (b == 1) { // Next Mapper (press) + if (i == (atarimapcount - 1)) + i = 0; + else + i++; + } + if (b == 3) { // Long Press - Execute (hold) + newatarimapper = atarimapselect; + break; + } + } + display.setCursor(0, 56); + print_Msg(F("MAPPER ")); + if (newatarimapper == 0x20) + println_Msg(F("2K")); + else if (newatarimapper == 0x40) + println_Msg(F("4K")); + if (newatarimapper == 0x0A) + print_Msg(F("UA")); + else if (newatarimapper == 0xC0) + print_Msg(F("CV")); + else if (newatarimapper == 0xD0) + println_Msg(F("DPC")); + else + print_Msg(newatarimapper, HEX); + println_Msg(F(" SELECTED")); + display_Update(); + delay(1000); +#else +setmapper: + String newmap; + Serial.println(F("SUPPORTED MAPPERS:")); + Serial.println(F("0 = 2K [Standard 2K]")); + Serial.println(F("1 = 3F [Tigervision]")); + Serial.println(F("2 = 4K [Standard 4K]")); + Serial.println(F("3 = CV [Commavid]")); + Serial.println(F("4 = DPC [Pitfall II]")); + Serial.println(F("5 = E0 [Parker Bros]")); + Serial.println(F("6 = E7 [M-Network]")); + Serial.println(F("7 = F0 [Megaboy]")); + Serial.println(F("8 = F4 [Atari 32K]")); + Serial.println(F("9 = F6 [Atari 16K]")); + Serial.println(F("10 = F8 [Atari 8K]")); + Serial.println(F("11 = FA [CBS RAM Plus]")); + Serial.println(F("12 = FE [Activision]")); + Serial.println(F("13 = UA [UA Ltd]")); + Serial.print(F("Enter Mapper [0-13]: ")); + while (Serial.available() == 0) {} + newmap = Serial.readStringUntil('\n'); + Serial.println(newmap); + atariindex = newmap.toInt() * 2; + newatarimapper = pgm_read_byte(atarimapsize + atariindex); +#endif + EEPROM_writeAnything(7, newatarimapper); + atarimapper = newatarimapper; + + atarisize = pgm_read_byte(atarimapsize + atariindex + 1); + EEPROM_writeAnything(8, atarisize); +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +FsFile ataricsvFile; +char atarigame[36]; // title +char atarimm[4]; // mapper +char atarill[4]; // linelength (previous line) +unsigned long ataricsvpos; // CSV File Position +char ataricartCSV[] = "ataricart.txt"; // CSV List +char ataricsvEND[] = "EOF"; // CSV End Marker for scrolling + +bool readLine_ATARI(FsFile& f, char* line, size_t maxLen) { + for (size_t n = 0; n < maxLen; n++) { + int c = f.read(); + if (c < 0 && n == 0) return false; // EOF + if (c < 0 || c == '\n') { + line[n] = 0; + return true; + } + line[n] = c; + } + return false; // line too long +} + +bool readVals_ATARI(char* atarigame, char* atarimm, char* atarill) { + char line[42]; + ataricsvpos = ataricsvFile.position(); + if (!readLine_ATARI(ataricsvFile, line, sizeof(line))) { + return false; // EOF or too long + } + char* comma = strtok(line, ","); + int x = 0; + while (comma != NULL) { + if (x == 0) + strcpy(atarigame, comma); + else if (x == 1) + strcpy(atarimm, comma); + else if (x == 2) + strcpy(atarill, comma); + comma = strtok(NULL, ","); + x += 1; + } + return true; +} + +bool getCartListInfo_ATARI() { + bool buttonreleased = 0; + bool cartselected = 0; +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F(" HOLD TO FAST CYCLE")); + display_Update(); +#else + Serial.println(F("HOLD BUTTON TO FAST CYCLE")); +#endif + delay(2000); +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + boolean buttonVal1 = (PING & (1 << 2)); //PG2 +#endif + if (buttonVal1 == LOW) { // Button Held - Fast Cycle + while (1) { // Scroll Game List + while (readVals_ATARI(atarigame, atarimm, atarill)) { + if (strcmp(ataricsvEND, atarigame) == 0) { + ataricsvFile.seek(0); // Restart + } else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART TITLE:")); + println_Msg(F("")); + println_Msg(atarigame); + display_Update(); +#else + Serial.print(F("CART TITLE:")); + Serial.println(atarigame); +#endif +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + buttonVal1 = (PING & (1 << 2)); //PG2 +#endif + if (buttonVal1 == HIGH) { // Button Released + buttonreleased = 1; + break; + } + if (buttonreleased) { + buttonreleased = 0; // Reset Flag + break; + } + } + } +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + buttonVal1 = (PING & (1 << 2)); //PG2 +#endif + if (buttonVal1 == HIGH) // Button Released + break; + } + } +#if (defined(enable_OLED) || defined(enable_LCD)) + display.setCursor(0, 56); + println_Msg(F("FAST CYCLE OFF")); + display_Update(); +#else + Serial.println(F("")); + Serial.println(F("FAST CYCLE OFF")); + Serial.println(F("PRESS BUTTON TO STEP FORWARD")); + Serial.println(F("DOUBLE CLICK TO STEP BACK")); + Serial.println(F("HOLD TO SELECT")); + Serial.println(F("")); +#endif + while (readVals_ATARI(atarigame, atarimm, atarill)) { + if (strcmp(ataricsvEND, atarigame) == 0) { + ataricsvFile.seek(0); // Restart + } else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART TITLE:")); + println_Msg(F("")); + println_Msg(atarigame); + display.setCursor(0, 48); + println_Msg(F("Press to Change")); + println_Msg(F("Hold to Select")); + display_Update(); +#else + Serial.print(F("CART TITLE:")); + Serial.println(atarigame); +#endif + while (1) { // Single Step + int b = checkButton(); + if (b == 1) { // Continue (press) + break; + } + if (b == 2) { // Reset to Start of List (doubleclick) + byte prevline = strtol(atarill, NULL, 10); + ataricsvpos -= prevline; + ataricsvFile.seek(ataricsvpos); + break; + } + if (b == 3) { // Long Press - Select Cart (hold) + newatarimapper = strtol(atarimm, NULL, 10); + EEPROM_writeAnything(7, newatarimapper); + cartselected = 1; // SELECTION MADE +#if (defined(enable_OLED) || defined(enable_LCD)) + println_Msg(F("SELECTION MADE")); + display_Update(); +#else + Serial.println(F("SELECTION MADE")); +#endif + break; + } + } + if (cartselected) { + cartselected = 0; // Reset Flag + return true; + } + } + } +#if (defined(enable_OLED) || defined(enable_LCD)) + println_Msg(F("")); + println_Msg(F("END OF FILE")); + display_Update(); +#else + Serial.println(F("END OF FILE")); +#endif + + return false; +} + +void checkCSV_ATARI() { + if (getCartListInfo_ATARI()) { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART SELECTED")); + println_Msg(F("")); + println_Msg(atarigame); + display_Update(); + // Display Settings + display.setCursor(0, 56); + print_Msg(F("CODE: ")); + println_Msg(newatarimapper, HEX); + display_Update(); +#else + Serial.println(F("")); + Serial.println(F("CART SELECTED")); + Serial.println(atarigame); + // Display Settings + Serial.print(F("CODE: ")); + Serial.println(newatarimapper, HEX); + Serial.println(F("")); +#endif + } else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display.setCursor(0, 56); + println_Msg(F("NO SELECTION")); + display_Update(); +#else + Serial.println(F("NO SELECTION")); +#endif + } +} + +void checkSize_ATARI() { + EEPROM_readAnything(7, atarimapper); + for (int i = 0; i < atarimapcount; i++) { + atariindex = i * 2; + if (atarimapper == pgm_read_byte(atarimapsize + atariindex)) { + atarisize = pgm_read_byte(atarimapsize + atariindex + 1); + EEPROM_writeAnything(8, atarisize); + break; + } + } +} + +void setCart_ATARI() { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(ataricartCSV); + display_Update(); +#endif + sd.chdir(); + sprintf(folder, "ATARI/CSV"); + sd.chdir(folder); // Switch Folder + ataricsvFile = sd.open(ataricartCSV, O_READ); + if (!ataricsvFile) { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CSV FILE NOT FOUND!")); + display_Update(); +#else + Serial.println(F("CSV FILE NOT FOUND!")); +#endif + while (1) { + if (checkButton() != 0) + setup_ATARI(); + } + } + checkCSV_ATARI(); + ataricsvFile.close(); + + checkSize_ATARI(); +} +#endif \ No newline at end of file diff --git a/Cart_Reader/Cart_Reader.ino b/Cart_Reader/Cart_Reader.ino index c694df6..25f8fd6 100644 --- a/Cart_Reader/Cart_Reader.ino +++ b/Cart_Reader/Cart_Reader.ino @@ -4,8 +4,8 @@ This project represents a community-driven effort to provide an easy to build and easy to modify cartridge dumper. - Date: 20.12.2022 - Version: 11.5 + Date: 03.01.2023 + Version: 12.0 SD lib: https://github.com/greiman/SdFat LCD lib: https://github.com/olikraus/u8g2 @@ -21,7 +21,7 @@ MichlK - ROM Reader for Super Nintendo Jeff Saltzman - 4-Way Button Wayne and Layne - Video Game Shield menu - skaman - Cart ROM READER SNES ENHANCED, Famicom Cart Dumper, Coleco-, Intellivision, Virtual Boy, WSV, PCW modules + skaman - Cart ROM READER SNES ENHANCED, Famicom Cart Dumper, Coleco-, Intellivision, Virtual Boy, WSV, PCW, ARC, Atari, ODY2, Fairchild modules Tamanegi_taro - PCE and Satellaview modules splash5 - GBSmart, Wonderswan and NGP modules hkz & themanbehindthecurtain - N64 flashram commands @@ -38,7 +38,7 @@ jiyunomegami, splash5, Kreeblah, ramapcsx2, PsyK0p4T, Dakkaron, majorpbx, Pickle, sdhizumi, Uzlopak, sakman55, Tombo89, scrap-a, borti4938, vogelfreiheit, CaitSith2, Modman, philenotfound, karimhadjsalem, nsx0r, ducky92, niklasweber, Lesserkuma, BacteriaMage, - vpelletier, Ancyker + vpelletier, Ancyker, mattiacci, RWeick And to nocash for figuring out the secrets of the SFC Nintendo Power cartridge. @@ -57,7 +57,7 @@ **********************************************************************************/ -char ver[5] = "11.5"; +char ver[5] = "12.0"; //****************************************** // !!! CHOOSE HARDWARE VERSION !!! @@ -79,10 +79,24 @@ char ver[5] = "11.5"; //****************************************** // add/remove // before #define to disable/enable modules you // don't need/need to save programm storage space and dynamic memory +// If you only get an empty or "Press Button" screen after flashing +// you have enabled too many modules + +// Atari 2600 +//#define enable_ATARI + +// Benesse Pocket Challenge W +//#define enable_PCW // ColecoVision #define enable_COLV +// Emerson Arcadia 2001 +//#define enable_ARC + +// Fairchild Channel F +//#define enable_FAIRCHILD + // Flashrom Programmer for SNES repros #define enable_FLASH //#define enable_FLASH16 @@ -93,8 +107,8 @@ char ver[5] = "11.5"; // Intellivision #define enable_INTV -// Sega Mega Drive/Genesis -#define enable_MD +// Neo Geo Pocket +#define enable_NGP // Nintendo 64 #define enable_N64 @@ -102,20 +116,17 @@ char ver[5] = "11.5"; // Nintendo Entertainment System/Family Computer #define enable_NES -// Neo Geo Pocket -#define enable_NGP +// Magnavox Odyssey 2 +//#define enable_ODY2 // PC Engine/TurboGrafx 16 #define enable_PCE -// Benesse Pocket Challenge W -#define enable_PCW - // Sega Master System #define enable_SMS -// Super Nintendo -#define enable_SNES +// Sega Mega Drive/Genesis +#define enable_MD // Super Famicom SF Memory Cassette #define enable_SFM @@ -123,15 +134,18 @@ char ver[5] = "11.5"; // Super Famicom Satellaview #define enable_SV +// Super Nintendo +#define enable_SNES + // Virtual Boy #define enable_VBOY +// Watara Supervision +//#define enable_WSV + // WonderSwan #define enable_WS -// Watara Supervision -#define enable_WSV - //****************************************** // HW CONFIGS //****************************************** @@ -390,6 +404,10 @@ void print_STR(byte string_number, boolean newline) { #define mode_VBOY 25 #define mode_WSV 26 #define mode_PCW 27 +#define mode_ATARI 28 +#define mode_ODY2 29 +#define mode_ARC 30 +#define mode_FAIRCHILD 31 // optimization-safe nop delay #define NOP __asm__ __volatile__("nop\n\t") @@ -630,6 +648,52 @@ uint32_t calculateCRC(char* fileName, char* folder, int offset) { } } +/****************************************** + CRC Functions for Atari, Fairchild, Ody2, Arc modules + *****************************************/ +#if (defined(enable_ATARI) || defined(enable_ODY2) || defined(enable_ARC) || defined(enable_FAIRCHILD)) + +inline uint32_t updateCRC(uint8_t ch, uint32_t crc) { + uint32_t idx = ((crc) ^ (ch)) & 0xff; + uint32_t tab_value = pgm_read_dword(crc_32_tab + idx); + return tab_value ^ ((crc) >> 8); +} + +FsFile crcFile; +char tempCRC[9]; + +uint32_t crc32(FsFile& file, uint32_t& charcnt) { + uint32_t oldcrc32 = 0xFFFFFFFF; + charcnt = 0; + while (file.available()) { + crcFile.read(sdBuffer, 512); + for (int x = 0; x < 512; x++) { + uint8_t c = sdBuffer[x]; + charcnt++; + oldcrc32 = updateCRC(c, oldcrc32); + } + } + return ~oldcrc32; +} + +void calcCRC(char* checkFile, unsigned long filesize, uint32_t* crcCopy, unsigned long offset) { + uint32_t crc; + crcFile = sd.open(checkFile); + crcFile.seek(offset); + crc = crc32(crcFile, filesize); + crcFile.close(); + sprintf(tempCRC, "%08lX", crc); + + if (crcCopy != NULL) { + *crcCopy = crc; + } + + print_Msg(F("CRC: ")); + println_Msg(tempCRC); + display_Update(); +} +#endif + //****************************************** // Functions for CRC32 database //****************************************** @@ -932,18 +996,22 @@ static const char modeItem11[] PROGMEM = "Colecovision"; static const char modeItem12[] PROGMEM = "Virtual Boy"; static const char modeItem13[] PROGMEM = "Watara Supervision"; static const char modeItem14[] PROGMEM = "Pocket Challenge W"; -static const char modeItem15[] PROGMEM = "Flashrom Programmer"; -static const char modeItem16[] PROGMEM = "About"; -static const char* const modeOptions[] PROGMEM = { modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7, modeItem8, modeItem9, modeItem10, modeItem11, modeItem12, modeItem13, modeItem14, modeItem15, modeItem16 }; +static const char modeItem15[] PROGMEM = "Atari 2600"; +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 }; // All included slots void mainMenu() { - // create menu with title and 15 options to choose from + // create menu with title and 20 options to choose from unsigned char modeMenu; byte num_answers; byte option_offset; - // Main menu spans across two pages + // Main menu spans across three pages currPage = 1; lastPage = 1; numPages = 3; @@ -957,7 +1025,7 @@ void mainMenu() { num_answers = 7; } else { // currPage == 3 option_offset = 14; - num_answers = 2; + num_answers = 6; } // Copy menuOptions out of progmem convertPgm(modeOptions + option_offset, num_answers); @@ -1077,8 +1145,36 @@ void mainMenu() { break; #endif -#ifdef enable_FLASH +#ifdef enable_ATARI case 14: + setup_ATARI(); + atariMenu(); + break; +#endif + +#ifdef enable_ODY2 + case 15: + setup_ODY2(); + ody2Menu(); + break; +#endif + +#ifdef enable_ARC + case 16: + setup_ARC(); + arcMenu(); + break; +#endif + +#ifdef enable_FAIRCHILD + case 17: + setup_FAIRCHILD(); + fairchildMenu(); + break; +#endif + +#ifdef enable_FLASH + case 18: #ifdef enable_FLASH16 flashMenu(); #else @@ -1087,7 +1183,7 @@ void mainMenu() { break; #endif - case 15: + case 19: aboutScreen(); break; @@ -1115,20 +1211,29 @@ static const char modeItem6[] PROGMEM = "About"; static const char* const modeOptions[] PROGMEM = { modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, string_reset2 }; // Add-ons submenu -static const char addonsItem1[] PROGMEM = "Consoles"; -static const char addonsItem2[] PROGMEM = "Handhelds"; -static const char addonsItem3[] PROGMEM = "Flashrom Programmer"; -//static const char addonsItem4[] PROGMEM = "Reset"; (stored in common strings array) -static const char* const addonsOptions[] PROGMEM = { addonsItem1, addonsItem2, addonsItem3, string_reset2 }; +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 addonsItem5[] PROGMEM = "Reset"; (stored in common strings array) +static const char* const addonsOptions[] PROGMEM = { addonsItem1, addonsItem2, addonsItem3, addonsItem4, string_reset2 }; -// Consoles submenu -static const char consolesItem1[] PROGMEM = "NES/Famicom"; -static const char consolesItem2[] PROGMEM = "PC Engine/TG16"; -static const char consolesItem3[] PROGMEM = "SMS/GG/MIII/SG-1000"; -static const char consolesItem4[] PROGMEM = "Intellivision"; -static const char consolesItem5[] PROGMEM = "Colecovision"; -//static const char consolesItem6[] PROGMEM = "Reset"; (stored in common strings array) -static const char* const consolesOptions[] PROGMEM = { consolesItem1, consolesItem2, consolesItem3, consolesItem4, consolesItem5, string_reset2 }; +// 70s Consoles submenu +static const char consoles70Item1[] PROGMEM = "Atari 2600"; +static const char consoles70Item2[] PROGMEM = "Magnavox Odyssey 2"; +static const char consoles70Item3[] PROGMEM = "Arcadia 2001"; +static const char consoles70Item4[] PROGMEM = "Fairchild Channel F"; +static const char consoles70Item5[] PROGMEM = "Intellivision"; +static const char consoles70Item6[] PROGMEM = "Colecovision"; +//static const char consoles70Item7[] PROGMEM = "Reset"; (stored in common strings array) +static const char* const consoles70Options[] PROGMEM = { consoles70Item1, consoles70Item2, consoles70Item3, consoles70Item4, consoles70Item5, consoles70Item6, string_reset2 }; + +// 80s Consoles submenu +static const char consoles80Item1[] PROGMEM = "NES/Famicom"; +static const char consoles80Item2[] PROGMEM = "PC Engine/TG16"; +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 }; // Handhelds submenu static const char handheldsItem1[] PROGMEM = "Virtual Boy"; @@ -1192,26 +1297,31 @@ void mainMenu() { // Everything that needs an adapter void addonMenu() { - // create menu with title and 4 options to choose from + // create menu with title and 5 options to choose from unsigned char addonsMenu; // Copy menuOptions out of progmem - convertPgm(addonsOptions, 4); - addonsMenu = question_box(F("Type"), menuOptions, 4, 0); + convertPgm(addonsOptions, 5); + addonsMenu = question_box(F("Type"), menuOptions, 5, 0); // wait for user choice to come back from the question box menu switch (addonsMenu) { - // Consoles + // 70s Consoles case 0: - consoleMenu(); + consoles70Menu(); + break; + + // 80s Consoles + case 1: + consoles80Menu(); break; // Handhelds - case 1: + case 2: handheldMenu(); break; #ifdef enable_FLASH - case 2: + case 3: #ifdef enable_FLASH16 flashMenu(); #else @@ -1220,7 +1330,7 @@ void addonMenu() { break; #endif - case 3: + case 4: resetArduino(); break; @@ -1230,15 +1340,76 @@ void addonMenu() { } // Everything that needs an adapter -void consoleMenu() { - // create menu with title and 6 options to choose from - unsigned char consolesMenu; +void consoles70Menu() { + // create menu with title and 7 options to choose from + unsigned char consoles70Menu; // Copy menuOptions out of progmem - convertPgm(consolesOptions, 6); - consolesMenu = question_box(F("Choose Adapter"), menuOptions, 6, 0); + 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 (consolesMenu) { + switch (consoles70Menu) { +#ifdef enable_ATARI + case 0: + setup_ATARI(); + atariMenu(); + break; +#endif + +#ifdef enable_ODY2 + case 1: + setup_ODY2(); + ody2Menu(); + break; +#endif + +#ifdef enable_ARC + case 2: + setup_ARC(); + arcMenu(); + break; +#endif + +#ifdef enable_FAIRCHILD + case 3: + setup_FAIRCHILD(); + fairchildMenu(); + break; +#endif + +#ifdef enable_INTV + case 4: + setup_INTV(); + intvMenu(); + break; +#endif + +#ifdef enable_COLV + case 5: + setup_COL(); + colMenu(); + break; +#endif + + case 6: + resetArduino(); + break; + + default: + print_MissingModule(); // does not return + } +} + +// Everything that needs an adapter +void consoles80Menu() { + // create menu with title and 6 options to choose from + unsigned char consoles80Menu; + // Copy menuOptions out of progmem + convertPgm(consoles80Options, 4); + consoles80Menu = question_box(F("Choose Adapter"), menuOptions, 4, 0); + + // wait for user choice to come back from the question box menu + switch (consoles80Menu) { #ifdef enable_NES case 0: mode = mode_NES; @@ -1265,21 +1436,7 @@ void consoleMenu() { break; #endif -#ifdef enable_INTV case 3: - setup_INTV(); - intvMenu(); - break; -#endif - -#ifdef enable_COLV - case 4: - setup_COL(); - colMenu(); - break; -#endif - - case 5: resetArduino(); break; @@ -3332,6 +3489,26 @@ void loop() { else if (mode == mode_PCW) { pcwMenu(); } +#endif +#ifdef enable_ATARI + else if (mode == mode_ATARI) { + atariMenu(); + } +#endif +#ifdef enable_ODY2 + else if (mode == mode_ODY2) { + ody2Menu(); + } +#endif +#ifdef enable_ARC + else if (mode == mode_ARC) { + arcMenu(); + } +#endif +#ifdef enable_FAIRCHILD + else if (mode == mode_FAIRCHILD) { + fairchildMenu(); + } #endif else { display_Clear(); diff --git a/Cart_Reader/FAIRCHILD.ino b/Cart_Reader/FAIRCHILD.ino new file mode 100644 index 0000000..e1ece11 --- /dev/null +++ b/Cart_Reader/FAIRCHILD.ino @@ -0,0 +1,865 @@ +//****************************************** +// FAIRCHILD CHANNEL F MODULE +//****************************************** +#if defined(enable_FAIRCHILD) +// Fairchild Channel F +// Cartridge Pinout +// 22P (27P Width) 2.54mm pitch connector +// +// TOP BOTTOM +// SIDE SIDE +// +-------+ +// | == | +// | 1 |- GND +// | 2 |- GND +// | 3 |- D0 +// | 4 |- D1 +// | 5 |- /INTREQ +// | 6 |- ROMC0 +// | 7 |- ROMC1 +// | 8 |- ROMC2 +// | 9 |- D2 +// | 10 |- ROMC3 +// | 11 |- D3 +// | == | +// | == | +// | == | +// | 12 |- ROMC4 +// | 13 |- PHI +// | 14 |- D4 +// | 15 |- WRITE +// | 16 |- D5 +// | 17 |- D6 +// | 18 |- D7 +// | 19 |- VDD(+5V) +// | 20 |- VDD(+5V) +// | 21 |- NC +// | 22 |- VGG(+12V) +// | == | +// +-------+ +// +// TOP +// +----------------------------------------------------------------------------------+ +// | | +// LEFT | | RIGHT +// | == 22 21 20 19 18 17 16 15 14 13 12 == == == 11 10 9 8 7 6 5 4 3 2 1 == | +// +----------------------------------------------------------------------------------+ +// BOTTOM +// + +// CONTROL PINS: +// PHI(PH3) - SNES /CS +// /INTREQ(PH4) - SNES /IRQ +// WRITE(PH5) - SNES /WR +// ROMC0(PF0) - SNES A0 +// ROMC1(PF1) - SNES A1 +// ROMC2(PF2) - SNES A2 +// ROMC3(PF3) - SNES A3 +// ROMC4(PF4) - SNES A4 + +/****************************************** + Defines + *****************************************/ +#define PHI_HI PORTH |= (1 << 3) +#define PHI_LOW PORTH &= ~(1 << 3) +#define WRITE_HI PORTH |= (1 << 5) +#define WRITE_LOW PORTH &= ~(1 << 5) + +byte FAIRCHILD[] = { 2, 3, 4, 6 }; +byte fairchildlo = 0; // Lowest Entry +byte fairchildhi = 3; // Highest Entry + +byte fairchildsize; +byte newfairchildsize; + +// EEPROM MAPPING +// 08 ROM SIZE + +//****************************************** +// Menu +//****************************************** +// Base Menu +static const char fairchildMenuItem1[] PROGMEM = "Select Cart"; +static const char fairchildMenuItem2[] PROGMEM = "Read ROM"; +static const char fairchildMenuItem3[] PROGMEM = "Set Size"; +static const char fairchildMenuItem4[] PROGMEM = "Read 16K"; +static const char fairchildMenuItem5[] PROGMEM = "Reset"; +static const char* const menuOptionsFAIRCHILD[] PROGMEM = { fairchildMenuItem1, fairchildMenuItem2, fairchildMenuItem3, fairchildMenuItem4, fairchildMenuItem5 }; + +void setup_FAIRCHILD() { + // Set Address Pins to Output + // Channel F uses A0-A4 [A5-A23 UNUSED] + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output + // ---(PH0) ---(PH1) PHI(PH3) /INTREQ(PH4) WRITE(PH5) ---(PH6) + DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Set TIME(PJ0) to Output (UNUSED) + DDRJ |= (1 << 0); + + // Set Pins (D0-D7) to Input + DDRC = 0x00; + + // Setting Unused Control Pins to HIGH + // ---(PH0) ---(PH1) PHI(PH3) /INTREQ(PH4) WRITE(PH5) ---(PH6) + PORTH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Set Unused Data Pins (PA0-PA7) to Output + DDRA = 0xFF; + + // Set Unused Pins HIGH + PORTA = 0xFF; + PORTK = 0xFF; // A8-A15 + PORTL = 0xFF; // A16-A23 + PORTJ |= (1 << 0); // TIME(PJ0) + + checkStatus_FAIRCHILD(); + strcpy(romName, "FAIRCHILD"); + + mode = mode_FAIRCHILD; +} + +void fairchildMenu() { + convertPgm(menuOptionsFAIRCHILD, 5); + uint8_t mainMenu = question_box(F("CHANNEL F MENU"), menuOptions, 5, 0); + + switch (mainMenu) { + case 0: + // Select Cart + setCart_FAIRCHILD(); + wait(); + setup_FAIRCHILD(); + break; + + case 1: + // Read ROM + sd.chdir("/"); + readROM_FAIRCHILD(); + sd.chdir("/"); + break; + + case 2: + // Set Size + setROMSize_FAIRCHILD(); + break; + + case 3: + // Read 16K + sd.chdir("/"); + read16K_FAIRCHILD(); + sd.chdir("/"); + break; + + case 4: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// READ CODE +//****************************************** + +// Sean Riddle Dumper Routine +// clear PC0 with ROMC state 8 +// loop 256 times +// fetch 16 bytes into buffer with ROMC state 0 +// dump buffer to serial port +// clear PC0 + +// Clear PC0 +void clearRegister_FAIRCHILD() { + PHI_LOW; + WRITE_LOW; + PORTF = 0; // ROMC3 LOW + + delay(2000); + + PHI_HI; + WRITE_HI; + NOP; + NOP; + NOP; + + PHI_LOW; + NOP; + NOP; + NOP; + + WRITE_LOW; + PHI_HI; + PORTF = 0; // ROMC3 LOW + NOP; + NOP; + NOP; + + PHI_LOW; + NOP; + NOP; + NOP; + + PORTF = 0; // ROMC3 LOW + PHI_HI; + PORTF = 0x8; // this puts us in ROMC state 8 - clear PC0 + NOP; + NOP; + NOP; + + PHI_LOW; + NOP; + NOP; + NOP; + + PORTF = 0x08; // ROMC3 HIGH + PHI_HI; + PORTF = 0x08; // ROMC3 HIGH + NOP; + NOP; + NOP; + + PHI_LOW; + NOP; + NOP; + NOP; + + PHI_HI; + WRITE_HI; + NOP; + NOP; + NOP; + + PHI_LOW; + NOP; + NOP; + NOP; + + WRITE_LOW; + PHI_HI; + PORTF = 0; // ROMC3 LOW + NOP; + NOP; + NOP; + + PHI_LOW; + NOP; + NOP; + NOP; + + WRITE_LOW; + PHI_HI; + PORTF = 0; // ROMC3 LOW + NOP; + NOP; + NOP; + + PHI_LOW; + NOP; + NOP; + NOP; + + WRITE_LOW; +} + +void setROMC_FAIRCHILD(uint8_t command) { + PHI_LOW; + WRITE_LOW; + NOP; + + WRITE_HI; + PHI_HI; + NOP; + + PHI_LOW; + NOP; + NOP; + + WRITE_LOW; + PHI_HI; + NOP; + NOP; + + // PWs = 4 PHI Cycles + // PWl = 6 PHI Cycles + for (int x = 0; x < 2; x++) { // 2 PHI + PHI_LOW; + NOP; + NOP; + PHI_HI; + NOP; + NOP; + } + PORTF = command; // ROMC3 = command + + for (int x = 0; x < 3; x++) { // 4 PHI + PHI_LOW; + NOP; + NOP; + PHI_HI; + NOP; + NOP; + } + + PHI_LOW; + NOP; + NOP; + + PHI_HI; + WRITE_HI; + NOP; + + PHI_LOW; + NOP; + NOP; + + PHI_HI; + WRITE_LOW; + NOP; + + PHI_LOW; + NOP; + NOP; +} + +void setREAD_FAIRCHILD() { + PHI_LOW; + WRITE_LOW; + NOP; + + WRITE_HI; + PHI_HI; + NOP; + + PHI_LOW; + NOP; + NOP; + + WRITE_LOW; + PHI_HI; + NOP; + NOP; + + // PWs = 4 PHI Cycles + // PWl = 6 PHI Cycles + for (int x = 0; x < 2; x++) { // 2 PHI + PHI_LOW; + NOP; + NOP; + PHI_HI; + NOP; + NOP; + } + PORTF = 0; // ROMC3 = 0 = Fetch Data +} + +uint8_t readData_FAIRCHILD() { + for (int x = 0; x < 3; x++) { // 4 PHI + PHI_LOW; + NOP; + NOP; + PHI_HI; + NOP; + NOP; + } + + PHI_LOW; + NOP; + NOP; + + PHI_HI; + WRITE_HI; + NOP; + + PHI_LOW; + NOP; + NOP; + + PHI_HI; + WRITE_LOW; + NOP; + + uint8_t ret = PINC; // read databus into buffer + + PHI_LOW; + NOP; + NOP; + + return ret; +} + +void readROM_FAIRCHILD() { + strcpy(fileName, romName); + strcat(fileName, ".bin"); + + // create a new folder for storing rom file + EEPROM_readAnything(0, foldern); + sprintf(folder, "FAIRCHILD/ROM/%d", foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // open file on sdcard + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_FatalError(create_file_STR); + } + + // write new folder number back to EEPROM + foldern++; + EEPROM_writeAnything(0, foldern); + + unsigned long cartsize = FAIRCHILD[fairchildsize] * 0x400; + uint8_t blocks = cartsize / 0x200; + setROMC_FAIRCHILD(0x8); // Clear PC0 + setREAD_FAIRCHILD(); + + // ROM Start Bytes + // 0x55,0x08 - desert fox, muehle, space war, tic-tac-toe (all 2K) + // 0x55,0x2B - most carts + // 0x55,0xAA - alien invasion (4K) + // 0x55,0xBB - video whizball (3K) + for (int y = 0; y < 0x4800; y++) { + uint8_t startbyte = readData_FAIRCHILD(); + if (startbyte == 0x55) { // Start Byte + sdBuffer[0] = startbyte; + startbyte = readData_FAIRCHILD(); + if ((startbyte == 0x08) || (startbyte == 0x2B) || (startbyte == 0xAA) || (startbyte == 0xBB)) { + sdBuffer[1] = startbyte; + for (int w = 2; w < 512; w++) { + startbyte = readData_FAIRCHILD(); + sdBuffer[w] = startbyte; + } + myFile.write(sdBuffer, 512); + delay(1); // Added delay + for (int z = 1; z < blocks; z++) { + // Skip BIOS/Blocks Code for 4K Cart + if (cartsize == 0x1000) { // Pro Football 4K + setROMC_FAIRCHILD(0x8); // Clear PC0 + setREAD_FAIRCHILD(); + uint16_t offset = z * 0x200; + for (int x = 0; x < 0x800 + offset; x++) { // Skip BIOS/Previous Blocks + readData_FAIRCHILD(); + } + } + for (int w = 0; w < 512; w++) { + uint8_t temp = readData_FAIRCHILD(); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + delay(1); // Added delay + } + break; + } + } + } + myFile.close(); + + calcCRC(fileName, cartsize, NULL, 0); + + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +void read16K_FAIRCHILD() // Read 16K Bytes +{ + strcpy(fileName, romName); + strcat(fileName, ".bin"); + + // create a new folder for storing rom file + EEPROM_readAnything(0, foldern); + sprintf(folder, "FAIRCHILD/ROM/%d", foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // open file on sdcard + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_FatalError(create_file_STR); + } + + // write new folder number back to EEPROM + foldern++; + EEPROM_writeAnything(0, foldern); + + unsigned long cartsize = FAIRCHILD[fairchildsize] * 0x400; + for (int y = 0; y < 0x20; y++) { + // Skip BIOS/Blocks Code for 4K Cart + // 3K/6K CARTS MAY NEED THE SKIP BIOS/BLOCKS ROUTINE USED FOR THE 4K CART + // TEST 3K/6K CARTS BY SETTING ROM SIZE TO 2K/4K AND COMPARE 16K DUMPS + if (cartsize == 0x1000) { // Pro Football 4K + setROMC_FAIRCHILD(0x8); // Clear PC0 + setREAD_FAIRCHILD(); + uint16_t offset = y * 0x200; + for (int x = 0; x < 0x800 + offset; x++) { // Skip BIOS/Previous Blocks + readData_FAIRCHILD(); + } + } + for (int w = 0; w < 512; w++) { + uint8_t temp = readData_FAIRCHILD(); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + delay(1); // Added delay + } + myFile.close(); + + calcCRC(fileName, 0x4000, NULL, 0); + + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +void setROMSize_FAIRCHILD() { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + if (fairchildlo == fairchildhi) + newfairchildsize = fairchildlo; + else { + int b = 0; + int i = fairchildlo; + while (1) { + display_Clear(); + print_Msg(F("ROM Size: ")); + println_Msg(FAIRCHILD[i]); + println_Msg(F("")); + println_Msg(F("Press to Change")); + println_Msg(F("Hold to Select")); + display_Update(); + b = checkButton(); + if (b == 2) { // Previous (doubleclick) + if (i == fairchildlo) + i = fairchildhi; + else + i--; + } + if (b == 1) { // Next (press) + if (i == fairchildhi) + i = fairchildlo; + else + i++; + } + if (b == 3) { // Long Press - Execute (hold) + newfairchildsize = i; + break; + } + } + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(F("ROM SIZE ")); + print_Msg(FAIRCHILD[newfairchildsize]); + println_Msg(F("K")); + display_Update(); + delay(1000); +#else + if (fairchildlo == fairchildhi) + newfairchildsize = fairchildlo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (fairchildhi - fairchildlo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(FAIRCHILD[i + fairchildlo]); + Serial.println(F("K")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newfairchildsize = sizeROM.toInt() + fairchildlo; + if (newfairchildsize > fairchildhi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(F("")); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(FAIRCHILD[newfairchildsize]); + Serial.println(F("K")); +#endif + EEPROM_writeAnything(8, newfairchildsize); + fairchildsize = newfairchildsize; +} + +void checkStatus_FAIRCHILD() { + EEPROM_readAnything(8, fairchildsize); + if (fairchildsize > 3) { + fairchildsize = 0; + EEPROM_writeAnything(8, fairchildsize); + } + +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CHANNEL F READER")); + println_Msg(F("CURRENT SETTINGS")); + println_Msg(F("")); + print_Msg(F("ROM SIZE: ")); + print_Msg(FAIRCHILD[fairchildsize]); + println_Msg(F("K")); + display_Update(); + wait(); +#else + Serial.print(F("CURRENT ROM SIZE: ")); + Serial.print(FAIRCHILD[fairchildsize]); + Serial.println(F("K")); + Serial.println(F("")); +#endif +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +FsFile fairchildcsvFile; +char fairchildgame[33]; // title +char fairchildrr[3]; // romsize +char fairchildll[4]; // linelength (previous line) +unsigned long fairchildcsvpos; // CSV File Position +char fairchildcartCSV[] = "fairchildcart.txt"; // CSV List +char fairchildcsvEND[] = "EOF"; // CSV End Marker for scrolling + +bool readLine_FAIRCHILD(FsFile& f, char* line, size_t maxLen) { + for (size_t n = 0; n < maxLen; n++) { + int c = f.read(); + if (c < 0 && n == 0) return false; // EOF + if (c < 0 || c == '\n') { + line[n] = 0; + return true; + } + line[n] = c; + } + return false; // line too long +} + +bool readVals_FAIRCHILD(char* fairchildgame, char* fairchildrr, char* fairchildll) { + char line[39]; + fairchildcsvpos = fairchildcsvFile.position(); + if (!readLine_FAIRCHILD(fairchildcsvFile, line, sizeof(line))) { + return false; // EOF or too long + } + char* comma = strtok(line, ","); + int x = 0; + while (comma != NULL) { + if (x == 0) + strcpy(fairchildgame, comma); + else if (x == 1) + strcpy(fairchildrr, comma); + else if (x == 2) + strcpy(fairchildll, comma); + comma = strtok(NULL, ","); + x += 1; + } + return true; +} + +bool getCartListInfo_FAIRCHILD() { + bool buttonreleased = 0; + bool cartselected = 0; +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F(" HOLD TO FAST CYCLE")); + display_Update(); +#else + Serial.println(F("HOLD BUTTON TO FAST CYCLE")); +#endif + delay(2000); +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + boolean buttonVal1 = (PING & (1 << 2)); //PG2 +#endif + if (buttonVal1 == LOW) { // Button Held - Fast Cycle + while (1) { // Scroll Game List + while (readVals_FAIRCHILD(fairchildgame, fairchildrr, fairchildll)) { + if (strcmp(fairchildcsvEND, fairchildgame) == 0) { + fairchildcsvFile.seek(0); // Restart + } else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART TITLE:")); + println_Msg(F("")); + println_Msg(fairchildgame); + display_Update(); +#else + Serial.print(F("CART TITLE:")); + Serial.println(fairchildgame); +#endif +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + buttonVal1 = (PING & (1 << 2)); //PG2 +#endif + if (buttonVal1 == HIGH) { // Button Released + buttonreleased = 1; + break; + } + if (buttonreleased) { + buttonreleased = 0; // Reset Flag + break; + } + } + } +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + buttonVal1 = (PING & (1 << 2)); //PG2 +#endif + if (buttonVal1 == HIGH) // Button Released + break; + } + } +#if (defined(enable_OLED) || defined(enable_LCD)) + display.setCursor(0, 56); + println_Msg(F("FAST CYCLE OFF")); + display_Update(); +#else + Serial.println(F("")); + Serial.println(F("FAST CYCLE OFF")); + Serial.println(F("PRESS BUTTON TO STEP FORWARD")); + Serial.println(F("DOUBLE CLICK TO STEP BACK")); + Serial.println(F("HOLD TO SELECT")); + Serial.println(F("")); +#endif + while (readVals_FAIRCHILD(fairchildgame, fairchildrr, fairchildll)) { + if (strcmp(fairchildcsvEND, fairchildgame) == 0) { + fairchildcsvFile.seek(0); // Restart + } else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART TITLE:")); + println_Msg(F("")); + println_Msg(fairchildgame); + display.setCursor(0, 48); + println_Msg(F("Press to Change")); + println_Msg(F("Hold to Select")); + display_Update(); +#else + Serial.print(F("CART TITLE:")); + Serial.println(fairchildgame); +#endif + while (1) { // Single Step + int b = checkButton(); + if (b == 1) { // Continue (press) + break; + } + if (b == 2) { // Reset to Start of List (doubleclick) + byte prevline = strtol(fairchildll, NULL, 10); + fairchildcsvpos -= prevline; + fairchildcsvFile.seek(fairchildcsvpos); + break; + } + if (b == 3) { // Long Press - Select Cart (hold) + newfairchildsize = strtol(fairchildrr, NULL, 10); + EEPROM_writeAnything(8, newfairchildsize); + cartselected = 1; // SELECTION MADE +#if (defined(enable_OLED) || defined(enable_LCD)) + println_Msg(F("SELECTION MADE")); + display_Update(); +#else + Serial.println(F("SELECTION MADE")); +#endif + break; + } + } + if (cartselected) { + cartselected = 0; // Reset Flag + return true; + } + } + } +#if (defined(enable_OLED) || defined(enable_LCD)) + println_Msg(F("")); + println_Msg(F("END OF FILE")); + display_Update(); +#else + Serial.println(F("END OF FILE")); +#endif + + return false; +} + +void checkCSV_FAIRCHILD() { + if (getCartListInfo_FAIRCHILD()) { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART SELECTED")); + println_Msg(F("")); + println_Msg(fairchildgame); + display_Update(); + // Display Settings + display.setCursor(0, 56); + print_Msg(F("CODE: R")); + println_Msg(newfairchildsize); + display_Update(); +#else + Serial.println(F("")); + Serial.println(F("CART SELECTED")); + Serial.println(fairchildgame); + // Display Settings + Serial.print(F("CODE: R")); + Serial.println(newfairchildsize); + Serial.println(F("")); +#endif + } else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display.setCursor(0, 56); + println_Msg(F("NO SELECTION")); + display_Update(); +#else + Serial.println(F("NO SELECTION")); +#endif + } +} + +void setCart_FAIRCHILD() { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(fairchildcartCSV); + display_Update(); +#endif + sd.chdir(); + sprintf(folder, "FAIRCHILD/CSV"); + sd.chdir(folder); // Switch Folder + fairchildcsvFile = sd.open(fairchildcartCSV, O_READ); + if (!fairchildcsvFile) { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CSV FILE NOT FOUND!")); + display_Update(); +#else + Serial.println(F("CSV FILE NOT FOUND!")); +#endif + while (1) { + if (checkButton() != 0) + setup_FAIRCHILD(); + } + } + checkCSV_FAIRCHILD(); + + fairchildcsvFile.close(); +} +#endif \ No newline at end of file diff --git a/Cart_Reader/NES.ino b/Cart_Reader/NES.ino index d6670e6..7fb552e 100644 --- a/Cart_Reader/NES.ino +++ b/Cart_Reader/NES.ino @@ -2797,7 +2797,7 @@ void readPRG(boolean readrom) { } } break; - + case 31: banks = int_pow(2, prgsize) * 4; for (int i = 0; i < banks; i += 8) { @@ -2960,7 +2960,7 @@ void readPRG(boolean readrom) { } } break; - + case 56: banks = int_pow(2, prgsize) * 2; for (int i = 0; i < banks; i++) { @@ -2971,7 +2971,7 @@ void readPRG(boolean readrom) { } } break; - + case 57: banks = int_pow(2, prgsize); for (int i = 0; i < banks; i++) { @@ -3060,7 +3060,7 @@ void readPRG(boolean readrom) { case 68: case 73: // 128K banks = int_pow(2, prgsize); - for (int i = 0; i < banks; i++) { // 128K + for (int i = 0; i < banks; i++) { // 128K write_prg_byte(0xF000, i); for (word address = 0x0; address < 0x4000; address += 512) { // 16K Banks ($8000-$BFFF) dumpPRG(base, address); @@ -3230,7 +3230,7 @@ void readPRG(boolean readrom) { case 94: // bus conflicts - fixed last bank banks = int_pow(2, prgsize); busConflict = true; - for (int i = 0; i < banks-1; i++) { + for (int i = 0; i < banks - 1; i++) { for (int x = 0; x < 0x4000; x++) { if (read_prg_byte(0xC000 + x) == (i << 2)) { write_prg_byte(0xC000 + x, i << 2); @@ -3250,13 +3250,13 @@ void readPRG(boolean readrom) { } break; - case 97: // fixed first bank + case 97: // fixed first bank case 180: // bus conflicts - fixed fist bank banks = int_pow(2, prgsize); busConflict = true; for (word address = 0x0; address < 0x4000; address += 512) { - dumpPRG(base, address); - } + dumpPRG(base, address); + } for (int i = 1; i < banks; i++) { for (int x = 0; x < 0x4000; x++) { if (read_prg_byte(0x8000 + x) == i) { @@ -3504,7 +3504,7 @@ void readPRG(boolean readrom) { } } break; - + case 214: banks = int_pow(2, prgsize); for (int i = 0; i < banks; i++) { @@ -3621,12 +3621,12 @@ void readPRG(boolean readrom) { } } break; - + case 236: banks = int_pow(2, prgsize); for (int i = 0; i < banks; i++) { write_prg_byte(0x8000 | ((i & 0x38) >> 3), 0); // A19-A17 - write_prg_byte(0xC030 | (i & 0x0F), 0); // A17-A14 + write_prg_byte(0xC030 | (i & 0x0F), 0); // A17-A14 for (word address = 0x0; address < 0x4000; address += 512) { dumpPRG(base, address); } @@ -3920,8 +3920,8 @@ void readCHR(boolean readrom) { case 25: // 128K/256K banks = int_pow(2, chrsize) * 4; for (int i = 0; i < banks; i++) { - write_prg_byte(0xB000, i & 0xF); // CHR Bank Lower 4 bits - write_prg_byte(0xB00A, (i >> 4) & 0xF); // Combine VRC2c and VRC4b, VRC4d reg + write_prg_byte(0xB000, i & 0xF); // CHR Bank Lower 4 bits + write_prg_byte(0xB00A, (i >> 4) & 0xF); // Combine VRC2c and VRC4b, VRC4d reg for (word address = 0x0; address < 0x400; address += 512) { dumpCHR(address); } @@ -4076,7 +4076,7 @@ void readCHR(boolean readrom) { } } break; - + case 56: banks = int_pow(2, chrsize) * 4; for (int i = 0; i < banks; i++) { @@ -4086,11 +4086,11 @@ void readCHR(boolean readrom) { } } break; - + case 57: banks = int_pow(2, chrsize) / 2; for (int i = 0; i < banks; i++) { - write_prg_byte(0x8800, i & 0x07); // A15-A13 + write_prg_byte(0x8800, i & 0x07); // A15-A13 write_prg_byte(0x8000, 0x80 | ((i & 0x08) << 3)); // A16 for (word address = 0x0; address < 0x2000; address += 512) { dumpCHR(address); @@ -4530,7 +4530,7 @@ void readCHR(boolean readrom) { } } break; - + case 214: banks = int_pow(2, chrsize) / 2; for (int i = 0; i < banks; i++) { @@ -4570,7 +4570,7 @@ void readCHR(boolean readrom) { } } break; - + case 236: banks = int_pow(2, chrsize) / 2; for (int i = 0; i < banks; i++) { diff --git a/Cart_Reader/ODY2.ino b/Cart_Reader/ODY2.ino new file mode 100644 index 0000000..4b83b06 --- /dev/null +++ b/Cart_Reader/ODY2.ino @@ -0,0 +1,632 @@ +//****************************************** +// MAGNAVOX ODYSSEY 2 MODULE +//****************************************** +#if defined(enable_ODY2) +// Magnavox Odyssey 2 +// Philips Videopac/Videopac+ +// Cartridge Pinout +// 30P 3.96mm pitch connector +// +// FRONT BACK +// SIDE SIDE +// +------+ +// T0 -| 1 A |- /WR +// D0 -| 2 B |- GND +// D1 -| 3 C |- GND +// D2 -| 4 D |- VCC (+5V) +// D3 -| 5 E |- CS3 +// D4 -| 6 F |- /PSEN (/CE) +// D5 -| 7 G |- A0 +// D6 -| 8 H |- A1 +// D7 -| 9 J |- A2 +// A10 (P22) -| 10 K |- A3 +// /CS1 (P14) -| 11 L |- A4 +// P11 -| 12 M |- A5 +// P10 -| 13 N |- A7 +// A11 (P23) -| 14 P |- A6 +// A9 (P21) -| 15 R |- A8 (P20) +// +------+ +// +// NOTE: ADDRESS A7/A6 PIN ORDER ON PINS N & P. +// NOTE: MOST CARTS DO NOT CONNECT A10 ON PIN 10. +// +// BACK +// +---------------------------------------------+ +// | A B C D E F G H J K L M N P R | +// LEFT | | RIGHT +// | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | +// +---------------------------------------------+ +// FRONT +// +// CONTROL PINS: +// T0(PH0) - SNES RESET +// CS3(PH3) - SNES /CS +// /CS1(PH4) - SNES /IRQ +// /WR(PH5) - SNES /WR +// /PSEN(PH6) - SNES /RD + +byte ODY2[] = { 2, 4, 8, 12, 16 }; +byte ody2lo = 0; // Lowest Entry +byte ody2hi = 4; // Highest Entry + +byte ody2mapper; +byte newody2mapper; +byte ody2size; +byte newody2size; + +// EEPROM MAPPING +// 07 MAPPER +// 08 ROM SIZE + +//****************************************** +// Menu +//****************************************** +// Base Menu +static const char ody2MenuItem1[] PROGMEM = "Select Cart"; +static const char ody2MenuItem2[] PROGMEM = "Read ROM"; +static const char ody2MenuItem3[] PROGMEM = "Set Size"; +static const char ody2MenuItem4[] PROGMEM = "Reset"; +static const char* const menuOptionsODY2[] PROGMEM = { ody2MenuItem1, ody2MenuItem2, ody2MenuItem3, ody2MenuItem4 }; + +void setup_ODY2() { + // Set Address Pins to Output + // Odyssey 2 uses A0-A13 [A14-A23 UNUSED] + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output + // T0(PH0) ---(PH1) CS3(PH3) /CS1(PH4) /WR(PH5) /RD(PH6) + DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + + // Set TIME(PJ0) to Output (UNUSED) + DDRJ |= (1 << 0); + + // Set Pins (D0-D7) to Input + DDRC = 0x00; + + // Setting Control Pins to HIGH + // T0(PH0) ---(PH1) /CS1(PH4) /WR(PH5) /RD(PH6) + PORTH |= (1 << 0) | (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6); + + // Set CS3(PH3) to LOW + PORTH &= ~(1 << 3); + + // Set Unused Data Pins (PA0-PA7) to Output + DDRA = 0xFF; + + // Set Unused Pins HIGH + PORTA = 0xFF; + PORTL = 0xFF; // A16-A23 + PORTJ |= (1 << 0); // TIME(PJ0) + + checkStatus_ODY2(); + strcpy(romName, "ODYSSEY2"); + + mode = mode_ODY2; +} + +void ody2Menu() { + convertPgm(menuOptionsODY2, 4); + uint8_t mainMenu = question_box(F("ODYSSEY 2 MENU"), menuOptions, 4, 0); + + switch (mainMenu) { + case 0: + // Select Cart + setCart_ODY2(); + wait(); + setup_ODY2(); + break; + + case 1: + // Read ROM + sd.chdir("/"); + readROM_ODY2(); + sd.chdir("/"); + break; + + case 2: + // Set Size + setROMSize_ODY2(); + break; + + case 3: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// READ CODE +//****************************************** + +uint8_t readData_ODY2(uint16_t addr) { + PORTF = addr & 0xFF; // A0-A7 + PORTK = (addr >> 8) & 0xFF; // A8-A13 + + // Set /PSEN (/CE) to LOW + PORTH &= ~(1 << 6); // /PSEN LOW (ENABLE) + NOP; + NOP; + NOP; + NOP; + NOP; + + uint8_t ret = PINC; + + // Pull /PSEN (/CE) to HIGH + PORTH |= (1 << 6); // /PSEN HIGH (DISABLE) + + return ret; +} + +void readSegment_ODY2(uint16_t startaddr, uint16_t endaddr) { + for (uint16_t addr = startaddr; addr < endaddr; addr += 512) { + for (int w = 0; w < 512; w++) { + uint8_t temp = readData_ODY2(addr + w); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + } +} + +void bankSwitch_ODY2(uint16_t addr, uint8_t data) { + PORTF = addr & 0xFF; // A0-A7 + PORTK = (addr >> 8) & 0xFF; // A8-A13 + NOP; + NOP; + NOP; + NOP; + NOP; + + // Set /CS1(PH4) to LOW + PORTH &= ~(1 << 4); + // Set /WR(PH5) to LOW + PORTH &= ~(1 << 5); + NOP; + NOP; + NOP; + NOP; + NOP; + + DDRC = 0xFF; // Set to Output + NOP; + NOP; + NOP; + NOP; + NOP; + + PORTC = data; + NOP; + NOP; + NOP; + NOP; + NOP; + + // Set /WR(PH5) to HIGH + PORTH |= (1 << 5); + // Set /CS1(PH4) to HIGH + PORTH |= (1 << 4); + + DDRC = 0x00; // Reset to Input +} + +void readROM_ODY2() { + strcpy(fileName, romName); + strcat(fileName, ".bin"); + + // create a new folder for storing rom file + EEPROM_readAnything(0, foldern); + sprintf(folder, "ODY2/ROM/%d", foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // open file on sdcard + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_FatalError(create_file_STR); + } + + // write new folder number back to EEPROM + foldern++; + EEPROM_writeAnything(0, foldern); + + if (ody2mapper == 1) { // A10 CONNECTED + // Videopac 31: Musician + // Videopac 40: 4 in 1 Row/4 en 1 Ligne + readSegment_ODY2(0x0000, 0x1000); // 4K + } + // A10 NOT CONNECTED + else if (ody2size > 2) { // 12K/16K (BANKSWITCH) + // Videopac+ 55: Neutron Star 12K = 2K x 6 Banks + // Videopac+ 58: Norseman 12K = 2K x 6 Banks + // Videopac+ 59: Helicopter Rescue 16K = 2K x 8 Banks + // Videopac+ 60: Trans American Rally 16K = 2K x 8 Banks + uint8_t ody2banks = (ody2size * 4) / 2; + for (int x = (ody2banks - 1); x >= 0; x--) { + bankSwitch_ODY2(0x80, ~x); + readSegment_ODY2(0x0400, 0x0C00); // 2K x 6/8 = 12K/16K + } + } else { // STANDARD SIZES + readSegment_ODY2(0x0400, 0x0C00); // 2K + if (ody2size > 0) { + readSegment_ODY2(0x1400, 0x1C00); // +2K = 4K + if (ody2size > 1) { + readSegment_ODY2(0x2400, 0x2C00); // +2K = 6K + readSegment_ODY2(0x3400, 0x3C00); // +2K = 8K + } + } + } + myFile.close(); + + unsigned long crcsize = ODY2[ody2size] * 0x400; + calcCRC(fileName, crcsize, NULL, 0); + + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +void setROMSize_ODY2() { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + if (ody2lo == ody2hi) + newody2size = ody2lo; + else { + int b = 0; + int i = ody2lo; + while (1) { + display_Clear(); + print_Msg(F("ROM Size: ")); + println_Msg(ODY2[i]); + println_Msg(F("")); + println_Msg(F("Press to Change")); + println_Msg(F("Hold to Select")); + display_Update(); + b = checkButton(); + if (b == 2) { // Previous (doubleclick) + if (i == ody2lo) + i = ody2hi; + else + i--; + } + if (b == 1) { // Next (press) + if (i == ody2hi) + i = ody2lo; + else + i++; + } + if (b == 3) { // Long Press - Execute (hold) + newody2size = i; + break; + } + } + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(F("ROM SIZE ")); + print_Msg(ODY2[newody2size]); + println_Msg(F("K")); + display_Update(); + delay(1000); +#else + if (ody2lo == ody2hi) + newody2size = ody2lo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (ody2hi - ody2lo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(ODY2[i + ody2lo]); + Serial.println(F("K")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newody2size = sizeROM.toInt() + ody2lo; + if (newody2size > ody2hi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(F("")); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(ODY2[newody2size]); + Serial.println(F("K")); +#endif + EEPROM_writeAnything(8, newody2size); + ody2size = newody2size; +} + +void checkStatus_ODY2() { + EEPROM_readAnything(7, ody2mapper); + EEPROM_readAnything(8, ody2size); + if (ody2mapper > 1) { + ody2mapper = 0; + EEPROM_writeAnything(7, ody2mapper); + } + if (ody2size > 4) { + ody2size = 0; + EEPROM_writeAnything(8, ody2size); + } + +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("ODYSSEY 2 READER")); + println_Msg(F("CURRENT SETTINGS")); + println_Msg(F("")); + print_Msg(F("MAPPER: ")); + println_Msg(ody2mapper); + print_Msg(F("ROM SIZE: ")); + print_Msg(ODY2[ody2size]); + println_Msg(F("K")); + display_Update(); + wait(); +#else + Serial.print(F("CURRENT MAPPER: ")); + Serial.println(ody2mapper); + Serial.print(F("CURRENT ROM SIZE: ")); + Serial.print(ODY2[ody2size]); + Serial.println(F("K")); + Serial.println(F("")); +#endif +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +FsFile ody2csvFile; +char ody2game[51]; // title +char ody2mm[3]; // mapper (A10) +char ody2rr[3]; // romsize +char ody2ll[4]; // linelength (previous line) +unsigned long ody2csvpos; // CSV File Position +char ody2cartCSV[] = "ody2cart.txt"; // CSV List +char ody2csvEND[] = "EOF"; // CSV End Marker for scrolling + +bool readLine_ODY2(FsFile& f, char* line, size_t maxLen) { + for (size_t n = 0; n < maxLen; n++) { + int c = f.read(); + if (c < 0 && n == 0) return false; // EOF + if (c < 0 || c == '\n') { + line[n] = 0; + return true; + } + line[n] = c; + } + return false; // line too long +} + +bool readVals_ODY2(char* ody2game, char* ody2mm, char* ody2rr, char* ody2ll) { + char line[59]; + ody2csvpos = ody2csvFile.position(); + if (!readLine_ODY2(ody2csvFile, line, sizeof(line))) { + return false; // EOF or too long + } + char* comma = strtok(line, ","); + int x = 0; + while (comma != NULL) { + if (x == 0) + strcpy(ody2game, comma); + else if (x == 1) + strcpy(ody2mm, comma); + else if (x == 2) + strcpy(ody2rr, comma); + else if (x == 3) + strcpy(ody2ll, comma); + comma = strtok(NULL, ","); + x += 1; + } + return true; +} + +bool getCartListInfo_ODY2() { + bool buttonreleased = 0; + bool cartselected = 0; +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F(" HOLD TO FAST CYCLE")); + display_Update(); +#else + Serial.println(F("HOLD BUTTON TO FAST CYCLE")); +#endif + delay(2000); +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + boolean buttonVal1 = (PING & (1 << 2)); //PG2 +#endif + if (buttonVal1 == LOW) { // Button Held - Fast Cycle + while (1) { // Scroll Game List + while (readVals_ODY2(ody2game, ody2mm, ody2rr, ody2ll)) { + if (strcmp(ody2csvEND, ody2game) == 0) { + ody2csvFile.seek(0); // Restart + } else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART TITLE:")); + println_Msg(F("")); + println_Msg(ody2game); + display_Update(); +#else + Serial.print(F("CART TITLE:")); + Serial.println(ody2game); +#endif +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + buttonVal1 = (PING & (1 << 2)); //PG2 +#endif + if (buttonVal1 == HIGH) { // Button Released + buttonreleased = 1; + break; + } + if (buttonreleased) { + buttonreleased = 0; // Reset Flag + break; + } + } + } +#if defined(enable_OLED) + buttonVal1 = (PIND & (1 << 7)); // PD7 +#elif defined(enable_LCD) + buttonVal1 = (PING & (1 << 2)); //PG2 +#endif + if (buttonVal1 == HIGH) // Button Released + break; + } + } +#if (defined(enable_OLED) || defined(enable_LCD)) + display.setCursor(0, 56); + println_Msg(F("FAST CYCLE OFF")); + display_Update(); +#else + Serial.println(F("")); + Serial.println(F("FAST CYCLE OFF")); + Serial.println(F("PRESS BUTTON TO STEP FORWARD")); + Serial.println(F("DOUBLE CLICK TO STEP BACK")); + Serial.println(F("HOLD TO SELECT")); + Serial.println(F("")); +#endif + while (readVals_ODY2(ody2game, ody2mm, ody2rr, ody2ll)) { + if (strcmp(ody2csvEND, ody2game) == 0) { + ody2csvFile.seek(0); // Restart + } else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART TITLE:")); + println_Msg(F("")); + println_Msg(ody2game); + display.setCursor(0, 48); + println_Msg(F("Press to Change")); + println_Msg(F("Hold to Select")); + display_Update(); +#else + Serial.print(F("CART TITLE:")); + Serial.println(ody2game); +#endif + while (1) { // Single Step + int b = checkButton(); + if (b == 1) { // Continue (press) + break; + } + if (b == 2) { // Reset to Start of List (doubleclick) + byte prevline = strtol(ody2ll, NULL, 10); + ody2csvpos -= prevline; + ody2csvFile.seek(ody2csvpos); + break; + } + if (b == 3) { // Long Press - Select Cart (hold) + newody2mapper = strtol(ody2mm, NULL, 10); + newody2size = strtol(ody2rr, NULL, 10); + EEPROM_writeAnything(7, newody2mapper); + EEPROM_writeAnything(8, newody2size); + cartselected = 1; // SELECTION MADE +#if (defined(enable_OLED) || defined(enable_LCD)) + println_Msg(F("SELECTION MADE")); + display_Update(); +#else + Serial.println(F("SELECTION MADE")); +#endif + break; + } + } + if (cartselected) { + cartselected = 0; // Reset Flag + return true; + } + } + } +#if (defined(enable_OLED) || defined(enable_LCD)) + println_Msg(F("")); + println_Msg(F("END OF FILE")); + display_Update(); +#else + Serial.println(F("END OF FILE")); +#endif + + return false; +} + +void checkCSV_ODY2() { + if (getCartListInfo_ODY2()) { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CART SELECTED")); + println_Msg(F("")); + println_Msg(ody2game); + display_Update(); + // Display Settings + display.setCursor(0, 56); + print_Msg(F("CODE: M")); + print_Msg(newody2mapper); + print_Msg(F("/R")); + println_Msg(newody2size); + display_Update(); +#else + Serial.println(F("")); + Serial.println(F("CART SELECTED")); + Serial.println(ody2game); + // Display Settings + Serial.print(F("CODE: M")); + Serial.print(newody2mapper); + Serial.print(F("/R")); + Serial.println(newody2size); + Serial.println(F("")); +#endif + } else { +#if (defined(enable_OLED) || defined(enable_LCD)) + display.setCursor(0, 56); + println_Msg(F("NO SELECTION")); + display_Update(); +#else + Serial.println(F("NO SELECTION")); +#endif + } +} + +void setCart_ODY2() { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(ody2cartCSV); + display_Update(); +#endif + sd.chdir(); + sprintf(folder, "ODY2/CSV"); + sd.chdir(folder); // Switch Folder + ody2csvFile = sd.open(ody2cartCSV, O_READ); + if (!ody2csvFile) { +#if (defined(enable_OLED) || defined(enable_LCD)) + display_Clear(); + println_Msg(F("CSV FILE NOT FOUND!")); + display_Update(); +#else + Serial.println(F("CSV FILE NOT FOUND!")); +#endif + while (1) { + if (checkButton() != 0) + setup_ODY2(); + } + } + checkCSV_ODY2(); + + ody2csvFile.close(); +} +#endif \ No newline at end of file diff --git a/sd/arccart.txt b/sd/arccart.txt new file mode 100644 index 0000000..0dd2e39 --- /dev/null +++ b/sd/arccart.txt @@ -0,0 +1,49 @@ +3D Bowling,1,0 +3D Soccer,3,16 +Alien Invaders,1,16 +American Football,2,21 +Astro Invader,3,24 +Autorace,3,20 +Baseball,1,15 +Basketball,1,15 +Blackjack and Poker,1,17 +Boxing,1,26 +Brain Quiz,1,13 +Breakaway,1,17 +Capture,0,16 +Cat Trax,1,14 +Circus,1,15 +Combat,3,13 +Crazy Climber,1,13 +Crazy Gobbler,0,20 +Escape,1,20 +Funky Fish,3,13 +Golf,3,17 +Grand Slam Tennis,3,11 +Hobo,3,24 +Horse Racing,1,11 +Jump Bug,3,19 +Jungler,3,15 +Missile War,1,14 +Monaco Grand Prix,1,18 +Nibblemen,1,24 +Ocean Battle,1,16 +Parashooter,1,19 +Pleiades,3,18 +R2D Tank,1,15 +Red Clash,3,15 +Robot Killer,1,16 +Route 16,3,19 +Soccer,3,15 +Space Attack,1,13 +Space Mission,1,19 +Space Raiders,1,20 +Space Squadron,1,20 +Space Vultures,2,21 +Spiders,3,21 +Star Chess,1,14 +Super Gobbler,1,17 +Tanks a Lot,1,20 +The End,1,18 +Turtles,3,14 +EOF,0,0 \ No newline at end of file diff --git a/sd/ataricart.txt b/sd/ataricart.txt new file mode 100644 index 0000000..1ebfa19 --- /dev/null +++ b/sd/ataricart.txt @@ -0,0 +1,494 @@ +32 in 1,240,0 +3-D Tic-Tac-Toe,32,15 +Acid Drop,246,23 +Action Force,64,18 +Adventure,64,20 +Adventures of TRON,64,17 +Air Raid,64,26 +Air Raiders,64,16 +Airlock,64,19 +Air-Sea Battle,32,15 +AKA Space Adventure,64,22 +Alien,64,27 +Alpha Beam with Ernie,248,13 +Amidar,64,30 +Armor Ambush,64,14 +Artillery Duel,248,20 +Assault,64,23 +Asterix,248,15 +Asteroids,248,16 +Astroblast,64,18 +Atari Video Cube,64,18 +Atlantis,64,24 +Atlantis II,64,16 +Bachelor Party,64,19 +Bachelorette Party,64,22 +Backgammon,64,26 +Bank Heist,64,18 +Barnstorming,64,18 +Base Attack,64,20 +Basic Math,32,19 +BASIC Programming,64,18 +Basketball,32,25 +Battlezone,248,18 +Beamrider,248,19 +Beany Bopper,64,18 +Beat 'Em & Eat 'Em,64,20 +Berenstain Bears,248,26 +Bermuda,64,25 +Bermuda Triangle,64,15 +Berzerk,64,24 +Big Bird's Egg Catch,248,15 +Blackjack,32,29 +Blueprint,248,17 +BMX Air Master,246,18 +Bobby Is Going Home,64,23 +Boing!,64,27 +Bowling,32,14 +Boxing,32,15 +Brain Games,32,14 +Breakout,32,19 +Bridge,64,16 +Buck Rogers - Planet of Zoom,248,14 +Bugs,64,37 +Bump 'n' Jump,231,12 +Bumper Bash,64,22 +BurgerTime,231,19 +Burning Desire,64,19 +Cakewalk,64,22 +California Games,246,16 +Canyon Bomber,32,25 +Carnival,64,21 +Casino,64,16 +Cat Trax,64,14 +Cathouse Blues,64,16 +Centipede,248,22 +Challenge,248,18 +Challenge of.... Nexar,64,18 +Chase the Chuckwagon,64,30 +Checkers,32,28 +China Syndrome,64,16 +Chopper Command,64,22 +Chuck Norris Superkicks,248,23 +Circus Atari,64,32 +Coco Nuts,64,20 +Codebreaker,32,17 +Combat,32,19 +Commando,246,14 +Commando Raid,64,17 +Condor Attack,64,21 +Confrontation,64,21 +Congo Bongo,248,21 +Cookie Monster Munch,248,20 +Cosmic Ark,64,29 +Cosmic Commuter,64,18 +Cosmic Corridor,64,23 +Cosmic Creeps,64,23 +Cosmic Swarm,32,21 +Crackpots,64,20 +Crash Dive,64,17 +Crazy Climber,248,18 +Cross Force,64,22 +Crossbow,246,19 +Crypts of Chaos,64,17 +Crystal Castles,246,23 +Cubicolor,64,24 +Cubo Magico,64,17 +Custer's Revenge,64,19 +Dancing Plate,64,24 +Dark Cavern,64,21 +Dark Chambers,246,19 +Dark Mage,248,22 +Deadly Duck,64,18 +Death Trap,64,19 +Decathlon,254,18 +Defender,64,18 +Defender II,248,16 +Demolition Herby,64,20 +Demon Attack,64,24 +Demons to Diamonds,64,20 +Desert Falcon,246,26 +Dice Puzzle,64,22 +Dig Dug,246,19 +Dishaster,64,16 +Dodge 'Em,64,17 +Dolphin,64,17 +Donald Duck's Speedboat,248,15 +Donkey Kong,64,32 +Donkey Kong Jr,248,19 +Double Dragon,246,23 +Double Dunk,246,22 +Dragon Defender,64,20 +Dragonfire,64,23 +Dragster,32,18 +Dukes of Hazzard,246,16 +E.T.,248,25 +Earth Dies Screaming,64,13 +Eggomania,64,28 +Elevator Action,248,17 +Eli's Ladder,64,24 +Encounter at L-5,64,20 +Enduro,64,24 +Entombed,64,14 +Espial,63,16 +Exocet,64,14 +Extra Terrestrials,64,14 +Fantastic Voyage,64,26 +Farmyard Fun,64,24 +Fast Eddie,64,20 +Fast Food,64,18 +Fatal Run,244,17 +Fathom,248,18 +Fighter Pilot,246,15 +Final Approach,64,22 +Fire Fighter,64,22 +Fire Fly,64,20 +Fishing Derby,32,16 +Flag Capture,32,21 +Flash Gordon,64,20 +Football,32,20 +Forest,64,16 +Frankenstein's Monster,64,14 +Freeway,32,30 +Frisco,64,15 +Frog Pond,248,14 +Frogger,64,18 +Frogger II,224,15 +Frogs and Flies,64,19 +Front Line,248,23 +Frostbite,64,19 +G.I. Joe,64,17 +Galaxian,248,16 +Gamma Attack,32,17 +Gangster Alley,64,20 +Gas Hog,64,22 +Gauntlet,64,15 +Ghost Manor,248,16 +Ghostbusters,248,20 +Ghostbusters II,246,21 +Gigolo,64,24 +Glacier Patrol,64,14 +Glib,64,22 +Golf,32,12 +Gopher,64,12 +Gorf,64,14 +Grand Prix,246,12 +Gravitar,248,19 +Great Escape,64,17 +Gremlins,248,20 +Guardian,64,17 +Gyruss,224,16 +H.E.R.O.,248,15 +Halloween,64,17 +Hangman,64,17 +Harbor Escape,64,15 +Haunted House,64,21 +Hole Hunter,64,21 +Home Run,32,19 +Human Cannonball,32,16 +Hunt & Score,32,24 +I Want My Mommy,64,20 +Ice Hockey,64,23 +Ikari Warriors,246,18 +Indy 500,32,23 +Infiltrate,64,16 +International Soccer,64,18 +IQ 180,64,28 +James Bond 007,224,14 +Jaw Breaker,64,23 +Journey Escape,64,19 +Joust,248,22 +JoustPong,64,14 +Jr. Pac-Man,246,17 +Jungle Fever,64,20 +Jungle Hunt,248,20 +Kaboom!,32,20 +Kangaroo,248,15 +Karate,64,17 +Keystone Kapers,64,14 +King Kong,64,23 +Klax,246,17 +Knight on the Town,64,13 +Kool-Aid Man,64,26 +Krull,248,20 +Kung Fu Superkicks,248,14 +Kung-Fu Master,248,27 +Lady in Wading,64,23 +Laser Blast,32,22 +Laser Gates,64,19 +Lilly Advenure,64,19 +Lochjaw,64,22 +Lock 'n' Chase,64,15 +London Blitz,64,22 +Lost Luggage,64,20 +M.A.D.,64,20 +M.A.S.H,64,14 +MagiCard,192,15 +Malagai,64,17 +Mangia',64,15 +Marauder,64,15 +Marine Wars,64,16 +Mario Bros.,248,19 +Master Builder,64,20 +Masters of the Universe,231,22 +Math Gran Prix,64,32 +Maze Craze,64,22 +Mega Force,64,18 +MegaBoy,240,18 +MegaMania,64,16 +Midnight Magic,246,17 +Millipede,246,23 +Mind Maze,248,18 +Miner 2049er,63,18 +Miner 2049er Volume II,63,20 +Mines of Minos,64,30 +Miniature Golf,32,22 +Miss Piggy's Wedding,248,22 +Missile Command,64,29 +Missile Control,64,23 +Mission 3000 A.D.,64,23 +Mission Survive,64,25 +Mogul Maniac,64,23 +Montezuma's Revenge,224,20 +Moon Patrol,248,28 +Moonsweeper,248,20 +Motocross,64,20 +Motocross Racer,248,17 +MotoRodeo,246,24 +Mountain King,250,18 +Mouse Trap,64,22 +Mr. Do!,248,18 +Mr. Do!'s Castle,224,16 +Mr. Postman,64,25 +Ms. Pac-Man,248,19 +Music Machine,64,20 +My Golf,248,21 +Name This Game,64,16 +Night Driver,32,22 +Night Stalker,64,20 +Nightmare,64,21 +No Escape!,64,17 +Nuts,64,18 +Obelix,248,12 +Off the Wall,246,15 +Oink!,64,21 +Omega Race,250,13 +Open Sesame,64,19 +Oscar's Trash Race,248,19 +Othello,32,27 +Out of Control,64,15 +Outlaw,32,22 +Pac-Kong,64,14 +Pac-Man,64,16 +Panda Chase,64,15 +Parachute,64,19 +Pele's Soccer,64,17 +Pengo,248,21 +Pepsi Invaders,64,14 +Pete Rose Baseball,246,22 +Phantom Tank,64,27 +Pharaoh's Curse,64,20 +Philly Flasher,64,23 +Phoenix,248,22 +Pick 'n' Pile,246,16 +Picnic,64,22 +Piece o' Cake,64,14 +Pigs in Space,248,21 +Pitfall II,208,22 +Pitfall!,64,19 +Planet Patrol,64,16 +Plaque Attack,64,21 +Polaris,63,21 +Pole Position,248,15 +Polo,32,22 +Pompeii,64,12 +Pooyan,64,15 +Popeye,224,14 +Porky's,248,15 +Pressure Cooker,248,16 +Private Eye,248,24 +Pyramid War,64,20 +Q-bert,64,19 +Q-bert's Qubes,224,14 +Quadrun,248,23 +Quest for Quintana Roo,248,16 +Quick Step!,64,31 +Rabbit Transit,248,19 +Racing Car,64,23 +Racquetball,64,18 +Radar Lock,246,19 +Raft Rider,64,19 +Raiders of the Lost Ark,248,18 +Ram It,64,32 +Rampage!,246,14 +Raumpatrouille,64,17 +Reactor,64,22 +RealSports Baseball,248,15 +RealSports Boxing,246,28 +RealSports Football,248,26 +RealSports Soccer,248,28 +RealSports Tennis,248,26 +RealSports Volleyball,64,26 +Rescue Terra I,64,29 +Resgate Espacial,248,22 +Revenge of the Beefsteak Tomatoes,64,25 +Riddle of the Sphinx,64,41 +River Patrol,63,28 +River Raid,64,20 +River Raid II,246,18 +Road Runner,246,22 +Robin Hood,248,20 +Robot Tank,254,19 +Roc 'n Rope,248,19 +Room of Doom,64,20 +Rubik's Cube,64,20 +Save Our Ship,64,20 +Scuba Diver,64,21 +Sea Hawk,64,19 +Sea Hunt,64,16 +Sea Monster,64,16 +Seaquest,64,19 +Secret Quest,246,16 +Sentinel,246,21 +Shark Attack,64,17 +Shootin' Gallery,64,20 +Shuttle Orbiter,64,24 +Sir Lancelot,248,23 +Skate Boardin',248,21 +Skeet Shoot,32,23 +Ski Hunt,64,19 +Ski Run,64,16 +Skiing,32,15 +Sky Diver,32,14 +Sky Jinks,32,17 +Sky Skipper,64,17 +Slot Machine,32,19 +Slot Racers,32,20 +Smurf - Rescue in Gargamel's Castle,248,19 +Smurfs Save the Day,248,44 +Sneak 'n Peek,64,28 +Snoopy and the Red Baron,248,21 +Solar Fox,248,33 +Solar Storm,64,18 +Solaris,246,19 +Sorcerer,64,16 +Sorcerer's Apprentice,248,16 +Space Attack,64,30 +Space Canyon,64,20 +Space Cavern,64,20 +Space Invaders,64,20 +Space Jockey,32,22 +Space Shuttle,248,20 +Space Tunnel,64,22 +Space War,32,20 +Spacechase,64,17 +SpaceMaster X-7,64,18 +Spider Fighter,64,23 +Spider Maze,64,22 +Spider-Man,64,19 +Spike's Peak,248,18 +Spitfire Attack,64,21 +Springer,63,23 +Sprint Master,246,16 +Spy Hunter,248,22 +Squeeze Box,64,19 +Squirrel,64,19 +Sssnake,64,16 +Stampede,32,15 +Star Fox,64,16 +Star Raiders,248,16 +Star Ship,32,21 +Star Strike,64,17 +Star Trek,248,19 +Star Voyager,64,18 +Star Wars - Arcade Game,224,20 +Star Wars - Death Star Battle,224,32 +Star Wars - Empire Strikes Back,64,38 +Star Wars - Jedi Arena,64,39 +Stargate,246,30 +Stargunner,64,17 +StarMaster,64,18 +Steeplechase,32,18 +Stellar Track,64,20 +Strategy X,64,21 +Strawberry Shortcake,64,18 +Street Racer,32,28 +Stronghold,64,20 +Stunt Man,64,18 +Submarine Commander,64,17 +Sub-Scan,64,27 +Subterranea,248,16 +Summer Games,246,20 +Super Baseball,246,21 +Super Breakout,64,23 +Super Challenge Baseball,64,22 +Super Challenge Football,64,32 +Super Cobra,224,32 +Super Football,248,20 +Superman,64,23 +Surfer's Paradise,64,16 +Surround,32,25 +Survival Run,64,16 +SwordQuest - EarthWorld,248,20 +SwordQuest - FireWorld,248,32 +SwordQuest - WaterWorld,248,31 +Tac-Scan,64,32 +Tanks But No Tanks,64,16 +Tapeworm,64,26 +Tapper,248,16 +Tax Avoiders,248,15 +Taz,248,21 +Tempest,248,12 +Tennis,32,16 +Texas Chainsaw Massacre,64,14 +Threshold,64,31 +Thunderground,64,17 +Time Pilot,248,21 +Time Warp,64,19 +Title Match Pro Wrestling,248,17 +Tomarc the Barbarian,248,34 +Tomcat,246,29 +Tooth Protectors,224,15 +Towering Inferno,64,25 +Track and Field,246,24 +Treasure Below,64,24 +Treasure Island,64,22 +Trick Shot,64,23 +TRON - Deadly Discs,64,18 +Tunnel Runner,250,27 +Turmoil,64,22 +Tutankham,224,15 +Universal Chaos,64,18 +Up 'n Down,248,23 +Vanguard,248,19 +Venture,64,17 +Video Checkers,64,15 +Video Chess,64,22 +Video Jogger,64,19 +Video Life,32,20 +Video Olympics,32,18 +Video Pinball,64,22 +Video Reflex,64,21 +Vogel Flieh,64,20 +Vulture Attack,64,19 +Wabbit,64,22 +Walker,64,14 +Wall Ball,64,14 +Wall Break,64,17 +Wall-Defender,64,18 +Warlords,64,21 +Warplock,64,16 +Wing War,248,16 +Wings,250,17 +Winter Games,246,14 +Wizard of Wor,64,21 +Word Zapper,64,21 +World End,64,19 +Worm War I,64,17 +Xenophobe,246,18 +X-Man,64,18 +Yars' Revenge,64,13 +Zaxxon,248,21 +Zoo Fun,64,15 +Z-Tack,64,15 +EOF,0,0 diff --git a/sd/fairchildcart.txt b/sd/fairchildcart.txt new file mode 100644 index 0000000..dd00e44 --- /dev/null +++ b/sd/fairchildcart.txt @@ -0,0 +1,33 @@ +Alien Invasion,2,0 +Backgammon/Acey-Deucey,0,20 +Baseball,0,29 +Bowling,0,15 +Casino Poker,2,14 +Checkers,0,19 +Demonstration Cartridge,0,15 +Demonstration Cartridge 2,0,30 +Desert Fox/Shooting Gallery,0,32 +Dodge It,0,34 +Drag Race,0,15 +Galactic Space Wars/Lunar Lander,0,16 +Hangman,1,39 +Magic Numbers,0,14 +Math Quiz I,0,20 +Math Quiz II,0,18 +Maze (Multi),0,19 +Memory Match 1 & 2,0,19 +Muehle (Multi),0,25 +Ordtaevling,1,21 +Pinball Challenge,0,18 +Pro Football,2,24 +Rat' Mal,1,19 +Robot War/Torpedo Alley,0,15 +Schach,3,30 +Slot Machine,0,13 +Sonar Search,0,19 +Space War,0,19 +Spitfire,0,16 +Tic-Tac-Toe (Multi),0,15 +Video Blackjack,0,26 +Video Whizball,1,22 +EOF,0,0 diff --git a/sd/ody2cart.txt b/sd/ody2cart.txt new file mode 100644 index 0000000..22654f3 --- /dev/null +++ b/sd/ody2cart.txt @@ -0,0 +1,134 @@ +4 en 1 Ligne,1,1,0 +4 in 1 Row,1,1,20 +Air Battle (V+),0,3,19 +Alien Invaders - Plus,0,0,24 +Alpine Skiing,0,0,30 +Armored Encounter + Sub Chase,0,0,22 +Atlantis,0,1,38 +Attack of the Timelord,0,1,17 +Baseball,0,0,31 +Billard Americain,0,0,17 +Billard Americain (V+),0,2,26 +Billard Electrique,0,0,31 +Billard Electrique (V+),0,2,27 +Blackjack,0,0,32 +Blobbers,0,2,18 +Blockout + Breakdown,0,0,17 +Bombardeio Submarino + Tiro ao Alvo,0,0,29 +Bowling + Basketball,0,0,44 +Buraco Negro,0,2,29 +Casino Slot Machine,0,0,21 +Catch the Ball + Noughts and Crosses,0,0,28 +Chat et Souris,0,1,45 +Chess,0,0,23 +Chez Maxime (V+),0,1,14 +Chinese Logic,0,0,25 +Clay Pigeon,0,1,22 +Comando Noturno,0,2,20 +Computer Golf,0,0,24 +Computer Intro,0,0,22 +Computer Programmer,0,0,23 +Conflit Cosmique,0,0,28 +Conquest of the World,0,1,25 +Cosmic Conflict,0,0,30 +Cosmic Conflict (V+),0,2,24 +Course de Voitures + Autodrome + Cryptogramme,0,0,29 +Course de Voitures + Autodrome + Cryptogramme (V+),0,2,54 +Demon Attack,0,1,59 +Demon Attack (V+),0,2,21 +Depth Charge + Marksman,0,0,26 +Desafio Chines,0,0,32 +Dynasty,0,0,23 +Electronic Table Soccer,0,0,16 +Exojet (V+),0,2,32 +Football,0,0,20 +Football Electronique + Hockey Electronique,0,0,17 +Freedom Fighters,0,1,52 +Freedom Fighters (V+),0,2,25 +Frogger,0,2,30 +Golf,0,0,16 +Great Wall Street Fortune Hunt,0,1,13 +Guerre de l'Espace (V+),0,2,39 +Guerre Laser,0,0,32 +Gunfighter,0,0,21 +Helicopter Rescue (V+),0,4,19 +Hockey + Soccer,0,0,31 +I've Got Your Number,0,0,24 +Invaders from Hyperspace,0,0,29 +Jeu de Quilles + Basketball,0,0,33 +Jeu de Quilles + Basketball (V+),0,2,36 +Jumping Acrobats,0,0,41 +K.C. Munchkin,0,1,25 +K.C.'s Krazy Chase,0,1,22 +Keyboard Creations,0,0,27 +Killer Bees,0,2,27 +Killer Bees (V+),0,2,20 +Kinder im Verkehr 1,0,0,25 +La Quete des Anneaux,0,1,28 +La Ruche Infernale (V+),0,2,29 +Labyrinth Game + Supermind,0,0,32 +Las Vegas,0,0,35 +Las Vegas Blackjack,0,0,18 +Laser War,0,0,28 +Le Mur Magique,0,0,18 +Le Tresor Englouti (V+),0,2,23 +Loony Balloon (V+),0,2,32 +Matchmaker + Logix + Buzzword,0,0,27 +Math-A-Magic + Echo,0,0,38 +Mathematicien + Echo,0,0,28 +Monkeyshines,0,1,29 +Morse,0,0,21 +Moto-Crash (V+),0,2,14 +Mousing Cat,0,1,24 +Munchkin,0,1,20 +Musician,1,1,17 +Neutron Star (V+),0,3,17 +Newscaster,0,0,26 +Nightmare,0,2,19 +Nightmare (V+),0,2,18 +Nimble Numbers Ned,0,1,23 +Norseman (V+),0,2,27 +O Malabarista + Jogo da Velha,0,0,22 +O Segredo do Farao,0,1,38 +Out of this World + Helicopter Rescue,0,0,27 +P.T. Barnum's Acrobats,0,1,46 +Pachinko,0,0,31 +Pairs + Space Rendezvous + Logic,0,0,17 +Pick Axe Pete,0,1,41 +Pickaxe Pete (V+),0,2,22 +Pocket Billiards,0,0,26 +Popeye,0,2,25 +Power Lords,0,1,15 +Power Lords (Alt),0,2,20 +Prendre l'Argent et Fuir,0,0,26 +Q-Bert,0,2,33 +Quest for the Rings,0,1,15 +Race + Spin-out + Cryptogram (V+),0,2,28 +Samurai,0,0,42 +Satellite Attack,0,0,16 +Satellite Attack (V+),0,2,25 +Secret of the Pharaohs,0,0,30 +Showdown in 2100 A.D.,0,0,31 +Sid the Spellbinder,0,1,30 +Skiing,0,0,28 +Smithereens,0,1,15 +Speedway + Spin-out + Crypto-logic,0,0,20 +Stone Sling,0,0,43 +Stone Sling (V+),0,2,20 +Super Bee,0,1,25 +Super Cobra,0,2,18 +Syracuse (V+),0,1,20 +Take the Money and Run,0,0,22 +Terrahawks (V+),0,2,31 +Thunderball,0,0,24 +Trans American Rally (V+),0,4,20 +Turtles,0,2,34 +Type & Tell,0,1,16 +UFO,0,0,20 +Verkehrsspiele 1,0,0,12 +Verkehrsspiele 2,0,0,25 +Volleyball,0,0,25 +Volleyball Electronique,0,0,19 +Wall Street,0,1,32 +War of Nerves,0,0,20 +EOF,0,0,22