aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorsanni <[email protected]>2024-07-08 19:25:29 +0200
committersanni <[email protected]>2024-07-08 19:25:29 +0200
commit72dfb04624907a9e7236d9bcdc78d21ac633c14a (patch)
tree9010a2e0c1ec8988e6f378493b83919ac48caca9
parent08cb8213877ec9dd930b0c50ad9c28707b8d2676 (diff)
downloadcartreader-72dfb04624907a9e7236d9bcdc78d21ac633c14a.tar.gz
cartreader-72dfb04624907a9e7236d9bcdc78d21ac633c14a.zip
Add new modules (thx to skaman)
-rw-r--r--Cart_Reader/ATARI8.ino499
-rw-r--r--Cart_Reader/BALLY.ino321
-rw-r--r--Cart_Reader/Cart_Reader.ino212
-rw-r--r--Cart_Reader/Config.h94
-rw-r--r--Cart_Reader/LEAP.ino1052
-rw-r--r--Cart_Reader/LJ.ino356
-rw-r--r--Cart_Reader/LJPRO.ino554
-rw-r--r--Cart_Reader/OSCR.cpp2
-rw-r--r--Cart_Reader/OSCR.h74
-rw-r--r--Cart_Reader/PV1000.ino324
-rw-r--r--Cart_Reader/PYUUTA.ino327
-rw-r--r--Cart_Reader/RCA.ino327
-rw-r--r--Cart_Reader/TI99.ino1071
-rw-r--r--Cart_Reader/TRS80.ino398
-rw-r--r--Cart_Reader/VIC20.ino576
-rw-r--r--Cart_Reader/VSMILE.ino365
-rw-r--r--sd/README.md132
-rw-r--r--sd/atari8cart.txt946
-rw-r--r--sd/ballycart.txt103
-rw-r--r--sd/leapster.txt217
-rw-r--r--sd/ljcart.txt109
-rw-r--r--sd/pv1000cart.txt72
-rw-r--r--sd/pyuutacart.txt97
-rw-r--r--sd/rcacart.txt49
-rw-r--r--sd/ti99cart.txt835
-rw-r--r--sd/trs80cart.txt328
-rw-r--r--sd/vic20cart.txt718
-rw-r--r--sd/vsmilecart.txt223
28 files changed, 10357 insertions, 24 deletions
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
@@ -3609,6 +3751,42 @@ void loop() {
#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
@@ -228,6 +228,42 @@ enum CORES: uint8_t {
# 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
+
+