From 72dfb04624907a9e7236d9bcdc78d21ac633c14a Mon Sep 17 00:00:00 2001 From: sanni Date: Mon, 8 Jul 2024 19:25:29 +0200 Subject: [PATCH] Add new modules (thx to skaman) --- Cart_Reader/ATARI8.ino | 499 ++++++++++++++++ Cart_Reader/BALLY.ino | 321 +++++++++++ Cart_Reader/Cart_Reader.ino | 212 ++++++- Cart_Reader/Config.h | 94 ++- Cart_Reader/LEAP.ino | 1052 ++++++++++++++++++++++++++++++++++ Cart_Reader/LJ.ino | 356 ++++++++++++ Cart_Reader/LJPRO.ino | 554 ++++++++++++++++++ Cart_Reader/OSCR.cpp | 2 +- Cart_Reader/OSCR.h | 74 ++- Cart_Reader/PV1000.ino | 324 +++++++++++ Cart_Reader/PYUUTA.ino | 327 +++++++++++ Cart_Reader/RCA.ino | 327 +++++++++++ Cart_Reader/TI99.ino | 1071 +++++++++++++++++++++++++++++++++++ Cart_Reader/TRS80.ino | 398 +++++++++++++ Cart_Reader/VIC20.ino | 576 +++++++++++++++++++ Cart_Reader/VSMILE.ino | 365 ++++++++++++ sd/README.md | 132 +++++ sd/atari8cart.txt | 946 +++++++++++++++++++++++++++++++ sd/ballycart.txt | 103 ++++ sd/leapster.txt | 217 +++++++ sd/ljcart.txt | 109 ++++ sd/pv1000cart.txt | 72 +++ sd/pyuutacart.txt | 97 ++++ sd/rcacart.txt | 49 ++ sd/ti99cart.txt | 835 +++++++++++++++++++++++++++ sd/trs80cart.txt | 328 +++++++++++ sd/vic20cart.txt | 718 +++++++++++++++++++++++ sd/vsmilecart.txt | 223 ++++++++ 28 files changed, 10357 insertions(+), 24 deletions(-) create mode 100644 Cart_Reader/ATARI8.ino create mode 100644 Cart_Reader/BALLY.ino create mode 100644 Cart_Reader/LEAP.ino create mode 100644 Cart_Reader/LJ.ino create mode 100644 Cart_Reader/LJPRO.ino create mode 100644 Cart_Reader/PV1000.ino create mode 100644 Cart_Reader/PYUUTA.ino create mode 100644 Cart_Reader/RCA.ino create mode 100644 Cart_Reader/TI99.ino create mode 100644 Cart_Reader/TRS80.ino create mode 100644 Cart_Reader/VIC20.ino create mode 100644 Cart_Reader/VSMILE.ino create mode 100644 sd/atari8cart.txt create mode 100644 sd/ballycart.txt create mode 100644 sd/leapster.txt create mode 100644 sd/ljcart.txt create mode 100644 sd/pv1000cart.txt create mode 100644 sd/pyuutacart.txt create mode 100644 sd/rcacart.txt create mode 100644 sd/ti99cart.txt create mode 100644 sd/trs80cart.txt create mode 100644 sd/vic20cart.txt create mode 100644 sd/vsmilecart.txt diff --git a/Cart_Reader/ATARI8.ino b/Cart_Reader/ATARI8.ino new file mode 100644 index 0000000..8e0a6d2 --- /dev/null +++ b/Cart_Reader/ATARI8.ino @@ -0,0 +1,499 @@ +//****************************************** +// ATARI 8-bit (400/800/XL/XE) MODULE +//****************************************** +#ifdef ENABLE_ATARI8 +// Atari 8-bit (400/800/XL/XE) +// Left Slot Cartridge Pinout +// 30P 2.54mm pitch +// +// RIGHT +// +------+ +// PHI2 -| S 15 |- /CCTL +// R/W -| R 14 |- RD5 +// L A10 -| P 13 |- +5V B +// A A11 -| N 12 |- /S5 O +// B D7 -| M 11 |- D6 T +// E D3 -| L 10 |- D0 T +// L A12 -| K 9 |- D1 O +// A9 -| J 8 |- D2 M +// S A8 -| H 7 |- D5 +// I A7 -| F 6 |- D4 S +// D A6 -| E 5 |- A0 I +// E A5 -| D 4 |- A1 D +// A4 -| C 3 |- A2 E +// GND -| B 2 |- A3 +// RD4 -| A 1 |- /S4 +// +------+ +// LEFT +// +// LABEL SIDE +// +// RD4 GND A4 A5 A6 A7 A8 A9 A12 D3 D7 A11 A10 R/W PHI2 +// +-------------------------------------------------------------+ +// | A B C D E F H J K L M N P R S | +// LEFT | | RIGHT +// | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | +// +-------------------------------------------------------------+ +// /S4 A3 A2 A1 A0 D4 D5 D2 D1 D0 D6 /S5 +5V RD5 /CCTL +// +// BOTTOM SIDE +// +// CONTROL PINS: +// RD4(PH0) - SNES RESET +// PHI2(PH1) - SNES CPUCLK +// /S5(PH3) - SNES /CS +// RD5(PH4) - SNES /IRQ +// R/W(PH5) - SNES /WR +// /S4(PH6) - SNES /RD +// /CCTL(PL0) - SNES A16 + +//****************************************** +// DEFINES +//****************************************** +#define DISABLE_S4 PORTH |= (1 << 6) // ROM SELECT $8000-$9FFF +#define ENABLE_S4 PORTH &= ~(1 << 6) +#define DISABLE_S5 PORTH |= (1 << 3) // ROM SELECT $A000-$BFFF +#define ENABLE_S5 PORTH &= ~(1 << 3) +#define DISABLE_CCTL PORTL |= (1 << 0) // CARTRIDGE CONTROL BLOCK $D500-$D5FF +#define ENABLE_CCTL PORTL &= ~(1 << 0) + +//****************************************** +// VARIABLES +//****************************************** +byte ATARI8[] = {8,16,32,40,64,128}; +byte atari8lo = 0; // Lowest Entry +byte atari8hi = 5; // Highest Entry +byte atari8size; +byte newatari8size; +boolean atari8right = 0; // 0 = LEFT Slot, 1 = RIGHT Slot + +// EEPROM MAPPING +// 07 MAPPER [ATARI 8-BIT SLOT] +// 08 ROM SIZE + +//****************************************** +// MENU +//****************************************** +// Base Menu +static const char atari8MenuItem3[] PROGMEM = "Read RIGHT ROM"; +static const char* const menuOptionsATARI8[] PROGMEM = { FSTRING_SELECT_CART, FSTRING_READ_ROM, atari8MenuItem3, FSTRING_SET_SIZE, FSTRING_RESET }; + +void atari8Menu() +{ + convertPgm(menuOptionsATARI8, 5); + uint8_t mainMenu = question_box(F("ATARI 8-BIT MENU"), menuOptions, 5, 0); + + switch (mainMenu) + { + // Select Cart + case 0: + setCart_ATARI8(); + setup_ATARI8(); + break; + + // Read LEFT Slot Cart + case 1: + sd.chdir("/"); + atari8right = 0; // LEFT Slot + readROM_ATARI8(); + sd.chdir("/"); + break; + + // Read RIGHT Slot Cart + case 2: + sd.chdir("/"); + atari8right = 1; // RIGHT Slot + readROM_ATARI8(); + sd.chdir("/"); + break; + + case 3: + // Set Size + setROMSize_ATARI8(); + break; + + case 4: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// SETUP +//****************************************** + +void setup_ATARI8() +{ + // Request 5V + setVoltage(VOLTS_SET_5V); + + // Set Address Pins to Output + // Atari 8-bit uses A0-A12 [A13-A23 UNUSED] + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; // Use A16 for /CCTL Output + + // Set Control Pins to Output + // PHI2(PH1) /S5(PH3) R/W(PH5) /S4(PH6) + DDRH |= (1 << 1) | (1 << 3) | (1 << 5) | (1 << 6); + // Set RD4 & RD5 to Input + // RD4(PH0) RD5(PH4) + DDRH &= ~((1 << 0) | (1 << 4)); + + // Set TIME(PJ0) to Output (UNUSED) + DDRJ |= (1 << 0); + + // Set Pins (D0-D7) to Input + DDRC = 0x00; + + // Setting Control Pins to HIGH + // PHI2(PH1) /S5(PH3) R/W(PH5) /S4(PH6) + //PORTH |= (1 << 1) | (1 << 3) | (1 << 5) | (1 << 6); + // /S5(PH3) R/W(PH5) /S4(PH6) + PORTH |= (1 << 3) | (1 << 5) | (1 << 6); + + // Set Unused Data Pins (PA0-PA7) to Output + DDRA = 0xFF; + + // Set Unused Pins HIGH + PORTA = 0xFF; + PORTL = 0xFF; // A16-A23 (A16 used for /CCTL Output) + PORTJ |= (1 << 0); // TIME(PJ0) + + checkStatus_ATARI8(); + strcpy(romName, "ATARI"); + + mode = CORE_ATARI8; +} + +//****************************************** +// READ FUNCTIONS +//****************************************** + +uint8_t readData_ATARI8(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; + // Extended Delay + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + + uint8_t ret = PINC; + NOP; + NOP; + NOP; + NOP; + NOP; + + return ret; +} + +void readSegment_ATARI8(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_ATARI8(addr + w); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + } +} + +void readBountyBobBank_ATARI8(uint16_t startaddr) +{ + for (uint8_t w = 0; w < 4; w++) { + readData_ATARI8(startaddr + 0x0FF6 + w); + readSegment_ATARI8(startaddr, startaddr + 0x0E00); + // Split Read of Last 0x200 bytes + for (int x = 0; x < 0x1F6; x++) { + sdBuffer[x] = readData_ATARI8(startaddr + 0x0E00 + x); + } + myFile.write(sdBuffer, 502); + // Bank Registers 0xFF6-0xFF9 + for (int y = 0; y < 4; y++){ + readData_ATARI8(startaddr + 0x0FFF); // Reset Bank + sdBuffer[y] = readData_ATARI8(startaddr + 0x0FF6 + y); + } + // End of Bank 0x8FFA-0x8FFF + readData_ATARI8(startaddr + 0x0FFF); // Reset Bank + readData_ATARI8(startaddr + 0x0FF6 + w); // Set Bank + for (int z = 4; z < 10; z++) { + sdBuffer[z] = readData_ATARI8(startaddr + 0x0FF6 + z); // 0xFFA-0xFFF + } + myFile.write(sdBuffer, 10); + } + readData_ATARI8(startaddr + 0x0FFF); // Reset Bank +} + +void bankSwitch_ATARI8(uint8_t bank) +{ + // write to $D5XX using /CCTL + // CCTL sets upper 3 bits of 16-bit address to 110 + // CCTL = [110] 1 0101 0000 0000 = $D500 + ENABLE_CCTL; + // Set Address $D500 + PORTF = 0x00; // A0-A7 + PORTK = 0xD5; // A8-A12 + NOP; + NOP; + NOP; + NOP; + NOP; + + // Set Data to Output + DDRC = 0xFF; + // Set R/W to WRITE + PORTH &= ~(1 << 5); + + // Set Bank + PORTC = bank; + NOP; + NOP; + NOP; + NOP; + NOP; + + // Pulse Clock + // CPU CLOCK 1.7897725MHz(NTSC)/1.7734470MHz(PAL/SECAM) + // 1.7897725MHz = 1 cycle = 558.73023ns = 279.365115/279.365115 + for (int i = 0; i < 2; i++) { + PORTH ^= (1 << 1); + // NOP (62.5ns) x 4 = 250ns = 0.25us + NOP; + NOP; + NOP; + NOP; // 4 NOPs = 4 x 62.5ns = 250ns x 2 = 500ns = 2 MHz + } + + // Set R/W to READ + PORTH |= (1 << 5); + // Reset Data to Input + DDRC = 0x00; + DISABLE_CCTL; +} + +//****************************************** +// READ ROM +//****************************************** + +void readROM_ATARI8() +{ + createFolderAndOpenFile("ATARI8", "ROM", romName, "bin"); + + // Store Slot Setting to EEPROM + EEPROM_writeAnything(7, atari8right); // 0 = LEFT, 1 = RIGHT + + // ATARI 8-bit A12-A0 = 1 0000 0000 0000 + // S4 [100]/S5 [101] are the upper 3 bits for 16-bit address + // S4 = [100] 1 1111 1111 1111 = $8000-$9FFF + // S5 = [101] 1 1111 1111 1111 = $A000-$BFFF + + if (atari8right) { // RIGHT Slot Cartridge 8K + // Right slot carts are 8K mapped to $8000-$9FFF + // Right slot carts use /S4 assigned to Pin 12 + // Pin 12 = RIGHT Slot /S4 = LEFT Slot /S5 + ENABLE_S5; + readSegment_ATARI8(0x8000,0xA000); // 8K + DISABLE_S5; + // Correct Size to 8K + atari8size = 0; // 8K + EEPROM_writeAnything(8, atari8size); + } + else if (atari8size == 3) { // Bounty Bob Strikes Back 40K + ENABLE_S4; + // First 16KB (4KB x 4) + readBountyBobBank_ATARI8(0x8000); + // Second 16KB (4KB x 4) + readBountyBobBank_ATARI8(0x9000); + DISABLE_S4; + ENABLE_S5; + readSegment_ATARI8(0xA000, 0xC000); // +8K = 40K + DISABLE_S5; + } + else if (atari8size > 1) { // XE Carts 32K/64K/128K + // Bug Hunt and Lode Runner dump as 128K. Trim beginning 64K as both carts start from Bank 8. + int banks = (ATARI8[atari8size] / 8) - 1; + for (int x = 0; x < banks; x++) { + bankSwitch_ATARI8(x); + ENABLE_S4; + readSegment_ATARI8(0x8000,0xA000); // 8K + DISABLE_S4; + } + // Last Bank + ENABLE_S5; + readSegment_ATARI8(0xA000,0xC000); // +8K + DISABLE_S5; + } + else { // Standard LEFT Cart 8K/16K + if (atari8size == 1) { + // Add XE Bankswitch for Necromancer 16K + bankSwitch_ATARI8(0); + // Standard 16K + ENABLE_S4; + readSegment_ATARI8(0x8000,0xA000); // +8K = 16K + DISABLE_S4; + } + ENABLE_S5; + readSegment_ATARI8(0xA000,0xC000); // 8K + DISABLE_S5; + } + myFile.close(); + + printCRC(fileName, NULL, 0); + + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) +void printRomSize_ATARI8(int index) +{ + display_Clear(); + print_Msg(FS(FSTRING_ROM_SIZE)); + println_Msg(ATARI8[index]); +} +#endif + +void setROMSize_ATARI8() +{ + byte newatari8size; +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + if (atari8lo == atari8hi) + newatari8size = atari8lo; + else { + newatari8size = navigateMenu(atari8lo, atari8hi, &printRomSize_ATARI8); + + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(ATARI8[newatari8size]); + println_Msg(F("KB")); + display_Update(); + delay(1000); +#else + if (atari8lo == atari8hi) + newatari8size = atari8lo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (atari8hi - atari8lo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(ATARI8[i + atari8lo]); + Serial.println(F("KB")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newatari8size = sizeROM.toInt() + atari8lo; + if (newatari8size > atari8hi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(FS(FSTRING_EMPTY)); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(ATARI8[newatari8size]); + Serial.println(F("KB")); +#endif + EEPROM_writeAnything(8, newatari8size); + atari8size = newatari8size; +} + +void checkStatus_ATARI8() +{ + EEPROM_readAnything(7, atari8right); + if (atari8right != 1) { + atari8right = 0; // default LEFT Slot + EEPROM_writeAnything(7, atari8right); + } + EEPROM_readAnything(8, atari8size); + if (atari8size > atari8hi) { + atari8size = 1; // default 16K + EEPROM_writeAnything(8, atari8size); + } + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + println_Msg(F("ATARI 8-BIT READER")); + println_Msg(FS(FSTRING_CURRENT_SETTINGS)); + println_Msg(FS(FSTRING_EMPTY)); + print_Msg(F("SLOT: ")); + if (atari8right) + println_Msg(F("RIGHT")); + else + println_Msg(F("LEFT")); + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(ATARI8[atari8size]); + println_Msg(F("KB")); + display_Update(); + wait(); +#else + Serial.print(FS(FSTRING_ROM_SIZE)); + Serial.print(ATARI8[atari8size]); + Serial.println(F("KB")); + Serial.println(FS(FSTRING_EMPTY)); +#endif +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +void setCart_ATARI8() +{ + //go to root + sd.chdir(); + + struct database_entry_mapper_size entry; + + // Select starting letter + byte myLetter = starting_letter(); + + // Open database + if (myFile.open("atari8cart.txt", O_READ)) { + seek_first_letter_in_database(myFile, myLetter); + + if(checkCartSelection(myFile, &readDataLineMapperSize, &entry)) { + EEPROM_writeAnything(7, entry.gameMapper); + EEPROM_writeAnything(8, entry.gameSize); + } + } else { + print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND)); + } +} +#endif diff --git a/Cart_Reader/BALLY.ino b/Cart_Reader/BALLY.ino new file mode 100644 index 0000000..108eff4 --- /dev/null +++ b/Cart_Reader/BALLY.ino @@ -0,0 +1,321 @@ +//****************************************** +// BALLY ASTROCADE MODULE +//****************************************** +#ifdef ENABLE_BALLY +// Bally Astrocade +// Cartridge Pinout +// 26P 2.54mm pitch connector +// +// BOTTOM +// +-------+ +// GND -| 1 | +// A7 -| 2 | +// A6 -| 3 | +// A5 -| 4 | +// A4 -| 5 | +// A3 -| 6 | +// A2 -| 7 | +// A1 -| 8 | +// A0 -| 9 | +// D0 -| 10 | +// D1 -| 11 | +// D2 -| 12 | +// GND -| 13 | +// D3 -| 14 | +// D4 -| 15 | +// D5 -| 16 | +// D6 -| 17 | +// D7 -| 18 | +// A11 -| 19 | +// A10 -| 20 | +// /ENABLE -| 21 | +// A12 -| 22 | +// A9 -| 23 | +// A8 -| 24 | +// VCC(+5V) -| 25 | +// GND -| 26 | +// +-------+ +// +// TOP SIDE +// +-----------------------------------------------------------------------------------------------------------+ +// LEFT | | RIGHT +// | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | +// +-----------------------------------------------------------------------------------------------------------+ +// GND A7 A6 A5 A4 A3 A2 A1 A0 D0 D1 D2 GND D3 D4 D5 D6 D7 A11 A10 /EN A12 A9 A8 +5V GND +// +// BOTTOM SIDE + +// CONTROL PINS: +// /ENABLE(PH3) - SNES /CS + +//****************************************** +// VARIABLES +//****************************************** + +byte BALLY[] = {2,4,8}; +byte ballylo = 0; // Lowest Entry +byte ballyhi = 2; // Highest Entry + +byte ballysize; +byte newballysize; + +// EEPROM MAPPING +// 08 ROM SIZE + +//****************************************** +// MENU +//****************************************** +// Base Menu +static const char* const menuOptionsBALLY[] PROGMEM = { FSTRING_SELECT_CART, FSTRING_READ_ROM, FSTRING_SET_SIZE, FSTRING_RESET }; + +void ballyMenu() +{ + convertPgm(menuOptionsBALLY, 4); + uint8_t mainMenu = question_box(F("BALLY ASTROCADE MENU"), menuOptions, 4, 0); + + switch (mainMenu) + { + case 0: + // Select Cart + setCart_BALLY(); + setup_BALLY(); + break; + + case 1: + // Read ROM + sd.chdir("/"); + readROM_BALLY(); + sd.chdir("/"); + break; + + case 2: + // Set Size + setROMSize_BALLY(); + break; + + case 3: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// SETUP +//****************************************** + +void setup_BALLY() +{ + // Request 5V + setVoltage(VOLTS_SET_5V); + + // Set Address Pins to Output + // Bally Astrocade uses A0-A12 [A13-A23 UNUSED] + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output + // ---(PH0) ---(PH1) /ENABLE(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 + // ---(PH0) ---(PH1) /ENABLE(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_BALLY(); + strcpy(romName, "BALLY"); + + mode = CORE_BALLY; +} + +//****************************************** +// READ FUNCTIONS +//****************************************** + +uint8_t readData_BALLY(uint16_t addr) +{ + PORTF = addr & 0xFF; // A0-A7 + PORTK = (addr >> 8) & 0xFF; // A8-A15 + + PORTC = 0xFF; // Input Pullup + PORTH &= ~(1 << 3); // /ENABLE LOW + NOP; + NOP; + NOP; + NOP; + NOP; + + uint8_t ret = PINC; + PORTH |= (1 << 3); // /ENABLE HIGH + + return ret; +} + +void readSegment_BALLY(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_BALLY(addr + w); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + } +} + +//****************************************** +// READ ROM +//****************************************** + +void readROM_BALLY() +{ + createFolderAndOpenFile("BALLY", "ROM", romName, "bin"); + + readSegment_BALLY(0x0000,0x0800); // 2K + if (ballysize > 0) { + readSegment_BALLY(0x0800,0x1000); // +2K = 4K + if (ballysize > 1) { + readSegment_BALLY(0x1000,0x2000); // +4K = 8K + } + } + myFile.close(); + + printCRC(fileName, NULL, 0); + + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) +void printRomSize_BALLY(int index) +{ + display_Clear(); + print_Msg(FS(FSTRING_ROM_SIZE)); + println_Msg(BALLY[index]); +} +#endif + +void setROMSize_BALLY() +{ + byte newballysize; +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + if (ballylo == ballyhi) + newballysize = ballylo; + else { + newballysize = navigateMenu(ballylo, ballyhi, &printRomSize_BALLY); + + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(BALLY[newballysize]); + println_Msg(F("KB")); + display_Update(); + delay(1000); +#else + if (ballylo == ballyhi) + newballysize = ballylo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (ballyhi - ballylo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(BALLY[i + ballylo]); + Serial.println(F("KB")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newballysize = sizeROM.toInt() + ballylo; + if (newballysize > ballyhi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(FS(FSTRING_EMPTY)); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(BALLY[newballysize]); + Serial.println(F("KB")); +#endif + EEPROM_writeAnything(8, newballysize); + ballysize = newballysize; +} + +void checkStatus_BALLY() +{ + EEPROM_readAnything(8, ballysize); + if (ballysize > ballyhi) { + ballysize = 0; // default 2K + EEPROM_writeAnything(8, ballysize); + } + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + println_Msg(F("BALLY ASTROCADE")); + println_Msg(FS(FSTRING_CURRENT_SETTINGS)); + println_Msg(FS(FSTRING_EMPTY)); + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(BALLY[ballysize]); + println_Msg(F("KB")); + display_Update(); + wait(); +#else + Serial.print(FS(FSTRING_ROM_SIZE)); + Serial.print(BALLY[ballysize]); + Serial.println(F("KB")); + Serial.println(FS(FSTRING_EMPTY)); +#endif +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +void setCart_BALLY() +{ + //go to root + sd.chdir(); + + byte gameSize; + + // Select starting letter + //byte myLetter = starting_letter(); + + // Open database + if (myFile.open("ballycart.txt", O_READ)) { + // seek_first_letter_in_database(myFile, myLetter); + + if(checkCartSelection(myFile, &readDataLineSingleDigit, &gameSize)) { + EEPROM_writeAnything(8, gameSize); + } + } else { + print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND)); + } +} +#endif diff --git a/Cart_Reader/Cart_Reader.ino b/Cart_Reader/Cart_Reader.ino index 0188f36..c0c62f9 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: 2024-07-05 - Version: 13.5 + Date: 2024-07-08 + Version: 14.0 SD lib: https://github.com/greiman/SdFat LCD lib: https://github.com/olikraus/u8g2 @@ -21,10 +21,11 @@ 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, ARC, Atari 2600/5200/7800, ODY2, Fairchild, MSX, Pokemon Mini, C64, Vectrex modules + skaman - Cart ROM READER SNES ENHANCED, Famicom Cart Dumper, 2600, 5200, 7800, ARC, ATARI8, BALLY, C64, COLV, FAIRCHILD, + INTV, LEAP, LJ, LJPRO, MSX, ODY2, PCW, POKEMINI, PV1000, PYUUTA, RCA, TI99, TRS80, VBOY, VECTREX, WSV, VIC20, VSMILE modules Tamanegi_taro - PCE and Satellaview modules splash5 - GBSmart, Wonderswan, NGP and Super A'can modules - partlyhuman - Casio Loopy module + partlyhuman - Casio Loopy & Atari Lynx module hkz & themanbehindthecurtain - N64 flashram commands Andrew Brown & Peter Den Hartog - N64 controller protocol libdragon - N64 controller checksum functions @@ -43,7 +44,7 @@ philenotfound, karimhadjsalem, nsx0r, ducky92, niklasweber, Lesserkuma, BacteriaMage, qufb, vpelletier, Ancyker, mattiacci, RWeick, ButThouMust, partlyhuman, fakkuyuu, hxlnt, breyell, smesgr9000, joshman196, PsychoFox11, plaidpants, LuigiBlood, InvalidInterrupt - + And to nocash for figuring out the secrets of the SFC Nintendo Power cartridge. This program is free software: you can redistribute it and/or modify @@ -458,7 +459,7 @@ uint32_t calculateCRC(char* fileName, char* folder, unsigned long offset) { /****************************************** CRC Functions for Atari, Fairchild, Ody2, Arc, etc. modules *****************************************/ -#if (defined(ENABLE_ODY2) || defined(ENABLE_ARC) || defined(ENABLE_FAIRCHILD) || defined(ENABLE_MSX) || defined(ENABLE_POKE) || defined(ENABLE_2600) || defined(ENABLE_5200) || defined(ENABLE_7800) || defined(ENABLE_C64) || defined(ENABLE_VECTREX) || defined(ENABLE_NES) || defined(ENABLE_LYNX)) +#if (defined(ENABLE_ODY2) || defined(ENABLE_ARC) || defined(ENABLE_FAIRCHILD) || defined(ENABLE_MSX) || defined(ENABLE_POKE) || defined(ENABLE_2600) || defined(ENABLE_5200) || defined(ENABLE_7800) || defined(ENABLE_C64) || defined(ENABLE_VECTREX) || defined(ENABLE_NES) || defined(ENABLE_LYNX) || defined(ENABLE_ATARI8) || defined(ENABLE_BALLY) || defined(ENABLE_LEAP) || defined(ENABLE_LJ) || defined(ENABLE_LJPRO) || defined(ENABLE_PV1000) || defined(ENABLE_PYUUTA) || defined(ENABLE_RCA) || defined(ENABLE_TI99) || defined(ENABLE_TRS80) || defined(ENABLE_VIC20) || defined(ENABLE_VSMILE)) void printCRC(char* checkFile, uint32_t* crcCopy, unsigned long offset) { uint32_t crc = calculateCRC(checkFile, folder, offset); @@ -658,7 +659,7 @@ boolean compareCRC(const char* database, uint32_t crc32sum, boolean renamerom, i //****************************************** // Math Functions //****************************************** -#if (defined(ENABLE_NES) || defined(ENABLE_MSX) || defined(ENABLE_GBX)) +#if (defined(ENABLE_NES) || defined(ENABLE_MSX) || defined(ENABLE_GBX) || defined(ENABLE_TRS80)) int int_pow(int base, int exp) { // Power for int int result = 1; while (exp) { @@ -737,7 +738,11 @@ void seek_first_letter_in_database(FsFile& database, byte myLetter) { #endif } -#if (defined(ENABLE_ARC) || defined(ENABLE_FAIRCHILD) || defined(ENABLE_VECTREX)) +#if ( \ + defined(ENABLE_ARC) || defined(ENABLE_FAIRCHILD) || defined(ENABLE_VECTREX) || defined(ENABLE_BALLY) || \ + defined(ENABLE_PV1000) || defined(ENABLE_PYUUTA) || defined(ENABLE_RCA) || defined(ENABLE_TRS80) || \ + defined(ENABLE_LEAP) || defined(ENABLE_LJ) || defined(ENABLE_VSMILE)\ + ) // read single digit data line as byte void readDataLineSingleDigit(FsFile& database, void* byteData) { // Read rom size @@ -748,7 +753,10 @@ void readDataLineSingleDigit(FsFile& database, void* byteData) { } #endif -#if (defined(ENABLE_ODY2) || defined(ENABLE_5200) || defined(ENABLE_7800) || defined(ENABLE_C64)) +#if ( \ + defined(ENABLE_ODY2) || defined(ENABLE_5200) || defined(ENABLE_7800) || defined(ENABLE_C64) || \ + defined(ENABLE_VIC20)|| defined(ENABLE_ATARI8)\ + ) struct database_entry_mapper_size { byte gameMapper; byte gameSize; @@ -881,7 +889,10 @@ boolean checkCartSelection(FsFile& database, void (*readData)(FsFile&, void*), v # if ( \ defined(ENABLE_ODY2) || defined(ENABLE_ARC) || defined(ENABLE_FAIRCHILD) || defined(ENABLE_MSX) || \ defined(ENABLE_POKE) || defined(ENABLE_2600) || defined(ENABLE_5200) || defined(ENABLE_7800) || \ - defined(ENABLE_C64) || defined(ENABLE_VECTREX) || defined(ENABLE_NES) || defined(ENABLE_GBX) \ + defined(ENABLE_C64) || defined(ENABLE_VECTREX) || defined(ENABLE_NES) || defined(ENABLE_GBX) || \ + defined(ENABLE_BALLY) || defined(ENABLE_PV1000) || defined(ENABLE_PYUUTA) || defined(ENABLE_RCA) || \ + defined(ENABLE_TRS80) || defined(ENABLE_VIC20) || defined(ENABLE_LEAP) || defined(ENABLE_LJ) || \ + defined(ENABLE_VSMILE)|| defined(ENABLE_TI99) || defined(ENABLE_ATARI8)\ ) void printInstructions() { println_Msg(FS(FSTRING_EMPTY)); @@ -1095,9 +1106,21 @@ constexpr char modeItem24[] PROGMEM = "Atari 5200"; constexpr char modeItem25[] PROGMEM = "Atari 7800"; constexpr char modeItem26[] PROGMEM = "Atari Lynx"; constexpr char modeItem27[] PROGMEM = "Vectrex"; -constexpr char modeItem28[] PROGMEM = "Flashrom Programmer"; -constexpr char modeItem29[] PROGMEM = "Self Test (3V)"; -constexpr char modeItem30[] PROGMEM = "About"; +constexpr char modeItem28[] PROGMEM = "Atari 8-bit"; +constexpr char modeItem29[] PROGMEM = "Bally Astrocade"; +constexpr char modeItem30[] PROGMEM = "Bandai LJ"; +constexpr char modeItem31[] PROGMEM = "Bandai LJ Pro"; +constexpr char modeItem32[] PROGMEM = "Casio PV-1000"; +constexpr char modeItem33[] PROGMEM = "Commodore VIC-20"; +constexpr char modeItem34[] PROGMEM = "LF Leapster (3V)"; +constexpr char modeItem35[] PROGMEM = "RCA Studio II"; +constexpr char modeItem36[] PROGMEM = "TI-99"; +constexpr char modeItem37[] PROGMEM = "Tomy Pyuuta"; +constexpr char modeItem38[] PROGMEM = "TRS-80"; +constexpr char modeItem39[] PROGMEM = "Vtech V.Smile (3V)"; +constexpr char modeItem40[] PROGMEM = "Flashrom Programmer"; +constexpr char modeItem41[] PROGMEM = "Self Test (3V)"; +constexpr char modeItem42[] PROGMEM = "About"; static const char* const modeOptions[] PROGMEM = { #ifdef ENABLE_GBX @@ -1181,14 +1204,49 @@ static const char* const modeOptions[] PROGMEM = { #ifdef ENABLE_VECTREX modeItem27, #endif -#ifdef ENABLE_FLASH +#ifdef ENABLE_ATARI8 modeItem28, #endif -#ifdef ENABLE_SELFTEST +#ifdef ENABLE_BALLY modeItem29, #endif +#ifdef ENABLE_LJ modeItem30, - FSTRING_RESET +#endif +#ifdef ENABLE_LJPRO + modeItem31, +#endif +#ifdef ENABLE_PV1000 + modeItem32, +#endif +#ifdef ENABLE_VIC20 + modeItem33, +#endif +#ifdef ENABLE_LEAP + modeItem34, +#endif +#ifdef ENABLE_RCA + modeItem35, +#endif +#ifdef ENABLE_TI99 + modeItem36, +#endif +#ifdef ENABLE_PYUUTA + modeItem37, +#endif +#ifdef ENABLE_TRS80 + modeItem38, +#endif +#ifdef ENABLE_VSMILE + modeItem39, +#endif +#ifdef ENABLE_FLASH + modeItem40, +#endif +#ifdef ENABLE_SELFTEST + modeItem41, +#endif + modeItem42, FSTRING_RESET }; uint8_t pageMenu(const __FlashStringHelper* question, const char* const* menuStrings, uint8_t entryCount, uint8_t default_choice = 0) { @@ -1397,7 +1455,7 @@ void mainMenu() { setup_LYNX(); return lynxMenu(); break; -#endif +#endif #ifdef ENABLE_VECTREX case SYSTEM_MENU_VECTREX: @@ -1406,6 +1464,90 @@ void mainMenu() { break; #endif +#ifdef ENABLE_ATARI8 + case SYSTEM_MENU_ATARI8: + setup_ATARI8(); + return atari8Menu(); + break; +#endif + +#ifdef ENABLE_BALLY + case SYSTEM_MENU_BALLY: + setup_BALLY(); + return ballyMenu(); + break; +#endif + +#ifdef ENABLE_LJ + case SYSTEM_MENU_LJ: + setup_LJ(); + return ljMenu(); + break; +#endif + +#ifdef ENABLE_LJPRO + case SYSTEM_MENU_LJPRO: + setup_LJPRO(); + return ljproMenu(); + break; +#endif + +#ifdef ENABLE_PV1000 + case SYSTEM_MENU_PV1000: + setup_PV1000(); + return pv1000Menu(); + break; +#endif + +#ifdef ENABLE_VIC20 + case SYSTEM_MENU_VIC20: + setup_VIC20(); + return vic20Menu(); + break; +#endif + +#ifdef ENABLE_LEAP + case SYSTEM_MENU_LEAP: + setup_LEAP(); + return leapMenu(); + break; +#endif + +#ifdef ENABLE_RCA + case SYSTEM_MENU_RCA: + setup_RCA(); + return rcaMenu(); + break; +#endif + +#ifdef ENABLE_TI99 + case SYSTEM_MENU_TI99: + setup_TI99(); + return ti99Menu(); + break; +#endif + +#ifdef ENABLE_PYUUTA + case SYSTEM_MENU_PYUUTA: + setup_PYUUTA(); + return pyuutaMenu(); + break; +#endif + +#ifdef ENABLE_TRS80 + case SYSTEM_MENU_TRS80: + setup_TRS80(); + return trs80Menu(); + break; +#endif + +#ifdef ENABLE_VSMILE + case SYSTEM_MENU_VSMILE: + setup_VSMILE(); + return vsmileMenu(); + break; +#endif + #ifdef ENABLE_FLASH case SYSTEM_MENU_FLASH: # ifdef ENABLE_VSELECT @@ -3608,6 +3750,42 @@ void loop() { #endif #ifdef ENABLE_GPC case CORE_GPC: return gpcMenu(); +#endif +#ifdef ENABLE_ATARI8 + case CORE_ATARI8: return atari8Menu(); +#endif +#ifdef ENABLE_BALLY + case CORE_BALLY: return ballyMenu(); +#endif +#ifdef ENABLE_LJ + case CORE_LJ: return ljMenu(); +#endif +#ifdef ENABLE_LJPRO + case CORE_LJPRO: return ljproMenu(); +#endif +#ifdef ENABLE_PV1000 + case CORE_PV1000: return pv1000Menu(); +#endif +#ifdef ENABLE_VIC20 + case CORE_VIC20: return vic20Menu(); +#endif +#ifdef ENABLE_LEAP + case CORE_LEAP: return leapMenu(); +#endif +#ifdef ENABLE_RCA + case CORE_RCA: return rcaMenu(); +#endif +#ifdef ENABLE_TI99 + case CORE_TI99: return ti99Menu(); +#endif +#ifdef ENABLE_PYUUTA + case CORE_PYUUTA: return pyuutaMenu(); +#endif +#ifdef ENABLE_TRS80 + case CORE_TRS80: return trs80Menu(); +#endif +#ifdef ENABLE_VSMILE + case CORE_VSMILE: return vsmileMenu(); #endif case CORE_MAX: return resetArduino(); } diff --git a/Cart_Reader/Config.h b/Cart_Reader/Config.h index 8424328..0fc64ce 100644 --- a/Cart_Reader/Config.h +++ b/Cart_Reader/Config.h @@ -96,6 +96,34 @@ /****/ +/* [ Atari 8-bit -------------------------------------------------- ] +*/ + +//#define ENABLE_ATARI8 + +/****/ + +/* [ Bally Astrocade ---------------------------------------------- ] +*/ + +//#define ENABLE_BALLY + +/****/ + +/* [ Bandai Little Jammer ----------------------------------------- ] +*/ + +//#define ENABLE_LJ + +/****/ + +/* [ Bandai Little Jammer Pro ------------------------------------- ] +*/ + +//#define ENABLE_LJPRO + +/****/ + /* [ Benesse Pocket Challenge W ----------------------------------- ] */ @@ -103,10 +131,10 @@ /****/ -/* [ C64 --------------------------------------------------- ] +/* [ Casio PV-1000 ------------------------------------------------ ] */ -//#define ENABLE_C64 +//#define ENABLE_PV1000 /****/ @@ -117,6 +145,20 @@ /****/ +/* [ Commodore 64 ------------------------------------------------- ] +*/ + +//#define ENABLE_C64 + +/****/ + +/* [ Commodore VIC-20 --------------------------------------------- ] +*/ + +//#define ENABLE_VIC20 + +/****/ + /* [ Emerson Arcadia 2001 ----------------------------------------- ] */ @@ -153,6 +195,13 @@ /****/ +/* [ LeapFrog Leapster -------------------------------------------- ] +*/ + +//#define ENABLE_LEAP + +/****/ + /* [ Neo Geo Pocket ----------------------------------------------- ] */ @@ -181,7 +230,7 @@ /****/ -/* [ MSX ------------------------------------------- ] +/* [ MSX ---------------------------------------------------------- ] */ //#define ENABLE_MSX @@ -195,13 +244,20 @@ /****/ -/* [ Pokemon Mini -------------------------------------- ] +/* [ Pokemon Mini ------------------------------------------------- ] */ //#define ENABLE_POKE /****/ +/* [ RCA Studio II ------------------------------------------------ ] +*/ + +//#define ENABLE_RCA + +/****/ + /* [ Sega Master System/Mark III/Game Gear/SG-1000 ---------------- ] */ @@ -251,7 +307,28 @@ /****/ -/* [ Vectrex --------------------------------------------------- ] +/* [ Texas Instruments TI-99 -------------------------------------- ] +*/ + +//#define ENABLE_TI99 + +/****/ + +/* [ Tomy Pyuuta -------------------------------------------------- ] +*/ + +//#define ENABLE_PYUUTA + +/****/ + +/* [ TRS-80 Color Computer ---------------------------------------- ] +*/ + +//#define ENABLE_TRS80 + +/****/ + +/* [ Vectrex ------------------------------------------------------ ] */ //#define ENABLE_VECTREX @@ -265,6 +342,13 @@ /****/ +/* [ Vtech V.Smile ------------------------------------------------ ] +*/ + +//#define ENABLE_VSMILE + +/****/ + /* [ Watara Supervision ------------------------------------------- ] */ diff --git a/Cart_Reader/LEAP.ino b/Cart_Reader/LEAP.ino new file mode 100644 index 0000000..16891b2 --- /dev/null +++ b/Cart_Reader/LEAP.ino @@ -0,0 +1,1052 @@ +//****************************************** +// LEAP MODULE +//****************************************** +#ifdef ENABLE_LEAP +// Leapster +// Cartridge pinout +// 60P 1.27mm pitch connector +// +// FRONT BACK +// SIDE SIDE +// +---------+ +// VCC -| 1B 1A |- VCC +// nc -| 2B 2A |- GND +// D11 -| 3B 3A |- D4 +// D3 -| 4B 4A |- D12 +// D10 -| 5B 5A |- D5 +// GND -| 6B 6A |- D2 +// D13 -| 7B 7A |- D9 +// nc -| 8B 8A |- nc +// D6 -| 9B 9A |- D1 +// D14 -| 10B 10A |- D8 +// |---------| +// D7 -| 11B 11A |- GND +// D0 -| 12B 12A |- D15 +// BYTE -| 13B 13A |- OE +// nc -| 14B 14A |- A22 +// FLA_CE -| 15B 15A |- A23 +// CE -| 16B 16A |- A16 +// A0 -| 17B 17A |- A15 +// A1 -| 18B 18A |- A14 +// A2 -| 19B 19A |- A13 +// GND -| 20B 20A |- A3 +// A12 -| 21B 21A |- A4 +// A11 -| 22B 22A |- A5 +// A10 -| 23B 23A |- A6 +// A9 -| 24B 24A |- A7 +// A8 -| 25B 25A |- A17 +// A19 -| 26B 26A |- A18 +// A20 -| 27B 27A |- A21 +// WE -| 28B 28A |- GND +// EEP_SDA -| 29B 29A |- EEP_WP +// nc -| 30B 30A |- EEP_SCL +// +---------+ +// +// CONTROL PINS: +// EEPROM WP (PH0) - SNES /RESET +// EEPROM SCL (PH1) - SNES CPUCLK +// EEPROM SDA (PH4) - SNES /IRQ +// OE (PH3) - SNES /CART +// WE (PH5) - SNES /WR +// CE (PH6) - SNES /RD +// BYTE (PE5) - SNES REFRESH +// FLASH CE (PJ0) - SNES /PARD + +//****************************************** +// DEFINES +//****************************************** +// CONTROL PINS - OE/WE/CE +#define OE_HIGH PORTH |= (1<<3) +#define OE_LOW PORTH &= ~(1<<3) +#define WE_HIGH PORTH |= (1<<5) +#define WE_LOW PORTH &= ~(1<<5) +#define CE_HIGH PORTH |= (1<<6) +#define CE_LOW PORTH &= ~(1<<6) + +// BYTE PIN IS A0 IN BYTE MODE - SHIFT ADDRESS >> 1 +#define BYTE_HIGH PORTE |= (1<<5) +#define BYTE_LOW PORTE &= ~(1<<5) + +// SERIAL I2C EEPROM PINS 24LC02B 2K +// PIN 5 - SDA +// PIN 6 - SCL +// PIN 7 - WP +// CART B30 = SDA +// CART A31 = SCL +// CART A30 = WP +#define WP_HIGH PORTH |= (1<<0) +#define WP_LOW PORTH &= ~(1<<0) +#define SCL_HIGH PORTH |= (1<<1) +#define SCL_LOW PORTH &= ~(1<<1) +#define SDA_HIGH PORTH |= (1<<4) +#define SDA_LOW PORTH &= ~(1<<4) + +// FLASH 39VF040 512K +// CART B16 = CE# +#define FL_CE_HIGH PORTJ |= (1<<0) +#define FL_CE_LOW PORTJ &= ~(1<<0) + +#define DATA_C_READ { DDRC = 0; PORTC = 0xFF; } // [INPUT PULLUP] +#define DATA_A_READ { DDRA = 0; PORTA = 0xFF; } // [INPUT PULLUP] + +//****************************************** +// VARIABLES +//****************************************** +// ROM File = LEAP.bin +// RAM File = SAVE.eep +// FLASH File = SAVE.fla + +byte LEAPSTER[] = {4,8,16}; +byte leaplo = 0; // Lowest Entry +byte leaphi = 2; // Highest Entry +byte leapsize; +byte newleapsize; + +byte tempbyte; +char tempword; +word ptrword; +word tempcheck; + +// EEPROM MAPPING +// 08 ROM SIZE + +//****************************************** +// ROM STRUCTURE +//****************************************** +char LEAP[] = {0x4C,0x45,0x41,0x50}; // "LEAP" MARKER AT 0x00 OR 0x144 (WORD 0xA2) +char TBL[] = {0x01,0x00,0x00,0x01}; // TABLE START +char TXT[] = {0x04,0x00,0x00,0x01}; // NEXT DWORD IS TEXT BLOCK [5 SENTENCES] START +char VER[] = {0x0A,0x00,0x00,0x01}; // NEXT DWORD IS ROM VERSION LOCATION +char TTL[] = {0x0B,0x00,0x00,0x01}; // NEXT DWORD IS ROM TITLE LOCATION +char END[] = {0x10,0x00,0x00,0x01}; // LAST BLOCK - END SEARCH +word sentenceAddr = 0; // Sentence Block Start Address +word versionAddr = 0; // Version Address +word titleAddr = 0; // Title Address +char ROMVersion[20]; // Fosters [20] "155-11172 152-11808" +char ROMTitle[50]; // "Mr. Pencils Learn to Draw and Write." [37] + // "Thomas and Friends Calling All Engines" [39] + // "The Letter Factory.v1.0 - Initial Release JBM3" [47] + +//****************************************** +// DATA INTEGRITY BLOCK +//****************************************** +// 5 Sentences - 172 bytes +// Location not static between ROMs + +static const unsigned char LeapCheck [] = { +0x4C,0x69,0x6C,0x20,0x64,0x75,0x63,0x6B,0x65,0x64,0x2E,0x20,0x20,0x54,0x68,0x65, +0x20,0x6A,0x65,0x74,0x20,0x7A,0x69,0x70,0x70,0x65,0x64,0x20,0x70,0x61,0x73,0x74, +0x20,0x68,0x65,0x72,0x20,0x68,0x65,0x61,0x64,0x2E,0x20,0x20,0x44,0x75,0x73,0x74, +0x20,0x66,0x6C,0x65,0x77,0x2C,0x20,0x4C,0x69,0x6C,0x20,0x73,0x6E,0x65,0x65,0x7A, +0x65,0x64,0x2C,0x20,0x61,0x6E,0x64,0x20,0x4C,0x65,0x61,0x70,0x20,0x74,0x75,0x72, +0x6E,0x65,0x64,0x20,0x72,0x65,0x64,0x2E,0x20,0x20,0x54,0x68,0x65,0x6E,0x20,0x4C, +0x69,0x6C,0x20,0x67,0x6F,0x74,0x20,0x75,0x70,0x2C,0x20,0x61,0x62,0x6F,0x75,0x74, +0x20,0x74,0x6F,0x20,0x79,0x65,0x6C,0x6C,0x2E,0x20,0x20,0x4C,0x65,0x61,0x70,0x20, +0x67,0x61,0x73,0x70,0x65,0x64,0x2C,0x20,0x22,0x4C,0x6F,0x6F,0x6B,0x2C,0x20,0x4C, +0x69,0x6C,0x21,0x20,0x20,0x59,0x6F,0x75,0x72,0x20,0x74,0x6F,0x6F,0x74,0x68,0x21, +0x20,0x20,0x49,0x74,0x20,0x66,0x65,0x6C,0x6C,0x21,0x22,0x00 +}; + +//****************************************** +// MENU +//****************************************** +// Base Menu +static const char leapmenuItem4[] PROGMEM = "Read EEPROM"; +static const char leapmenuItem5[] PROGMEM = "Write EEPROM"; +static const char leapmenuItem6[] PROGMEM = "Read FLASH"; +static const char leapmenuItem7[] PROGMEM = "Write FLASH"; +static const char* const menuOptionsLEAP[] PROGMEM = { FSTRING_SELECT_CART, FSTRING_READ_ROM, FSTRING_SET_SIZE, leapmenuItem4, leapmenuItem5, leapmenuItem6, leapmenuItem7 }; + +void leapMenu() +{ + convertPgm(menuOptionsLEAP, 7); + uint8_t mainMenu = question_box(F("LEAPSTER MENU"), menuOptions, 7, 0); + + switch (mainMenu) + { + // Select Cart + case 0: + setCart_LEAP(); + setup_LEAP(); + break; + + // Read ROM + case 1: + sd.chdir("/"); + readROM_LEAP(); + sd.chdir("/"); + break; + + // Set Size + case 2: + setROMSize_LEAP(); + break; + + // Read EEPROM + case 3: + sd.chdir("/"); + readEEP_LEAP(); + sd.chdir("/"); + break; + + // Write EEPROM + case 4: + writeEEP_LEAP(); + break; + + // Read FLASH + case 5: + idFLASH_LEAP(); + if (strcmp(flashid_str, "BFD7") == 0) { + sd.chdir("/"); + readFLASH_LEAP(); + sd.chdir("/"); + } + break; + + // Write FLASH + case 6: + idFLASH_LEAP(); + if (strcmp(flashid_str, "BFD7") == 0) + writeFLASH_LEAP(); + break; + } +} + +//****************************************** +// SETUP +//****************************************** + +void setup_LEAP() +{ + // Request 3.3V + setVoltage(VOLTS_SET_3V3); + + // Control Pins PH0..PH1, PH3..PH6 BYTE, OE, CE, WE + DDRH = 0x7B; // 0b01111011 - CE, WE, SDA, OE, SCL, WP [OUTPUT] - Unused Pins [INPUT] + PORTH = 0xFF; // 0b11111111 - CE, WE, SDA, OE, SCL, WP [HIGH] - Unused Pins [HIGH] + + // Address Pins + DDRF = 0xFF; // Address A0-A7 [OUTPUT] + DDRK = 0xFF; // Address A8-A15 [OUTPUT] + DDRL = 0xFF; // Address A16-A23 [OUTPUT] + // Data Pins + DDRC = 0x00; // D0-D7 [INPUT] + DDRA = 0x00; // D8-D15 [INPUT] + // BYTE Pin PE5 + DDRE = 0x20; // 0b00100000 BYTE [OUTPUT] - Unused Pins [INPUT] + PORTE = 0xFF; // 0b11111111 BYTE [HIGH] - Unused Pins [HIGH] + // Flash Pin PJ0 + DDRJ = 0x01; // 0b00000001 - FLA_CE [OUTPUT] - Unused Pins [INPUT] + PORTJ = 0xFF; // 0b11111111 - FLA_CE [HIGH] - Unused Pins {HIGH] + + // Check Start for "LEAP" marker + // Read ROM Version & Title + checkStart_LEAP(); + + // 39VF040 Flash Check +// idFLASH(); + + checkStatus_LEAP(); + strncpy(romName, ROMTitle, 16); // Truncate ROMTitle to fit + + mode = CORE_LEAP; +} + +//****************************************** +// READ FUNCTIONS +//****************************************** +// Max ROM Size 0x1000000 (Highest Address = 0xFFFFFF) - FF FFFF +word read_rom_word_LEAP(unsigned long address) // OLD TIMING #1 +{ + PORTL = (address >> 16) & 0xFF; + PORTK = (address >> 8) & 0xFF; + PORTF = address & 0xFF; + CE_LOW; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + OE_LOW; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + unsigned char data1 = PINC; + unsigned char data2 = PINA; + word data = (data1 << 8) | (data2); + OE_HIGH; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + CE_HIGH; + + return data; +} + +byte read_flash_byte_LEAP(unsigned long address) // CE HIGH, OE LOW, FL_CE LOW +{ + PORTL = (address >> 17) & 0x7F; + PORTK = (address >> 9) & 0xFF; + PORTF = (address >> 1) & 0xFF; + if (address & 0x1) // BYTE = A0 + BYTE_HIGH; + else + BYTE_LOW; + CE_HIGH; + OE_LOW; + FL_CE_LOW; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + unsigned char data = PINC; + FL_CE_HIGH; + OE_HIGH; + + return data; +} + +//****************************************** +// WRITE FUNCTION +//****************************************** + +void write_flash_byte_LEAP(unsigned long address, unsigned char data) +{ + PORTL = (address >> 17) & 0x7F; + PORTK = (address >> 9) & 0xFF; + PORTF = (address >> 1) & 0xFF; + if (address & 0x1) // BYTE = A0 + BYTE_HIGH; + else + BYTE_LOW; + OE_HIGH; + FL_CE_LOW; + NOP; + NOP; + NOP; + NOP; + NOP; + PORTC = data; + WE_LOW; + WE_HIGH; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + FL_CE_HIGH; + OE_LOW; +} + +//****************************************** +// DATA INTEGRITY FUNCTIONS +//****************************************** + +void checkStart_LEAP() +{ + delay(500); + OE_LOW; + CE_LOW; + BYTE_HIGH; + tempword = read_rom_word_LEAP(0x0); + if ((LEAP[0] == ((tempword >> 0x8) & 0xFF)) && (LEAP[1] == (tempword & 0xFF))) { + tempword = read_rom_word_LEAP(0x1); + findTable_LEAP(0x4, 0xA2); + } + else { + delay(500); + tempword = read_rom_word_LEAP(0xA2); + if ((LEAP[0] == ((tempword >> 0x8) & 0xFF)) && (LEAP[1] == (tempword & 0xFF))) { + tempword = read_rom_word_LEAP(0xA3); + findTable_LEAP(0xA4, 0x146); + } + } + CE_HIGH; + OE_HIGH; +} + +void findTable_LEAP(unsigned long startAddr, unsigned long endAddr) +{ + delay(500); + CE_LOW; + for (unsigned long addr = startAddr; addr < endAddr; addr +=2) { + tempword = read_rom_word_LEAP(addr); + if ((TBL[0] == ((tempword >> 0x8) & 0xFF))&&(TBL[1] == (tempword & 0xFF))) { + tempword = read_rom_word_LEAP(addr + 1); + if ((TBL[2] == ((tempword >> 0x8) & 0xFF))&&(TBL[3] == (tempword & 0xFF))) { + readTable_LEAP(startAddr, endAddr); + break; + } + } + } + CE_HIGH; +} + +void readTable_LEAP(unsigned long startAddr, unsigned long endAddr) +{ + delay(500); + CE_LOW; + for (unsigned long addr = startAddr; addr < endAddr; addr++) { + tempword = read_rom_word_LEAP(addr); + if ((TXT[0] == ((tempword >> 0x8) & 0xFF))&&(TXT[1] == (tempword & 0xFF))) { + tempword = read_rom_word_LEAP(addr + 1); + if ((TXT[2] == ((tempword >> 0x8) & 0xFF))&&(TXT[3] == (tempword & 0xFF))) { // Text Block Marker Found + ptrword = read_rom_word_LEAP(addr + 2); + sentenceAddr = (((ptrword >> 8) & 0xFF) | ((ptrword & 0xFF) << 8)); // Swap Byte Order + } + } + else if ((VER[0] == ((tempword >> 0x8) & 0xFF))&&(VER[1] == (tempword & 0xFF))) { + tempword = read_rom_word_LEAP(addr + 1); + if ((VER[2] == ((tempword >> 0x8) & 0xFF))&&(VER[3] == (tempword & 0xFF))) { // Version Marker Found + ptrword = read_rom_word_LEAP(addr + 2); + versionAddr = (((ptrword >> 8) & 0xFF) | ((ptrword & 0xFF) << 8)); // Swap Byte Order + } + } + else if ((TTL[0] == ((tempword >> 0x8) & 0xFF))&&(TTL[1] == (tempword & 0xFF))) { + tempword = read_rom_word_LEAP(addr + 1); + if ((TTL[2] == ((tempword >> 0x8) & 0xFF))&&(TTL[3] == (tempword & 0xFF))) { // Title Marker Found + ptrword = read_rom_word_LEAP(addr + 2); + titleAddr = (((ptrword >> 8) & 0xFF) | ((ptrword & 0xFF) << 8)); // Swap Byte Order + } + } + else if ((END[0] == ((tempword >> 0x8) & 0xFF))&&(END[1] == (tempword & 0xFF))) { + tempword = read_rom_word_LEAP(addr + 1); + if ((END[2] == ((tempword >> 0x8) & 0xFF))&&(END[3] == (tempword & 0xFF))) { // END OF TABLE + break; + } + } + } + CE_HIGH; +// print_Msg(F("Text Addr: ")); +// println_Msg(sentenceAddr, HEX); +// print_Msg(F("Version Addr: ")); +// println_Msg(versionAddr, HEX); +// print_Msg(F("Title Addr: ")); +// println_Msg(titleAddr, HEX); +// display_Update(); + + delay(500); + CE_LOW; + for (int x = 0; x < 10; x++) { + tempword = read_rom_word_LEAP((versionAddr / 2) + x); + ROMVersion[x * 2] = (tempword >> 0x8) & 0xFF; + ROMVersion[(x * 2) + 1] = tempword & 0xFF; + } + + delay(500); + CE_LOW; + for (int x = 0; x < 25; x++) { + tempword = read_rom_word_LEAP((titleAddr / 2) + x); + ROMTitle[x * 2] = (tempword >> 0x8) & 0xFF; + ROMTitle[(x * 2) + 1] = tempword & 0xFF; + } +} + +//****************************************** +// READ ROM +//****************************************** + +void readROM_LEAP() +{ + createFolderAndOpenFile("LEAP", "ROM", romName, "bin"); + + for (unsigned long address = 0; address < 0x200000; address += 256) { // 4MB + for (unsigned int x = 0; x < 256; x++) { + tempword = read_rom_word_LEAP(address + x); + sdBuffer[x * 2] = (tempword >> 0x8) & 0xFF; + sdBuffer[(x * 2) + 1] = tempword & 0xFF; + } + myFile.write(sdBuffer, 512); + } + if (leapsize > 0) { + for (unsigned long address = 0x200000; address < 0x400000; address += 256) { // +4MB = 8MB + for (unsigned int x = 0; x < 256; x++) { + tempword = read_rom_word_LEAP(address + x); + sdBuffer[x * 2] = (tempword >> 0x8) & 0xFF; + sdBuffer[(x * 2) + 1] = tempword & 0xFF; + } + myFile.write(sdBuffer, 512); + } + if (leapsize > 1) { + for (unsigned long address = 0x400000; address < 0x800000; address += 256) { // +8MB = 16MB + for (unsigned int x = 0; x < 256; x++) { + tempword = read_rom_word_LEAP(address + x); + sdBuffer[x * 2] = (tempword >> 0x8) & 0xFF; + sdBuffer[(x * 2) + 1] = tempword & 0xFF; + } + myFile.write(sdBuffer, 512); + } + } + } + myFile.close(); + + printCRC(fileName, NULL, 0); + + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) +void printRomSize_LEAP(int index) +{ + display_Clear(); + print_Msg(FS(FSTRING_ROM_SIZE)); + println_Msg(LEAPSTER[index]); +} +#endif + +void setROMSize_LEAP() +{ + byte newleapsize; +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + if (leaplo == leaphi) + newleapsize = leaplo; + else { + newleapsize = navigateMenu(leaplo, leaphi, &printRomSize_LEAP); + + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(LEAPSTER[newleapsize]); + println_Msg(F("KB")); + display_Update(); + delay(1000); +#else + if (leaplo == leaphi) + newleapsize = leaplo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (leaphi - leaplo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(LEAPSTER[i + leaplo]); + Serial.println(F("KB")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newleapsize = sizeROM.toInt() + leaplo; + if (newleapsize > leaphi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(FS(FSTRING_EMPTY)); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(LEAPSTER[newleapsize]); + Serial.println(F("KB")); +#endif + EEPROM_writeAnything(8, newleapsize); + leapsize = newleapsize; +} + +void checkStatus_LEAP() +{ + EEPROM_readAnything(8, leapsize); + if (leapsize > leaphi) { + leapsize = 1; // default 8M + EEPROM_writeAnything(8, leapsize); + } + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + println_Msg(F("LEAPSTER READER")); + println_Msg(FS(FSTRING_CURRENT_SETTINGS)); + println_Msg(FS(FSTRING_EMPTY)); + print_Msg(F("TITLE: ")); + println_Msg(ROMTitle); + print_Msg(F("VER: ")); + println_Msg(ROMVersion); + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(LEAPSTER[leapsize]); + println_Msg(F("MB")); + display_Update(); + wait(); +#else + Serial.print(F("TITLE: ")); + Serial.println(ROMTitle); + Serial.print(F("VER: ")); + Serial.println(ROMVersion); + Serial.print(FS(FSTRING_ROM_SIZE)); + Serial.print(LEAPSTER[leapsize]); + Serial.println(F("MB")); + Serial.println(FS(FSTRING_EMPTY)); +#endif +} + +//****************************************** +// FLASH SAVES +//****************************************** +// FLASH 39VF040 +// MR. PENCIL'S LEARN TO DRAW & WRITE +// TOP SECRET - PERSONAL BEESWAX +// FLASH IS BYTE MODE +// BYTE PIN IS A0 - SHIFT ADDRESS >> 1 + +void dataOut_LEAP() +{ + DDRC = 0xFF; + DDRA = 0xFF; +} + +void dataIn_LEAP() +{ + DDRC = 0x00; + DDRA = 0x00; +} + +void idFLASH_LEAP() // "BFD7" = 39VF040 +{ + int ID1 = 0; + int ID2 = 0; + dataOut_LEAP(); + resetFLASH_LEAP(); + write_flash_byte_LEAP(0x5555, 0xAA); + write_flash_byte_LEAP(0x2AAA, 0x55); + write_flash_byte_LEAP(0x5555, 0x90); + dataIn_LEAP(); + ID1 = read_flash_byte_LEAP(0x0000); + ID2 = read_flash_byte_LEAP(0x0001); + dataOut_LEAP(); + resetFLASH_LEAP(); + dataIn_LEAP(); + sprintf(flashid_str, "%02X%02X", ID1, ID2); + display_Clear(); + print_Msg(F("Flash ID: ")); + println_Msg(flashid_str); + display_Update(); +} + +void resetFLASH_LEAP() +{ + write_flash_byte_LEAP(0x5555, 0xF0); +} + +void eraseFLASH_LEAP() +{ + write_flash_byte_LEAP(0x5555, 0xAA); + write_flash_byte_LEAP(0x2AAA, 0x55); + write_flash_byte_LEAP(0x5555, 0x80); + write_flash_byte_LEAP(0x5555, 0xAA); + write_flash_byte_LEAP(0x2AAA, 0x55); + write_flash_byte_LEAP(0x5555, 0x10); +} + +void programFLASH_LEAP() +{ + write_flash_byte_LEAP(0x5555, 0xAA); + write_flash_byte_LEAP(0x2AAA, 0x55); + write_flash_byte_LEAP(0x5555, 0xA0); +} + +void statusFLASH_LEAP() +{ + byte flashStatus = 1; + do { + flashStatus = ((PORTA & 0x80) >> 7); // D7 = PORTA7 + _delay_us(4); + } + while (flashStatus == 1); +} + +void readFLASH_LEAP() +{ + createFolderAndOpenFile("LEAP", "SAVE", romName, "fla"); + + if(myFile) { + CE_HIGH; + OE_LOW; + FL_CE_LOW; + for (unsigned long address = 0x0; address < 0x80000; address += 512) { // 512K + if ((address % 0x8000) == 0) { + print_Msg(F("*")); + display_Update(); + } + for (unsigned int x = 0; x < 512; x++) { + // CONSOLE READS ADDRESS 4X + do { + tempbyte = read_flash_byte_LEAP(address + x); + tempcheck = read_flash_byte_LEAP(address + x); + } + while (tempbyte != tempcheck); + sdBuffer[x] = tempbyte; + } + myFile.write(sdBuffer, 512); + } + myFile.flush(); + myFile.close(); + FL_CE_HIGH; + OE_HIGH; + println_Msg(FS(FSTRING_EMPTY)); + printCRC(fileName, NULL, 0); // 512K + } + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +void writeFLASH_LEAP() +{ + fileBrowser(F("Select FLASH File")); + sd.chdir(); + sprintf(filePath, "%s/%s", filePath, fileName); + + display_Clear(); + println_Msg(F("Writing File: ")); + println_Msg(filePath); + println_Msg(fileName); + display_Update(); + + //open file on sd card + if (myFile.open(filePath, O_READ)) { + CE_HIGH; + dataOut_LEAP(); + eraseFLASH_LEAP(); + statusFLASH_LEAP(); + delay(100); // WORKS ~75 OK + for (unsigned long address = 0x0; address < 0x80000; address += 512) { // 512K + myFile.read(sdBuffer, 512); + if ((address % 0x10000) == 0) { + print_Msg(F("*")); + display_Update(); + } + for (unsigned int x = 0; x < 512; x++) { + programFLASH_LEAP(); + write_flash_byte_LEAP(address + x, sdBuffer[x]); + } + } + dataIn_LEAP(); + myFile.close(); + println_Msg(FS(FSTRING_EMPTY)); + println_Msg(F("FLASH FILE WRITTEN!")); + display_Update(); + } + else { + println_Msg(F("SD ERROR")); + display_Update(); + } + sd.chdir(); // root + filePath[0] = '\0'; // Reset filePath +} + +//****************************************** +// EEPROM SAVES +//****************************************** +// EEPROM 24LC02/24LC16 +// 24LC02B = 256 BYTES +// 24LC16B = 2048 BYTES +// DORA THE EXPLORER - WILDLIFE RESCUE 24LC16B +// SCHOLASTIC MATH MISSIONS 24LC16B + +// MODIFIED FOR COMPATIBILITY WITH ISSI EEPROM +// PET PALS ISSI 416A-2GLI = 24C16A + +// START - SDA HIGH TO LOW WHEN SCL HIGH +void eepromStart_LEAP() +{ + SCL_LOW; + SDA_LOW; + SCL_HIGH; + _delay_us(2); + SDA_HIGH; + _delay_us(2); + SDA_LOW; + _delay_us(2); + SCL_LOW; + _delay_us(2); +} + +void eepromSet0_LEAP() +{ + SCL_LOW; + SDA_LOW; + _delay_us(2); + SCL_HIGH; + _delay_us(5); + SCL_LOW; + _delay_us(2); +} + +void eepromSet1_LEAP() +{ + SCL_LOW; + SDA_LOW; + _delay_us(2); + SDA_HIGH; + _delay_us(2); + SCL_HIGH; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; // ~500ns + SCL_LOW; + SDA_LOW; + _delay_us(2); +} + +void eepromDevice_LEAP() +{ + eepromSet1_LEAP(); + eepromSet0_LEAP(); + eepromSet1_LEAP(); + eepromSet0_LEAP(); +} + +void eepromSetDeviceAddress_LEAP(unsigned long addrhi) +{ + for (int i = 0; i < 3; i++) { + if ((addrhi >> 2) & 0x1) // Bit is HIGH + eepromSet1_LEAP(); + else // Bit is LOW + eepromSet0_LEAP(); + addrhi <<= 1; // rotate to the next bit + } +} + +void eepromStatus_LEAP() // ACK +{ + byte eepStatus = 1; + DDRH &= ~(1 << 4); // SDA to INPUT + SCL_LOW; + _delay_us(2); + SCL_HIGH; + _delay_us(5); + SCL_LOW; + do { + eepStatus = ((PINH & 0x10) >> 4); // SDA = PORTH4 + } + while (eepStatus == 1); + DDRH |= (1 << 4); // SDA to OUTPUT +} + +void eepromReadMode_LEAP() +{ + eepromSet1_LEAP(); // READ + eepromStatus_LEAP(); // ACK +} + +void eepromWriteMode_LEAP() +{ + eepromSet0_LEAP(); // WRITE + eepromStatus_LEAP(); // ACK +} + +void eepromReadData_LEAP() +{ + DDRH &= ~(1 << 4); // SDA to INPUT + for (int i = 0; i < 8; i++) { + SCL_LOW; + SDA_LOW; + _delay_us(2); + SCL_HIGH; + eepbit[i] = ((PINH & 0x10) >> 4); // SDA = PORTH4 + if (eepbit[i] == 1) { + SCL_LOW; + _delay_us(4); + } + else { + _delay_us(4); + SCL_LOW; + } + _delay_us(2); + } + DDRH |= (1 << 4); // SDA to OUTPUT +} + +void eepromWriteData_LEAP(byte data) +{ + for (int i = 0; i < 8; i++) { + if ((data >> 7) & 0x1) // Bit is HIGH + eepromSet1_LEAP(); + else // Bit is LOW + eepromSet0_LEAP(); + data <<= 1; // rotate to the next bit + } + eepromStatus_LEAP(); // ACK +} + +// STOP - SDA LOW TO HIGH WHEN SCL HIGH +void eepromStop_LEAP() +{ + SCL_LOW; + SDA_LOW; + _delay_us(1); + SCL_HIGH; + _delay_us(2); + SDA_HIGH; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; + NOP; // ~500nS + SCL_LOW; + SDA_LOW; +} + +void eepromSetAddress_LEAP(word address) +{ + for (int i = 0; i < 8; i++) { + if ((address >> 7) & 0x1) // Bit is HIGH + eepromSet1_LEAP(); + else // Bit is LOW + eepromSet0_LEAP(); + address <<= 1; // rotate to the next bit + } + eepromStatus_LEAP(); // ACK +} + +void readEepromByte_LEAP(word address) +{ + word addrhi = address >> 8; + word addrlo = address & 0xFF; + eepromStart_LEAP(); // START + eepromDevice_LEAP(); // DEVICE [1010] + eepromSetDeviceAddress_LEAP(addrhi); // ADDR [A10..A8] + eepromWriteMode_LEAP(); + eepromSetAddress_LEAP(addrlo); + eepromStart_LEAP(); // START + eepromDevice_LEAP(); // DEVICE [1010] + eepromSetDeviceAddress_LEAP(addrhi); // ADDR [A10..A8] + eepromReadMode_LEAP(); + eepromReadData_LEAP(); + eepromStop_LEAP(); // STOP + // OR 8 bits into byte + eeptemp = eepbit[0] << 7 | eepbit[1] << 6 | eepbit[2] << 5 | eepbit[3] << 4 | eepbit[4] << 3 | eepbit[5] << 2 | eepbit[6] << 1 | eepbit[7]; + sdBuffer[addrlo] = eeptemp; +} + +void writeEepromByte_LEAP(word address) +{ + word addrhi = address >> 8; + word addrlo = address & 0xFF; + eeptemp = sdBuffer[addrlo]; + eepromStart_LEAP(); // START + eepromDevice_LEAP(); // DEVICE [1010] + eepromSetDeviceAddress_LEAP(addrhi); // ADDR [A10-A8] + eepromWriteMode_LEAP(); // WRITE + eepromSetAddress_LEAP(addrlo); + eepromWriteData_LEAP(eeptemp); + eepromStop_LEAP(); // STOP +} + +// Read EEPROM and save to the SD card +void readEEP_LEAP() +{ + createFolderAndOpenFile("LEAP", "SAVE", romName, "eep"); + + if (myFile) { + for (word currByte = 0; currByte < 2048; currByte += 256) { + for (int i = 0; i < 256; i++) { + readEepromByte_LEAP(currByte + i); + } + myFile.write(sdBuffer, 256); + } +// 24LC02 +// for (word currByte = 0; currByte < 256; currByte++) { +// readEepromByte_LEAP(currByte); +// } +// myFile.write(sdBuffer, 256); + myFile.close(); + + printCRC(fileName, NULL, 0); // 2048 + } + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +void writeEEP_LEAP() +{ + fileBrowser(F("Select EEPROM File")); + sd.chdir(); + sprintf(filePath, "%s/%s", filePath, fileName); + + display_Clear(); + println_Msg(F("Writing File: ")); +// println_Msg(filePath); + println_Msg(fileName); + display_Update(); + + //open file on sd card + if (myFile.open(filePath, O_READ)) { + WP_LOW; + for (word currByte = 0; currByte < 2048; currByte += 256) { + myFile.read(sdBuffer, 256); + for (int i = 0; i < 256; i++) { + writeEepromByte_LEAP(currByte + i); + delay(50); + } + print_Msg(F("*")); + display_Update(); + } + WP_HIGH; + myFile.close(); + println_Msg(FS(FSTRING_EMPTY)); + println_Msg(F("EEPROM FILE WRITTEN!")); + display_Update(); + } + else { + println_Msg(F("SD ERROR")); + display_Update(); + } + sd.chdir(); // root + filePath[0] = '\0'; // Reset filePath +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +void setCart_LEAP() +{ + //go to root + sd.chdir(); + + byte gameSize; + + // Select starting letter + //byte myLetter = starting_letter(); + + // Open database + if (myFile.open("leapster.txt", O_READ)) { + // seek_first_letter_in_database(myFile, myLetter); + + if(checkCartSelection(myFile, &readDataLineSingleDigit, &gameSize)) { + EEPROM_writeAnything(8, gameSize); + } + } else { + print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND)); + } +} +#endif diff --git a/Cart_Reader/LJ.ino b/Cart_Reader/LJ.ino new file mode 100644 index 0000000..8060bc7 --- /dev/null +++ b/Cart_Reader/LJ.ino @@ -0,0 +1,356 @@ +//****************************************** +// LITTLE JAMMER MODULE +//****************************************** +#ifdef ENABLE_LJ +// Little Jammer +// Cartridge Pinout +// 48P 1.25mm pitch connector +// +// FORM FACTOR IS SAME AS BANDAI WONDERSWAN/BENESSE POCKET CHALLENGE V2/LITTLE JAMMER PRO +// WIRING IS COMPLETELY DIFFERENT! +// +// LEFT SIDE +// 1 VSS (GND) +// 2 A-1 +// 3 A0 +// 4 A1 +// 5 A2 +// 6 A3 +// 7 A4 +// 8 A5 +// 9 A6 +// 10 A7 +// 11 A8 +// 12 A9 +// 13 A10 +// 14 A11 +// 15 A12 +// 16 A13 +// 17 A14 +// 18 A15 +// 19 A16 +// 20 A17 +// 21 A18 +// 22 A19 +// 23 A20 +// 24 VCC (+5V) +// 25 VCC (+5V) +// 26 D0 +// 27 D1 +// 28 D2 +// 29 D3 +// 30 D4 +// 31 D5 +// 32 D6 +// 33 D7 +// 34 /WE +// 35 /RESET +// 36 /CE +// 37 /OE +// 38 VSS (GND) +// 39 NC +// 40 NC +// 41 NC +// 42 NC +// 43 NC +// 44 NC +// 45 NC +// 46 NC +// 47 VSS (GND) +// 48 VSS (GND) +// RIGHT SIDE + +// CONTROL PINS: +// /RESET(PH0) - SNES RESET +// /CE(PH3) - SNES /CS +// /WE(PH5) - SNES /WR +// /OE(PH6) - SNES /RD + +// LITTLE JAMMER DIRECT ADDRESSING +// 1 1111 1111 1111 1111 1111 +// 1 F F F F F = 0x1FFFFF +// Size = 0x200000 = 2MB +// +// A20 connection on Pin 23 = 0x400000 = 4MB +// 11 1111 1111 1111 1111 1111 +// 3 F F F F F = 0x3FFFFF +// Size = 0x400000 = 4MB + +//****************************************** +// VARIABLES +//****************************************** +byte LJ[] = {1,2,4}; +byte ljlo = 0; // Lowest Entry +byte ljhi = 2; // Highest Entry +byte ljsize; +byte newljsize; + +boolean ljflashfound = false; +byte ljbytecheck; + +// EEPROM MAPPING +// 08 ROM SIZE + +//****************************************** +// MENU +//****************************************** +// Base Menu +static const char* const menuOptionsLJ[] PROGMEM = { FSTRING_SELECT_CART, FSTRING_READ_ROM, FSTRING_SET_SIZE, FSTRING_RESET }; + +void ljMenu() +{ + convertPgm(menuOptionsLJ, 4); + uint8_t mainMenu = question_box(F("LITTLE JAMMER MENU"), menuOptions, 4, 0); + + switch (mainMenu) + { + // Select Cart + case 0: + setCart_LJ(); + setup_LJ(); + break; + + // Read ROM + case 1: + sd.chdir("/"); + readROM_LJ(); + sd.chdir("/"); + break; + + // Set Size + case 2: + setROMSize_LJ(); + break; + + // Reset + case 3: + resetArduino(); + break; + } +} + +//****************************************** +// SETUP +//****************************************** + +void setup_LJ() +{ + // Request 5V + setVoltage(VOLTS_SET_5V); + + // Set Address Pins to Output + // LITTLE JAMMER uses A(-1)-A19 wired to A0-A20 [A21-A23 UNUSED] + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output + // /RST(PH0) ---(PH1) /CE(PH3) ---(PH4) /WR(PH5) /OE(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 + // /RST(PH0) ---(PH1) /CE(PH3) ---(PH4) /WR(PH5) /OE(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; + PORTJ |= (1 << 0); // TIME(PJ0) + + checkStatus_LJ(); + strcpy(romName, "LJ"); + + mode = CORE_LJ; +} + +//****************************************** +// READ FUNCTIONS +//****************************************** + +uint8_t readData_LJ(uint32_t addr) +{ + PORTF = addr & 0xFF; // A(-1)-A6 + PORTK = (addr >> 8) & 0xFF; // A7-A14 + PORTL = (addr >> 16) & 0xFF; // A15-A20 + NOP; + NOP; + + // switch /CE(PH3) to LOW + PORTH &= ~(1 << 3); + // switch /OE(PH6) to LOW + PORTH &= ~(1 << 6); + NOP; + NOP; + + uint8_t ret = PINC; + + // switch /CE(PH3) and /OE(PH6) to HIGH + PORTH |= (1 << 3) | (1 << 6); + + return ret; +} + +void readSegment_LJ(uint32_t startaddr, uint32_t endaddr) +{ + for (uint32_t addr = startaddr; addr < endaddr; addr += 512) { + for (int w = 0; w < 512; w++) { + uint8_t temp = readData_LJ(addr + w); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + } +} + +//****************************************** +// READ ROM +//****************************************** + +void readROM_LJ() +{ + createFolderAndOpenFile("LJ", "ROM", romName, "bin"); + + // Maximum Direct Address Size is 4MB + readSegment_LJ(0x000000,0x100000); // 1MB + if (ljsize > 0) // 2MB/4MB + { + readSegment_LJ(0x100000,0x200000); // +1MB = 2MB + if (ljsize > 1) // 4MB + { + readSegment_LJ(0x200000,0x400000); // +2MB = 4MB + } + } + myFile.close(); + + printCRC(fileName, NULL, 0); + + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) +void printRomSize_LJ(int index) +{ + display_Clear(); + print_Msg(FS(FSTRING_ROM_SIZE)); + println_Msg(LJ[index]); +} +#endif + +void setROMSize_LJ() +{ + byte newljsize; +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + if (ljlo == ljhi) + newljsize = ljlo; + else { + newljsize = navigateMenu(ljlo, ljhi, &printRomSize_LJ); + + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(LJ[newljsize]); + println_Msg(F("KB")); + display_Update(); + delay(1000); +#else + if (ljlo == ljhi) + newljsize = ljlo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (ljhi - ljlo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(LJ[i + ljlo]); + Serial.println(F("KB")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newljsize = sizeROM.toInt() + ljlo; + if (newljsize > ljhi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(FS(FSTRING_EMPTY)); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(LJ[newljsize]); + Serial.println(F("KB")); +#endif + EEPROM_writeAnything(8, newljsize); + ljsize = newljsize; +} + +void checkStatus_LJ() +{ + EEPROM_readAnything(8, ljsize); + if (ljsize > ljhi) { + ljsize = 1; // default 2M + EEPROM_writeAnything(8, ljsize); + } + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + println_Msg(F("LITTLE JAMMER")); + println_Msg(FS(FSTRING_CURRENT_SETTINGS)); + println_Msg(FS(FSTRING_EMPTY)); + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(LJ[ljsize]); + println_Msg(F("MB")); + display_Update(); + wait(); +#else + Serial.print(FS(FSTRING_ROM_SIZE)); + Serial.print(LJ[ljsize]); + Serial.println(F("MB")); + Serial.println(FS(FSTRING_EMPTY)); +#endif +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +void setCart_LJ() +{ + //go to root + sd.chdir(); + + byte gameSize; + + // Select starting letter + //byte myLetter = starting_letter(); + + // Open database + if (myFile.open("ljcart.txt", O_READ)) { + // seek_first_letter_in_database(myFile, myLetter); + + if(checkCartSelection(myFile, &readDataLineSingleDigit, &gameSize)) { + EEPROM_writeAnything(8, gameSize); + } + } else { + print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND)); + } +} +#endif diff --git a/Cart_Reader/LJPRO.ino b/Cart_Reader/LJPRO.ino new file mode 100644 index 0000000..6f9c2da --- /dev/null +++ b/Cart_Reader/LJPRO.ino @@ -0,0 +1,554 @@ +//****************************************** +// LITTLE JAMMER PRO MODULE +//****************************************** +#ifdef ENABLE_LJPRO +// Little Jammer Pro +// Cartridge Pinout +// 48P 1.25mm pitch connector +// +// FORM FACTOR IS SAME AS BANDAI WONDERSWAN/BENESSE POCKET CHALLENGE V2/LITTLE JAMMER +// WIRING IS COMPLETELY DIFFERENT! +// +// LEFT SIDE +// 1 GND +// 2 GND +// 3 S1 (GND) +// 4 S2 (GND) +// 5 U1_WP#/ACC +// 6 U1_SCLK +// 7 U1_SCLK +// 8 U1_SI +// 9 U1_SI +// 10 U1_SO/PO7 +// 11 U1_SO/PO7 +// 12 U1_PO6 +// 13 U1_PO5 +// 14 U1_PO4 +// 15 U1_PO3 +// 16 U1_PO2 +// 17 U1_PO1 +// 18 U1_PO0 +// 19 U1_CS# +// 20 U1_CS# +// 21 U1_HOLD# +// 22 U1_HOLD# +// 23 VCC (+3V) +// 24 VCC (+3V) +// 25 VCC (+3V) +// 26 VCC (+3V) +// 27 U2_SCLK +// 28 U2_SCLK +// 29 U2_SI +// 30 U2_SI +// 31 U2_SO/PO7 +// 32 U2_SO/PO7 +// 33 U2_PO6 +// 34 U2_PO5 +// 35 U2_PO4 +// 36 U2_PO3 +// 37 U2_PO2 +// 38 U2_PO1 +// 39 U2_PO0 +// 40 U2_CS# +// 41 U2_CS# +// 42 U2_HOLD# +// 43 U2_HOLD# +// 44 U2_WP#/ACC +// 45 S3 (GND) +// 46 S4 (GND) +// 47 GND +// 48 GND +// RIGHT SIDE + +// CONTROL PINS: +// U1_HOLD# (PH4) - SNES /IRQ +// U1_CS# (PK0) - SNES A8 +// U1_SI (PK1) - SNES A9 +// U1_WP#/ACC (PK2) - SNES A10 +// +// U2_HOLD# (PH0) - SNES RESET +// U2_SI (PH3) - SNES /CS +// U2_WP#/ACC (PH5) - SNES /WR +// U2_CS# (PH6) - SNES /RD +// +// S1 (PK4) - SNES A12 +// S2 (PK5) - SNES A13 +// S3 (PK6) - SNES A14 +// S4 (PK7) - SNES A15 + +// COMBINE U1_SCLK + U2_SCLK INTO SINGLE SCLK +// SCLK(PH1) - SNES CPUCLK + +// DATA PINS: +// U1 D0-D7 (PORTF) +// U2 D0-D7 (PORTC) + +// NOTES: +// HOLD# NOT USED FOR PARALLEL MODE - PULLED UP TO VCC ON CARTS +// WP#/ACC PULLED DOWN TO GND ON CARTS + +//****************************************** +// DEFINES +//****************************************** +#define CS1_LOW PORTK &= ~(1 << 0) +#define CS1_HIGH PORTK |= (1 << 0) +#define CS2_LOW PORTH &= ~(1 << 6) +#define CS2_HIGH PORTH |= (1 << 6) + +//****************************************** +// VARIABLES +//****************************************** +byte LJPRO[] = {2,4,6,8}; +byte ljprolo = 0; // Lowest Entry +byte ljprohi = 3; // Highest Entry +byte ljprosize; +byte newljprosize; + +char mnfID[3]; +char deviceID[5]; +boolean ljproflash1found = false; +boolean ljproflash2found = false; +byte ljproflash1size; +byte ljproflash2size; + +// EEPROM MAPPING +// 08 ROM SIZE + +//****************************************** +// MENU +//****************************************** +// Base Menu +static const char* const menuOptionsLJPRO[] PROGMEM = { FSTRING_READ_ROM, FSTRING_SET_SIZE, FSTRING_RESET }; + +// U1_HOLD#(PH4) - SNES /IRQ +// U1_CS# - SNES A8 +// U1_WP#/ACC - SNES A9 +// U1_SI - SNES A10 +// +// U2_HOLD#(PH0) - SNES RESET +// U2_SI(PH3) - SNES /CS +// U2_WP#/ACC(PH5) - SNES /WR +// U2_CS#(PH6) - SNES /RD + +void ljproMenu() +{ + convertPgm(menuOptionsLJPRO, 3); + uint8_t mainMenu = question_box(F("LITTLE JAMMER PRO"), menuOptions, 3, 0); + + switch (mainMenu) + { + case 0: + // Read ROM + sd.chdir("/"); + readROM_LJPRO(); + sd.chdir("/"); + break; + + case 1: + // Set Size + setROMSize_LJPRO(); + break; + + case 2: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// SETUP +//****************************************** + +void setup_LJPRO() +{ + // Request 3.3V + setVoltage(VOLTS_SET_3V3); + + // LITTLE JAMMER PRO uses Serial Flash + // Set Data Pins to Input + DDRF = 0x00; // U1 Data + DDRC = 0x00; // U2 Data + // Set Unused Address Pins to Output + DDRL = 0xFF; + + // Set Control Pins to Output + // U2_HLD(PH0) SCLK(PH1) U2_SI(PH3) U1_HLD(PH4) U2_WP(PH5) U2_CS(PH6) + DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + // U1_CS(PK0) U1_SI(PK1) U1_WP(PK2) -------- + DDRK |= (1 << 0) | (1 << 1) | (1 <<2) | (1 << 3); + + // FLASH Configuration Pins to Input + // S1(PK4) S2(PK5) S3(PK6) S4(PK7) + DDRK &= ~((1 << 4) | (1 << 5) | (1 << 6) | (1 << 7)); + + // Set TIME(PJ0) to Output (UNUSED) + DDRJ |= (1 << 0); + + // Setting Control Pins to HIGH + // U2_HLD(PH0) SCLK(PH1) U2_SI(PH3) U1_HLD(PH4) U2_WP(PH5) U2_CS(PH6) + PORTH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); + // U1_CS(PK0) U1_SI(PK1) U1_WP(PK2) -------- S1(PK4) S2(PK5) S3(PK6) S4(PK7) + PORTK |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7); + + // Set Unused Data Pins (PA0-PA7) to Output + DDRA = 0xFF; + + // Set Unused Pins HIGH + PORTA = 0xFF; + PORTJ |= (1 << 0); // TIME(PJ0) + + checkStatus_LJPRO(); + strcpy(romName, "LJPRO"); + + mode = CORE_LJPRO; +} + +//****************************************** +// SERIAL MODE +//****************************************** +// 25L1605/25L3205 +// Default Serial Mode + +void sendSerial_U1(uint8_t data) +{ + for (int i = 0; i < 8; i++) { + PORTH &= ~(1 << 1); // SCLK LOW + if ((data >> 7) & 0x1) { // Bit is HIGH + PORTK |= (1 << 1); // U1_SI HIGH; + } + else { + PORTK &= ~(1 << 1); // U1_SI LOW; + } + PORTH |= (1 << 1); // SCLK HIGH + // rotate to the next bit + data <<= 1; + } +} + +void sendSerial_U2(uint8_t data) +{ + for (int i = 0; i < 8; i++) { + PORTH &= ~(1 << 1); // SCLK LOW + if ((data >> 7) & 0x1) { // Bit is HIGH + PORTH |= (1 << 3); // U2_SI HIGH; + } + else { + PORTH &= ~(1 << 3); // U2_SI LOW; + } + PORTH |= (1 << 1); // SCLK HIGH + // rotate to the next bit + data <<= 1; + } +} + +uint8_t readSerial_U1() +{ + bool serBits[9]; + for (byte i = 0; i < 8; i++) { + pulseClock_LJPRO(1); + serBits[i] = (PINF >> 7) & 0x1; + } + byte tempdata = serBits[0] << 7 | serBits[1] << 6 | serBits[2] << 5 | serBits[3] << 4 | serBits[4] << 3 | serBits[5] << 2 | serBits[6] << 1 | serBits[7]; + + return tempdata; +} + +uint8_t readSerial_U2() +{ + bool serBits[9]; + for (byte i = 0; i < 8; i++) { + pulseClock_LJPRO(1); + serBits[i] = (PINC >> 7) & 0x1; + } + byte tempdata = serBits[0] << 7 | serBits[1] << 6 | serBits[2] << 5 | serBits[3] << 4 | serBits[4] << 3 | serBits[5] << 2 | serBits[6] << 1 | serBits[7]; + + return tempdata; +} + +//****************************************** +// PARALLEL MODE +//****************************************** +// 25L1605/25L3205 +// Parallel Mode - Command 0x55 +// SCLK Frequency 1.2MHz (Cycle 833.33ns) +// READ 0x03 +// WRITE 0x02 + +void pulseClock_LJPRO(unsigned int times) +{ + for (unsigned int i = 0; i < (times * 2); i++) { + // Switch the clock pin to 0 if it's 1 and 0 if it's 1 + PORTH ^= (1 << 1); + // without the delay the clock pulse would be 1.5us and 666kHz + //__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t")); + } +} + +// Send one byte of data to Serial FLASH [Parallel Mode] +void sendData_U1(byte data) +{ + DDRF = 0xFF; // U1 Data Output + PORTF = data; + pulseClock_LJPRO(8); + DDRF = 0x00; // U1 Data Input +} + +void sendData_U2(byte data) +{ + DDRC = 0xFF; // U2 Data Output + PORTC = data; + pulseClock_LJPRO(8); + DDRC = 0x00; // U2 Data Input +} + +void readData_U1(uint32_t startaddr, uint32_t endaddr) +{ + for (uint32_t addr = startaddr; addr < endaddr; addr += 512) { + for (int x = 0; x < 512; x++) { + pulseClock_LJPRO(1); + sdBuffer[x] = PINF; + } + myFile.write(sdBuffer, 512); + } +} + +void readData_U2(uint32_t startaddr, uint32_t endaddr) +{ + for (uint32_t addr = startaddr; addr < endaddr; addr += 512) { + for (int x = 0; x < 512; x++) { + pulseClock_LJPRO(1); + sdBuffer[x] = PINC; + } + myFile.write(sdBuffer, 512); + } +} + +// RDID +// Manufacturer 0xC2 +// Memory Density 0x20 +// Device ID 0x15 [25L1605]/0x16 [25L3205] + +// REMS +// Manufacturer 0xC2 +// Device ID 0x14 [25L1605]/0x15 [25L3205] + +void readID_U1() // Parallel Mode +{ + CS1_LOW; // U1 LOW + sendSerial_U1(0x9F); // RDID Command + pulseClock_LJPRO(1); + byte id0 = PINF; // 0xC2 + pulseClock_LJPRO(1); + byte id1 = PINF; // 0x20 + pulseClock_LJPRO(1); + byte id2 = PINF; // 0x15 [MX25L1605]/0x16 [MX25L3205] + CS1_HIGH; // U1 HIGH + // Flash ID + sprintf(mnfID, "%02X", id0); + sprintf(deviceID, "%02X%02X", id1, id2); +// println_Msg(mnfID); +// println_Msg(deviceID); +// display_Update(); + if(strcmp(deviceID, "2015") == 0) { // MX25L1605 + ljproflash1found = 1; + ljproflash1size = 2; + display_Clear(); + println_Msg(F("U1 MX25L1605 FOUND")); + display_Update(); + } + else if (strcmp(deviceID, "2016") == 0) { // MX25L3205 + ljproflash1found = 1; + ljproflash1size = 4; + display_Clear(); + println_Msg(F("U1 MX25L3205 FOUND")); + display_Update(); + } +} + +void readID_U2() // Parallel Mode +{ + CS2_LOW; // U2 LOW + sendSerial_U2(0x9F); // RDID Command + pulseClock_LJPRO(1); + byte id0 = PINC; // 0xC2 + pulseClock_LJPRO(1); + byte id1 = PINC; // 0x20 + pulseClock_LJPRO(1); + byte id2 = PINC; // 0x15 [MX25L1605]/0x16 [MX25L3205] + pulseClock_LJPRO(1); + CS2_HIGH; // U2 HIGH + // Flash ID + sprintf(mnfID, "%02X", id0); + sprintf(deviceID, "%02X%02X", id1, id2); +// println_Msg(mnfID); +// println_Msg(deviceID); +// display_Update(); + if(strcmp(deviceID, "2015") == 0) { // MX25L1605 + ljproflash2found = 1; + ljproflash2size = 2; + println_Msg(F("U2 MX25L1605 FOUND")); + display_Update(); + } + else if (strcmp(deviceID, "2016") == 0) { // MX25L3205 + ljproflash2found = 1; + ljproflash2size = 4; + println_Msg(F("U2 MX25L3205 FOUND")); + display_Update(); + } +} + +//****************************************** +// READ ROM +//****************************************** + +void readROM_LJPRO() +{ + createFolderAndOpenFile("LJPRO", "ROM", romName, "bin"); + + // Little Jammer Pro PCB B1043-02A + // Footprints for two 25L1605/25L3205 chips + // Test carts only have one 25L1605 (2MB) installed + // PCB could possibly install two 25L3205 chips (2x4MB = 8MB) + + // Set U1 FLASH to Parallel Mode + CS1_LOW; // U1 LOW + sendSerial_U1(0x55); // Parallel Mode + CS1_HIGH; // U1 HIGH + // Read ID + readID_U1(); + // Set U2 FLASH to Parallel Mode + CS2_LOW; // U2 LOW + sendSerial_U2(0x55); // Parallel Mode + CS2_HIGH; // U2 HIGH + // Read ID + readID_U2(); + + // Read U1 + println_Msg(F("Reading U1...")); + display_Update(); + CS1_LOW; // U1 LOW + DDRF = 0x00; // U1 Data Input + sendSerial_U1(0x03); // Read Array (Parallel) + sendSerial_U1(0x00); // Address A23-A16 + sendSerial_U1(0x00); // Address A15-A8 + sendSerial_U1(0x00); // Address A7-A0 + readData_U1(0x000000, 0x200000); + if (ljproflash1size == 4) { // 4MB + readData_U1(0x200000, 0x400000); + } + CS1_HIGH; // U1 HIGH + if (ljproflash2found) { + // Read U2 + println_Msg(F("Reading U2...")); + display_Update(); + CS2_LOW; // U2 LOW + DDRC = 0x00; // U2 Data Input + sendSerial_U2(0x03); // Read Array (Parallel) + sendSerial_U2(0x00); // Address A23-A16 + sendSerial_U2(0x00); // Address A15-A8 + sendSerial_U2(0x00); // Address A7-A0 + readData_U2(0x000000, 0x200000); + if (ljproflash2size == 4) { // 4MB + readData_U2(0x200000, 0x400000); + } + CS2_HIGH; // U2 HIGH + } + myFile.close(); + + printCRC(fileName, NULL, 0); + + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) +void printRomSize_LJPRO(int index) +{ + display_Clear(); + print_Msg(FS(FSTRING_ROM_SIZE)); + println_Msg(LJPRO[index]); +} +#endif + +void setROMSize_LJPRO() +{ + byte newljprosize; +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + if (ljprolo == ljprohi) + newljprosize = ljprolo; + else { + newljprosize = navigateMenu(ljprolo, ljprohi, &printRomSize_LJPRO); + + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(LJPRO[newljprosize]); + println_Msg(F("KB")); + display_Update(); + delay(1000); +#else + if (ljprolo == ljprohi) + newljprosize = ljprolo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (ljprohi - ljprolo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(LJPRO[i + ljprolo]); + Serial.println(F("KB")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newljprosize = sizeROM.toInt() + ljprolo; + if (newljprosize > ljprohi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(FS(FSTRING_EMPTY)); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(LJPRO[newljprosize]); + Serial.println(F("KB")); +#endif + EEPROM_writeAnything(8, newljprosize); + ljprosize = newljprosize; +} + +void checkStatus_LJPRO() +{ + EEPROM_readAnything(8, ljprosize); + if (ljprosize > ljprohi) { + ljprosize = 0; // default 2M + EEPROM_writeAnything(8, ljprosize); + } + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + println_Msg(F("LITTLE JAMMER PRO")); + println_Msg(FS(FSTRING_CURRENT_SETTINGS)); + println_Msg(FS(FSTRING_EMPTY)); + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(LJPRO[ljprosize]); + println_Msg(F("MB")); + display_Update(); + wait(); +#else + Serial.print(FS(FSTRING_ROM_SIZE)); + Serial.print(LJPRO[ljprosize]); + Serial.println(F("MB")); + Serial.println(FS(FSTRING_EMPTY)); +#endif +} +#endif diff --git a/Cart_Reader/OSCR.cpp b/Cart_Reader/OSCR.cpp index 81963c5..dbbbd4c 100644 --- a/Cart_Reader/OSCR.cpp +++ b/Cart_Reader/OSCR.cpp @@ -49,7 +49,7 @@ * String Constants **/ // Firmware Version -constexpr char PROGMEM FSTRING_VERSION[] = "V13.5"; +constexpr char PROGMEM FSTRING_VERSION[] = "V14.0"; // Universal constexpr char PROGMEM FSTRING_RESET[] = "Reset"; diff --git a/Cart_Reader/OSCR.h b/Cart_Reader/OSCR.h index 2546352..a6fdf9d 100644 --- a/Cart_Reader/OSCR.h +++ b/Cart_Reader/OSCR.h @@ -227,6 +227,42 @@ enum CORES: uint8_t { # endif # ifdef ENABLE_GPC CORE_GPC, +# endif +# ifdef ENABLE_ATARI8 + CORE_ATARI8, +# endif +# ifdef ENABLE_BALLY + CORE_BALLY, +# endif +# ifdef ENABLE_LJ + CORE_LJ, +# endif +# ifdef ENABLE_LJPRO + CORE_LJPRO, +# endif +# ifdef ENABLE_PV1000 + CORE_PV1000, +# endif +# ifdef ENABLE_VIC20 + CORE_VIC20, +# endif +# ifdef ENABLE_LEAP + CORE_LEAP, +# endif +# ifdef ENABLE_RCA + CORE_RCA, +# endif +# ifdef ENABLE_TI99 + CORE_TI99, +# endif +# ifdef ENABLE_PYUUTA + CORE_PYUUTA, +# endif +# ifdef ENABLE_TRS80 + CORE_TRS80, +# endif +# ifdef ENABLE_VSMILE + CORE_VSMILE, # endif CORE_MAX // Always last }; @@ -313,6 +349,42 @@ enum SYSTEM_MENU: uint8_t { # if defined(ENABLE_VECTREX) SYSTEM_MENU_VECTREX, # endif +# if defined(ENABLE_ATARI8) + SYSTEM_MENU_ATARI8, +# endif +# if defined(ENABLE_BALLY) + SYSTEM_MENU_BALLY, +# endif +# if defined(ENABLE_LJ) + SYSTEM_MENU_LJ, +# endif +# if defined(ENABLE_LJPRO) + SYSTEM_MENU_LJPRO, +# endif +# if defined(ENABLE_PV1000) + SYSTEM_MENU_PV1000, +# endif +# if defined(ENABLE_VIC20) + SYSTEM_MENU_VIC20, +# endif +# if defined(ENABLE_LEAP) + SYSTEM_MENU_LEAP, +# endif +# if defined(ENABLE_RCA) + SYSTEM_MENU_RCA, +# endif +# if defined(ENABLE_TI99) + SYSTEM_MENU_TI99, +# endif +# if defined(ENABLE_PYUUTA) + SYSTEM_MENU_PYUUTA, +# endif +# if defined(ENABLE_TRS80) + SYSTEM_MENU_TRS80, +# endif +# if defined(ENABLE_VSMILE) + SYSTEM_MENU_VSMILE, +# endif # if defined(ENABLE_FLASH) SYSTEM_MENU_FLASH, # endif @@ -391,4 +463,4 @@ extern long configGetLong(const __FlashStringHelper* key, int onFail = 0); #include "ClockedSerial.h" -#endif /* OSCR_H_ */ +#endif /* OSCR_H_ */ \ No newline at end of file diff --git a/Cart_Reader/PV1000.ino b/Cart_Reader/PV1000.ino new file mode 100644 index 0000000..ce98bd5 --- /dev/null +++ b/Cart_Reader/PV1000.ino @@ -0,0 +1,324 @@ +//****************************************** +// CASIO PV-1000/PV-2000 MODULE +//****************************************** +#ifdef ENABLE_PV1000 +// Casio PV-1000/PV-2000 +// Cartridge Pinout +// 36P 2.54mm pitch connector +// +// FRONT BACK +// SIDE SIDE +// +---------+ +// VCC -| B01 A01 |- GND +// NC -| B02 A02 |- NC +// A14 -| B03 A03 |- A15 +// A12 -| B04 A04 |- A13 +// A10 -| B05 A05 |- A11 +// A8 -| B06 A06 |- A9 +// A6 -| B07 A07 |- A7 +// A4 -| B08 A08 |- A5 +// A2 -| B09 A09 |- A3 +// A0 -| B10 A10 |- A1 +// D6 -| B11 A11 |- D7 +// D4 -| B12 A12 |- D5 +// D2 -| B13 A13 |- D3 +// D0 -| B14 A14 |- D1 +// /CS1 -| B15 A15 |- /CS2 +// /WR -| B16 A16 |- /RD +// CON -| B17 A17 |- /IORQ +// VCC -| B18 A18 |- GND +// +---------+ +// +// BACK +// GND NC A15 A13 A11 A9 A7 A5 A3 A1 D7 D5 D3 D1 /CS2 /RD /IO GND +// +-------------------------------------------------------------------------+ +// | A01 A02 A03 A04 A05 A06 A07 A08 A09 A10 A11 A12 A13 A14 A15 A16 A17 A18 | +// LEFT | | RIGHT +// | B01 B02 B03 B04 B05 B06 B07 B08 B09 B10 B11 B12 B13 B14 B15 B16 B17 B18 | +// +-------------------------------------------------------------------------+ +// VCC NC A14 A12 A10 A8 A6 A4 A2 A0 D6 D4 D2 D0 /CS1 /WR CON VCC +// FRONT + +// CONTROL PINS: +// /CS2(PH3) - SNES /CS +// /CS1(PH4) - SNES /IRQ +// /WR(PH5) - SNES /WR +// /RD(PH6) - SNES /RD + +//****************************************** +// VARIABLES +//****************************************** +byte PV1000[] = {8,16}; +byte pv1000lo = 0; // Lowest Entry +byte pv1000hi = 1; // Highest Entry +byte pv1000size; +byte newpv1000size; + +// EEPROM MAPPING +// 08 ROM SIZE + +//****************************************** +// MENU +//****************************************** +// Base Menu +static const char* const menuOptionsPV1000[] PROGMEM = { FSTRING_SELECT_CART, FSTRING_READ_ROM, FSTRING_SET_SIZE, FSTRING_RESET }; + +void pv1000Menu() +{ + convertPgm(menuOptionsPV1000, 4); + uint8_t mainMenu = question_box(F("PV-1000 MENU"), menuOptions, 4, 0); + + switch (mainMenu) + { + case 0: + // Select Cart + setCart_PV1000(); + setup_PV1000(); + break; + + case 1: + // Read ROM + sd.chdir("/"); + readROM_PV1000(); + sd.chdir("/"); + break; + + case 2: + // Set Size + setROMSize_PV1000(); + break; + + case 3: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// SETUP +//****************************************** + +void setup_PV1000() +{ + // Request 5V + setVoltage(VOLTS_SET_5V); + + // Set Address Pins to Output + // PV-1000 uses A0-A15 [A16-A23 UNUSED] + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output + // ---(PH0) ---(PH1) /CS2(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 + // ---(PH0) ---(PH1) /CS2(PH3) /CS1(PH4) /WR(PH5) /RD(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_PV1000(); + strcpy(romName, "PV1000"); + + mode = CORE_PV1000; +} + +//****************************************** +// READ FUNCTIONS +//****************************************** + +uint8_t readData_PV1000(uint16_t addr) +{ + PORTF = addr & 0xFF; // A0-A7 + PORTK = (addr >> 8) & 0xFF; // A8-A13 + NOP; + NOP; + NOP; + NOP; + NOP; + + // Set /RD to LOW + PORTH &= ~(1 << 6); // /RD LOW (ENABLE) + NOP; + NOP; + NOP; + + uint8_t ret = PINC; + + // Pull /RD to HIGH + PORTH |= (1 << 6); // /RD HIGH (DISABLE) + + return ret; +} + +void readSegment_PV1000(uint32_t startaddr, uint32_t endaddr) +{ + for (uint32_t addr = startaddr; addr < endaddr; addr += 512) { + for (int w = 0; w < 512; w++) { + uint8_t temp = readData_PV1000(addr + w); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + } +} + +//****************************************** +// READ ROM +//****************************************** + +void readROM_PV1000() +{ + createFolderAndOpenFile("PV1000", "ROM", romName, "bin"); + + if (pv1000size == 0) { // 8K + PORTH &= ~(1 << 4); // /CS1(PH4) LOW + readSegment_PV1000(0x0000,0x2000); // 8K + PORTH |= (1 << 4); // /CS1(PH4) HIGH + } + else { // 16K + PORTH &= ~(1 << 3); // /CS2(PH3) LOW + readSegment_PV1000(0x0000,0x4000); // 16K + PORTH |= (1 << 3); // /CS2(PH3) HIGH + } + myFile.close(); + + printCRC(fileName, NULL, 0); + + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) +void printRomSize_PV1000(int index) +{ + display_Clear(); + print_Msg(FS(FSTRING_ROM_SIZE)); + println_Msg(PV1000[index]); +} +#endif + +void setROMSize_PV1000() +{ + byte newpv1000size; +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + if (pv1000lo == pv1000hi) + newpv1000size = pv1000lo; + else { + newpv1000size = navigateMenu(pv1000lo, pv1000hi, &printRomSize_PV1000); + + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(PV1000[newpv1000size]); + println_Msg(F("KB")); + display_Update(); + delay(1000); +#else + if (pv1000lo == pv1000hi) + newpv1000size = pv1000lo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (pv1000hi - pv1000lo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(PV1000[i + pv1000lo]); + Serial.println(F("KB")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newpv1000size = sizeROM.toInt() + pv1000lo; + if (newpv1000size > pv1000hi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(FS(FSTRING_EMPTY)); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(PV1000[newpv1000size]); + Serial.println(F("KB")); +#endif + EEPROM_writeAnything(8, newpv1000size); + pv1000size = newpv1000size; +} + +void checkStatus_PV1000() +{ + EEPROM_readAnything(8, pv1000size); + if (pv1000size > pv1000hi) { + pv1000size = 0; // default 8K + EEPROM_writeAnything(8, pv1000size); + } + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + println_Msg(F("CASIO PV-1000")); + println_Msg(FS(FSTRING_CURRENT_SETTINGS)); + println_Msg(FS(FSTRING_EMPTY)); + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(PV1000[pv1000size]); + println_Msg(F("KB")); + display_Update(); + wait(); +#else + Serial.print(FS(FSTRING_ROM_SIZE)); + Serial.print(PV1000[pv1000size]); + Serial.println(F("KB")); + Serial.println(FS(FSTRING_EMPTY)); +#endif +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +void setCart_PV1000() +{ + //go to root + sd.chdir(); + + byte gameSize; + + // Select starting letter + //byte myLetter = starting_letter(); + + // Open database + if (myFile.open("pv1000cart.txt", O_READ)) { + // seek_first_letter_in_database(myFile, myLetter); + + if(checkCartSelection(myFile, &readDataLineSingleDigit, &gameSize)) { + EEPROM_writeAnything(8, gameSize); + } + } else { + print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND)); + } +} +#endif diff --git a/Cart_Reader/PYUUTA.ino b/Cart_Reader/PYUUTA.ino new file mode 100644 index 0000000..4801bb5 --- /dev/null +++ b/Cart_Reader/PYUUTA.ino @@ -0,0 +1,327 @@ +//****************************************** +// TOMY PYUUTA MODULE +//****************************************** +#ifdef ENABLE_PYUUTA +// Tomy Pyuuta +// Cartridge Pinout +// 36P 2.54mm pitch connector +// +// FRONT BACK +// SIDE SIDE +// +--------+ +// GND -| 2 1 |- GND +// /RESET -| 4 3 |- D7 +// J1-6 -| 6 5 |- D6 +// A15/CRUOUT -| 8 7 |- D5 +// A13 -| 10 9 |- D4 +// A12 -| 12 11 |- D3 +// A11 -| 14 13 |- D2 +// A10 -| 16 15 |- D1 +// A9 -| 18 17 |- D0 +// A8 -| 20 19 |- VCC +// A7 -| 22 21 |- /CS1 +// A3 -| 24 23 |- A14 +// A6 -| 26 25 |- A2 +// A5 -| 28 27 |- A1 +// A4 -| 30 29 |- /DBIN +// /WE/CPUCLK -| 32 31 |- A0 +// /INT4 /EC -| 34 33 |- SOUND +// CRUIN -| 36 35 |- /CS0 +// +--------+ +// +// BACK +// /CS0 SND A0 /DB A1 A2 A14 /CS1 VCC D0 D1 D2 D3 D4 D5 D6 D7 GND +// +----------------------------------------------------------------------------+ +// | 35 33 31 29 27 25 23 21 19 17 15 13 11 9 7 5 3 1 | +// LEFT | | RIGHT +// | 36 34 32 30 28 26 24 22 20 18 16 14 12 10 8 6 4 2 | +// +----------------------------------------------------------------------------+ +// CRIN /INT /WE A4 A5 A6 A3 A7 A8 A9 A10 A11 A12 A13 A15 J1-6 /RST GND +// FRONT + +// CONTROL PINS: +// /RESET(PH0) - SNES RESET +// /CS0(PH3) - SNES /CS +// /DBIN(PH4) - SNES /IRQ +// /CS1(PH6) - SNES /RD + +// NOTE: PYUUTA ADDRESS AND DATA BUS ARE BIG-ENDIAN +// LEAST SIGNIFICANT IS BIT 7 AND MOST SIGNIFICANT IS BIT 0 +// PCB ADAPTER WIRED FOR DIFFERENCE + +//****************************************** +// VARIABLES +//****************************************** +byte PYUUTA[] = {8,16,32}; +byte pyuutalo = 0; // Lowest Entry +byte pyuutahi = 2; // Highest Entry +byte pyuutasize; +byte newpyuutasize; + +// EEPROM MAPPING +// 08 ROM SIZE + +//****************************************** +// MENU +//****************************************** +// Base Menu +static const char* const menuOptionsPYUUTA[] PROGMEM = { FSTRING_SELECT_CART, FSTRING_READ_ROM, FSTRING_SET_SIZE, FSTRING_RESET }; + +void pyuutaMenu() +{ + convertPgm(menuOptionsPYUUTA, 4); + uint8_t mainMenu = question_box(F("TOMY PYUUTA MENU"), menuOptions, 4, 0); + + switch (mainMenu) + { + case 0: + // Select Cart + setCart_PYUUTA(); + setup_PYUUTA(); + break; + + case 1: + // Read ROM + sd.chdir("/"); + readROM_PYUUTA(); + sd.chdir("/"); + break; + + case 2: + // Set Size + setROMSize_PYUUTA(); + break; + + case 3: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// SETUP +//****************************************** + +void setup_PYUUTA() +{ + // Request 5V + setVoltage(VOLTS_SET_5V); + + // Set Address Pins to Output + // PYUUTA uses A0-A15 [A16-A23 UNUSED] + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output + // /RST(PH0) ---(PH1) /CS0(PH3) /DBIN(PH4) ---(PH5) /CS1(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 + // /RST(PH0) ---(PH1) /CS0(PH3) /DBIN(PH4) ---(PH5) /CS1(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_PYUUTA(); + strcpy(romName, "PYUUTA"); + + mode = CORE_PYUUTA; +} + +//****************************************** +// READ FUNCTIONS +//****************************************** + +uint8_t readData_PYUUTA(uint16_t addr) +{ + PORTF = addr & 0xFF; // A0-A7 + PORTK = (addr >> 8) & 0xFF; // A8-A15 + NOP; + NOP; + NOP; + NOP; + NOP; + + uint8_t ret = PINC; + + return ret; +} + +void readSegment_PYUUTA(uint32_t startaddr, uint32_t endaddr) +{ + for (uint32_t addr = startaddr; addr < endaddr; addr += 512) { + for (int w = 0; w < 512; w++) { + uint8_t temp = readData_PYUUTA(addr + w); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + } +} + +//****************************************** +// READ ROM +//****************************************** + +void readROM_PYUUTA() +{ + createFolderAndOpenFile("PYUUTA", "ROM", romName, "bin"); + + //8K $8000-$9FFF + //16K $8000-$BFFF + //32K $4000-$BFFF + PORTH &= ~(1 << 4); // /DBIN(PH4) LOW + if (pyuutasize > 1) { // 32K [3D CARTS] + PORTH &= ~(1 << 6); // /CS1(PH6) LOW + readSegment_PYUUTA(0x4000,0x8000); // +16K = 32K + PORTH |= (1 << 6); // /CS1(PH6) HIGH + } + PORTH &= ~(1 << 3); // /CS0(PH3) LOW + readSegment_PYUUTA(0x8000,0xA000); // 8K + PORTH |= (1 << 3); // /CS0(PH3) HIGH + if (pyuutasize > 0) { // 16K + PORTH &= ~(1 << 3); // /CS0(PH3) LOW + readSegment_PYUUTA(0xA000,0xC000); // +8K = 16K + PORTH |= (1 << 3); // /CS0(PH3) HIGH + } + PORTH |= (1 << 4); // /DBIN(PH4) HIGH + myFile.close(); + + printCRC(fileName, NULL, 0); + + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) +void printRomSize_PYUUTA(int index) +{ + display_Clear(); + print_Msg(FS(FSTRING_ROM_SIZE)); + println_Msg(PYUUTA[index]); +} +#endif + +void setROMSize_PYUUTA() +{ + byte newpyuutasize; +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + if (pyuutalo == pyuutahi) + newpyuutasize = pyuutalo; + else { + newpyuutasize = navigateMenu(pyuutalo, pyuutahi, &printRomSize_PYUUTA); + + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(PYUUTA[newpyuutasize]); + println_Msg(F("KB")); + display_Update(); + delay(1000); +#else + if (pyuutalo == pyuutahi) + newpyuutasize = pyuutalo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (pyuutahi - pyuutalo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(PYUUTA[i + pyuutalo]); + Serial.println(F("KB")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newpyuutasize = sizeROM.toInt() + pyuutalo; + if (newpyuutasize > pyuutahi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(FS(FSTRING_EMPTY)); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(PYUUTA[newpyuutasize]); + Serial.println(F("KB")); +#endif + EEPROM_writeAnything(8, newpyuutasize); + pyuutasize = newpyuutasize; +} + +void checkStatus_PYUUTA() +{ + EEPROM_readAnything(8, pyuutasize); + if (pyuutasize > pyuutahi) { + pyuutasize = 0; // default 8K + EEPROM_writeAnything(8, pyuutasize); + } + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + println_Msg(F("TOMY PYUUTA")); + println_Msg(FS(FSTRING_CURRENT_SETTINGS)); + println_Msg(FS(FSTRING_EMPTY)); + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(PYUUTA[pyuutasize]); + println_Msg(F("KB")); + display_Update(); + wait(); +#else + Serial.print(FS(FSTRING_ROM_SIZE)); + Serial.print(PYUUTA[pyuutasize]); + Serial.println(F("KB")); + Serial.println(FS(FSTRING_EMPTY)); +#endif +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +void setCart_PYUUTA() +{ + //go to root + sd.chdir(); + + byte gameSize; + + // Select starting letter + //byte myLetter = starting_letter(); + + // Open database + if (myFile.open("pyuutacart.txt", O_READ)) { + // seek_first_letter_in_database(myFile, myLetter); + + if(checkCartSelection(myFile, &readDataLineSingleDigit, &gameSize)) { + EEPROM_writeAnything(8, gameSize); + } + } else { + print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND)); + } +} +#endif diff --git a/Cart_Reader/RCA.ino b/Cart_Reader/RCA.ino new file mode 100644 index 0000000..aa69b90 --- /dev/null +++ b/Cart_Reader/RCA.ino @@ -0,0 +1,327 @@ +//****************************************** +// RCA STUDIO II MODULE +//****************************************** +#ifdef ENABLE_RCA +// RCA Studio II +// Cartridge Pinout +// 22P 3.96mm pitch connector +// +// FRONT +// +-------+ +// D7 -| 1 | +// D6 -| 2 | +// D5 -| 3 | +// D4 -| 4 | +// D3 -| 5 | +// ROM_DISABLE -| 6 | +// GND -| 7 | +// D2 -| 8 | +// D1 -| 9 | +// D0 -| 10 | +// A0 -| 11 | +// A1 -| 12 | +// A2 -| 13 | +// A3 -| 14 | +// VCC(+5V) -| 15 | +// A4 -| 16 | +// A5 -| 17 | +// A6 -| 18 | +// TPA -| 19 | +// A7 -| 20 | +// /MRD -| 21 | +// ROMCS -| 22 | +// +-------+ +// +// BACK SIDE +// +-------------------------------------------------------------------------------------------+ +// LEFT | | RIGHT +// | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | +// +-------------------------------------------------------------------------------------------+ +// D7 D6 D5 D4 D3 DIS GND D2 D1 D0 A0 A1 A2 A3 +5V A4 A5 A6 TPA A7 /MRD CS +// +// FRONT SIDE + +// CONTROL PINS: +// /MRD(PH3) - SNES /CS +// TPA(PH6) - SNES /RD + +//****************************************** +// VARIABLES +//****************************************** +byte RCA[] = {1,2}; +byte rcalo = 0; // Lowest Entry +byte rcahi = 1; // Highest Entry + +byte rcasize; +byte newrcasize; + +// EEPROM MAPPING +// 08 ROM SIZE + +//****************************************** +// MENU +//****************************************** +// Base Menu +static const char* const menuOptionsRCA[] PROGMEM = { FSTRING_SELECT_CART, FSTRING_READ_ROM, FSTRING_SET_SIZE, FSTRING_RESET }; + +void rcaMenu() +{ + convertPgm(menuOptionsRCA, 4); + uint8_t mainMenu = question_box(F("RCA STUDIO II MENU"), menuOptions, 4, 0); + + switch (mainMenu) + { + case 0: + // Select Cart + setCart_RCA(); + setup_RCA(); + break; + + case 1: + // Read ROM + sd.chdir("/"); + readROM_RCA(); + sd.chdir("/"); + break; + + case 2: + // Set Size + setROMSize_RCA(); + break; + + case 3: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// SETUP +//****************************************** + +void setup_RCA() +{ + // Request 5V + setVoltage(VOLTS_SET_5V); + + // Set Address Pins to Output + // RCA Studio II uses A0-A7 [A8-A23 UNUSED] + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output + // ---(PH0) ---(PH1) /MRD(PH3) ---(PH4) ---(PH5) TPA(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 + // ---(PH0) ---(PH1) /MRD(PH3) ---(PH4) ---(PH5) TPA(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_RCA(); + strcpy(romName, "RCA"); + + mode = CORE_RCA; +} + +//****************************************** +// READ FUNCTIONS +//****************************************** + +uint8_t readData_RCA(uint16_t addr) +{ + // Setup TPA + /MRD + PORTH &= ~(1 << 6); // TPA LOW + delayMicroseconds(4); + PORTH |= (1 << 3); // /MRD HIGH; + delayMicroseconds(4); + + // Set HIGH Address + PORTF = (addr >> 8) & 0xFF; + delayMicroseconds(4); + + // Latch HIGH Address + PORTH |= (1 << 6); // TPA HIGH + delayMicroseconds(4); + PORTH &= ~(1 << 3); // /MRD LOW + delayMicroseconds(4); + + // Switch TPA LOW + PORTH &= ~(1 << 6); // TPA LOW + delayMicroseconds(4); + + // Set LOW Address + PORTF = addr & 0xFF; + delayMicroseconds(4); + uint8_t ret = PINC; + // Reset /MRD + PORTH |= (1 << 3); // /MRD HIGH; + + return ret; +} + +void readSegment_RCA(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_RCA(addr + w); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + } +} + +//****************************************** +// READ ROM +//****************************************** + +void readROM_RCA() +{ + createFolderAndOpenFile("RCA", "ROM", romName, "bin"); + + readSegment_RCA(0x0400,0x0600); // 512B + if (rcasize > 0) + readSegment_RCA(0x0600,0x0800); // +512B = 1K + myFile.close(); + + printCRC(fileName, NULL, 0); + + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) +void printRomSize_RCA(int index) +{ + display_Clear(); + print_Msg(FS(FSTRING_ROM_SIZE)); + println_Msg(RCA[index]); +} +#endif + +void setROMSize_RCA() +{ + byte newrcasize; +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + if (rcalo == rcahi) + newrcasize = rcalo; + else { + newrcasize = navigateMenu(rcalo, rcahi, &printRomSize_RCA); + + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(RCA[newrcasize]); + println_Msg(F("KB")); + display_Update(); + delay(1000); +#else + if (rcalo == rcahi) + newrcasize = rcalo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (rcahi - rcalo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(RCA[i + rcalo]); + Serial.println(F("KB")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newrcasize = sizeROM.toInt() + rcalo; + if (newrcasize > rcahi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(FS(FSTRING_EMPTY)); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(RCA[newrcasize]); + Serial.println(F("KB")); +#endif + EEPROM_writeAnything(8, newrcasize); + rcasize = newrcasize; +} + +void checkStatus_RCA() +{ + EEPROM_readAnything(8, rcasize); + if (rcasize > rcahi) { + rcasize = 1; // default 1024B + EEPROM_writeAnything(8, rcasize); + } + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + println_Msg(F("RCA STUDIO II")); + println_Msg(FS(FSTRING_CURRENT_SETTINGS)); + println_Msg(FS(FSTRING_EMPTY)); + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(RCA[rcasize] * 512); + println_Msg(F("B")); + display_Update(); + wait(); +#else + Serial.print(FS(FSTRING_ROM_SIZE)); + Serial.print(RCA[rcasize] * 512); + Serial.println(F("B")); + Serial.println(FS(FSTRING_EMPTY)); +#endif +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +void setCart_RCA() +{ + //go to root + sd.chdir(); + + byte gameSize; + + // Select starting letter + //byte myLetter = starting_letter(); + + // Open database + if (myFile.open("rcacart.txt", O_READ)) { + // seek_first_letter_in_database(myFile, myLetter); + + if(checkCartSelection(myFile, &readDataLineSingleDigit, &gameSize)) { + EEPROM_writeAnything(8, gameSize); + } + } else { + print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND)); + } +} +#endif \ No newline at end of file diff --git a/Cart_Reader/TI99.ino b/Cart_Reader/TI99.ino new file mode 100644 index 0000000..bf4a13c --- /dev/null +++ b/Cart_Reader/TI99.ino @@ -0,0 +1,1071 @@ +//****************************************** +// TEXAS INSTRUMENTS TI-99 MODULE +//****************************************** +#ifdef ENABLE_TI99 +// Texas Instruments TI-99 +// Cartridge Pinout +// 36P 2.54mm pitch connector +// +// RIGHT +// +-------+ +// Vss -| 36 35 |- GND +// ROMS* -| 34 33 |- Vss +// WE* -| 32 31 |- GR +// A4 -| 30 29 |- -5V B +// A5 -| 28 27 |- GRC O +// T A6 -| 26 25 |- DBIN T +// O A3 -| 24 23 |- A14 T +// P A7 -| 22 21 |- GS* O +// A8 -| 20 19 |- +5V M +// S A9 -| 18 17 |- D0 +// I A10 -| 16 15 |- D1 S +// D A11 -| 14 13 |- D2 I +// E A12 -| 12 11 |- D3 D +// A13 -| 10 9 |- D4 E +// A15/OUT -| 8 7 |- D5 +// CRUIN -| 6 5 |- D6 +// CRUCLK* -| 4 3 |- D7 +// GND -| 2 1 |- RESET +// +-------+ +// LEFT +// +// TOP SIDE +// +// CRU CRU A15/ +// GND CLK* IN OUT A13 A12 A11 A10 A9 A8 A7 A3 A6 A5 A4 WE* ROMS* VSS +// +--------------------------------------------------------------------------+ +// | 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 | +// LEFT | | RIGHT +// | 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 | +// +--------------------------------------------------------------------------+ +// RST D7 D6 D5 D4 D3 D2 D1 D0 +5V GS* A14 DBIN GRC -5V GR VSS GND +// +// BOTTOM SIDE + +// CONTROL PINS: +// RESET(PH0) - SNES RESET +// GRC(PH1) - SNES CPUCLK [GROM CLOCK] +// /GS(PH3) - SNES /CS [GROM SELECT] +// DBIN(PH4) - SNES /IRQ [READ MEMORY/M(GROM DIRECTION)] +// /WE(PH5) - SNES /WR +// /ROMS(PH6) - SNES /RD [ROM SELECT] +// GR(PL0) - SNES A16 [GROM READY] + +// /GS = LOW FOR ADDR $9800-$9FFF +// /ROMS = LOW FOR ADDR $6000-$7FFF + +// NOTE: TI-99 ADDRESS AND DATA BUS ARE BIG-ENDIAN +// LEAST SIGNIFICANT IS BIT 7 AND MOST SIGNIFICANT IS BIT 0 +// PCB ADAPTER WIRED FOR DIFFERENCE + +// FOR GROM-ONLY BOARDS, TOP PINS DO NOT EXIST +// DATA PINS ARE MULTIPLEXED TO HANDLE BOTH ADDRESS AND DATA +// D7-D0 = AD7-AD0 + +//****************************************** +// DEFINES +//****************************************** +#define DISABLE_GROM PORTH |= (1 << 3) // GROM SELECT 9800-9FFF +#define ENABLE_GROM PORTH &= ~(1 << 3) +#define DISABLE_ROM PORTH |= (1 << 6) // ROM SELECT 6000-7FFF +#define ENABLE_ROM PORTH &= ~(1 << 6) + +#define GROM_WRITE PORTH &= ~(1 << 4) // DBIN/M LOW +#define GROM_READ PORTH |= (1 << 4) // DBIN/M HIGH +#define GROM_DATA PORTF &= ~(1 << 1) // A14/MO LOW +#define GROM_ADDR PORTF |= (1 << 1); // A14/MO HIGH + +#define GRC_HI PORTH |= (1 << 1) +#define GRC_LOW PORTH &= ~(1 << 1) + +//****************************************** +// VARIABLES +//****************************************** +// Cart Configurations +// Format = {mapper,gromlo,gromhi,romlo,romhi} +static const byte PROGMEM ti99mapsize [] = { +0,0,5,0,4, // Normal Carts (GROM 0K/6K/12K/18K/24K/30K + ROM 0K/4K/8K/12K/16K) +1,1,3,4,4, // MBX (GROM 6K/12K/18K + ROM 16K) +2,1,1,4,4, // TI-CALC (GROM 6K + ROM 16K) +}; + +byte ti99mapcount = 3; // (sizeof(mapsize)/sizeof(mapsize[0])) / 5; +boolean ti99mapfound = false; +byte ti99mapselect; +int ti99index; + +//int GROM[] = {0,8,16,24,32,40}; // padded sizes +int GROM[] = {0,6,12,18,24,30}; +byte gromlo = 0; // Lowest Entry +byte gromhi = 5; // Highest Entry + +int CROM[] = {0,4,8,12,16}; +byte cromlo = 0; // Lowest Entry +byte cromhi = 4; // Highest Entry + +byte ti99mapper; +byte newti99mapper; +byte gromsize; +byte newgromsize; +byte cromsize; +byte newcromsize; +byte grommap; +byte newgrommap; + +//boolean MBX = 0; // Normal/MBX + +// EEPROM MAPPING +// 07 MAPPER +// 08 GROM SIZE +// 09 CROM SIZE +// 13 GROM MAP + +//****************************************** +// MENU +//****************************************** +// Base Menu +static const char ti99MenuItem2[] PROGMEM = "Read Complete Cart"; +static const char ti99MenuItem3[] PROGMEM = "Read GROM"; +static const char ti99MenuItem6[] PROGMEM = "Set GROM Map"; +static const char* const menuOptionsTI99[] PROGMEM = { FSTRING_SELECT_CART, ti99MenuItem2, ti99MenuItem3, FSTRING_READ_ROM, FSTRING_SET_SIZE, ti99MenuItem6, FSTRING_RESET }; + +void ti99Menu() +{ + convertPgm(menuOptionsTI99, 7); + uint8_t mainMenu = question_box(F("TI-99 MENU"), menuOptions, 7, 0); + + switch (mainMenu) + { + case 0: + // Select Cart + setCart_TI99(); + setup_TI99(); + break; + + case 1: + // Read Complete Cart + CreateROMFolder_TI99(); + readGROM_TI99(); + readCROM_TI99(); + FinishROMFolder_TI99(); + break; + + case 2: + // Read GROM + CreateROMFolder_TI99(); + readGROM_TI99(); + FinishROMFolder_TI99(); + break; + + case 3: + // Read ROM + CreateROMFolder_TI99(); + readCROM_TI99(); + FinishROMFolder_TI99(); + break; + + case 4: + // Set Mapper + Sizes + setMapper_TI99(); + checkMapperSize_TI99(); + setGROMSize_TI99(); + setCROMSize_TI99(); + break; + + case 5: + // Set GROM Map + setGROMMap_TI99(); + break; + + case 6: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// SETUP +//****************************************** + +void setup_TI99() +{ + // Request 5V + setVoltage(VOLTS_SET_5V); + + // Set Address Pins to Output + // TI-99 uses A0-A15 + // SNES A0-A7 [TI-99 A15-A8] + DDRF = 0xFF; + // SNES A8-A15 [TI-99 A7-A0] + DDRK = 0xFF; + // SNES A16-A23 - Use A16 for GR + DDRL = 0xFE; // A16 to Input + + // Set Control Pins to Output + // RST(PH0) GRC(PH1) /GS(PH3) DBIN(PH4) /WE(PH5) /ROMS(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 + // RST(PH0) GRC(PH1) /GS(PH3) DBIN(PH4) /WE(PH5) /ROMS(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 PORTL Pins HIGH except GR(PL0) + PORTL |= (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6)| (1 << 7); + + // Set Unused Pins HIGH + PORTA = 0xFF; + PORTJ |= (1 << 0); // TIME(PJ0) + + // Set Reset LOW + PORTH &= ~(1 << 0); + + checkStatus_TI99(); + strcpy(romName, "TI99"); + + mode = CORE_TI99; +} + +//****************************************** +// READ FUNCTIONS +//****************************************** + +uint8_t readROM_TI99(uint16_t addr) // Add Input Pullup +{ + PORTF = addr & 0xFF; // A0-A7 + PORTK = (addr >> 8) & 0xFF; // A8-A15 + 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_TI99(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 = readROM_TI99(addr + w); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + } +} + +void setupGROM() +{ + // Setup GROM + DISABLE_GROM; + GROM_READ; // DBIN(M) HIGH = READ + GROM_ADDR; // A14(MO) HIGH = ADDR + GROM_DATA; // A14(MO) LOW = DATA +} + +void pulseGRC(int times) +{ + for (int i = 0; i < (times * 2); i++) { + PORTH ^= (1 << 1); + // NOP (62.5ns) x 20 = 1250ns = 1.25us +// NOP; NOP; NOP; NOP; NOP; // 20 NOPs = 20 x 62.5ns = 1250ns x 2 = 2.5us = 400 kHz +// NOP; NOP; NOP; NOP; NOP; +// NOP; NOP; NOP; NOP; NOP; +// NOP; NOP; NOP; NOP; NOP; + // Switch to 100 kHz due to some GROMs not reading out properly at faster speeds + delayMicroseconds(5); // 5us x 2 = 10us = 100 kHz + } +} + +void checkGRC() +{ + byte statusGR = PINL & 0x1; + pulseGRC(1); + while (!statusGR) { + statusGR = PINL & 0x1; + pulseGRC(1); + } +} + +void pulseGROM(uint16_t addr) // Controlled Pulse Code +{ + // GRC starts here + GRC_LOW; // START LOW + pulseGRC(16); + ENABLE_GROM; + pulseGRC(8); + DISABLE_GROM; + // Write Base + GROM_WRITE; // DBIN(M) LOW = WRITE + GROM_ADDR; // A14(MO) HIGH = ADDR + DDRC = 0xFF; // Output + PORTC = (addr >> 8) & 0xFF; + pulseGRC(16); + ENABLE_GROM; + pulseGRC(4); // this works +// checkGRC(); // Disable checkGRC() otherwise Q-bert hangs (GROM 7 at 0xE000 with NO GROMs at 0x6000/0x8000/0xA000/0xC000) + pulseGRC(2); // Replace checkGRC() with 2 pulse cycles + pulseGRC(10); + DISABLE_GROM; + pulseGRC(16); + PORTC = addr & 0xFF; + pulseGRC(16); + ENABLE_GROM; + pulseGRC(4); // this works +// checkGRC(); // Disable checkGRC() otherwise Q-bert hangs (GROM 7 at 0xE000 with NO GROMs at 0x6000/0x8000/0xA000/0xC000) + pulseGRC(2); // Replace checkGRC() with 2 pulse cycles + pulseGRC(10); + DISABLE_GROM; + pulseGRC(16); + GROM_READ; // DBIN(M) HIGH = READ + GROM_DATA; // A14(MO) LOW = DATA + DDRC = 0x00; + pulseGRC(16); +} + +void readSegmentGROM_TI99(uint32_t startaddr, uint32_t endaddr) +{ + for (uint32_t addr = startaddr; addr < endaddr; addr += 512) { + pulseGROM(addr); // + for (int y = 0; y < 512; y++) { + ENABLE_GROM; + pulseGRC(2); + sdBuffer[y] = PINC; + pulseGRC(6); + DISABLE_GROM; + pulseGRC(16); + } + myFile.write(sdBuffer, 512); + } +} + +//****************************************** +// WRITE FUNCTION +//****************************************** + +void writeData_TI99(uint16_t addr, uint8_t data) +{ + PORTF = addr & 0xFF; // A0-A7 + PORTK = (addr >> 8) & 0xFF; // A8-A15 + NOP; + NOP; + NOP; + NOP; + NOP; + + DDRC = 0xFF; // Set to Output + PORTC = data; + NOP; + NOP; + NOP; + NOP; + NOP; + + // Set /WE(PH5) to LOW + PORTH &= ~(1 << 5); // /WE LOW + NOP; + NOP; + NOP; + NOP; + NOP; + + // Set /WE(PH5) to HIGH + PORTH |= (1 << 5); + NOP; + NOP; + NOP; + NOP; + NOP; + + DDRC = 0x00; // Reset to Input +} + +//****************************************** +// ROM FOLDER +//****************************************** + +void CreateROMFolder_TI99() +{ + sd.chdir(); + EEPROM_readAnything(0, foldern); + sprintf(folder, "TI99/ROM/%d", foldern); + sd.mkdir(folder, true); + sd.chdir(folder); +} + +void FinishROMFolder_TI99() +{ + foldern += 1; + EEPROM_writeAnything(0, foldern); // FOLDER # + sd.chdir(); +} + +//****************************************** +// READ ROM +//****************************************** + +// CARTRIDGE GROMS 3-7 (GROMS 0-2 ARE IN THE CONSOLE) +// GROM 3 = $6000-$77FF +// GROM 4 = $8000-$97FF +// GROM 5 = $A000-$B7FF +// GROM 6 = $C000-$D7FF +// GROM 7 = $E000-$F7FF + +// GROM ACCESS DETAILS +// INTERNAL POINTER AUTO INCREMENTED EACH TIME CHIP ACCESSED +// SET VALUE OF POINTER BY PASSING TWO BYTES TO THE CHIP + +void readGROM_TI99() +{ + display_Clear(); + if (gromsize == 0) { +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + println_Msg(F("GROM SIZE 0K")); + display_Update(); +#else + Serial.println(F("GROM SIZE 0K")); +#endif + } + else { +// println_Msg(F("USE GOOD POWER SUPPLY")); +// println_Msg(FS(FSTRING_EMPTY)); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + // Split into Individual GROM Files + for (int x = 3; x < 8; x++) { + if (((grommap >> x) & 0x1) == 1) { + snprintf(fileName, sizeof(fileName), "%s.g%d.bin", romName, x); + // open file on sdcard + if (!myFile.open(fileName, O_RDWR | O_CREAT)) + print_Error(F("Can't create file on SD")); + + display_Clear(); + print_Msg(F("Reading GROM ")); + println_Msg(x); + println_Msg(FS(FSTRING_EMPTY)); + display_Update(); + + // Normal GROM 6KB/12KB/18KB/24KB/30K + // MBX GROM 6KB/12KB/18KB + DISABLE_ROM; + ENABLE_GROM; + setupGROM(); + if (x == 3) + readSegmentGROM_TI99(0x6000,0x7800); // 6K + else if (x == 4) + readSegmentGROM_TI99(0x8000,0x9800); // +6K = 12K + else if (x == 5) + readSegmentGROM_TI99(0xA000,0xB800); // +6K = 18K + else if (x == 6) + readSegmentGROM_TI99(0xC000,0xD800); // +6K = 24K + else if (x == 7) + readSegmentGROM_TI99(0xE000,0xF800); // +6K = 30K + DISABLE_GROM; + myFile.close(); + + printCRC(fileName, NULL, 0); + } + } + } + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +// CARTRIDGE ROM +// EACH CART UP TO 8K MAPPED AT $6000-$7FFF + +void readCROM_TI99() +{ + display_Clear(); + if (cromsize == 0) { +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + println_Msg(F("ROM SIZE 0K")); + display_Update(); +#else + Serial.println(F("ROM SIZE 0K")); +#endif + } + else { + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + snprintf(fileName, sizeof(fileName), "%s.c.bin", romName); + // open file on sdcard + if (!myFile.open(fileName, O_RDWR | O_CREAT)) + print_Error(F("Can't create file on SD")); + + if (ti99mapper == 1) { // MBX + // MBX Cart ROM 16K + // Fixed Bank 0 at 0x6000 (RAM at 0x6C00) + // Bankswitch 0x7000-0x7FFF using write 1/2/3 to 0x6FFE + DISABLE_GROM; + ENABLE_ROM; + readSegment_TI99(0x6000, 0x6C00); // 3K + for (int w = 0; w < 1024; w += 512) { // 1K RAM at 0x6C00 + for (int x = 0; x < 512; x++) { + sdBuffer[x] = 0xFF; // Replace Random RAM Contents with 0xFF + } + myFile.write(sdBuffer, 512); + } + for (int y = 1; y < 4; y++) { + writeData_TI99(0x6FFE, y); // Set Bank 1/2/3 + readSegment_TI99(0x7000, 0x8000); // 4K x 3 = +12K = 16K + } + } + else if (ti99mapper == 2) { // TI-CALC [UNTESTED] + // TI-CALC ROM 16K + // Fixed Bank 0 at 0x6000-0x6FFF + // Bankswitch 0x7000-0x7FFF using write to 0x7000/0x7002/0x7004/0x7006 + DISABLE_GROM; + ENABLE_ROM; + writeData_TI99(0x7000, 0x00); // Set Bank 0 + readSegment_TI99(0x7000,0x8000); // 4K + writeData_TI99(0x7002, 0x00); // Set Bank 1 + readSegment_TI99(0x7000,0x8000); // +4K = 8K + writeData_TI99(0x7004, 0x00); // Set Bank 2 + readSegment_TI99(0x7000, 0x8000); // +4K = 12K + writeData_TI99(0x7006, 0x00); // Set Bank 3 + readSegment_TI99(0x7000, 0x8000); // +4K = 16K + } + else { + // Normal Cart ROM 4K/8K/12K/16K + // ROM Space 0x6000-0x7FFF + // Bankswitch 0x7000-0x7FFF using write to 0x6000/0x6002 + DISABLE_GROM; + ENABLE_ROM; + writeData_TI99(0x6000, 0x00); // Set Bank 0 + readSegment_TI99(0x6000,0x7000); // 4K + if (cromsize > 1) { + readSegment_TI99(0x7000,0x8000); // +4K = 8K + if (cromsize > 2) { + writeData_TI99(0x6002, 0x00); // Set Bank 1 + if (cromsize > 3) // 16K + readSegment_TI99(0x6000, 0x7000); // +4K = 16K + readSegment_TI99(0x7000, 0x8000); // +4K = 12K + } + } + } + DISABLE_ROM; + myFile.close(); + + printCRC(fileName, NULL, 0); + } + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +//****************************************** +// CHECK STATUS +//****************************************** + +void checkStatus_TI99() +{ + EEPROM_readAnything(7, ti99mapper); + EEPROM_readAnything(8, gromsize); + EEPROM_readAnything(9, cromsize); + EEPROM_readAnything(13, grommap); + if (ti99mapper > (ti99mapcount - 1)) { + ti99mapper = 0; + EEPROM_writeAnything(7, ti99mapper); + } + if (gromsize > gromhi) { + gromsize = 1; // default 6K + EEPROM_writeAnything(8, gromsize); + } + if (cromsize > cromhi) { + cromsize = 2; // default 8K + EEPROM_writeAnything(9, cromsize); + } + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + println_Msg(F("TI-99 READER")); + println_Msg(FS(FSTRING_CURRENT_SETTINGS)); + println_Msg(FS(FSTRING_EMPTY)); + print_Msg(F("MAPPER: ")); +// println_Msg(ti99mapper); + printMapper_TI99(ti99mapper); + print_Msg(F("GROM SIZE: ")); + print_Msg(GROM[gromsize]); + println_Msg(F("KB")); + print_Msg(F("ROM SIZE: ")); + print_Msg(CROM[cromsize]); + println_Msg(F("KB")); + print_Msg(F("GROM MAP: ")); + display_Update(); + readGROMMap(); + wait(); +#else + Serial.print(F("CURRENT MAPPER: ")); + Serial.println(ti99mapper); + Serial.print(F("GROM SIZE: ")); + Serial.print(GROM[gromsize]); + Serial.println(F("KB")); + Serial.print(F("ROM SIZE: ")); + Serial.print(CROM[cromsize]); + Serial.println(F("KB")); + Serial.print(F("GROM MAP: ")); + readGROMMap(); + Serial.println(F("")); +#endif +} + +void printMapper_TI99(byte ti99maplabel) +{ +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + switch (ti99maplabel) + { + case 0: + println_Msg(F("NORMAL")); + break; + case 1: + println_Msg(F("MBX")); + break; + case 2: + println_Msg(F("TI-CALC")); + break; + } +#else + Serial.println(F("0 = NORMAL")); + Serial.println(F("1 = MBX")); + Serial.println(F("2 = TI-CALC")); +#endif +} + +//****************************************** +// MAPPER CODE +//****************************************** + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) +void printMapperSelection_TI99(int index) +{ + display_Clear(); + print_Msg(FS(FSTRING_MAPPER)); + ti99index = index * 5; + ti99mapselect = pgm_read_byte(ti99mapsize + ti99index); + println_Msg(ti99mapselect); +} +#endif + +void setMapper_TI99() +{ + byte newti99mapper; +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + navigateMenu(0, ti99mapcount - 1, &printMapperSelection_TI99); + newti99mapper = ti99mapselect; + + display.setCursor(0, 56); + print_Msg(F("MAPPER ")); + print_Msg(newti99mapper); + println_Msg(F(" SELECTED")); + display_Update(); + delay(1000); +#else +setmapper: + String newmap; + ti99mapfound = false; + printMapper_TI99(0); + Serial.print(F("Enter Mapper [0-2]: ")); + while (Serial.available() == 0) {} + newmap = Serial.readStringUntil('\n'); + Serial.println(newmap); + newti99mapper = newmap.toInt(); + for (int i = 0; i < ti99mapcount; i++) { + ti99index = i * 5; + ti99mapselect = pgm_read_byte(ti99mapsize + ti99index); + if (newti99mapper == ti99mapselect) + ti99mapfound = true; + } + if (ti99mapfound == false) { + Serial.println(F("MAPPER NOT SUPPORTED!")); + Serial.println(F("")); + newti99mapper = 0; + goto setmapper; + } +#endif + EEPROM_writeAnything(7, newti99mapper); + ti99mapper = newti99mapper; +} + +void checkMapperSize_TI99() +{ + for (int i = 0; i < ti99mapcount; i++) { + ti99index = i * 5; + byte mapcheck = pgm_read_byte(ti99mapsize + ti99index); + if (mapcheck == ti99mapper) { + gromlo = pgm_read_byte(ti99mapsize + ti99index + 1); + gromhi = pgm_read_byte(ti99mapsize + ti99index + 2); + cromlo = pgm_read_byte(ti99mapsize + ti99index + 3); + cromhi = pgm_read_byte(ti99mapsize + ti99index + 4); + break; + } + } +} + +//****************************************** +// GROM SIZE +//****************************************** + +void setGROMSize_TI99() +{ +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + if (gromlo == gromhi) + newgromsize = gromlo; + else { + int b = 0; + int i = gromlo; + while (1) { + display_Clear(); + print_Msg(F("GROM Size: ")); + println_Msg(GROM[i]); + println_Msg(FS(FSTRING_EMPTY)); + println_Msg(F("Press to Change")); + println_Msg(F("Hold to Select")); + display_Update(); + b = checkButton(); + if (b == 2) { // Previous + if (i == gromlo) + i = gromhi; + else + i--; + } + if (b == 1) { // Next + if (i == gromhi) + i = gromlo; + else + i++; + } + if (b == 3) { // Long Press - Execute + newgromsize = i; + break; + } + } + display.setCursor(0, 48); // Display selection at bottom + } + print_Msg(F("GROM SIZE ")); + print_Msg(GROM[newgromsize]); + println_Msg(F("KB")); + display_Update(); + EEPROM_writeAnything(8, newgromsize); + gromsize = newgromsize; + // Default GROM Map to sequential GROMs + newgrommap = 0; + for (int x = 0; x < gromsize; x++){ + newgrommap = (newgrommap | (1 << (x + 3))); + } + EEPROM_writeAnything(13, newgrommap); + grommap = newgrommap; + print_Msg(F("GROM MAP ")); + display_Update(); + readGROMMap(); + wait(); +#else + if (gromlo == gromhi) + newgromsize = gromlo; + else { +setgrom: + String sizeGROM; + for (int i = 0; i < (gromhi - gromlo + 1); i++) { + Serial.print(F("Select GROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(GROM[i + gromlo]); + Serial.println(F("KB")); + } + Serial.print(F("Enter GROM Size: ")); + while (Serial.available() == 0) {} + sizeGROM = Serial.readStringUntil('\n'); + Serial.println(sizeGROM); + newgromsize = sizeGROM.toInt() + gromlo; + if (newgromsize > gromhi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(FS(FSTRING_EMPTY)); + goto setgrom; + } + } + Serial.print(F("GROM SIZE ")); + Serial.print(GROM[newgromsize]); + Serial.println(F("KB")); + EEPROM_writeAnything(8, newgromsize); + gromsize = newgromsize; + // Default GROM Map to sequential GROMs + newgrommap = 0; + for (int x = 0; x < gromsize; x++){ + newgrommap = (newgrommap | (1 << (x + 3))); + } + EEPROM_writeAnything(13, newgrommap); + grommap = newgrommap; + Serial.print(F("GROM MAP ")); + readGROMMap(); +#endif +} + +void readGROMMap() +{ + newgromsize = 0; +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + if (grommap == 0) + print_Msg(FS(FSTRING_EMPTY)); + else { + for (int x = 3; x < 8; x++) { + if (((grommap >> x) & 0x1) == 1) { + print_Msg(x); + newgromsize++; + } + } + } + println_Msg(FS(FSTRING_EMPTY)); + display_Update(); +#else + if (grommap == 0) + Serial.print(0); + else ( + for (int x = 3; x < 8; x++) { + if (((grommap >> x) & 0x1) == 1) { + Serial.print(x); + newgromsize++; + } + } + } + Serial.println(FS(FSTRING_EMPTY)); +#endif + if (gromsize != newgromsize) { + EEPROM_writeAnything(8, newgromsize); + gromsize = newgromsize; + } +} + +void setGROMMap_TI99() +{ + newgrommap = 0; +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + int b = 0; + for(int i = 3; i < 8; i++) { + display_Clear(); + print_Msg(F("Enable GROM ")); + println_Msg(i); + println_Msg(FS(FSTRING_EMPTY)); + println_Msg(F("Press to Skip GROM")); + println_Msg(F("Hold to Enable GROM")); + display_Update(); + while (1) { + b = checkButton(); + if (b == 2) { // Previous + break; + } + if (b == 1) { // Next + break; + } + if (b == 3) { // Long Press - Execute + newgrommap = (newgrommap | (1 << i)); + println_Msg(FS(FSTRING_EMPTY)); + print_Msg(F("GROM ")); + print_Msg(i); + println_Msg(F(" ENABLED")); + display_Update(); + break; + } + } + b = 0; + delay(1000); + } + EEPROM_writeAnything(13, newgrommap); + grommap = newgrommap; + display.setCursor(0, 56); // Display selection at bottom + print_Msg(F("GROM MAP: ")); + display_Update(); + readGROMMap(); + delay(1000); +#else + String mapGROM; + for (int i = 3; i < 8; i++) { + Serial.print(F("Enable GROM ")); + Serial.println(i); + Serial.println(F("0 = NO")); + Serial.println(F("1 = YES")); + while (Serial.available() == 0) {} + mapCROM = Serial.readStringUntil('\n'); + Serial.println(mapGROM); + if (mapGROM.toInt() == 1) + newgrommap = (newgrommap | (1 << i)); + } + EEPROM_writeAnything(13, newgrommap); + grommap = newgrommap; + Serial.print(F("GROM MAP: ")); + readGROMMap(); +#endif +} + +//****************************************** +// ROM SIZE +//****************************************** + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) +void printRomSize_TI99(int index) +{ + display_Clear(); + print_Msg(FS(FSTRING_ROM_SIZE)); + println_Msg(CROM[index]); +} +#endif + +void setCROMSize_TI99() +{ + byte newcromsize; +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + if (cromlo == cromhi) + newcromsize = cromlo; + else { + newcromsize = navigateMenu(cromlo, cromhi, &printRomSize_TI99); + + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(CROM[newcromsize]); + println_Msg(F("KB")); + display_Update(); + delay(1000); +#else + if (cromlo == cromhi) + newcromsize = cromlo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (cromhi - cromlo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(CROM[i + cromlo]); + Serial.println(F("KB")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newcromsize = sizeROM.toInt() + cromlo; + if (newcromsize > cromhi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(FS(FSTRING_EMPTY)); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(CROM[newcromsize]); + Serial.println(F("KB")); +#endif + EEPROM_writeAnything(8, newcromsize); + cromsize = newcromsize; +} + +//****************************************** +// CART SELECT CODE +//****************************************** +struct database_entry_TI99 { + byte gameMapper; + byte gromSize; + byte gromMap; + byte romSize; +}; + +void readDataLine_TI99(FsFile& database, void* entry) +{ + struct database_entry_TI99* castEntry = (database_entry_TI99*)entry; + + // Read Maooer + castEntry->gameMapper = database.read() - 48; + + // Skip over semicolon + database.seekCur(1); + + // Read grom size + castEntry->gromSize = database.read() - 48; + + // Skip over semicolon + database.seekCur(1); + + // Read grom map + // Read the next ascii character and subtract 48 to convert to decimal + castEntry->gromMap = ((database.read() - 48) * 100) + ((database.read() - 48) * 10) + (database.read() - 48); + + // Skip over semicolon + database.seekCur(1); + + // Read grom size + castEntry->romSize = database.read() - 48; + + // Skip rest of line + database.seekCur(2); +} + +void printDataLine_TI99(void* entry) +{ + struct database_entry_TI99* castEntry = (database_entry_TI99*)entry; + print_Msg(F("CODE: M")); + print_Msg(castEntry->gameMapper); + print_Msg(F("/G")); + print_Msg(castEntry->gromSize); + print_Msg(F("/C")); + print_Msg(castEntry->romSize); + print_Msg(F("/X")); + println_Msg(castEntry->gromMap); +} + +void setCart_TI99() +{ + sd.chdir(); + + struct database_entry_TI99 entry; + + // Select starting letter + byte myLetter = starting_letter(); + + if (myFile.open("ti99cart.txt", O_READ)) { + seek_first_letter_in_database(myFile, myLetter); + + if(checkCartSelection(myFile, &readDataLine_TI99, &entry, &printDataLine_TI99)) { + EEPROM_writeAnything(7, entry.gameMapper); + EEPROM_writeAnything(8, entry.gromSize); + EEPROM_writeAnything(9, entry.romSize); + EEPROM_writeAnything(13, entry.gromMap); + } + } else { + print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND)); + } +} +#endif diff --git a/Cart_Reader/TRS80.ino b/Cart_Reader/TRS80.ino new file mode 100644 index 0000000..f39bb6c --- /dev/null +++ b/Cart_Reader/TRS80.ino @@ -0,0 +1,398 @@ +//****************************************** +// TRS-80 COLOR COMPUTER MODULE +//****************************************** +#ifdef ENABLE_TRS80 +// TRS-80 +// Color Computer +// Cartridge Pinout +// 40P 2.54mm pitch connector +// +// TOP BOTTOM +// SIDE SIDE +// +-------+ +// NC -| 1 2 |- NC +// /HALT -| 3 4 |- /NMI +// /RESET -| 5 6 |- E +// Q -| 7 8 |- /CART +// +5V -| 9 10 |- D0 +// D1 -| 11 12 |- D2 +// D3 -| 13 14 |- D4 +// D5 -| 15 16 |- D6 +// D7 -| 17 18 |- R/W +// A0 -| 19 20 |- A1 +// A2 -| 21 22 |- A3 +// A4 -| 23 24 |- A5 +// A6 -| 25 26 |- A7 +// A8 -| 27 28 |- A9 +// A10 -| 29 30 |- A11 +// A12 -| 31 32 |- /CTS +// GND -| 33 34 |- GND +// SND -| 35 36 |- /SCS +// A13 -| 37 38 |- A14 +// A15 -| 39 40 |- /SLENB +// +-------+ +// +// TOP +// A15 A13 SND GND A12 A10 A8 A6 A4 A2 A0 D7 D5 D3 D1 +5V Q /RST /HLT NC +// +-----------------------------------------------------------------------------------------------+ +// | 39 37 35 33 31 29 27 25 23 21 19 17 15 13 11 9 7 5 3 1 | +// LEFT | | RIGHT +// | 40 38 36 34 32 30 28 26 24 22 20 18 16 14 12 10 8 6 4 2 | +// +-----------------------------------------------------------------------------------------------+ +// /SLB A14 /SCS GND /CTS A11 A9 A7 A5 A3 A1 RW D6 D4 D2 D0 /CRT E /NMI NC +// BOTTOM + +// CONTROL PINS: +// /RESET(PH0) - SNES RESET +// E(PH1) - SNES CPUCLK +// /CTS(PH3) - SNES /CS +// /SCS(PH4) - SNES /IRQ +// R/W(PH5) - SNES /WR +// NOTE: CARTS CONNECT /CART TO Q + +//****************************************** +// VARIABLES +//****************************************** +byte TRS80[] = {2,4,8,10,16,32,64,128}; +byte trs80lo = 0; // Lowest Entry +byte trs80hi = 7; // Highest Entry + +byte trs80size; +byte newtrs80size; + +// EEPROM MAPPING +// 08 ROM SIZE + +//****************************************** +// MENU +//****************************************** +// Base Menu +static const char* const menuOptionsTRS80[] PROGMEM = { FSTRING_SELECT_CART, FSTRING_READ_ROM, FSTRING_SET_SIZE, FSTRING_RESET }; + +void trs80Menu() +{ + convertPgm(menuOptionsTRS80, 4); + uint8_t mainMenu = question_box(F("TRS-80 MENU"), menuOptions, 4, 0); + + switch (mainMenu) + { + case 0: + // Select Cart + setCart_TRS80(); + setup_TRS80(); + break; + + case 1: + // Read ROM + sd.chdir("/"); + readROM_TRS80(); + sd.chdir("/"); + break; + + case 2: + // Set Size + setROMSize_TRS80(); + break; + + case 3: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// SETUP +//****************************************** + +void setup_TRS80() +{ + // Request 5V + setVoltage(VOLTS_SET_5V); + + // Set Address Pins to Output + // TRS-80 uses A0-A15 [A16-A23 UNUSED] + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output + // /RST(PH0) E(PH1) /CTS(PH3) /SCS(PH4) R/W(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 + // /RST(PH0) E(PH1) /CTS(PH3) /SCS(PH4) R/W(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_TRS80(); + strcpy(romName, "TRS80"); + + mode = CORE_TRS80; +} + +//****************************************** +// READ FUNCTIONS +//****************************************** + +uint8_t readData_TRS80(uint16_t addr) +{ + PORTF = addr & 0xFF; // A0-A7 + PORTK = (addr >> 8) & 0xFF; // A8-A15 + + // Set /CTS to LOW + PORTH &= ~(1 << 3); // /CTS LOW + NOP; + NOP; + NOP; + NOP; + NOP; + + uint8_t ret = PINC; + + // Pull /CTS to HIGH + PORTH |= (1 << 3); // /CTS HIGH + + return ret; +} + +void readSegment_TRS80(uint32_t startaddr, uint32_t endaddr) +{ + for (uint32_t addr = startaddr; addr < endaddr; addr += 512) { + for (int w = 0; w < 512; w++) { + uint8_t temp = readData_TRS80(addr + w); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + } +} + +//****************************************** +// BANKSWITCH +//****************************************** + +// Bankswitch is a combination of IC1 74LS175 (Quad Latch) and IC3 74LS10 (NAND Gate) +// IC1 latches D0-D3 using /RESET and control (CP) from IC3 +// IC3 controls latch into IC1 using CP output based on R/W, E and /SCS +// IC3 CP LOW only when R/W LOW, /SCS LOW, and E HIGH +void bankSwitch_TRS80(uint16_t addr, uint8_t data) +{ + PORTF = addr & 0xFF; // A0-A7 + PORTK = (addr >> 8) & 0xFF; // A8-A15 + NOP; + NOP; + NOP; + NOP; + NOP; + + DDRC = 0xFF; // Set to Output + PORTC = data; + NOP; + NOP; + NOP; + NOP; + NOP; + + // SET CP (LATCH INPUT INTO 74LS175) TO LOW + PORTH &= ~(1 << 4); // /SCS(PH4) LOW + PORTH &= ~(1 << 5); // R/W(PH5) LOW = WRITE + // Pulse E to Latch Data + PORTH &= ~(1 << 1); // E(PH1) LOW + NOP; + PORTH |= (1 << 1); // E(PH1) HIGH + NOP; + + // SET CP TO HIGH + PORTH |= (1 << 4); // /SCS(PH5) HIGH + PORTH |= (1 << 5); // R/W(PH5) HIGH = READ + + DDRC = 0x00; // Reset to Input +} + +//****************************************** +// READ ROM +//****************************************** + +void readROM_TRS80() +{ + createFolderAndOpenFile("TRS80", "ROM", romName, "ccc"); + + // Set /RESET to LOW + PORTH &= ~(1 << 0); // /RESET LOW + delay(100); + // Set /RESET to HIGH + PORTH |= (1 << 0); // /RESET HIGH + // Set R/W to READ + PORTH |= (1 << 5); // R/W HIGH + + if (trs80size > 5) { // Bankswitch Carts - Predator 64K/Robocop 128K + // Predator 64K = (2^6) / 16 = 4 + // Robocop 128K = (2^7) / 16 = 8 + int banks = (int_pow(2, trs80size)) / 16; + for (int x = 0; x < banks; x++) { + bankSwitch_TRS80(0xFF40, x); // Bankswitch + readSegment_TRS80(0xC000, 0x10000); // 16K * 8 = 128K + } + } + else { // Normal Carts 2K/4K/8K/10K/16K/32K + readSegment_TRS80(0xC000,0xC800); // 2K + if (trs80size > 0) { + readSegment_TRS80(0xC800,0xD000); // +2K = 4K + if (trs80size > 1) { + readSegment_TRS80(0xD000,0xE000); // +4K = 8K + if (trs80size > 2) { + readSegment_TRS80(0xE000,0xE800); // +2K = 10K + if (trs80size > 3) { + readSegment_TRS80(0xE800,0x10000); // +6K = 16K + if (trs80size == 5) { // 32K + // Second Chip Select - Switch to Upper 16K (Mind-Roll) + PORTH &= ~(1 << 4); // /SCS LOW + NOP; NOP; NOP; NOP; NOP; + PORTH |= (1 << 4); // /SCS HIGH + readSegment_TRS80(0x8000,0xC000); // +16K = 32K + } + } + } + } + } + } + myFile.close(); + + printCRC(fileName, NULL, 0); + + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) +void printRomSize_TRS80(int index) +{ + display_Clear(); + print_Msg(FS(FSTRING_ROM_SIZE)); + println_Msg(TRS80[index]); +} +#endif + +void setROMSize_TRS80() +{ + byte newtrs80size; +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + if (trs80lo == trs80hi) + newtrs80size = trs80lo; + else { + newtrs80size = navigateMenu(trs80lo, trs80hi, &printRomSize_TRS80); + + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(TRS80[newtrs80size]); + println_Msg(F("KB")); + display_Update(); + delay(1000); +#else + if (trs80lo == trs80hi) + newtrs80size = trs80lo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (trs80hi - trs80lo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(TRS80[i + trs80lo]); + Serial.println(F("KB")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newtrs80size = sizeROM.toInt() + trs80lo; + if (newtrs80size > trs80hi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(FS(FSTRING_EMPTY)); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(TRS80[newtrs80size]); + Serial.println(F("KB")); +#endif + EEPROM_writeAnything(8, newtrs80size); + trs80size = newtrs80size; +} + +void checkStatus_TRS80() +{ + EEPROM_readAnything(8, trs80size); + if (trs80size > trs80hi) { + trs80size = 5; // default 32K + EEPROM_writeAnything(8, trs80size); + } + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + println_Msg(F("TRS-80 COLOR COMPUTER")); + println_Msg(FS(FSTRING_CURRENT_SETTINGS)); + println_Msg(FS(FSTRING_EMPTY)); + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(TRS80[trs80size]); + println_Msg(F("KB")); + display_Update(); + wait(); +#else + Serial.print(FS(FSTRING_ROM_SIZE)); + Serial.print(TRS80[trs80size]; + Serial.println(F("KB")); + Serial.println(FS(FSTRING_EMPTY)); +#endif +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +void setCart_TRS80() +{ + //go to root + sd.chdir(); + + byte gameSize; + + // Select starting letter + //byte myLetter = starting_letter(); + + // Open database + if (myFile.open("trs80cart.txt", O_READ)) { + // seek_first_letter_in_database(myFile, myLetter); + + if(checkCartSelection(myFile, &readDataLineSingleDigit, &gameSize)) { + EEPROM_writeAnything(8, gameSize); + } + } else { + print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND)); + } +} +#endif diff --git a/Cart_Reader/VIC20.ino b/Cart_Reader/VIC20.ino new file mode 100644 index 0000000..cdb4bbc --- /dev/null +++ b/Cart_Reader/VIC20.ino @@ -0,0 +1,576 @@ +//****************************************** +// COMMODORE VIC-20 MODULE +//****************************************** +#ifdef ENABLE_VIC20 +// Commodore VIC-20 +// Cartridge Pinout +// 44P 3.96mm pitch connector +// +// FRONT BACK +// SIDE SIDE +// +-------+ +// GND -| 1 A |- GND +// D0 -| 2 B |- A0 +// D1 -| 3 C |- A1 +// D2 -| 4 D |- A2 +// D3 -| 5 E |- A3 +// D4 -| 6 F |- A4 +// D5 -| 7 H |- A5 +// D6 -| 8 J |- A6 +// D7 -| 9 K |- A7 +// /BLK1 -| 10 L |- A8 +// /BLK2 -| 11 M |- A9 +// /BLK3 -| 12 N |- A10 +// /BLK5 -| 13 P |- A11 +// /RAM1 -| 14 R |- A12 +// /RAM2 -| 15 S |- A13 +// /RAM3 -| 16 T |- IO2 +// VR/W -| 17 U |- IO3 +// CR/W -| 18 V |- PHI2 +// IRQ -| 19 W |- /NMI +// NC -| 20 X |- /RESET +// +5V -| 21 Y |- NC +// GND -| 22 Z |- GND +// +-------+ +// +// BACK +// GND NC /RST /NMI PH2 I3 I2 A13 A12 A11 A10 A9 A8 A7 A6 A5 A4 A3 A2 A1 A0 GND +// +-------------------------------------------------------------------------------+ +// | Z Y X W V U T S R P N M L K J H F E D C B A | +// LEFT | | RIGHT +// | 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 | +// +-------------------------------------------------------------------------------+ +// GND +5V NC IRQ CRW VRW /R3 /R2 /R1 /B5 /B3 /B2 /B1 D7 D6 D5 D4 D3 D2 D1 D0 GND +// FRONT + +// CONTROL PINS: +// /BLK1(PH3) - SNES /CS - [$2000-$3FFF] +// /BLK2(PH4) - SNES /IRQ - [$4000-$5FFF] +// /BLK3(PH5) - SNES /WR - [$6000-$7FFF] +// /BLK5(PH6) - SNES /RD - [$A000-$BFFF] + +//****************************************** +// VARIABLES +//****************************************** +byte VIC20MAP[] = { +0x20, // 0x2000 +0x24, // 0x2000/0x4000 +0x2A, // 0x2000/0xA000 +0x46, // 0x4000/0x6000 - Adventure Games +0x60, // 0x6000 +0x6A, // 0x6000/0xA000 - Standard 16K +0x70, // 0x7000 +0xA0, // 0xA000 - Standard 8K +0xB0 // 0xB000 +}; + +byte vic20mapcount = 9; +byte vic20mapselect; +byte vic20maplo = 0; // Lowest Entry +byte vic20maphi = 8; // Highest Entry +byte vic20map = 0; +byte newvic20map; + +byte VIC20SIZE[] = { +0x20, // 2K/0K 0x800 +0x40, // 4K/0K 0x1000 +0x80, // 8K/0K 0x2000 +0x44, // 4K/4K 0x1000/0x1000 +0x48, // 4K/8K 0x1000/0x2000 +0x84, // 8K/4K 0x2000/0x1000 +0x88 // 8K/8K 0x2000/0x2000 +}; + +byte vic20lo = 0; // Lowest Entry +byte vic20hi = 6; // Highest Entry +byte vic20size; +byte newvic20size; + +//byte VIC20[] = {2,4,8}; +//byte vic20lo = 0; // Lowest Entry +//byte vic20hi = 2; // Highest Entry +//byte vic20size; +//byte newvic20size; + +// EEPROM MAPPING +// 07 MAPPER +// 08 ROM SIZE + +//****************************************** +// MENU +//****************************************** +// Base Menu +static const char* const menuOptionsVIC20[] PROGMEM = { FSTRING_SELECT_CART, FSTRING_READ_ROM, FSTRING_SET_SIZE, FSTRING_RESET }; + +void vic20Menu() +{ + convertPgm(menuOptionsVIC20, 4); + uint8_t mainMenu = question_box(F("VIC-20 MENU"), menuOptions, 4, 0); + + switch (mainMenu) + { + case 0: + // Select Cart + setCart_VIC20(); + setup_VIC20(); + break; + + case 1: + // Read ROM + sd.chdir("/"); + CreateROMFolder_VIC20(); + readROM_VIC20(); + FinishROMFolder_VIC20(); + sd.chdir("/"); + break; + + case 2: + // Set ROM Map + Size + setROMMap_VIC20(); + setROMSize_VIC20(); + break; + + case 3: + // reset + resetArduino(); + break; + } +} + +//****************************************** +// SETUP +//****************************************** + +void setup_VIC20() +{ + // Request 5V + setVoltage(VOLTS_SET_5V); + + // Set Address Pins to Output + // VIC-20 uses A0-A13 [A14-A23 UNUSED] + //A0-A7 + DDRF = 0xFF; + //A8-A15 + DDRK = 0xFF; + //A16-A23 + DDRL = 0xFF; + + // Set Control Pins to Output + // /RST(PH0) ---(PH1) /BLK1(PH3) /BLK2(PH4) /BLK3(PH5) /BLK5(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 + // /RST(PH0) ---(PH1) /BLK1(PH3) /BLK2(PH4) /BLK3(PH5) /BLK5(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_VIC20(); + strcpy(romName, "VIC20"); + + mode = CORE_VIC20; +} + +//****************************************** +// READ FUNCTIONS +//****************************************** + +uint8_t readData_VIC20(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_VIC20(uint32_t startaddr, uint32_t endaddr) +{ + for (uint32_t addr = startaddr; addr < endaddr; addr += 512) { + for (int w = 0; w < 512; w++) { + uint8_t temp = readData_VIC20(addr + w); + sdBuffer[w] = temp; + } + myFile.write(sdBuffer, 512); + } +} + +//****************************************** +// ROM FOLDER +//****************************************** + +void CreateROMFolder_VIC20() +{ + sd.chdir(); + EEPROM_readAnything(0, foldern); + sprintf(folder, "VIC20/ROM/%d", foldern); + sd.mkdir(folder, true); + sd.chdir(folder); +} + +void FinishROMFolder_VIC20() +{ + foldern += 1; + EEPROM_writeAnything(0, foldern); // FOLDER # + sd.chdir(); +} + +//****************************************** +// READ ROM +//****************************************** + +void readROM_VIC20() +{ + display_Clear(); + print_Msg(F("Saving to ")); + print_Msg(folder); + println_Msg(F("/...")); + display_Update(); + + byte rommap = 0; + byte romsize = 0; + + // Split into Individual ROM Files + for (int x = 0; x < 2; x++) { // ROM0/ROM1 + if (x == 1) { + if ((VIC20MAP[vic20map] & 0x0F) == 0) + break; + rommap = ((VIC20MAP[vic20map] & 0x0F) << 4); + romsize = VIC20SIZE[vic20size] & 0x0F; + } + else { + rommap = VIC20MAP[vic20map] & 0xF0; + romsize = ((VIC20SIZE[vic20size] & 0xF0) >> 4); + } + snprintf(fileName, sizeof(fileName), "%s.%x", romName, rommap); + // open file on sdcard + if (!myFile.open(fileName, O_RDWR | O_CREAT)) + print_FatalError(F("Can't create file on SD")); + + if (rommap == 0x20) { // BLK1 + PORTH &= ~(1 << 3); // BLK1(PH3) LOW + readSegment_VIC20(0x2000,0x3000); // 4K + if (romsize == 8) + readSegment_VIC20(0x3000,0x4000); // +4K = 8K + PORTH |= (1 << 3); // BLK1(PH3) HIGH + } + else if (rommap == 0x40) { // BLK2 + PORTH &= ~(1 << 4); // BLK2(PH4) LOW + readSegment_VIC20(0x4000,0x5000); // 4K + if (romsize == 8) + readSegment_VIC20(0x5000,0x6000); // +4K = 8K + PORTH |= (1 << 4); // BLK2(PH4) HIGH + } + else if (rommap == 0x60) { // BLK3 + PORTH &= ~(1 << 5); // BLK3(PH5) LOW + readSegment_VIC20(0x6000,0x7000); // 4K + if (romsize == 8) + readSegment_VIC20(0x7000,0x8000); // +4K = 8K + PORTH |= (1 << 5); // BLK3(PH5) HIGH + } + else if (rommap == 0x70) { // BLK3 UPPER HALF + PORTH &= ~(1 << 5); // BLK3(PH5) LOW + readSegment_VIC20(0x7000,0x8000); + PORTH |= (1 << 5); // BLK3(PH5) HIGH + } + else if (rommap == 0xA0) { // BLK5 + PORTH &= ~(1 << 6); // BLK5(PH6) LOW + readSegment_VIC20(0xA000,0xA800); // 2K + if (romsize > 2) { + readSegment_VIC20(0xA800,0xB000); // +2K = 4K + if (romsize > 4) + readSegment_VIC20(0xB000,0xC000); // +4K = 8K + } + PORTH |= (1 << 6); // BLK5(PH6) HIGH + } + else if (rommap == 0xB0) { // BLK5 UPPER HALF + PORTH &= ~(1 << 6); // BLK5(PH6) LOW + readSegment_VIC20(0xB000,0xB800); // 2K + if (romsize > 2) + readSegment_VIC20(0xB800,0xC000); // +2K = 4K + PORTH |= (1 << 6); // BLK5(PH6) HIGH + } + myFile.close(); + + print_Msg(F("ROM")); + print_Msg(x); + print_Msg(FS(FSTRING_SPACE)); + printCRC(fileName, NULL, 0); + } + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +void setROMSize_VIC20() +{ +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + if (vic20lo == vic20hi) + newvic20size = vic20lo; + else { + int b = 0; + int i = vic20lo; + while (1) { + display_Clear(); + print_Msg(F("ROM0 Size: ")); + println_Msg((VIC20SIZE[i] & 0xF0) >> 4); + print_Msg(F("ROM1 Size: ")); + println_Msg(VIC20SIZE[i] & 0x0F); + println_Msg(FS(FSTRING_EMPTY)); + println_Msg(F("Press to Change")); + println_Msg(F("Hold to Select")); + display_Update(); + b = checkButton(); + if (b == 2) { // Previous (doubleclick) + if (i == vic20lo) + i = vic20hi; + else + i--; + } + if (b == 1) { // Next (press) + if (i == vic20hi) + i = vic20lo; + else + i++; + } + if (b == 3) { // Long Press - Execute (hold) + newvic20size = i; + break; + } + } + display.setCursor(0, 48); // Display selection at bottom + } + print_Msg(F("ROM0 SIZE ")); + print_Msg((VIC20SIZE[newvic20size] & 0xF0) >> 4); + println_Msg(F("KB")); + print_Msg(F("ROM1 SIZE ")); + print_Msg(VIC20SIZE[newvic20size] & 0x0F); + println_Msg(F("KB")); + display_Update(); + delay(1000); +#else + if (vic20lo == vic20hi) + newvic20size = vic20lo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (vic20hi - vic20lo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print((VIC20SIZE[i + vic20lo] & 0xF0) >> 4); + Serial.print(F("KB/")); + Serial.print(VIC20SIZE[i + vic20lo] & 0x0F); + Serial.println(F("KB")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newvic20size = sizeROM.toInt() + vic20lo; + if (newvic20size > vic20hi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(FS(FSTRING_EMPTY)); + goto setrom; + } + } + Serial.print(F("ROM0 Size = ")); + Serial.print((VIC20SIZE[newvic20size] & 0xF0) >> 4); + Serial.println(F("KB")); + Serial.print(F("ROM1 Size = ")); + Serial.print(VIC20SIZE[newvic20size] & 0x0F); + Serial.println(F("KB")); +#endif + EEPROM_writeAnything(8, newvic20size); + vic20size = newvic20size; +} + +void checkStatus_VIC20() +{ + EEPROM_readAnything(7, vic20map); + EEPROM_readAnything(8, vic20size); + if (vic20map > 8) { + vic20map = 7; // default 0xA000 + EEPROM_writeAnything(7, vic20map); + } + if (vic20size > vic20hi) { + vic20size = 2; // default 8K + EEPROM_writeAnything(8, vic20size); + } + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + println_Msg(F("COMMODORE VIC-20")); + println_Msg(FS(FSTRING_CURRENT_SETTINGS)); + println_Msg(FS(FSTRING_EMPTY)); + print_Msg(F("ROM MAP: ")); + println_Msg(VIC20MAP[vic20map], HEX); + print_Msg(F("ROM0 SIZE: ")); + print_Msg((VIC20SIZE[vic20size] & 0xF0) >> 4); + println_Msg(F("KB")); + print_Msg(F("ROM1 SIZE: ")); + print_Msg(VIC20SIZE[vic20size] & 0x0F); + println_Msg(F("KB")); + display_Update(); + wait(); +#else + Serial.print(F("CURRENT ROM MAP: ")); + Serial.println(VIC20MAP[vic20map]); + Serial.print(F("CURRENT ROM0 SIZE: ")); + Serial.print((VIC20SIZE[vic20size] & 0xF0) >> 4); + Serial.println(F("KB")); + Serial.print(F("CURRENT ROM1 SIZE: ")); + Serial.print(VIC20SIZE[vic20size] & 0x0F); + Serial.println(F("KB")); + Serial.println(FS(FSTRING_EMPTY)); +#endif +} + +//****************************************** +// SET ROM MAP +//****************************************** + +void setROMMap_VIC20() +{ +#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 /* ENABLE_OLED | ENABLE_LCD */ + + 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 /* ENABLE_OLED | ENABLE_LCD */ + if (buttonVal1 == HIGH) { // Button Released + // Correct Overshoot + if (i == 0) + i = vic20mapcount - 1; + else + i--; + break; + } + display_Clear(); + print_Msg(F("ROM Map: ")); + vic20mapselect = VIC20MAP[i]; + println_Msg(vic20mapselect, HEX); + if (i == (vic20mapcount - 1)) + i = 0; + else + i++; + delay(250); + } + } + while (1) { + display_Clear(); + print_Msg(F("ROM Map: ")); + vic20mapselect = VIC20MAP[i]; + println_Msg(vic20mapselect, HEX); + println_Msg(FS(FSTRING_EMPTY)); + println_Msg(F("Press to Change")); + println_Msg(F("Hold to Select")); + display_Update(); + b = checkButton(); + if (b == 2) { // Previous ROM Map (doubleclick) + if (i == 0) + i = vic20mapcount - 1; + else + i--; + } + if (b == 1) { // Next ROM Map (press) + if (i == (vic20mapcount - 1)) + i = 0; + else + i++; + } + if (b == 3) { // Long Press - Execute (hold) + newvic20map = i; + break; + } + } + display.setCursor(0, 56); + print_Msg(F("ROM MAP ")); + print_Msg(vic20mapselect, HEX); + println_Msg(F(" SELECTED")); + display_Update(); + delay(1000); +#else + String newmap; + Serial.println(F("ROM MAP:")); + Serial.println(F("0 = 0x2000")); + Serial.println(F("1 = 0x2000/0x4000")); + Serial.println(F("2 = 0x2000/0xA000")); + Serial.println(F("3 = 0x4000/0x6000")); + Serial.println(F("4 = 0x6000")); + Serial.println(F("5 = 0x6000/0xA000")); + Serial.println(F("6 = 0x7000")); + Serial.println(F("7 = 0xA000")); + Serial.println(F("8 = 0xB000")); + Serial.print(F("Enter Mapper [0-8]: ")); + while (Serial.available() == 0) {} + newmap = Serial.readStringUntil('\n'); + Serial.println(newmap); + newvic20map = newmap.toInt(); +#endif + EEPROM_writeAnything(7, newvic20map); + vic20map = newvic20map; +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +void setCart_VIC20() +{ + //go to root + sd.chdir(); + + struct database_entry_mapper_size entry; + + // Select starting letter + byte myLetter = starting_letter(); + + // Open database + if (myFile.open("vic20cart.txt", O_READ)) { + seek_first_letter_in_database(myFile, myLetter); + + if(checkCartSelection(myFile, &readDataLineMapperSize, &entry)) { + EEPROM_writeAnything(7, entry.gameMapper); + EEPROM_writeAnything(8, entry.gameSize); + } + } else { + print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND)); + } +} +#endif \ No newline at end of file diff --git a/Cart_Reader/VSMILE.ino b/Cart_Reader/VSMILE.ino new file mode 100644 index 0000000..330f880 --- /dev/null +++ b/Cart_Reader/VSMILE.ino @@ -0,0 +1,365 @@ +//****************************************** +// VSMILE MODULE +//****************************************** +#ifdef ENABLE_VSMILE +// V.Smile +// Cartridge pinout +// 48P 2.54mm pitch connector +// +// FRONT BACK +// SIDE SIDE +// +---------+ +// VCC -| 25 1 |- WE +// RAMCS -| 26 2 |- OE +// VCC -| 27 3 |- GND +// CSB2 -| 28 4 |- D3 +// D2 -| 29 5 |- D4 +// D1 -| 30 6 |- D5 +// D0 -| 31 7 |- D6 +// D7 -| 32 8 |- D11 +// D10 -| 33 9 |- D12 +// D9 -| 34 10 |- D13 +// D8 -| 35 11 |- D15 +// D14 -| 36 12 |- A1 +// A17 -| 37 13 |- A2 +// A3 -| 38 14 |- A4 +// |---------| +// A5 -| 39 15 |- A6 +// A7 -| 40 16 |- A8 +// A18 -| 41 17 |- A19 +// A9 -| 42 18 |- A10 +// A11 -| 43 19 |- A12 +// A13 -| 44 20 |- A14 +// A15 -| 45 21 |- A16 +// A20 -| 46 22 |- A21 +// A22 -| 47 23 |- GND +// CSB1 -| 48 24 |- VCC +// +---------+ +// +// DATA D0-D7 - PORTC +// DATA D8-D15 - PORTA +// ADDR A1-A8 - PORTF +// ADDR A9-A16 - PORTK +// ADDR A17-A22 - PORTL +// CONTROL PINS - PORTH + +//****************************************** +// DEFINES +//****************************************** +#define OE_HIGH PORTH |= (1<<3) // SNES /CART +#define OE_LOW PORTH &= ~(1<<3) +#define CSB1_HIGH PORTH |= (1<<6) // SNES /RD +#define CSB1_LOW PORTH &= ~(1<<6) +#define CSB2_HIGH PORTH |= (1<<4) // SNES /IRQ +#define CSB2_LOW PORTH &= ~(1<<4) + +//****************************************** +// VARIABLES +//****************************************** +byte VSMILE[] = {4,6,8,16}; +byte vsmilelo = 0; // Lowest Entry +byte vsmilehi = 3; // Highest Entry +byte vsmilesize; +byte newvsmilesize; + +// EEPROM MAPPING +// 08 ROM SIZE + +//****************************************** +// MENU +//****************************************** +// Base Menu +static const char* const menuOptionsVSMILE[] PROGMEM = { FSTRING_SELECT_CART, FSTRING_READ_ROM, FSTRING_SET_SIZE, FSTRING_RESET }; + +void vsmileMenu() +{ + convertPgm(menuOptionsVSMILE, 4); + uint8_t mainMenu = question_box(F("V.SMILE MENU"), menuOptions, 4, 0); + + // wait for user choice to come back from the question box menu + switch (mainMenu) { + // Select Cart + case 0: + setCart_VSMILE(); + setup_VSMILE(); + break; + + // Read ROM + case 1: + sd.chdir("/"); + readROM_VSMILE(); + sd.chdir("/"); + break; + + // Set Size + case 2: + setROMSize_VSMILE(); + break; + + // Reset + case 3: + resetArduino(); + break; + } +} + +//****************************************** +// SETUP +//****************************************** + +void setup_VSMILE() +{ + // Request 3.3V + setVoltage(VOLTS_SET_3V3); + + // Control Pins OE(PH3), CSB2(PH4), CSB1(PH6) + DDRH = 0x58; // 0b01011000 - CSB1, CSB2, OE [OUTPUT] - Unused Pins [INPUT] + PORTH = 0xFF; // 0b11111111 - CSB1, CSB2, OE [HIGH] - Unused Pins [HIGH] + + // Address Pins + DDRF = 0xFF; // Address A1-A8 [OUTPUT] + DDRK = 0xFF; // Address A9-A16 [OUTPUT] + DDRL = 0x3F; // Address A17-A22 [OUTPUT] 0b00111111 + // Data Pins + DDRC = 0x00; // D0-D7 [INPUT] + DDRA = 0x00; // D8-D15 [INPUT] + + checkStatus_VSMILE(); + strcpy(romName, "VSMILE"); + + mode = CORE_VSMILE; +} + +//****************************************** +// READ FUNCTIONS +//****************************************** +// Max Single ROM Size 0x800000 (Highest WORD Address = 0x3FFFFF) +word read_rom_word_VSMILE(unsigned long address) +{ + PORTL = (address >> 16) & 0xFF; + PORTK = (address >> 8) & 0xFF; + PORTF = address & 0xFF; + _delay_us(1); // Need longer delay + CSB2_HIGH; + CSB1_LOW; + OE_LOW; + unsigned char data1 = PINC; + unsigned char data2 = PINA; + word data = (data1 << 8) | (data2); + OE_HIGH; + CSB1_HIGH; + + return data; +} + +// VSMILE 2ND EPOXY CHIP [+2MB] +// CSB2 LOW ONLY +word read_rom2_word_VSMILE(unsigned long address) +{ + PORTL = (address >> 16) & 0xFF; + PORTK = (address >> 8) & 0xFF; + PORTF = address & 0xFF; + _delay_us(1); // Need longer delay + CSB1_HIGH; + CSB2_LOW; + OE_LOW; + _delay_us(1); // Need longer delay + unsigned char data1 = PINC; + unsigned char data2 = PINA; + word data = (data1 << 8) | (data2); + OE_HIGH; + CSB2_HIGH; + + return data; +} + +// VSMILE MOTION 16MB 2ND CHIP [+8MB] +// CSB1 + CSB2 LOW +word read_rom3_word_VSMILE(unsigned long address) +{ + PORTL = (address >> 16) & 0xFF; + PORTK = (address >> 8) & 0xFF; + PORTF = address & 0xFF; + CSB1_LOW; + CSB2_LOW; + OE_LOW; + unsigned char data1 = PINC; + unsigned char data2 = PINA; + word data = (data1 << 8) | (data2); + OE_HIGH; + CSB1_HIGH; + CSB2_HIGH; + + return data; +} + +//****************************************** +// READ ROM +//****************************************** + +void readROM_VSMILE() +{ + createFolderAndOpenFile("VSMILE", "ROM", romName, "bin"); + + for (unsigned long address = 0; address < 0x200000; address += 256) { // 4MB + for (unsigned int x = 0; x < 256; x++) { + word tempword = read_rom_word_VSMILE(address + x); // CSB1 LOW [CSB2 HIGH] + sdBuffer[x * 2] = (tempword >> 0x8) & 0xFF; + sdBuffer[(x * 2) + 1] = tempword & 0xFF; + } + myFile.write(sdBuffer, 512); + } + if (vsmilesize == 1) { // 6MB - 2 EPOXY CHIPS [4MB + 2MB] Alphabet Park/Care Bears + for (unsigned long address = 0; address < 0x100000; address += 256) { // +2MB HIGH = 6MB + for (unsigned int x = 0; x < 256; x++) { + word tempword = read_rom2_word_VSMILE(address + x); // CSB2 LOW [CSB1 HIGH] + sdBuffer[x * 2] = (tempword >> 0x8) & 0xFF; + sdBuffer[(x * 2) + 1] = tempword & 0xFF; + } + myFile.write(sdBuffer, 512); + } + } + else if (vsmilesize > 1) { // Normal 8MB + for (unsigned long address = 0x200000; address < 0x400000; address += 256) { // +4MB = 8MB + for (unsigned int x = 0; x < 256; x++) { + word tempword = read_rom_word_VSMILE(address + x); // CSB1 LOW [CSB2 HIGH] + sdBuffer[x * 2] = (tempword >> 0x8) & 0xFF; + sdBuffer[(x * 2) + 1] = tempword & 0xFF; + } + myFile.write(sdBuffer, 512); + } + if (vsmilesize > 2) { // Motion 16MB [8MB + 8MB] - Cars 2/Shrek Forever After/Super WHY!/Toy Story 3 + for (unsigned long address = 0; address < 0x400000; address += 256) { // +8MB HIGH = 16MB + for (unsigned int x = 0; x < 256; x++) { + word tempword = read_rom3_word_VSMILE(address + x); // CSB1 + CSB2 LOW + sdBuffer[x * 2] = (tempword >> 0x8) & 0xFF; + sdBuffer[(x * 2) + 1] = tempword & 0xFF; + } + myFile.write(sdBuffer, 512); + } + } + } + myFile.close(); + + printCRC(fileName, NULL, 0); + + println_Msg(FS(FSTRING_EMPTY)); + print_STR(press_button_STR, 1); + display_Update(); + wait(); +} + +//****************************************** +// ROM SIZE +//****************************************** + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) +void printRomSize_VSMILE(int index) +{ + display_Clear(); + print_Msg(FS(FSTRING_ROM_SIZE)); + println_Msg(VSMILE[index]); +} +#endif + +void setROMSize_VSMILE() +{ + byte newvsmilesize; +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + if (vsmilelo == vsmilehi) + newvsmilesize = vsmilelo; + else { + newvsmilesize = navigateMenu(vsmilelo, vsmilehi, &printRomSize_VSMILE); + + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(VSMILE[newvsmilesize]); + println_Msg(F("KB")); + display_Update(); + delay(1000); +#else + if (vsmilelo == vsmilehi) + newvsmilesize = vsmilelo; + else { +setrom: + String sizeROM; + for (int i = 0; i < (vsmilehi - vsmilelo + 1); i++) { + Serial.print(F("Select ROM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(VSMILE[i + vsmilelo]); + Serial.println(F("KB")); + } + Serial.print(F("Enter ROM Size: ")); + while (Serial.available() == 0) {} + sizeROM = Serial.readStringUntil('\n'); + Serial.println(sizeROM); + newvsmilesize = sizeROM.toInt() + vsmilelo; + if (newvsmilesize > vsmilehi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(FS(FSTRING_EMPTY)); + goto setrom; + } + } + Serial.print(F("ROM Size = ")); + Serial.print(VSMILE[newvsmilesize]); + Serial.println(F("KB")); +#endif + EEPROM_writeAnything(8, newvsmilesize); + vsmilesize = newvsmilesize; +} + +void checkStatus_VSMILE() +{ + EEPROM_readAnything(8, vsmilesize); + if (vsmilesize > vsmilehi) { + vsmilesize = 2; // default 8M + EEPROM_writeAnything(8, vsmilesize); + } + +#if (defined(ENABLE_OLED) || defined(ENABLE_LCD)) + display_Clear(); + println_Msg(F("V.SMILE READER")); + println_Msg(FS(FSTRING_CURRENT_SETTINGS)); + println_Msg(FS(FSTRING_EMPTY)); + print_Msg(FS(FSTRING_ROM_SIZE)); + print_Msg(VSMILE[vsmilesize]); + println_Msg(F("MB")); + display_Update(); + wait(); +#else + Serial.print(FS(FSTRING_ROM_SIZE)); + Serial.print(VSMILE[vsmilesize]); + Serial.println(F("MB")); + Serial.println(FS(FSTRING_EMPTY)); +#endif +} + +//****************************************** +// CART SELECT CODE +//****************************************** + +void setCart_VSMILE() +{ + //go to root + sd.chdir(); + + byte gameSize; + + // Select starting letter + //byte myLetter = starting_letter(); + + // Open database + if (myFile.open("vsmilecart.txt", O_READ)) { + // seek_first_letter_in_database(myFile, myLetter); + + if(checkCartSelection(myFile, &readDataLineSingleDigit, &gameSize)) { + EEPROM_writeAnything(8, gameSize); + } + } else { + print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND)); + } +} +#endif diff --git a/sd/README.md b/sd/README.md index 7ccc087..70bdc56 100644 --- a/sd/README.md +++ b/sd/README.md @@ -339,3 +339,135 @@ cart size: 3 = 16K 4 = 32K 5 = 64K + +## ballycart.txt +romsize +0 = 2K +1 = 4K +2 = 8K + +## pv1000cart.txt +romsize +0 = 8K +1 = 16K + +## pyuutacart.txt +romsize +0 = 8K +1 = 16K +2 = 32K + +## rcacart.txt +romsize +0 = 512B +1 = 1024B (1K) + +## trs80cart.txt +romsize +0 = 2K +1 = 4K +2 = 8K +3 = 10K +4 = 16K +5 = 32K +6 = 64K +7 = 128K + +## vic20cart.txt (rommap,romsize) + +rommap +upper nibble = ROM0 {0x20,0x40,0x60,0x70,0xA0,0xB0} +lower nibble = ROM1 {0x40,0x60,0xA0} +0 = 0x20 = 0x2000 +1 = 0x24 = 0x2000/0x4000 +2 = 0x2A = 0x2000/0xA000 +3 = 0x46 = 0x4000/0x6000 +4 = 0x60 = 0x6000 +5 = 0x6A = 0x6000/0xA000 +6 = 0x70 = 0x7000 +7 = 0xA0 = 0xA000 +8 = 0xB0 = 0xB000 + +romsize +upper nibble = ROM0 {2,4,8} +lower nibble = ROM1 {0,4,8} +0 = 0x20 = 2K/0K 0x800 +1 = 0x40 = 4K/0K 0x1000 +2 = 0x80 = 8K/0K 0x2000 +3 = 0x44 = 4K/4K 0x1000/0x1000 +4 = 0x48 = 4K/8K 0x1000/0x2000 +5 = 0x84 = 8K/4K 0x2000/0x1000 +6 = 0x88 = 8K/8K 0x2000/0x2000 + +## ti99cart.txt (mapper,gromsize,grommap,romsize) +mapper +0 = Normal Carts +1 = MBX +2 = TI-CALC [UNTESTED] + +gromsize +0 = 0K +1 = 6K +2 = 12K +3 = 18K +4 = 24K +5 = 30K + +grommap +Map corresponding bits +GROM# BITMAP HEX DEC +3 = 0000 1000 = 08 = 8 +4 = 0001 0000 = 10 = 16 +5 = 0010 0000 = 20 = 32 +7 = 1000 0000 = 80 = 128 +34 = 0001 1000 = 18 = 24 +35 = 0010 1000 = 28 = 40 +45 = 0011 0000 = 30 = 48 +56 = 0110 0000 = 60 = 96 +67 = 1100 0000 = C0 = 192 +345 = 0011 1000 = 38 = 56 +356 = 0110 1000 = 68 = 104 +456 = 0111 0000 = 70 = 112 +3456 = 0111 1000 = 78 = 120 +3457 = 1011 1000 = B8 = 184 +3467 = 1101 1000 = D8 = 216 +34567 = 1111 1000 = F8 = 248 + +romsize +0 = 0K +1 = 4K +2 = 8K +3 = 12K +4 = 16K + +## leapster.txt +romsize +0 = 4K +1 = 8K +2 = 16K + +## vsmilecart.txt +romsize +0 = 4K +1 = 6K +2 = 8K +3 = 16K + +## ljcart.txt +romsize +0 = 1M +1 = 2M +2 = 4M + +## atari8cart.txt (slot,romsize) +slot +0 = left slot +1 = right slot + +romsize +0 = 8K +1 = 16K +2 = 32K +3 = 40K +4 = 64K +5 = 128K diff --git a/sd/atari8cart.txt b/sd/atari8cart.txt new file mode 100644 index 0000000..61d28c7 --- /dev/null +++ b/sd/atari8cart.txt @@ -0,0 +1,946 @@ +1400 Super SALT +0,1 + +3-D Tic-Tac-Toe +0,0 + +400/800 SALT +0,0 + +600xl/800xl SALT +0,1 + +810 Diagnostic Cartridge +0,0 + +Abracadabra +0,1 + +ACE-80XL +1,1 + +Ace of Aces +0,5 + +Action! +0,1 + +Adventure Creator +0,1 + +Airball +0,5 + +Alf in the Color Caves +0,1 + +Alien Ambush +0,0 + +Alien Garden +0,0 + +Alpha Shield +0,0 + +Alphabet Zoo +0,0 + +Ant Eater +0,0 + +Archon +0,2 + +Assembler Editor +0,0 + +AST +0,0 + +AST 2000 +0,1 + +Asteroids +0,0 + +Astro Chase +0,1 + +Astro Grover +0,1 + +Atari BASIC +0,0 + +Atari LOGO +0,1 + +AtariArtist +0,1 + +AtariGraphics +0,1 + +AtariLab Light Module +0,1 + +Atarilab Temperature Module +0,1 + +AtariSchreiber +0,1 + +AtariTexte +0,1 + +AtariWriter +0,1 + +Atlantis +0,0 + +Attack at EP-CYG-4 +0,1 + +Attack of the Mutant Camels +0,0 + +Ballblazer +0,4 + +Barnyard Blaster +0,5 + +Baseball +0,1 + +BASIC +0,0 + +Basic XE +0,1 + +Basic XL +0,1 + +Basketball +0,0 + +BattleZone +0,4 + +BC's Quest for Tires +0,1 + +Beamrider +0,1 + +BearJam +0,0 + +Big Bird's Funhouse +0,1 + +Big Bird's Special Delivery +0,1 + +Blue Max +0,2 + +Boulder Dash +0,1 + +Boulders and Bombs +0,0 + +Bounty Bob Strikes Back! +0,3 + +Bristles +0,1 + +Buck Rogers +0,1 + +Bug Hunt +0,4 + +Captain Beeble +0,1 + +Carnival Massacre +0,1 + +Castle Hassle +0,1 + +Castles and Keys +0,1 + +Caverns of Mars +0,1 + +Centipede +0,0 + +Chess +0,0 + +Chicken +0,0 + +Chiffres et des Lettres +0,1 + +Choplifter! +0,1 + +Choplifter! (XE) +0,4 + +Claim Jumper +0,1 + +Cloudburst +0,0 + +CocoNotes +0,1 + +Compu=Prompt +0,0 + +Computer Chess +0,0 + +Computer War +0,1 + +Computrac 4000-8000 +0,1 + +Congo Bongo +0,1 + +Conquest of the Crown +0,1 + +Cosmic Life +0,0 + +CPS Super SALT 400/800/XL +0,1 + +Crime Buster +0,5 + +Crossbow +0,5 + +CrossFire +0,0 + +Crystal Castles +0,2 + +Da' Fuzz +0,0 + +Dance Fantasy +0,0 + +Dark Chambers +0,4 + +David's Midnight Magic +0,4 + +Decathlon +0,1 + +Defender +0,1 + +Delta Drawing +0,0 + +Deluxe Invaders +0,0 + +Demon Attack +0,0 + +Desert Falcon +0,4 + +Designer's Pencil +0,1 + +Diamond Graphic OS +0,4 + +Diamond Mine +0,1 + +Dig Dug +0,1 + +Direct Access +0,1 + +Donkey Kong +0,1 + +Donkey Kong Jr. +0,1 + +Dreadnaught Factor +0,0 + +Droids +0,0 + +DT-80 +1,0 + +Ducks Ahoy! +0,1 + +E.T. Phone Home! +0,1 + +Eastern Front 1941 +0,1 + +Edit 6502 +0,0 + +Editor Assembler +0,0 + +Educational System +0,0 + +Embargo +0,0 + +Ernie's Magic Shapes +0,1 + +Espial +0,1 + +Expando-Vision +0,0 + +Express! +0,4 + +FaceMaker +0,0 + +Fantastic Voyage +0,0 + +Fast Eddie +0,0 + +Fight Night +0,5 + +Final Legacy +0,1 + +Final Orbit +0,0 + +Firebird +0,0 + +Flapper +0,1 + +Flight Simulator II +0,5 + +Flip and Flop +0,1 + +Food Fight +0,2 + +Fort Apocalypse +0,1 + +Fortune Hunter +0,0 + +Fraction Fever +0,0 + +Frogger +0,0 + +Frogger II +0,1 + +Fun with Art +0,1 + +Galaxian +0,0 + +Gateway To Apshai +0,1 + +GATO Submarine Simulation +0,5 + +Gold Mine +0,0 + +Gorf +0,0 + +Gridrunner +0,0 + +Gyruss +0,1 + +H.E.R.O. +0,1 + +Halftime Battlin' Bands +0,1 + +Ham Text +0,0 + +Hamsoft / Amtor +0,0 + +Hard Hat Willy +0,1 + +Hardball! +0,4 + +Homebase Electronic Banking +0,0 + +Hypnotic Land +0,1 + +In-Store Demonstration +0,1 + +INFO-soft +0,1 + +Info/Gen +0,0 + +Into the Eagle's Nest +0,2 + +James Bond 007 +0,1 + +Jawbreaker II +0,0 + +Journey to the Planets +0,1 + +Joust +0,1 + +Jumbo Jet Pilot +0,1 + +Jumpman Junior +0,1 + +Jungle Hunt +0,1 + +K-Razy Antiks +0,0 + +K-Razy Kritters +0,0 + +K-Razy Shoot Out +0,0 + +K-Star Patrol +0,0 + +Kaboom! +0,0 + +Karateka +0,5 + +Keystone Kapers +0,0 + +Kickback +0,0 + +Kids on Keys +0,0 + +KinderComp +0,0 + +Koala Painter +0,1 + +Learning Phone +0,0 + +Learning with Leeper +0,1 + +Leo's 'Lectric Paintbrush +0,0 + +Letter Perfect +0,0 + +Linking Logic +0,0 + +Lode Runner +0,4 + +Logic Levels +0,0 + +LogicMaster +0,0 + +M*A*S*H +0,0 + +MAC-65 +0,1 + +Magic Dump II +1,0 + +Major League Hockey +0,0 + +Mario Bros. +0,4 + +Master Type +0,1 + +Math Encounter +0,0 + +Math Mileage +0,0 + +Math Works +0,0 + +Matterhorn +0,1 + +MegaMania +0,0 + +Memory Manor +0,0 + +Microcalc XE +0,2 + +Microfiler +0,0 + +MicroIllustrator +0,1 + +MicroMaestro +0,0 + +Microprinter System +0,0 + +Microsoft BASIC II +0,1 + +Millipede +0,1 + +Miner 2049er +0,1 + +Missile Command +0,0 + +Mogul Maniac +0,1 + +Monkey Wrench +1,0 + +Monkey Wrench II +1,0 + +Monster Maze +0,0 + +Moon Patrol +0,1 + +Mountain King +0,0 + +Movie Musical Madness +0,1 + +Mr. Cool +0,0 + +Mr. TNT +0,0 + +Ms. Pac-Man +0,1 + +Multi Fischa +0,0 + +Music Composer +0,0 + +Necromancer +0,1 + +Night Strike! +0,0 + +Oil's Well +0,1 + +One-on-One +0,2 + +Orc Attack +0,1 + +Ozzy's Orchard +0,1 + +Pac-Man +0,0 + +Paddle Jitter Test +0,1 + +Pastfinder +0,1 + +Peanut Butter Panic +0,0 + +Pengo +0,1 + +Picnic Paranoia +0,1 + +PILOT +0,0 + +Pitfall! +0,0 + +Pitfall II +0,1 + +Pitstop +0,1 + +PLATO +0,0 + +PlatterMania +0,0 + +Pocket Modem Software +0,0 + +Pole Position +0,1 + +Pool 400 +0,0 + +Popeye +0,1 + +Porky's +0,1 + +Powerstar +0,1 + +Princess and the Frog +0,0 + +Pronto +0,1 + +Protector II +0,1 + +Q*bert +0,0 + +Qix +0,0 + +Rack 'em Up! +0,1 + +Rally Speedway +0,1 + +RealSports Football +0,1 + +Rescue on Fractalus! +0,4 + +River Raid +0,0 + +River Rescue +0,1 + +Robotron 2084 +0,1 + +Sea Chase +0,0 + +Sea Horse Hide 'n Seek +0,1 + +Seafox +0,1 + +Serpentine +0,0 + +Sesame Street Letter-Go-Round +0,1 + +Shamus +0,1 + +Silicon Warrior +0,1 + +Sky Writer +0,1 + +Slime +0,1 + +Smart Terminal +0,0 + +Soccer +0,0 + +Space Invaders +0,0 + +Space Journey +0,1 + +Space Shuttle +0,1 + +Spark Bugs +0,0 + +Sparta DOS X +0,4 + +Spectraview II +0,1 + +Speedway Blast +0,0 + +Spider City +0,0 + +Springer +0,1 + +Spy Hunter +0,1 + +Star Maze +0,1 + +Star Raiders +0,0 + +Star Raiders II +0,2 + +Star Trek +0,1 + +Star Wars The Arcade Game +0,1 + +Star Wars Return of the Jedi +0,0 + +Starion +0,1 + +Story Machine +0,1 + +Submarine Commander +0,1 + +Summer Games +0,5 + +Super Breakout +0,0 + +Super Cobra +0,0 + +Super E-Burner +0,0 + +Super Sketch Graphics Master +0,0 + +Super Zaxxon +0,1 + +Survival of the Fittest +0,0 + +Synassembler +0,0 + +Tamlilan +0,1 + +Target +0,1 + +Telelink I +0,0 + +Telelink II +0,0 + +Tennis +0,1 + +Test Atari 65-130XE +0,0 + +Thera-Med Zahnschutz-Spiel +0,0 + +Thunderfox +0,4 + +Timebound +0,1 + +Topper +0,1 + +Track & Field +0,1 + +Trion +0,1 + +Turbo System +0,0 + +Turmoil +0,0 + +Twin Pack +0,0 + +Typo Attack +0,0 + +Typo +0,0 + +Up 'n Down +0,1 + +Up for Grabs +0,0 + +Video Easel +0,0 + +Video Poker Card Game +0,1 + +VisiCopy III +0,0 + +Webster +0,0 + +Weltraumkolonie +0,0 + +Whiz Kid +0,0 + +Wilcox Message Display +0,0 + +Wizard of Wor +0,1 + +Worm War 1 +0,0 + +Writer's Tool +0,0 + +Zaxxon +0,1 + +Zenji +0,0 + +Zone Ranger +0,1 + +Zybex +0,4 + + diff --git a/sd/ballycart.txt b/sd/ballycart.txt new file mode 100644 index 0000000..cc24ced --- /dev/null +++ b/sd/ballycart.txt @@ -0,0 +1,103 @@ +280 Zzzap + Dodgem +0 + +Amazing Maze + Tic-Tac-Toe +0 + +Artillery Duel +1 + +Astro Battle +1 + +Bally BASIC +1 + +Bally Pin +1 + +Biorhythm +1 + +Blackjack +2 +1 + +Blast Droids +1 + +Brickyard + Clowns +1 + +Cosmic Raiders +2 + +Dog Patch +0 + +Elementary Math + Bingo Math +0 + +Football +1 + +Galactic Invasion +1 + +Galaxian +1 + +Grand Prix + Demolition Derby +1 + +ICBM Attack +1 + +Incredible Wizard +2 + +Letter Match +2 +1 + +Machine Language Manager +0 + +Ms. Candyman +1 + +Muncher +2 + +Panzer Attack + Red Baron +1 + +Pirate's Chase +1 + +Sea Devil +1 + +Seawolf + Missile +0 + +Sneaky Snake +1 + +Solar Conqueror +2 + +Space Fortress +1 + +Space Invaders +1 + +Star Battle +0 + +Tornado Baseball +3 +1 + +Treasure Cove +2 + + diff --git a/sd/leapster.txt b/sd/leapster.txt new file mode 100644 index 0000000..41805f8 --- /dev/null +++ b/sd/leapster.txt @@ -0,0 +1,217 @@ +1st Grade +1 + +2nd Grade +1 + +Animal Genius +1 + +Backyardigans +1 + +Batman Multiply, Divide and Conquer +1 + +Batman Strength in Numbers +1 + +Bratz World +1 + +Cars +1 + +Cars Supercharged +1 + +Cars 2 +1 + +Clifford The Big Red Dog +1 + +Cosmic Math +0 + +Crayola Art Adventure +1 + +Creature Create +1 + +Digging for Dinosaurs +1 + +Disney Fairies +1 + +Disney Princess +1 + +Disney Princess Enchanted Learning +1 + +Disney Princess Worlds of Enchantment +1 + +Disney-Pixar Collection +1 + +Dora the Explorer Animal Rescuer +1 + +Dora the Explorer Camping Adventure +1 + +Dora the Explorer Pinata Party +0 + +Dora the Explorer Wildlife Rescue +1 + +Finding Nemo +1 + +Foster's Home for Imaginary Friends +1 + +Get Puzzled! +1 + +Go Diego Go! +1 + +I Spy Challenger +1 + +I Spy Treasure Hunt +1 + +Incredibles +1 + +Kindergarten +1 + +Learning with Leap +1 + +Letter Factory +2 + +Letterpillar +0 + +Letters on the Loose +1 + +Madagascar +1 + +Math Baseball +1 + +Math Missions +1 + +Mr. Pencil's Learn to Draw & Write +1 + +My Amusement Park +1 + +NASCAR +1 + +nihao kai-lan +1 + +Noddy +1 + +Number Raiders +0 + +Numbers on the Run +1 + +OutWit! +1 + +Penguins of Madagascar +1 + +Pet Pals +1 + +Princess and the Frog +1 + +Ratatouille +1 + +Reading with Phonics +1 + +Rock the World +1 + +School House Rock! America Rock +2 + +School House Rock! Grammar Rock +2 + +Scooby Doo! +1 + +Scooby Doo! Spooky Snacks! +0 + +Sonic X +1 + +Spider-Man +1 + +SpongeBob SquarePants Saves the Day +1 + +SpongeBob SquarePants Through The Wormhole +0 + +Star Wars Jedi Math +1 + +Star Wars Jedi Reading +1 + +Talking WORDS Factory +2 + +Tangled +1 + +Thomas & Friends +1 + +Top-Secret Personal Beeswax +1 + +Toy Story 3 +1 + +Up +1 + +Wall-E +1 + +Wolverine and the X-Men +1 + +Word Chasers +0 + + diff --git a/sd/ljcart.txt b/sd/ljcart.txt new file mode 100644 index 0000000..d0ed506 --- /dev/null +++ b/sd/ljcart.txt @@ -0,0 +1,109 @@ +70's Rock&Pops Selection Vol. 5 +1 + +Asahi PRIME TIME Selection +0 + +Ballad Selection Vol 3 +1 + +Basic Selection +1 + +BigBand Selection Vol. 10 +1 + +BossaNova Selection Vol. 4 +1 + +Cinema Selection Camel Package Vol. 7 +1 + +Cinema Selection Navy Package Vol. 6 +1 + +Cinema Selection Olive Package Vol. 8 +1 + +Classical Jazz Selection Emerald Package Vol. 12 +1 + +Classical Jazz Selection Sapphire Package vol. 13 +1 + +DESKTOP JAM Basic Selection +1 + +Disco Hits Selection +1 + +Forever Rock & Pops Selection Vol. 11 +1 + +Healing Selection +1 + +High Note Selection +1 + +LIMITED S Edition +1 + +Latin Selection +1 + +Lighted Nights Selection +1 + +Little Jammer Selection +1 + +LITTLE JAMMER SELECTION-1 +1 + +NIKKA Selection +0 + +Owner's Club Selection +1 + +Owner's Club Selection2 Christmas Limited +1 + +Owner's Club Selection3 Jazz Ballad +1 + +Owner's Club Selection4 SAVOY Collection +0 + +Owner's Club Welcome Selection +1 + +Relaxing Selection Vol. 2 +1 + +Rhythm Jam +1 + +STRIPES LIMITED Selection +1 + +Standard Selection Vol. 1 +1 + +SwingJournal Selection +1 + +TVCM HITS! Wine and JAZZ&POPS Selection Vol. 9 +1 + +TeaTime Selection Vol. 14 +0 + +Wonderful Christmas Selection +0 + +Youth Song Selection Vol. 15 +0 + + diff --git a/sd/pv1000cart.txt b/sd/pv1000cart.txt new file mode 100644 index 0000000..9c1a942 --- /dev/null +++ b/sd/pv1000cart.txt @@ -0,0 +1,72 @@ +Amidar +0 + +Dig Dug +1 + +Dirty Chameleon +0 + +Excite Mahjong +1 + +Excite Mahjong 2 +1 + +Exciting Jockey +1 + +Fighting Bug +0 + +Front Line +0 + +Galaga +0 + +Mr. Packn +0 + +Naughty Boy +0 + +Pachinko - UFO +1 + +Pooyan (PV1000) +0 + +Pooyan (PV2000) +1 + +Rakugaki Special +1 + +Real Number Basic +1 + +Roc'n Rope +0 + +Ski Command +1 + +Space Panic +0 + +Super Cobra +0 + +Turpin +0 + +Tutankham +0 + +Warp & Warp +0 + + + +0 \ No newline at end of file diff --git a/sd/pyuutacart.txt b/sd/pyuutacart.txt new file mode 100644 index 0000000..70ac28e --- /dev/null +++ b/sd/pyuutacart.txt @@ -0,0 +1,97 @@ +4-nin Mahjong +1 +0 +Baseball [3D] +2 +19 +Battle Fighter [3D] +2 +20 +Bermuda Triangle +0 +26 +Bombman +0 +23 +Car-azy Racer +1 + +Cave Crawlers +0 + +Deep Six +0 + +Don Pan +0 + +Frogger +0 + +Guttang Gottong +0 + +Hyperspace +0 + +Jungler +0 + +Loco-Motion +0 + +Marine Adventure +0 + +Maze Patrol +0 + +Mickey Athletic Land +0 + +Mission Attack +0 + +Monster Inn +0 + +Mystery Gold +0 + +Mr. Do! +1 + +Night Flight +0 + +Pooyan +0 + +Rescue Copter [3D] +2 + +Saurusland +0 + +Scramble +0 + +Super Bike +1 + +Torpedo Terror +0 + +Traffic Jam +0 + +Triple Command +0 + +TRON +0 + +Turpin +0 + + diff --git a/sd/rcacart.txt b/sd/rcacart.txt new file mode 100644 index 0000000..f5257c2 --- /dev/null +++ b/sd/rcacart.txt @@ -0,0 +1,49 @@ +Baseball +1 + +Biorhythm +1 + +Blackjack +1 + +Concentration Match +1 + +Demonstration Cartridge +1 + +Fun with Numbers +0 + +Gunfighter + Moonship Battle +1 + +Pinball +1 + +Space War +0 + +Speedway + Tag (Europe) +1 + +Speedway + Tag (USA) +0 + +Star Wars +1 + +Tennis + Squash +0 + +TV Bingo +1 + +TV School House I +0 + +TV School House II - Math Fun +1 + + diff --git a/sd/ti99cart.txt b/sd/ti99cart.txt new file mode 100644 index 0000000..7911d1d --- /dev/null +++ b/sd/ti99cart.txt @@ -0,0 +1,835 @@ +4A DOS +0,0,000,2 + +4A Flyer +0,0,000,2 + +99 Home Sentry +0,0,000,2 + +Accounting Assistant +0,5,248,0 + +Activity Accountant +0,3,056,0 + +Addition +0,2,040,0 + +Addition and Subtraction 1 +0,3,056,0 + +Addition and Subtraction 2 +0,3,056,0 + +Addition and Subtraction 3 +0,4,120,0 + +Adventure +0,1,008,0 + +Alien Addition +0,2,024,0 + +Alligator Mix +0,2,024,0 + +Alpiner +0,4,120,1 + +A-Maze-Ing +0,1,008,0 + +Ambulance +0,0,000,2 + +Ant Colony +0,0,000,2 + +Ant-Eater +0,0,000,2 + +Attendance Recorder +0,5,248,0 + +Barrage +0,0,000,4 + +Beginning Grammar +0,2,024,0 + +Beyond Parsec +0,0,000,2 + +Beyond Space +0,0,000,2 + +Bigfoot +1,1,008,4 + +Black Hole +0,0,000,2 + +Blackjack and Poker +0,1,008,0 + +Blasto +0,1,008,0 + +Boxer +0,0,000,2 + +Break Thru +0,0,000,2 + +Buck Rogers +0,2,024,2 + +Burger Builder +0,0,000,2 + +Burgertime +0,2,024,2 + +Car Wars +0,1,008,0 + +Card Sharp +0,1,008,0 + +Centipede +0,0,000,2 + +Championship Baseball +1,3,056,4 + +Chicken Coop +0,0,000,2 + +Chisholm Trail +0,1,008,2 + +Class Data Recorder +0,5,248,0 + +Computer Math Games I +0,4,120,2 + +Computer Math Games II +0,3,056,0 + +Computer Math Games III +0,4,120,0 + +Computer Math Games IV +0,4,120,0 + +Computer Math Games VI +0,2,024,0 + +Computer War +0,0,000,4 + +Congo Bongo +0,2,024,2 + +Connect Four +0,1,016,0 + +Console Writer +0,0,000,2 + +Course Manager +0,5,248,0 + +Crossfire +0,1,008,2 + +Data Base Management System v2.0 +0,0,000,2 + +Decimal Deli 2 +0,4,120,0 + +Decimals +0,3,056,0 + +Defender +0,0,000,2 + +Demolition Division +0,2,024,0 + +Demonstration +0,4,120,0 + +Diagnostic +0,1,008,0 + +Diagnostic (FRA) +0,2,024,0 + +Dig Dug +0,0,000,4 + +Disk Fixer +0,0,000,2 + +Disk Fixer v2.0 +0,0,000,2 + +Disk Manager +0,2,024,0 + +Disk Manager 1 +0,2,024,0 + +Disk Manager 2 +0,2,024,0 + +Disk Manager 3 +0,2,024,0 + +Division +0,2,024,0 + +Donkey Kong +0,0,000,4 + +Dragon Mix +0,2,024,1 + +Driving Demon +0,0,000,2 + +D-Station I +0,0,000,2 + +D-Station II +0,0,000,2 + +E.T. +0,2,024,4 + +E.T. In His Adventure at Sea +0,5,248,2 + +Early Learning Fun +0,2,024,0 + +Early LOGO Learning Fun +0,1,008,2 + +Early Reading +0,5,248,0 + +Editor/Assembler +0,1,008,0 + +Edu-Pack +0,0,000,2 + +Electrifying Fractions 2 +0,4,120,0 + +Equations +0,2,024,0 + +Escape +0,0,000,2 + +Extended Basic +0,4,120,3 + +Face Chase +0,0,000,2 + +Facemaker +0,2,024,2 + +Fantastic Fractions 1 +0,3,112,0 + +Fantasy +0,5,248,2 + +Fathom +0,3,056,2 + +fig FORTH +0,0,000,2 + +Football +0,2,024,0 + +Fractional Numbers +0,3,104,0 + +Frog Jump +0,2,024,0 + +Frog Stickers +0,0,000,2 + +Frogger +0,1,128,2 + +Germ Patrol +0,5,248,2 + +Gestion Privee +0,3,056,0 + +Hangman +0,1,032,0 + +Hen Pecked +0,0,000,2 + +Henhouse +0,0,000,2 + +Home Financial Decisions +0,2,024,0 + +Homework Helper+ +0,0,000,2 + +Honey Hunt +1,3,056,4 + +Hopper +0,1,008,2 + +Household Budget Management +0,2,024,0 + +Hunt The Wumpus +0,1,008,0 + +Hustle +0,1,008,0 + +I'm hiding +1,2,024,4 + +Integers +0,2,024,0 + +Introduction to Plant Genetics +0,4,120,2 + +Jawbreaker II +0,1,008,2 + +Jeu d'Echecs +0,4,120,1 + +Jumpy +0,0,000,4 + +Jungle Hunt +0,0,000,4 + +Junkman Junior +0,0,000,4 + +Key to Spanish 1/2 +0,5,248,2 + +Key to Spanish 3/4 +0,5,248,2 + +Key to Spanish 5/6 +0,5,248,2 + +King of the Castle +0,0,000,2 + +Laws of Arithmetic +0,2,040,0 + +Link v0.99 +0,0,000,2 + +M.A.S.H +0,3,056,2 + +Mancala +0,5,248,2 + +Measurement Formulas +0,5,248,4 + +Meteor Belt +1,1,008,4 + +Meteor Multiplication +0,2,024,0 + +Micro Pinball 2 +0,0,000,4 + +Micro Tennis +0,0,000,4 + +Microsoft Multiplan +0,5,248,0 + +Microsurgeon +0,3,056,2 + +Midnite Mason +0,0,000,2 + +Milliken Manager +0,5,248,0 + +Milton Bradley Gamevision +0,5,248,0 + +Mini Memory +0,1,008,1 + +Miniwriter II +0,0,000,2 + +Minus Mission +0,2,024,0 + +Moon Mine +0,5,248,2 + +Moon Patrol +0,0,000,4 + +Moonsweeper +0,3,056,2 + +Mousk-Attack +0,4,120,2 + +Ms. Pac-Man +0,0,000,4 + +Multiplication +0,2,040,0 + +Munch Man +0,1,008,1 + +Munch Man II +0,0,000,2 + +Munchmobile +0,3,056,2 + +Music Maker +0,3,056,0 + +Music SDA +0,3,056,0 + +Nature's Way +0,0,000,2 + +Number Bowling +0,2,024,0 + +Number Magic +0,1,008,0 + +Number Magic (MULTI) +0,2,024,0 + +Number Readiness +0,2,024,0 + +Numeration 1 +0,4,120,0 + +Numeration 2 +0,3,056,0 + +Othello +0,1,008,1 + +Pac-Man +0,0,000,4 + +Paint 'N Print A +0,0,000,2 + +Paint 'N Print C +0,0,000,2 + +Parsec +0,3,056,2 + +Payroll Assistant +0,5,248,0 + +Percents +0,5,248,0 + +Peripheral Diagnostic Module +0,0,000,2 + +Personal Real Estate +0,4,120,0 + +Personal Record Keeping +0,4,120,0 + +Personal Record Keeping (GER/ITA) +0,5,248,0 + +Personal Report Generator (ENG/GER) +0,2,024,0 + +Peter Pan's Space Odyssey +0,5,248,2 + +Physical Fitness +0,2,024,0 + +Picnic Paranoia +0,0,000,4 + +Picture Parts +0,2,096,0 + +Pinocchio's Great Escape +0,5,248,2 + +Plato Interpreter +0,4,120,2 + +Pole Position +0,0,000,4 + +Popeye +0,2,192,2 + +Princess and Frog +0,0,000,2 + +Pro Typer +0,0,000,2 + +Protector II +0,0,000,4 + +Pyramid Puzzler +0,2,096,0 + +Q-Bert +0,1,128,2 + +Rabbit Trail +0,0,000,2 + +Reading Adventures +0,5,248,0 + +Reading Cheers +0,5,248,0 + +Reading Fun +0,5,248,0 + +Reading On +0,5,248,0 + +Reading Power +0,5,248,0 + +Reading Rainbows +0,5,248,0 + +Reading Trail +0,5,248,0 + +Reading Wonders +0,5,248,0 + +Return to Pirate's Isle +0,5,248,2 + +Robotron:2084 +0,0,000,4 + +Romox Demo +0,0,000,2 + +Rotor Raiders +0,0,000,2 + +Salary Planner +0,4,120,0 + +Schachmeister +0,4,120,1 + +Schnoz-ola +0,0,000,2 + +Scholastic Spelling Level 3 +0,5,248,1 + +Scholastic Spelling Level 4 +0,5,248,1 + +Scholastic Spelling Level 5 +0,5,248,4 + +Scholastic Spelling Level 6 +0,5,248,4 + +School Mailer +0,4,120,0 + +Securities Analysis +0,4,120,0 + +Sewermania +1,2,024,4 + +Shamus +0,0,000,4 + +Shanghai +0,0,000,2 + +Simon Says +0,1,008,0 + +Slymoids +0,1,008,2 + +SMU Electrical Engineering Library +0,2,024,0 + +Sneggit +0,1,008,2 + +Soccer +0,2,024,0 + +Sorgan II +0,0,000,4 + +Soundtrack Trolley +1,1,008,4 + +Space Bandits +1,2,024,4 + +Space Journey +0,2,096,0 + +Space Patrol +0,0,000,2 + +Speech Editor +0,1,008,0 + +Speed Reading A +0,0,000,2 + +Speed Reading B +0,0,000,2 + +Spy's Demise +0,0,000,2 + +St. Nick +0,1,008,2 + +Star Gazer I +0,0,000,2 + +Star Gazer II +0,0,000,2 + +Star Gazer III +0,0,000,2 + +Star Maze +0,2,024,0 + +Star Runner +0,0,000,4 + +Star Trek +0,2,024,2 + +Star Wars +0,0,000,2 + +Statistics +0,5,248,0 + +Statistik +0,5,248,0 + +Story Machine v1.4 +0,2,048,2 + +Strike Three! +0,0,000,4 + +Subtraction +0,2,024,0 + +Sudoku +0,0,000,2 + +Super Demon Attack +0,3,056,2 + +Super Duper v1.1 +0,0,000,2 + +Super Sketch Model G2400 +0,0,000,2 + +Super Sort +0,0,000,2 + +Super Storm +0,0,000,4 + +Superfly +1,2,024,4 + +SuperSpace II +0,1,008,0 + +T.I. Toad +0,0,000,2 + +Tax Investment Record Keeping +0,4,120,2 + +Tennis +0,0,000,4 + +Terminal Emulator I +0,2,024,0 + +Terminal Emulator II +0,4,216,1 + +Terry Turtle's Adventure +1,3,056,4 + +Testtrainer 1 +0,2,024,0 + +The Attack +0,1,008,0 + +The Castle (Preview) +0,0,000,2 + +TI Invaders +0,1,008,1 + +TI LOGO +0,4,120,0 + +TI LOGO II +0,3,056,2 + +TI LOGO II (GER) +0,4,120,2 + +TI LOGO II (ITA) +0,4,184,2 + +TI Planner +0,0,000,2 + +TI-CALC +2,1,008,4 + +TI-IBM Copier +0,0,000,2 + +Tile Breaker +0,0,000,2 + +TI-Writer +0,1,008,0 + +Tombstone City +0,1,008,1 + +Topper +0,0,000,2 + +Touch Typing Tutor +0,3,056,0 + +Treasure Island +0,3,056,2 + +Tunnels of Doom +0,5,248,0 + +Typo II +0,0,000,2 + +Typoman +0,0,000,2 + +UCSD Pascal Development System +0,1,008,2 + +VAT Accounting +0,4,120,0 + +Verb Viper +0,2,024,0 + +Video Chess +0,4,120,1 + +Video Games 1 +0,2,024,0 + +Video Games 2 +0,1,008,0 + +Video Vegas +0,0,000,2 + +Video-Graphs +0,1,008,0 + +Von Drake's Molecular Mission +0,5,248,2 + +Weight Control and Nutrition +0,5,248,1 + +Win with Decimals 1 +0,4,120,0 + +Wing War +0,3,056,2 + +Word Invasion +0,2,024,0 + +Word Radar +0,1,008,1 + +Yahtzee +0,1,008,0 + +Zero Zap +0,1,128,0 + + diff --git a/sd/trs80cart.txt b/sd/trs80cart.txt new file mode 100644 index 0000000..be32c95 --- /dev/null +++ b/sd/trs80cart.txt @@ -0,0 +1,328 @@ +7 Card Stud +2 + +A Mazing World of Malcom Mortar +4 + +Androne +2 + +Appliance and Light Controller +4 + +Arkanoid (Coco 1-2) +4 + +Arkanoid (Coco 3) +5 + +Art Gallery +1 + +Atom +4 + +Audio Spectrum Analyzer +0 + +Backgammon +1 + +BASIC AID +0 + +Bingo Math +1 + +Bridge Tutor I +2 + +Bustout +1 + +Canyon Climber +2 + +Castle Guard +1 + +Castle of Tharoggad +4 + +Checker King +1 + +Clowns & Balloons +2 + +Coco Tuner +1 + +Color Baseball +2 + +Color Computer Disk BASIC +2 + +Color Cubes +1 + +Color File +1 + +Color File II +4 + +Color Forth +3 + +Color Logo +2 + +Color Robot Battle +2 + +Color Scripsit +2 + +Color Scripsit II +4 + +Crosswords +1 + +Cyrus World Class Chess +4 + +Deluxe RS-232 Program Pak +1 + +Demolition Derby +2 + +Demon Attack +4 + +Diagnostics +0 + +Diagnostics v2.0 +0 + +Dino Wars +2 + +Direct Connect Modem Pak +2 + +DON-PAN +2 + +Doodle Bug (Computerware) +4 + +Doodle Bug (Computerware) (Buff/Green) +2 + +Doodle Bug (Dragon Data Ltd) +2 + +Doubleback +1 + +Downland +2 + +Dragon Fire +4 + +Dungeons of Daggorath +2 + +EDTASM+ +4 + +Facemaker +2 + +Football +2 + +Fraction Fever +2 + +Galactic Attack +1 + +GFL Championship Football II +5 + +Gin Champion +2 + +Gomoku-Renju +2 + +Graphic Pak +4 + +Handyman +1 + +JDOS v1.08/v1.10/v1.11 +2 + +JDOS v1.23 +4 + +Kids on Keys +2 + +Kindercomp +2 + +Math Tutor +4 + +Mega-Bug +2 + +Micro Chess v2.0 +2 + +Micro Painter +1 + +Microbes +1 + +Mind-Roll +5 + +Monster Maze +1 + +Music +1 + +Orchestra 90-CC +2 + +Panic Button +2 + +Personal Finance +2 + +Personal Finance II +2 + +Pinball +1 + +Polaris +1 + +Poltergeist +2 + +Popcorn +0 + +Predator +6 + +Project Nebula +2 + +Quasar Commander +1 + +RAD Warrior +5 + +Rampage! +5 + +Reactoid +1 + +Robocop +7 + +Roman Checkers +2 + +SDS80C +2 + +Shanghai +4 + +Shooting Gallery +2 + +Silpheed (Coco 1-2) +4 + +Silpheed (Coco 3) +5 + +Skiing +2 + +Slay the Nereis +2 + +Soko-Ban +4 + +Space Assault +1 + +Spectaculator +2 + +Spidercide +1 + +Springster +4 + +Starblaze +2 + +Stellar Lifeline +2 + +Super Logo +4 + +Super Pitfall +5 + +Temple of ROM +2 + +Tennis +1 + +Tetris +4 + +Thexder +4 + +TypeMate +4 + +Typing Tutor +1 + +Videotex +0 + +Wildcatting +1 + + diff --git a/sd/vic20cart.txt b/sd/vic20cart.txt new file mode 100644 index 0000000..52f46e9 --- /dev/null +++ b/sd/vic20cart.txt @@ -0,0 +1,718 @@ +A World at War +7,2 + +A.E. +5,6 + +Adventureland +3,6 + +Aggressor +7,2 + +Alien +7,2 + +Alien Blitz +7,1 + +Alien Sidestep +7,1 + +Alphabet Zoo +5,6 + +Amazing Maze +7,1 + +Amidar +7,2 + +Amok! +7,1 + +Ape Escape +7,2 + +Apple Panic +5,3 + +Arachnoid +5,6 + +Arcadia +7,2 + +Artillery Duel +5,6 + +Assam - Assembler +7,2 + +Astroblitz +7,1 + +Atlantis +7,1 + +Attack of the Mutant Camels +7,2 + +Avenger +7,2 + +Baldor's Castle +1,5 + +Bandits +7,2 + +Bank Robber +7,2 + +Battle Zone +5,6 + +Black Hole +7,1 + +Bridge 20 +5,6 + +Buck Rogers +5,6 + +Bug Crusher +7,1 + +BUTI - Basic UTIlity +7,1 + +Cannonball Blitz +2,4 + +Capture the Flag +7,2 + +Car Race +7,2 + +Cassette Turbo +7,1 + +Catch-a-Snatch +7,2 + +Cave-In +7,2 + +CBM BASIC v4.0 +7,1 + +CBM BASIC v5.0 +7,2 + +Centipede +2,6 + +Choplifter +7,2 + +Chuck Norris Superkicks +7,2 + +Close Encounters +7,1 + +Cloudburst +7,1 + +Clowns +7,2 + +ComBASIC +8,1 + +Commodore Artist +7,1 + +Computer War +7,2 + +Congo Bongo +7,2 + +Cosmic Cruncher +7,2 + +Cosmic Jailbreak +7,2 + +Crater Raider +7,1 + +Creepy Corridors +7,2 + +Crossfire +7,2 + +Cyclon +7,2 + +Dancing Bear +2,6 + +Data 20 Display Manager +7,0 + +Deadly Duck +7,1 + +Deadly Skies +7,1 + +Defender +5,6 + +Demon Attack +7,1 + +D'Fuse +7,2 + +Dig Dug +2,6 + +Donkey Kong +2,6 + +Dot Gobbler +7,1 + +Dragonfire +7,2 + +Droids +7,2 + +E.T. +7,2 + +ExBasic Level II +7,2 + +Face Maker +7,2 + +Fast Eddie +7,2 + +Final Orbit + Bumper Bash +7,2 + +Flipper 1.0 +7,2 + +Frogger +7,2 + +Frogman +7,2 + +Fun with Music +7,2 + +Galaxian +7,2 + +Garden Wars +7,2 + +Ghost Manor +7,2 + +Gold Fever +7,1 + +Gorf +7,2 + +Gridrunner +7,1 + +Handy Toolkit +7,2 + +HesMon v1.1 +7,1 + +HesWriter v1.1 +7,2 + +HiRes Toolkit +8,1 + +Home Baby Sitter II +7,2 + +Household Finance +7,2 + +IEEE-488 +8,0 + +IFR: Flight Simulator +7,2 + +In The Chips +5,6 + +Jawbreaker II +7,2 + +Jelly Monster v.1 +7,2 + +Jelly Monster v.2 +7,2 + +Jungle Hunt +5,6 + +Jupiter Lander +7,2 + +Key-Quest +7,2 + +Kids on Keys +7,2 + +Kindercomp +5,6 + +K-Razy Antiks +5,6 + +K-Star Patrol +5,6 + +Laser Zone +7,2 + +Lode Runner +5,6 + +Lunar Leeper +2,4 + +Machine Language Monitor +0,1 + +Mastertype +5,6 + +Maze +7,2 + +Medieval Joust +7,2 + +Meteor Run +7,2 + +Mine Madness +7,2 + +Miner 2049er +7,2 + +Mission Impossible +3,6 + +Mobile Attack +7,2 + +Mole Attack +7,2 + +Money Wars +7,2 + +Monster Maze +7,2 + +Moon Patrol +5,6 + +Mosquito Infestation +7,1 + +Motocross Racer +7,2 + +Mountain King +5,6 + +Ms. Pac-Man +5,6 + +Mutant Herd +7,2 + +Number Crunch +7,2 + +Number Nabber & Shape Grabber +7,2 + +Omega Race +7,2 + +Outworld (NTSC) +5,3 + +Outworld (PAL) +7,2 + +Pac-Man +7,2 + +Paratrooper +7,2 + +Personal Finance +7,2 + +PET Loader +7,2 + +Pharaoh's Curse +5,6 + +Pinball +5,6 + +Pinball Spectacular +5,6 + +Pipes +7,2 + +Pirate's Cove +3,6 + +Poker +7,2 + +Polaris +7,2 + +Pole Position +5,6 + +Predator +7,2 + +Princess and Frog +7,2 + +Programmer's Aid Cartridge +6,1 + +Protector +7,2 + +Q*bert +7,2 + +Quackers +7,2 + +Quick Brown Fox +4,2 + +Radar Rat Race +7,2 + +Raid on Fort Knox +7,2 + +Rally-X +7,2 + +Rat Hotel +7,2 + +Renaissance +5,3 + +River Rescue +7,2 + +Road Race +7,2 + +Robin Hood +7,2 + +Robot Panic +7,2 + +Robotron: 2084 +5,4 + +SamMON +0,2 + +Sargon II Chess +7,2 + +Satellite and Meteorites! (NTSC) +5,3 + +Satellite and Meteorites! (PAL) +7,2 + +Satellite Patrol +7,1 + +Scorpion +7,2 + +Scram 20 +7,1 + +Screen Master +7,1 + +Sea Wolf +7,2 + +Seafox +5,6 + +Serpentine +7,2 + +Shamus +7,2 + +Sir Lancelot +7,2 + +Skibbereen +7,1 + +Skyblazer +5,6 + +Slot +7,2 + +Snake Byte +7,1 + +Space Ric-o-Shay +7,1 + +Space Snake +7,2 + +Speed Math & Bingo Math +7,2 + +Spider City +7,1 + +Spider of Mars (NTSC) +7,2 + +Spider of Mars (PAL) +5,3 + +Spike's Peak +7,2 + +Spills & Fills +7,2 + +Springer +5,6 + +Squish 'em +5,6 + +Star Battle +7,2 + +Star Post +7,2 + +Star Trek +7,2 + +Story Machine +5,5 + +Sub Chase +7,1 + +Sub Commander +5,4 + +Super Amok +7,2 + +Super Expander +7,1 + +Super Slot +7,2 + +Super Smash +7,2 + +Synthesound +7,2 + +Tank Atak +7,2 + +Tank Wars +7,1 + +Terraguard +7,1 + +The Count +3,6 + +The Fourth Encounter +7,2 + +The Sky is Falling +7,2 + +Threshold +7,2 + +Titan +7,2 + +Tomarc the Barbarian +7,2 + +Tooth Invaders +7,2 + +Topper +7,2 + +Trashman +5,3 + +Turmoil +7,2 + +Turtle Graphics +7,2 + +Tutankham +7,2 + +Type Attack +7,2 + +Typo +7,2 + +VC Extra +7,2 + +VC Song-Cartridge +7,2 + +VIC 20 Diagnostic Cartridge +7,1 + +VIC 20 Micromon v1.2 +8,1 + +VIC 20 Mikro Assembler +5,3 + +VIC Color Test +7,0 + +VIC Forth v1.0 +7,2 + +VIC Forth v1.1 +7,2 + +VIC Graph +7,2 + +VIC Menagerie +7,2 + +VIC Music Composer +7,2 + +VIC Stat +7,2 + +VIC Super Lander +7,2 + +Vic Tool +8,0 + +VICKIT 1 +8,0 + +VICKIT 2 +8,1 + +VICKIT 3 +8,1 + +VICKIT 4 +7,1 + +VICKIT 5 +8,1 + +VICterm 40 +7,1 + +Video Vermin +7,2 + +Videomania +7,1 + +Visible Solar System +7,2 + +Voodoo Castle +3,6 + +Wacky Waiters +7,2 + +Waterloo BASIC +7,1 + +Witch Way +7,2 + +Wordcraft 20 +5,6 + +Write Now! +7,2 + + diff --git a/sd/vsmilecart.txt b/sd/vsmilecart.txt new file mode 100644 index 0000000..5bbd06d --- /dev/null +++ b/sd/vsmilecart.txt @@ -0,0 +1,223 @@ +A Day On The Farm +2 + +Action Mania +2 + +Adventures of Little Red Riding Hood +2 + +Aladdin +2 + +Alphabet Park Adventure +2 + +Alphabet Park Adventure (6MB) +1 + +Baby Einstein +2 + +Backyardigans +2 + +Bailey Goes To Town +2 + +Barney +2 + +Batman +2 + +Blue's Clues +2 + +Bob the Builder +2 + +Care Bears +2 + +Care Bears (6MB) +1 + +Cars +2 + +Cars 2 +3 + +Cinderella +2 + +Cranium +2 + +Discovery with Baby Mickey and Friends +2 + +Disney Fairies +2 + +Disney's Little Einsteins +2 + +Dora The Explorer +2 + +Elmo's World +2 + +Finding Nemo +2 + +Go Diego Go! +2 + +Handy Manny +2 + +Kung Fu Panda +2 + +Learn and Discover Home +2 + +Learnin' Wheels +2 + +Lil' Bratz +2 + +Lion King +2 + +Little Mermaid +2 + +Mickey Mouse +2 + +Mickey Mouse Clubhouse +2 + +Monsters vs. Aliens +2 + +Mother Goose +2 + +My Pet Puppy +2 + +Nascar Academy +2 + +Ni Hao Kai Lan +2 + +Noah's Ark Animal Adventure +2 + +Noddy +2 + +Pooh's Hundred Acre Wood Adventure +2 + +Princess and the Frog +2 + +Ratatouille +2 + +Scooby-Doo! +2 + +Sesame Street +2 + +Shrek +2 + +Shrek Forever After +3 + +Shrek the Third +2 + +Snow Park Challenge +2 + +Soccer Challenge +2 + +Spider-Man & Friends +2 + +Spongebob Squarepants +2 + +Super WHY! +3 + +Superman +2 + +Teletubbies +2 + +Thomas & Friends +2 + +Toy Story 2 +2 + +Toy Story 3 +2 + +Toy Story 3 (Motion) +3 + +Up +2 + +V.Smile Art Studio +0 + +V.Smile Jamming Gym Class +2 + +V.Smile PC Pal Island +2 + +V.Smile Smart Keyboard +2 + +Wall-E +2 + +Whiz Kid Wheels +2 + +Wiggles +2 + +Wild Waves +2 + +Winnie The Pooh +2 + +Wonder Pets! +2 + +Wow! Wow! Wubbzy! +2 + +Zayzoo +2 + +