V12.0: Add Atari 2600, Emerson Arcadia 2001, Fairchild Channel F, Magnavox Odyssey 2 modules (thx to skaman)

This commit is contained in:
sanni 2023-01-03 19:33:34 +01:00
parent 0ba515adcf
commit 248a1d9af5
10 changed files with 3914 additions and 84 deletions

534
Cart_Reader/ARC.ino Normal file
View File

@ -0,0 +1,534 @@
//******************************************
// EMERSON ARCADIA 2001 MODULE
//******************************************
#if defined(enable_ARC)
// Emerson Arcadia 2001
// Cartridge Pinout
// 30P 2.54mm pitch connector
//
// FRONT BACK
// SIDE SIDE
// +-------+
// GND -| 2 1 |- A13 (A12)
// VCC (+5V) -| 4 3 |- D3
// A0 -| 6 5 |- D4
// A1 -| 8 7 |- D5
// A2 -| 10 9 |- D6
// A3 -| 12 11 |- D7
// A4 -| 14 13 |- D0
// A5 -| 16 15 |- D2
// A6 -| 18 17 |- D1
// A7 -| 20 19 |- NC
// A8 -| 22 21 |- NC
// A9 -| 24 23 |- NC
// A10 -| 26 25 |- GND
// A11 -| 28 27 |- GND
// A12 (/EN) -| 30 29 |- NC
// +-------+
//
// BACK
// +------------------------------------------------------------+
// | 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 |
// LEFT | | RIGHT
// | 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 |
// +------------------------------------------------------------+
// FRONT
//
byte ARC[] = { 2, 4, 6, 8 };
byte arclo = 0; // Lowest Entry
byte archi = 3; // Highest Entry
byte arcsize;
byte newarcsize;
// EEPROM MAPPING
// 08 ROM SIZE
//******************************************
// Menu
//******************************************
// Base Menu
static const char arcMenuItem1[] PROGMEM = "Select Cart";
static const char arcMenuItem2[] PROGMEM = "Read ROM";
static const char arcMenuItem3[] PROGMEM = "Set Size";
static const char arcMenuItem4[] PROGMEM = "Reset";
static const char* const menuOptionsARC[] PROGMEM = { arcMenuItem1, arcMenuItem2, arcMenuItem3, arcMenuItem4 };
void setup_ARC() {
// Set Address Pins to Output
// Arcadia 2001 uses A0-A13 [A14-A23 UNUSED]
//A0-A7
DDRF = 0xFF;
//A8-A15
DDRK = 0xFF;
//A16-A23
DDRL = 0xFF;
// Set Control Pins to Output
// ---(PH0) ---(PH1) ---(PH3) ---(PH4) ---(PH5) ---(PH6)
DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// Set TIME(PJ0) to Output (UNUSED)
DDRJ |= (1 << 0);
// Set Pins (D0-D7) to Input
DDRC = 0x00;
// Setting Unused Control Pins to HIGH
// ---(PH0) ---(PH1) ---(PH3) ---(PH4) ---(PH5) ---(PH6)
PORTH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// Set Unused Data Pins (PA0-PA7) to Output
DDRA = 0xFF;
// Set Unused Pins HIGH
PORTA = 0xFF;
PORTL = 0xFF; // A16-A23
PORTJ |= (1 << 0); // TIME(PJ0)
checkStatus_ARC();
strcpy(romName, "ARCADIA");
mode = mode_ARC;
}
void arcMenu() {
convertPgm(menuOptionsARC, 4);
uint8_t mainMenu = question_box(F("ARCADIA 2001 MENU"), menuOptions, 4, 0);
switch (mainMenu) {
case 0:
// Select Cart
setCart_ARC();
wait();
setup_ARC();
break;
case 1:
// Read ROM
sd.chdir("/");
readROM_ARC();
sd.chdir("/");
break;
case 2:
// Set Size
setROMSize_ARC();
break;
case 3:
// reset
resetArduino();
break;
}
}
//******************************************
// READ CODE
//******************************************
uint8_t readData_ARC(uint16_t addr) {
PORTF = addr & 0xFF; // A0-A7
PORTK = (addr >> 8) & 0xFF; // A8-A13
NOP;
NOP;
NOP;
NOP;
NOP;
uint8_t ret = PINC;
return ret;
}
void readSegment_ARC(uint16_t startaddr, uint16_t endaddr) {
for (uint16_t addr = startaddr; addr < endaddr; addr += 512) {
for (int w = 0; w < 512; w++) {
uint8_t temp = readData_ARC(addr + w);
sdBuffer[w] = temp;
}
myFile.write(sdBuffer, 512);
}
}
void readROM_ARC() {
strcpy(fileName, romName);
strcat(fileName, ".bin");
// create a new folder for storing rom file
EEPROM_readAnything(0, foldern);
sprintf(folder, "ARC/ROM/%d", foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
display_Clear();
print_Msg(F("Saving to "));
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
// open file on sdcard
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
print_FatalError(create_file_STR);
}
// write new folder number back to EEPROM
foldern++;
EEPROM_writeAnything(0, foldern);
readSegment_ARC(0x0000, 0x0800); // 2K
if (arcsize > 0) {
readSegment_ARC(0x0800, 0x1000); // +2K = 4K
if (arcsize > 1) {
readSegment_ARC(0x2000, 0x2800); // +2K = 6K
if (arcsize > 2) {
readSegment_ARC(0x2800, 0x3000); // +2K = 8K
}
}
}
myFile.close();
unsigned long crcsize = ARC[arcsize] * 0x400;
calcCRC(fileName, crcsize, NULL, 0);
println_Msg(F(""));
println_Msg(F("Press Button..."));
display_Update();
wait();
}
//******************************************
// ROM SIZE
//******************************************
void setROMSize_ARC() {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
if (arclo == archi)
newarcsize = arclo;
else {
int b = 0;
int i = arclo;
while (1) {
display_Clear();
print_Msg(F("ROM Size: "));
println_Msg(ARC[i]);
println_Msg(F(""));
println_Msg(F("Press to Change"));
println_Msg(F("Hold to Select"));
display_Update();
b = checkButton();
if (b == 2) { // Previous (doubleclick)
if (i == arclo)
i = archi;
else
i--;
}
if (b == 1) { // Next (press)
if (i == archi)
i = arclo;
else
i++;
}
if (b == 3) { // Long Press - Execute (hold)
newarcsize = i;
break;
}
}
display.setCursor(0, 56); // Display selection at bottom
}
print_Msg(F("ROM SIZE "));
print_Msg(ARC[newarcsize]);
println_Msg(F("K"));
display_Update();
delay(1000);
#else
if (arclo == archi)
newarcsize = arclo;
else {
setrom:
String sizeROM;
for (int i = 0; i < (archi - arclo + 1); i++) {
Serial.print(F("Select ROM Size: "));
Serial.print(i);
Serial.print(F(" = "));
Serial.print(ARC[i + arclo]);
Serial.println(F("K"));
}
Serial.print(F("Enter ROM Size: "));
while (Serial.available() == 0) {}
sizeROM = Serial.readStringUntil('\n');
Serial.println(sizeROM);
newarcsize = sizeROM.toInt() + arclo;
if (newarcsize > archi) {
Serial.println(F("SIZE NOT SUPPORTED"));
Serial.println(F(""));
goto setrom;
}
}
Serial.print(F("ROM Size = "));
Serial.print(ARC[newarcsize]);
Serial.println(F("K"));
#endif
EEPROM_writeAnything(8, newarcsize);
arcsize = newarcsize;
}
void checkStatus_ARC() {
EEPROM_readAnything(8, arcsize);
if (arcsize > 3) {
arcsize = 0;
EEPROM_writeAnything(8, arcsize);
}
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("ARCADIA 2001 READER"));
println_Msg(F("CURRENT SETTINGS"));
println_Msg(F(""));
print_Msg(F("ROM SIZE: "));
print_Msg(ARC[arcsize]);
println_Msg(F("K"));
display_Update();
wait();
#else
Serial.print(F("CURRENT ROM SIZE: "));
Serial.print(ARC[arcsize]);
Serial.println(F("K"));
Serial.println(F(""));
#endif
}
//******************************************
// CART SELECT CODE
//******************************************
FsFile arccsvFile;
char arcgame[20]; // title
char arcrr[3]; // romsize
char arcll[4]; // linelength (previous line)
unsigned long arccsvpos; // CSV File Position
char arccartCSV[] = "arccart.txt"; // CSV List
char arccsvEND[] = "EOF"; // CSV End Marker for scrolling
bool readLine_ARC(FsFile& f, char* line, size_t maxLen) {
for (size_t n = 0; n < maxLen; n++) {
int c = f.read();
if (c < 0 && n == 0) return false; // EOF
if (c < 0 || c == '\n') {
line[n] = 0;
return true;
}
line[n] = c;
}
return false; // line too long
}
bool readVals_ARC(char* arcgame, char* arcrr, char* arcll) {
char line[26];
arccsvpos = arccsvFile.position();
if (!readLine_ARC(arccsvFile, line, sizeof(line))) {
return false; // EOF or too long
}
char* comma = strtok(line, ",");
int x = 0;
while (comma != NULL) {
if (x == 0)
strcpy(arcgame, comma);
else if (x == 1)
strcpy(arcrr, comma);
else if (x == 2)
strcpy(arcll, comma);
comma = strtok(NULL, ",");
x += 1;
}
return true;
}
bool getCartListInfo_ARC() {
bool buttonreleased = 0;
bool cartselected = 0;
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F(" HOLD TO FAST CYCLE"));
display_Update();
#else
Serial.println(F("HOLD BUTTON TO FAST CYCLE"));
#endif
delay(2000);
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
boolean buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == LOW) { // Button Held - Fast Cycle
while (1) { // Scroll Game List
while (readVals_ARC(arcgame, arcrr, arcll)) {
if (strcmp(arccsvEND, arcgame) == 0) {
arccsvFile.seek(0); // Restart
} else {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART TITLE:"));
println_Msg(F(""));
println_Msg(arcgame);
display_Update();
#else
Serial.print(F("CART TITLE:"));
Serial.println(arcgame);
#endif
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == HIGH) { // Button Released
buttonreleased = 1;
break;
}
if (buttonreleased) {
buttonreleased = 0; // Reset Flag
break;
}
}
}
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == HIGH) // Button Released
break;
}
}
#if (defined(enable_OLED) || defined(enable_LCD))
display.setCursor(0, 56);
println_Msg(F("FAST CYCLE OFF"));
display_Update();
#else
Serial.println(F(""));
Serial.println(F("FAST CYCLE OFF"));
Serial.println(F("PRESS BUTTON TO STEP FORWARD"));
Serial.println(F("DOUBLE CLICK TO STEP BACK"));
Serial.println(F("HOLD TO SELECT"));
Serial.println(F(""));
#endif
while (readVals_ARC(arcgame, arcrr, arcll)) {
if (strcmp(arccsvEND, arcgame) == 0) {
arccsvFile.seek(0); // Restart
} else {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART TITLE:"));
println_Msg(F(""));
println_Msg(arcgame);
display.setCursor(0, 48);
println_Msg(F("Press to Change"));
println_Msg(F("Hold to Select"));
display_Update();
#else
Serial.print(F("CART TITLE:"));
Serial.println(arcgame);
#endif
while (1) { // Single Step
int b = checkButton();
if (b == 1) { // Continue (press)
break;
}
if (b == 2) { // Reset to Start of List (doubleclick)
byte prevline = strtol(arcll, NULL, 10);
arccsvpos -= prevline;
arccsvFile.seek(arccsvpos);
break;
}
if (b == 3) { // Long Press - Select Cart (hold)
newarcsize = strtol(arcrr, NULL, 10);
EEPROM_writeAnything(8, newarcsize);
cartselected = 1; // SELECTION MADE
#if (defined(enable_OLED) || defined(enable_LCD))
println_Msg(F("SELECTION MADE"));
display_Update();
#else
Serial.println(F("SELECTION MADE"));
#endif
break;
}
}
if (cartselected) {
cartselected = 0; // Reset Flag
return true;
}
}
}
#if (defined(enable_OLED) || defined(enable_LCD))
println_Msg(F(""));
println_Msg(F("END OF FILE"));
display_Update();
#else
Serial.println(F("END OF FILE"));
#endif
return false;
}
void checkCSV_ARC() {
if (getCartListInfo_ARC()) {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART SELECTED"));
println_Msg(F(""));
println_Msg(arcgame);
display_Update();
// Display Settings
display.setCursor(0, 56);
print_Msg(F("CODE: R"));
println_Msg(newarcsize);
display_Update();
#else
Serial.println(F(""));
Serial.println(F("CART SELECTED"));
Serial.println(arcgame);
// Display Settings
Serial.print(F("CODE: R"));
Serial.println(newarcsize);
Serial.println(F(""));
#endif
} else {
#ifdef enable_OLED
display.setCursor(0, 56);
println_Msg(F("NO SELECTION"));
display_Update();
#else
Serial.println(F("NO SELECTION"));
#endif
}
}
void setCart_ARC() {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(arccartCSV);
display_Update();
#endif
sd.chdir();
sprintf(folder, "ARC/CSV");
sd.chdir(folder); // Switch Folder
arccsvFile = sd.open(arccartCSV, O_READ);
if (!arccsvFile) {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CSV FILE NOT FOUND!"));
display_Update();
#else
Serial.println(F("CSV FILE NOT FOUND!"));
#endif
while (1) {
if (checkButton() != 0)
setup_ARC();
}
}
checkCSV_ARC();
arccsvFile.close();
}
#endif

912
Cart_Reader/ATARI.ino Normal file
View File

@ -0,0 +1,912 @@
//******************************************
// ATARI 2600 MODULE
//******************************************
#if defined(enable_ATARI)
// Atari 2600
// Cartridge Pinout
// 24P 2.54mm pitch connector
//
// LABEL SIDE
//
// GND +5V A8 A9 A11 A10 A12 D7 D6 D5 D4 D3
// +--------------------------------------------------+
// | 24 23 22 21 20 19 18 17 16 15 14 13 |
// LEFT | | RIGHT
// | 1 2 3 4 5 6 7 8 9 10 11 12 |
// +--------------------------------------------------+
// A7 A6 A5 A4 A3 A2 A1 A0 D0 D1 D2 GND
//
// BOTTOM SIDE
// Cart Configurations
// Format = {mapper,romsize}
static const byte PROGMEM atarimapsize[] = {
0x20, 0, // 2K
0x3F, 2, // Tigervision 8K
0x40, 1, // 4K [DEFAULT]
0xC0, 0, // "CV" Commavid 2K
0xD0, 2, // "DPC" Pitfall II 10K
0xE0, 2, // Parker Bros 8K
0xE7, 4, // M-Network 16K
0xF0, 6, // Megaboy 64K
0xF4, 5, // Atari 32K
0xF6, 4, // Atari 16K
0xF8, 2, // Atari 8K
0xFA, 3, // CBS RAM Plus 12K
0xFE, 2, // Activision 8K
0x0A, 2, // "UA" UA Ltd 8K
};
//byte atarimapcount = 14;
byte atarimapcount = (sizeof(atarimapsize) / sizeof(atarimapsize[0])) / 2;
byte atarimapselect;
int atariindex;
byte ATARI[] = { 2, 4, 8, 12, 16, 32, 64 };
byte atarimapper = 0;
byte newatarimapper;
byte atarisize;
// EEPROM MAPPING
// 07 MAPPER
// 08 ROM SIZE
//******************************************
// Menu
//******************************************
// Base Menu
static const char atariMenuItem1[] PROGMEM = "Select Cart";
static const char atariMenuItem2[] PROGMEM = "Read ROM";
static const char atariMenuItem3[] PROGMEM = "Set Mapper";
static const char atariMenuItem4[] PROGMEM = "Reset";
static const char* const menuOptionsATARI[] PROGMEM = { atariMenuItem1, atariMenuItem2, atariMenuItem3, atariMenuItem4 };
void setup_ATARI() {
// Set Address Pins to Output
// Atari 2600 uses A0-A12 [A13-A23 UNUSED]
//A0-A7
DDRF = 0xFF;
//A8-A15
DDRK = 0xFF;
//A16-A23
DDRL = 0xFF;
// Set Control Pins to Output [UNUSED]
// ---(PH0) ---(PH1) ---(PH3) ---(PH4) ---(PH5) ---(PH6)
DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// Set TIME(PJ0) to Output (UNUSED)
DDRJ |= (1 << 0);
// Set Pins (D0-D7) to Input
DDRC = 0x00;
// Setting Control Pins to HIGH [UNUSED]
// ---(PH0) ---(PH1) ---(PH3) ---(PH4) ---(PH5) ---(PH6)
PORTH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// Set Unused Data Pins (PA0-PA7) to Output
DDRA = 0xFF;
// Set Unused Pins HIGH
PORTA = 0xFF;
PORTL = 0xFF; // A16-A23
PORTJ |= (1 << 0); // TIME(PJ0)
checkStatus_ATARI();
strcpy(romName, "ATARI");
mode = mode_ATARI;
}
void atariMenu() {
convertPgm(menuOptionsATARI, 4);
uint8_t mainMenu = question_box(F("ATARI 2600 MENU"), menuOptions, 4, 0);
switch (mainMenu) {
case 0:
// Select Cart
setCart_ATARI();
wait();
setup_ATARI();
break;
case 1:
// Read ROM
sd.chdir("/");
readROM_ATARI();
sd.chdir("/");
break;
case 2:
// Set Mapper
setMapper_ATARI();
checkStatus_ATARI();
break;
case 3:
// reset
resetArduino();
break;
}
}
//******************************************
// READ CODE
//******************************************
uint8_t readData_ATARI(uint16_t addr) // Add Input Pullup
{
PORTF = addr & 0xFF; // A0-A7
PORTK = (addr >> 8) & 0xFF; // A8-A12
NOP;
NOP;
NOP;
NOP;
NOP;
DDRC = 0x00; // Set to Input
PORTC = 0xFF; // Input Pullup
NOP;
NOP;
NOP;
NOP;
NOP;
uint8_t ret = PINC;
NOP;
NOP;
NOP;
NOP;
NOP;
return ret;
}
void readSegment_ATARI(uint16_t startaddr, uint16_t endaddr) {
for (uint16_t addr = startaddr; addr < endaddr; addr += 512) {
for (int w = 0; w < 512; w++) {
uint8_t temp = readData_ATARI(addr + w);
sdBuffer[w] = temp;
}
myFile.write(sdBuffer, 512);
}
}
void readSegmentF8_ATARI(uint16_t startaddr, uint16_t endaddr, uint16_t bankaddr) {
for (uint16_t addr = startaddr; addr < endaddr; addr += 512) {
for (int w = 0; w < 512; w++) {
if (addr > 0x1FF9) // SET BANK ADDRESS FOR 0x1FFA-0x1FFF
readData_ATARI(bankaddr);
uint8_t temp = readData_ATARI(addr + w);
sdBuffer[w] = temp;
}
myFile.write(sdBuffer, 512);
}
}
void writeData_ATARI(uint16_t addr, uint8_t data) {
PORTF = addr & 0xFF; // A0-A7
PORTK = (addr >> 8) & 0xFF; // A8-A12
NOP;
NOP;
NOP;
NOP;
NOP;
DDRC = 0xFF; // Set to Output
NOP;
NOP;
NOP;
NOP;
NOP;
PORTC = data;
NOP;
NOP;
NOP;
NOP;
NOP;
DDRC = 0x00; // Reset to Input
}
void writeData3F_ATARI(uint16_t addr, uint8_t data) {
PORTF = addr & 0xFF; // A0-A7
PORTK = (addr >> 8) & 0xFF; // A8-A12
NOP;
NOP;
NOP;
NOP;
NOP;
DDRC = 0xFF; // Set to Output
NOP;
NOP;
NOP;
NOP;
NOP;
PORTC = data;
NOP;
NOP;
NOP;
NOP;
NOP;
// Address (0x1000);
PORTF = 0x00; // A0-A7
PORTK = 0x10; // A8-A12
NOP;
NOP;
NOP;
NOP;
NOP;
NOP;
NOP;
NOP;
DDRC = 0x00; // Reset to Input
}
void readROM_ATARI() {
strcpy(fileName, romName);
strcat(fileName, ".a26");
// create a new folder for storing rom file
EEPROM_readAnything(0, foldern);
sprintf(folder, "ATARI/ROM/%d", foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
display_Clear();
print_Msg(F("Saving to "));
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
// open file on sdcard
if (!myFile.open(fileName, O_RDWR | O_CREAT))
print_FatalError(create_file_STR);
// write new folder number back to EEPROM
foldern++;
EEPROM_writeAnything(0, foldern);
// ROM Start 0xF000
// Address A12-A0 = 0x1000 = 1 0000 0000 0000 = 4KB
// Read Start 0x1000
switch (atarimapper) {
case 0x20: // 2K Standard 2KB
readSegment_ATARI(0x1000, 0x1800);
break;
case 0x3F: // 3F Mapper 8KB
for (int x = 0; x < 0x3; x++) {
writeData3F_ATARI(0x3F, x);
readSegment_ATARI(0x1000, 0x1800);
}
readSegment_ATARI(0x1800, 0x2000);
break;
case 0x40: // 4K Default 4KB
readSegment_ATARI(0x1000, 0x2000);
break;
case 0xC0: // CV Mapper 2KB
readSegment_ATARI(0x1800, 0x2000);
break;
case 0xD0: // DPC Mapper 10KB
// 8K ROM
for (int x = 0; x < 0x2; x++) {
readData_ATARI(0x1FF8 + x);
// Split Read of 1st 0x200 bytes
// 0x0000-0x0080 are DPC Registers (Random on boot)
for (int y = 0; y < 0x80; y++) {
sdBuffer[y] = 0xFF; // Output 0xFFs for Registers
}
myFile.write(sdBuffer, 128);
for (int z = 0; z < 0x180; z++) {
sdBuffer[z] = readData_ATARI(0x1080 + z);
}
myFile.write(sdBuffer, 384);
// Read Segment
readSegment_ATARI(0x1200, 0x1800);
// 0x1000-0x1080 are DPC Registers (Random on boot)
for (int y = 0; y < 0x80; y++) {
sdBuffer[y] = 0xFF; // Output 0xFFs for Registers
}
myFile.write(sdBuffer, 128);
for (int z = 0; z < 0x180; z++) {
sdBuffer[z] = readData_ATARI(0x1880 + z);
}
myFile.write(sdBuffer, 384);
// Read Segment
readSegment_ATARI(0x1A00, 0x1E00);
// Split Read of Last 0x200 bytes
for (int y = 0; y < 0x1F8; y++) {
sdBuffer[y] = readData_ATARI(0x1E00 + y);
}
myFile.write(sdBuffer, 504);
for (int z = 0; z < 8; z++) {
// Set Bank to ensure 0x1FFA-0x1FFF is correct
readData_ATARI(0x1FF8 + x);
sdBuffer[z] = readData_ATARI(0x1FF8 + z);
}
myFile.write(sdBuffer, 8);
}
// 2K DPC Internal Graphics ROM
// Read Registers 0x1008-0x100F (Graphics 0x1008-0x100C)
// Write Registers LSB 0x1050-0x1057 AND MSB 0x1058-0x105F
// Set Data Fetcher 0 Limits
writeData_ATARI(0x1040, 0xFF); // MAX for Data Fetcher 0
writeData_ATARI(0x1048, 0x00); // MIN for Data Fetcher 0
// Set Data Fetcher 0 Counter (0x7FF)
writeData_ATARI(0x1050, 0xFF); // LSB for Data Fetcher 0
writeData_ATARI(0x1058, 0x07); // MSB for Data Fetcher 0
// Set Data Fetcher 1 Counter (0x7FF)
writeData_ATARI(0x1051, 0xFF); // LSB for Data Fetcher 1
writeData_ATARI(0x1059, 0x07); // MSB for Data Fetcher 1
for (int x = 0; x < 0x800; x += 512) {
for (int y = 0; y < 512; y++) {
sdBuffer[y] = readData_ATARI(0x1008); // Data Fetcher 0
readData_ATARI(0x1009); // Data Fetcher 1
}
myFile.write(sdBuffer, 512);
}
break;
case 0xE0: // E0 Mapper 8KB
for (int x = 0; x < 0x7; x++) {
readData_ATARI(0x1FE0 + x);
readSegment_ATARI(0x1000, 0x1400);
}
readSegment_ATARI(0x1C00, 0x2000);
break;
case 0xE7: // E7 Mapper 16KB
writeData_ATARI(0x1800, 0xFF);
for (int x = 0; x < 0x7; x++) {
readData_ATARI(0x1FE0 + x);
readSegment_ATARI(0x1000, 0x1800);
}
readSegment_ATARI(0x1800, 0x2000);
break;
case 0xF0: // F0 Mapper 64KB
for (int x = 0; x < 0x10; x++) {
readData_ATARI(0x1FF0);
readSegment_ATARI(0x1000, 0x2000);
}
break;
case 0xF4: // F4 Mapper 32KB
for (int x = 0; x < 0x8; x++) {
readData_ATARI(0x1FF4 + x);
readSegment_ATARI(0x1000, 0x2000);
}
break;
case 0xF6: // F6 Mapper 16KB
for (int x = 0; x < 0x4; x++) {
readData_ATARI(0x1FF6 + x);
readSegment_ATARI(0x1000, 0x2000);
}
break;
case 0xF8: // F8 Mapper 8KB
for (int x = 0; x < 0x2; x++) {
readData_ATARI(0x1FF8 + x);
readSegment_ATARI(0x1000, 0x1E00);
// Split Read of Last 0x200 bytes
for (int y = 0; y < 0x1F8; y++) {
sdBuffer[y] = readData_ATARI(0x1E00 + y);
}
myFile.write(sdBuffer, 504);
for (int z = 0; z < 8; z++) {
// Set Bank to ensure 0x1FFA-0x1FFF is correct
readData_ATARI(0x1FF8 + x);
sdBuffer[z] = readData_ATARI(0x1FF8 + z);
}
myFile.write(sdBuffer, 8);
}
break;
case 0xFA: // FA Mapper 12KB
for (int x = 0; x < 0x3; x++) {
writeData_ATARI(0x1FF8 + x, 0x1); // Set Bank with D0 HIGH
readSegment_ATARI(0x1000, 0x1E00);
// Split Read of Last 0x200 bytes
for (int y = 0; y < 0x1F8; y++) {
sdBuffer[y] = readData_ATARI(0x1E00 + y);
}
myFile.write(sdBuffer, 504);
for (int z = 0; z < 8; z++) {
// Set Bank to ensure 0x1FFB-0x1FFF is correct
writeData_ATARI(0x1FF8 + x, 0x1); // Set Bank with D0 HIGH
sdBuffer[z] = readData_ATARI(0x1FF8 + z);
}
myFile.write(sdBuffer, 8);
}
break;
case 0xFE: // FE Mapper 8KB
for (int x = 0; x < 0x2; x++) {
writeData_ATARI(0x01FE, 0xF0 ^ (x << 5));
writeData_ATARI(0x01FF, 0xF0 ^ (x << 5));
readSegment_ATARI(0x1000, 0x2000);
}
break;
case 0x0A: // UA Mapper 8KB
readData_ATARI(0x220);
readSegment_ATARI(0x1000, 0x2000);
readData_ATARI(0x240);
readSegment_ATARI(0x1000, 0x2000);
break;
}
myFile.close();
unsigned long crcsize = ATARI[atarisize] * 0x400;
calcCRC(fileName, crcsize, NULL, 0);
println_Msg(F(""));
println_Msg(F("Press Button..."));
display_Update();
wait();
}
//******************************************
// ROM SIZE
//******************************************
void checkStatus_ATARI() {
EEPROM_readAnything(7, atarimapper);
EEPROM_readAnything(8, atarisize);
if (atarisize > 6) {
atarisize = 1; // default 4KB
EEPROM_writeAnything(8, atarisize);
}
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("ATARI 2600 READER"));
println_Msg(F("CURRENT SETTINGS"));
println_Msg(F(""));
print_Msg(F("MAPPER: "));
if (atarimapper == 0x20)
println_Msg(F("2K"));
else if (atarimapper == 0x40)
println_Msg(F("4K"));
else if (atarimapper == 0x0A)
println_Msg(F("UA"));
else if (atarimapper == 0xC0)
println_Msg(F("CV"));
else if (atarimapper == 0xD0)
println_Msg(F("DPC"));
else
println_Msg(atarimapper, HEX);
print_Msg(F("ROM SIZE: "));
if (atarimapper == 0xD0)
print_Msg(F("10"));
else
print_Msg(ATARI[atarisize]);
println_Msg(F("K"));
display_Update();
wait();
#else
Serial.print(F("MAPPER: "));
if (atarimapper == 0x20)
Serial.println(F("2K"));
else if (atarimapper == 0x40)
Serial.println(F("4K"));
else if (atarimapper == 0x0A)
Serial.println(F("UA"));
else if (atarimapper == 0xC0)
Serial.println(F("CV"));
else if (atarimapper == 0xD0)
Serial.println(F("DPC"));
else
Serial.println(atarimapper, HEX);
Serial.print(F("ROM SIZE: "));
if (atarimapper == 0xD0)
Serial.print(F("10"));
else
Serial.print(ATARI[atarisize]);
Serial.println(F("K"));
Serial.println(F(""));
#endif
}
//******************************************
// SET MAPPER
//******************************************
void setMapper_ATARI() {
#if (defined(enable_OLED) || defined(enable_LCD))
int b = 0;
int i = 0;
// Check Button Status
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
boolean buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == LOW) { // Button Pressed
while (1) { // Scroll Mapper List
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == HIGH) { // Button Released
// Correct Overshoot
if (i == 0)
i = atarimapcount - 1;
else
i--;
break;
}
display_Clear();
print_Msg(F("Mapper: "));
atariindex = i * 2;
atarimapselect = pgm_read_byte(atarimapsize + atariindex);
if (atarimapselect == 0x20)
println_Msg(F("2K"));
else if (atarimapselect == 0x40)
println_Msg(F("4K"));
else if (atarimapselect == 0x0A)
println_Msg(F("UA"));
else if (atarimapselect == 0xC0)
println_Msg(F("CV"));
else if (atarimapselect == 0xD0)
println_Msg(F("DPC"));
else
println_Msg(atarimapselect, HEX);
display_Update();
if (i == (atarimapcount - 1))
i = 0;
else
i++;
delay(250);
}
}
while (1) {
display_Clear();
print_Msg(F("Mapper: "));
atariindex = i * 2;
atarimapselect = pgm_read_byte(atarimapsize + atariindex);
if (atarimapselect == 0x20)
println_Msg(F("2K"));
else if (atarimapselect == 0x40)
println_Msg(F("4K"));
else if (atarimapselect == 0x0A)
println_Msg(F("UA"));
else if (atarimapselect == 0xC0)
println_Msg(F("CV"));
else if (atarimapselect == 0xD0)
println_Msg(F("DPC"));
else
println_Msg(atarimapselect, HEX);
println_Msg(F(""));
println_Msg(F("Press to Change"));
println_Msg(F("Hold to Select"));
display_Update();
b = checkButton();
if (b == 2) { // Previous Mapper (doubleclick)
if (i == 0)
i = atarimapcount - 1;
else
i--;
}
if (b == 1) { // Next Mapper (press)
if (i == (atarimapcount - 1))
i = 0;
else
i++;
}
if (b == 3) { // Long Press - Execute (hold)
newatarimapper = atarimapselect;
break;
}
}
display.setCursor(0, 56);
print_Msg(F("MAPPER "));
if (newatarimapper == 0x20)
println_Msg(F("2K"));
else if (newatarimapper == 0x40)
println_Msg(F("4K"));
if (newatarimapper == 0x0A)
print_Msg(F("UA"));
else if (newatarimapper == 0xC0)
print_Msg(F("CV"));
else if (newatarimapper == 0xD0)
println_Msg(F("DPC"));
else
print_Msg(newatarimapper, HEX);
println_Msg(F(" SELECTED"));
display_Update();
delay(1000);
#else
setmapper:
String newmap;
Serial.println(F("SUPPORTED MAPPERS:"));
Serial.println(F("0 = 2K [Standard 2K]"));
Serial.println(F("1 = 3F [Tigervision]"));
Serial.println(F("2 = 4K [Standard 4K]"));
Serial.println(F("3 = CV [Commavid]"));
Serial.println(F("4 = DPC [Pitfall II]"));
Serial.println(F("5 = E0 [Parker Bros]"));
Serial.println(F("6 = E7 [M-Network]"));
Serial.println(F("7 = F0 [Megaboy]"));
Serial.println(F("8 = F4 [Atari 32K]"));
Serial.println(F("9 = F6 [Atari 16K]"));
Serial.println(F("10 = F8 [Atari 8K]"));
Serial.println(F("11 = FA [CBS RAM Plus]"));
Serial.println(F("12 = FE [Activision]"));
Serial.println(F("13 = UA [UA Ltd]"));
Serial.print(F("Enter Mapper [0-13]: "));
while (Serial.available() == 0) {}
newmap = Serial.readStringUntil('\n');
Serial.println(newmap);
atariindex = newmap.toInt() * 2;
newatarimapper = pgm_read_byte(atarimapsize + atariindex);
#endif
EEPROM_writeAnything(7, newatarimapper);
atarimapper = newatarimapper;
atarisize = pgm_read_byte(atarimapsize + atariindex + 1);
EEPROM_writeAnything(8, atarisize);
}
//******************************************
// CART SELECT CODE
//******************************************
FsFile ataricsvFile;
char atarigame[36]; // title
char atarimm[4]; // mapper
char atarill[4]; // linelength (previous line)
unsigned long ataricsvpos; // CSV File Position
char ataricartCSV[] = "ataricart.txt"; // CSV List
char ataricsvEND[] = "EOF"; // CSV End Marker for scrolling
bool readLine_ATARI(FsFile& f, char* line, size_t maxLen) {
for (size_t n = 0; n < maxLen; n++) {
int c = f.read();
if (c < 0 && n == 0) return false; // EOF
if (c < 0 || c == '\n') {
line[n] = 0;
return true;
}
line[n] = c;
}
return false; // line too long
}
bool readVals_ATARI(char* atarigame, char* atarimm, char* atarill) {
char line[42];
ataricsvpos = ataricsvFile.position();
if (!readLine_ATARI(ataricsvFile, line, sizeof(line))) {
return false; // EOF or too long
}
char* comma = strtok(line, ",");
int x = 0;
while (comma != NULL) {
if (x == 0)
strcpy(atarigame, comma);
else if (x == 1)
strcpy(atarimm, comma);
else if (x == 2)
strcpy(atarill, comma);
comma = strtok(NULL, ",");
x += 1;
}
return true;
}
bool getCartListInfo_ATARI() {
bool buttonreleased = 0;
bool cartselected = 0;
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F(" HOLD TO FAST CYCLE"));
display_Update();
#else
Serial.println(F("HOLD BUTTON TO FAST CYCLE"));
#endif
delay(2000);
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
boolean buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == LOW) { // Button Held - Fast Cycle
while (1) { // Scroll Game List
while (readVals_ATARI(atarigame, atarimm, atarill)) {
if (strcmp(ataricsvEND, atarigame) == 0) {
ataricsvFile.seek(0); // Restart
} else {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART TITLE:"));
println_Msg(F(""));
println_Msg(atarigame);
display_Update();
#else
Serial.print(F("CART TITLE:"));
Serial.println(atarigame);
#endif
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == HIGH) { // Button Released
buttonreleased = 1;
break;
}
if (buttonreleased) {
buttonreleased = 0; // Reset Flag
break;
}
}
}
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == HIGH) // Button Released
break;
}
}
#if (defined(enable_OLED) || defined(enable_LCD))
display.setCursor(0, 56);
println_Msg(F("FAST CYCLE OFF"));
display_Update();
#else
Serial.println(F(""));
Serial.println(F("FAST CYCLE OFF"));
Serial.println(F("PRESS BUTTON TO STEP FORWARD"));
Serial.println(F("DOUBLE CLICK TO STEP BACK"));
Serial.println(F("HOLD TO SELECT"));
Serial.println(F(""));
#endif
while (readVals_ATARI(atarigame, atarimm, atarill)) {
if (strcmp(ataricsvEND, atarigame) == 0) {
ataricsvFile.seek(0); // Restart
} else {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART TITLE:"));
println_Msg(F(""));
println_Msg(atarigame);
display.setCursor(0, 48);
println_Msg(F("Press to Change"));
println_Msg(F("Hold to Select"));
display_Update();
#else
Serial.print(F("CART TITLE:"));
Serial.println(atarigame);
#endif
while (1) { // Single Step
int b = checkButton();
if (b == 1) { // Continue (press)
break;
}
if (b == 2) { // Reset to Start of List (doubleclick)
byte prevline = strtol(atarill, NULL, 10);
ataricsvpos -= prevline;
ataricsvFile.seek(ataricsvpos);
break;
}
if (b == 3) { // Long Press - Select Cart (hold)
newatarimapper = strtol(atarimm, NULL, 10);
EEPROM_writeAnything(7, newatarimapper);
cartselected = 1; // SELECTION MADE
#if (defined(enable_OLED) || defined(enable_LCD))
println_Msg(F("SELECTION MADE"));
display_Update();
#else
Serial.println(F("SELECTION MADE"));
#endif
break;
}
}
if (cartselected) {
cartselected = 0; // Reset Flag
return true;
}
}
}
#if (defined(enable_OLED) || defined(enable_LCD))
println_Msg(F(""));
println_Msg(F("END OF FILE"));
display_Update();
#else
Serial.println(F("END OF FILE"));
#endif
return false;
}
void checkCSV_ATARI() {
if (getCartListInfo_ATARI()) {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART SELECTED"));
println_Msg(F(""));
println_Msg(atarigame);
display_Update();
// Display Settings
display.setCursor(0, 56);
print_Msg(F("CODE: "));
println_Msg(newatarimapper, HEX);
display_Update();
#else
Serial.println(F(""));
Serial.println(F("CART SELECTED"));
Serial.println(atarigame);
// Display Settings
Serial.print(F("CODE: "));
Serial.println(newatarimapper, HEX);
Serial.println(F(""));
#endif
} else {
#if (defined(enable_OLED) || defined(enable_LCD))
display.setCursor(0, 56);
println_Msg(F("NO SELECTION"));
display_Update();
#else
Serial.println(F("NO SELECTION"));
#endif
}
}
void checkSize_ATARI() {
EEPROM_readAnything(7, atarimapper);
for (int i = 0; i < atarimapcount; i++) {
atariindex = i * 2;
if (atarimapper == pgm_read_byte(atarimapsize + atariindex)) {
atarisize = pgm_read_byte(atarimapsize + atariindex + 1);
EEPROM_writeAnything(8, atarisize);
break;
}
}
}
void setCart_ATARI() {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(ataricartCSV);
display_Update();
#endif
sd.chdir();
sprintf(folder, "ATARI/CSV");
sd.chdir(folder); // Switch Folder
ataricsvFile = sd.open(ataricartCSV, O_READ);
if (!ataricsvFile) {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CSV FILE NOT FOUND!"));
display_Update();
#else
Serial.println(F("CSV FILE NOT FOUND!"));
#endif
while (1) {
if (checkButton() != 0)
setup_ATARI();
}
}
checkCSV_ATARI();
ataricsvFile.close();
checkSize_ATARI();
}
#endif

View File

@ -4,8 +4,8 @@
This project represents a community-driven effort to provide
an easy to build and easy to modify cartridge dumper.
Date: 20.12.2022
Version: 11.5
Date: 03.01.2023
Version: 12.0
SD lib: https://github.com/greiman/SdFat
LCD lib: https://github.com/olikraus/u8g2
@ -21,7 +21,7 @@
MichlK - ROM Reader for Super Nintendo
Jeff Saltzman - 4-Way Button
Wayne and Layne - Video Game Shield menu
skaman - Cart ROM READER SNES ENHANCED, Famicom Cart Dumper, Coleco-, Intellivision, Virtual Boy, WSV, PCW modules
skaman - Cart ROM READER SNES ENHANCED, Famicom Cart Dumper, Coleco-, Intellivision, Virtual Boy, WSV, PCW, ARC, Atari, ODY2, Fairchild modules
Tamanegi_taro - PCE and Satellaview modules
splash5 - GBSmart, Wonderswan and NGP modules
hkz & themanbehindthecurtain - N64 flashram commands
@ -38,7 +38,7 @@
jiyunomegami, splash5, Kreeblah, ramapcsx2, PsyK0p4T, Dakkaron, majorpbx, Pickle, sdhizumi,
Uzlopak, sakman55, Tombo89, scrap-a, borti4938, vogelfreiheit, CaitSith2, Modman,
philenotfound, karimhadjsalem, nsx0r, ducky92, niklasweber, Lesserkuma, BacteriaMage,
vpelletier, Ancyker
vpelletier, Ancyker, mattiacci, RWeick
And to nocash for figuring out the secrets of the SFC Nintendo Power cartridge.
@ -57,7 +57,7 @@
**********************************************************************************/
char ver[5] = "11.5";
char ver[5] = "12.0";
//******************************************
// !!! CHOOSE HARDWARE VERSION !!!
@ -79,10 +79,24 @@ char ver[5] = "11.5";
//******************************************
// add/remove // before #define to disable/enable modules you
// don't need/need to save programm storage space and dynamic memory
// If you only get an empty or "Press Button" screen after flashing
// you have enabled too many modules
// Atari 2600
//#define enable_ATARI
// Benesse Pocket Challenge W
//#define enable_PCW
// ColecoVision
#define enable_COLV
// Emerson Arcadia 2001
//#define enable_ARC
// Fairchild Channel F
//#define enable_FAIRCHILD
// Flashrom Programmer for SNES repros
#define enable_FLASH
//#define enable_FLASH16
@ -93,8 +107,8 @@ char ver[5] = "11.5";
// Intellivision
#define enable_INTV
// Sega Mega Drive/Genesis
#define enable_MD
// Neo Geo Pocket
#define enable_NGP
// Nintendo 64
#define enable_N64
@ -102,20 +116,17 @@ char ver[5] = "11.5";
// Nintendo Entertainment System/Family Computer
#define enable_NES
// Neo Geo Pocket
#define enable_NGP
// Magnavox Odyssey 2
//#define enable_ODY2
// PC Engine/TurboGrafx 16
#define enable_PCE
// Benesse Pocket Challenge W
#define enable_PCW
// Sega Master System
#define enable_SMS
// Super Nintendo
#define enable_SNES
// Sega Mega Drive/Genesis
#define enable_MD
// Super Famicom SF Memory Cassette
#define enable_SFM
@ -123,15 +134,18 @@ char ver[5] = "11.5";
// Super Famicom Satellaview
#define enable_SV
// Super Nintendo
#define enable_SNES
// Virtual Boy
#define enable_VBOY
// Watara Supervision
//#define enable_WSV
// WonderSwan
#define enable_WS
// Watara Supervision
#define enable_WSV
//******************************************
// HW CONFIGS
//******************************************
@ -390,6 +404,10 @@ void print_STR(byte string_number, boolean newline) {
#define mode_VBOY 25
#define mode_WSV 26
#define mode_PCW 27
#define mode_ATARI 28
#define mode_ODY2 29
#define mode_ARC 30
#define mode_FAIRCHILD 31
// optimization-safe nop delay
#define NOP __asm__ __volatile__("nop\n\t")
@ -630,6 +648,52 @@ uint32_t calculateCRC(char* fileName, char* folder, int offset) {
}
}
/******************************************
CRC Functions for Atari, Fairchild, Ody2, Arc modules
*****************************************/
#if (defined(enable_ATARI) || defined(enable_ODY2) || defined(enable_ARC) || defined(enable_FAIRCHILD))
inline uint32_t updateCRC(uint8_t ch, uint32_t crc) {
uint32_t idx = ((crc) ^ (ch)) & 0xff;
uint32_t tab_value = pgm_read_dword(crc_32_tab + idx);
return tab_value ^ ((crc) >> 8);
}
FsFile crcFile;
char tempCRC[9];
uint32_t crc32(FsFile& file, uint32_t& charcnt) {
uint32_t oldcrc32 = 0xFFFFFFFF;
charcnt = 0;
while (file.available()) {
crcFile.read(sdBuffer, 512);
for (int x = 0; x < 512; x++) {
uint8_t c = sdBuffer[x];
charcnt++;
oldcrc32 = updateCRC(c, oldcrc32);
}
}
return ~oldcrc32;
}
void calcCRC(char* checkFile, unsigned long filesize, uint32_t* crcCopy, unsigned long offset) {
uint32_t crc;
crcFile = sd.open(checkFile);
crcFile.seek(offset);
crc = crc32(crcFile, filesize);
crcFile.close();
sprintf(tempCRC, "%08lX", crc);
if (crcCopy != NULL) {
*crcCopy = crc;
}
print_Msg(F("CRC: "));
println_Msg(tempCRC);
display_Update();
}
#endif
//******************************************
// Functions for CRC32 database
//******************************************
@ -932,18 +996,22 @@ static const char modeItem11[] PROGMEM = "Colecovision";
static const char modeItem12[] PROGMEM = "Virtual Boy";
static const char modeItem13[] PROGMEM = "Watara Supervision";
static const char modeItem14[] PROGMEM = "Pocket Challenge W";
static const char modeItem15[] PROGMEM = "Flashrom Programmer";
static const char modeItem16[] PROGMEM = "About";
static const char* const modeOptions[] PROGMEM = { modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7, modeItem8, modeItem9, modeItem10, modeItem11, modeItem12, modeItem13, modeItem14, modeItem15, modeItem16 };
static const char modeItem15[] PROGMEM = "Atari 2600";
static const char modeItem16[] PROGMEM = "Magnavox Odyssey 2";
static const char modeItem17[] PROGMEM = "Arcadia 2001";
static const char modeItem18[] PROGMEM = "Fairchild Channel F";
static const char modeItem19[] PROGMEM = "Flashrom Programmer";
static const char modeItem20[] PROGMEM = "About";
static const char* const modeOptions[] PROGMEM = { modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7, modeItem8, modeItem9, modeItem10, modeItem11, modeItem12, modeItem13, modeItem14, modeItem15, modeItem16, modeItem17, modeItem18, modeItem19, modeItem20 };
// All included slots
void mainMenu() {
// create menu with title and 15 options to choose from
// create menu with title and 20 options to choose from
unsigned char modeMenu;
byte num_answers;
byte option_offset;
// Main menu spans across two pages
// Main menu spans across three pages
currPage = 1;
lastPage = 1;
numPages = 3;
@ -957,7 +1025,7 @@ void mainMenu() {
num_answers = 7;
} else { // currPage == 3
option_offset = 14;
num_answers = 2;
num_answers = 6;
}
// Copy menuOptions out of progmem
convertPgm(modeOptions + option_offset, num_answers);
@ -1077,8 +1145,36 @@ void mainMenu() {
break;
#endif
#ifdef enable_FLASH
#ifdef enable_ATARI
case 14:
setup_ATARI();
atariMenu();
break;
#endif
#ifdef enable_ODY2
case 15:
setup_ODY2();
ody2Menu();
break;
#endif
#ifdef enable_ARC
case 16:
setup_ARC();
arcMenu();
break;
#endif
#ifdef enable_FAIRCHILD
case 17:
setup_FAIRCHILD();
fairchildMenu();
break;
#endif
#ifdef enable_FLASH
case 18:
#ifdef enable_FLASH16
flashMenu();
#else
@ -1087,7 +1183,7 @@ void mainMenu() {
break;
#endif
case 15:
case 19:
aboutScreen();
break;
@ -1115,20 +1211,29 @@ static const char modeItem6[] PROGMEM = "About";
static const char* const modeOptions[] PROGMEM = { modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, string_reset2 };
// Add-ons submenu
static const char addonsItem1[] PROGMEM = "Consoles";
static const char addonsItem2[] PROGMEM = "Handhelds";
static const char addonsItem3[] PROGMEM = "Flashrom Programmer";
//static const char addonsItem4[] PROGMEM = "Reset"; (stored in common strings array)
static const char* const addonsOptions[] PROGMEM = { addonsItem1, addonsItem2, addonsItem3, string_reset2 };
static const char addonsItem1[] PROGMEM = "70s Consoles";
static const char addonsItem2[] PROGMEM = "80s Consoles";
static const char addonsItem3[] PROGMEM = "Handhelds";
static const char addonsItem4[] PROGMEM = "Flashrom Programmer";
//static const char addonsItem5[] PROGMEM = "Reset"; (stored in common strings array)
static const char* const addonsOptions[] PROGMEM = { addonsItem1, addonsItem2, addonsItem3, addonsItem4, string_reset2 };
// Consoles submenu
static const char consolesItem1[] PROGMEM = "NES/Famicom";
static const char consolesItem2[] PROGMEM = "PC Engine/TG16";
static const char consolesItem3[] PROGMEM = "SMS/GG/MIII/SG-1000";
static const char consolesItem4[] PROGMEM = "Intellivision";
static const char consolesItem5[] PROGMEM = "Colecovision";
//static const char consolesItem6[] PROGMEM = "Reset"; (stored in common strings array)
static const char* const consolesOptions[] PROGMEM = { consolesItem1, consolesItem2, consolesItem3, consolesItem4, consolesItem5, string_reset2 };
// 70s Consoles submenu
static const char consoles70Item1[] PROGMEM = "Atari 2600";
static const char consoles70Item2[] PROGMEM = "Magnavox Odyssey 2";
static const char consoles70Item3[] PROGMEM = "Arcadia 2001";
static const char consoles70Item4[] PROGMEM = "Fairchild Channel F";
static const char consoles70Item5[] PROGMEM = "Intellivision";
static const char consoles70Item6[] PROGMEM = "Colecovision";
//static const char consoles70Item7[] PROGMEM = "Reset"; (stored in common strings array)
static const char* const consoles70Options[] PROGMEM = { consoles70Item1, consoles70Item2, consoles70Item3, consoles70Item4, consoles70Item5, consoles70Item6, string_reset2 };
// 80s Consoles submenu
static const char consoles80Item1[] PROGMEM = "NES/Famicom";
static const char consoles80Item2[] PROGMEM = "PC Engine/TG16";
static const char consoles80Item3[] PROGMEM = "SMS/GG/MIII/SG-1000";
//static const char consoles80Item4[] PROGMEM = "Reset"; (stored in common strings array)
static const char* const consoles80Options[] PROGMEM = { consoles80Item1, consoles80Item2, consoles80Item3, string_reset2 };
// Handhelds submenu
static const char handheldsItem1[] PROGMEM = "Virtual Boy";
@ -1192,26 +1297,31 @@ void mainMenu() {
// Everything that needs an adapter
void addonMenu() {
// create menu with title and 4 options to choose from
// create menu with title and 5 options to choose from
unsigned char addonsMenu;
// Copy menuOptions out of progmem
convertPgm(addonsOptions, 4);
addonsMenu = question_box(F("Type"), menuOptions, 4, 0);
convertPgm(addonsOptions, 5);
addonsMenu = question_box(F("Type"), menuOptions, 5, 0);
// wait for user choice to come back from the question box menu
switch (addonsMenu) {
// Consoles
// 70s Consoles
case 0:
consoleMenu();
consoles70Menu();
break;
// 80s Consoles
case 1:
consoles80Menu();
break;
// Handhelds
case 1:
case 2:
handheldMenu();
break;
#ifdef enable_FLASH
case 2:
case 3:
#ifdef enable_FLASH16
flashMenu();
#else
@ -1220,7 +1330,7 @@ void addonMenu() {
break;
#endif
case 3:
case 4:
resetArduino();
break;
@ -1230,15 +1340,76 @@ void addonMenu() {
}
// Everything that needs an adapter
void consoleMenu() {
// create menu with title and 6 options to choose from
unsigned char consolesMenu;
void consoles70Menu() {
// create menu with title and 7 options to choose from
unsigned char consoles70Menu;
// Copy menuOptions out of progmem
convertPgm(consolesOptions, 6);
consolesMenu = question_box(F("Choose Adapter"), menuOptions, 6, 0);
convertPgm(consoles70Options, 7);
consoles70Menu = question_box(F("Choose Adapter"), menuOptions, 7, 0);
// wait for user choice to come back from the question box menu
switch (consolesMenu) {
switch (consoles70Menu) {
#ifdef enable_ATARI
case 0:
setup_ATARI();
atariMenu();
break;
#endif
#ifdef enable_ODY2
case 1:
setup_ODY2();
ody2Menu();
break;
#endif
#ifdef enable_ARC
case 2:
setup_ARC();
arcMenu();
break;
#endif
#ifdef enable_FAIRCHILD
case 3:
setup_FAIRCHILD();
fairchildMenu();
break;
#endif
#ifdef enable_INTV
case 4:
setup_INTV();
intvMenu();
break;
#endif
#ifdef enable_COLV
case 5:
setup_COL();
colMenu();
break;
#endif
case 6:
resetArduino();
break;
default:
print_MissingModule(); // does not return
}
}
// Everything that needs an adapter
void consoles80Menu() {
// create menu with title and 6 options to choose from
unsigned char consoles80Menu;
// Copy menuOptions out of progmem
convertPgm(consoles80Options, 4);
consoles80Menu = question_box(F("Choose Adapter"), menuOptions, 4, 0);
// wait for user choice to come back from the question box menu
switch (consoles80Menu) {
#ifdef enable_NES
case 0:
mode = mode_NES;
@ -1265,21 +1436,7 @@ void consoleMenu() {
break;
#endif
#ifdef enable_INTV
case 3:
setup_INTV();
intvMenu();
break;
#endif
#ifdef enable_COLV
case 4:
setup_COL();
colMenu();
break;
#endif
case 5:
resetArduino();
break;
@ -3332,6 +3489,26 @@ void loop() {
else if (mode == mode_PCW) {
pcwMenu();
}
#endif
#ifdef enable_ATARI
else if (mode == mode_ATARI) {
atariMenu();
}
#endif
#ifdef enable_ODY2
else if (mode == mode_ODY2) {
ody2Menu();
}
#endif
#ifdef enable_ARC
else if (mode == mode_ARC) {
arcMenu();
}
#endif
#ifdef enable_FAIRCHILD
else if (mode == mode_FAIRCHILD) {
fairchildMenu();
}
#endif
else {
display_Clear();

865
Cart_Reader/FAIRCHILD.ino Normal file
View File

@ -0,0 +1,865 @@
//******************************************
// FAIRCHILD CHANNEL F MODULE
//******************************************
#if defined(enable_FAIRCHILD)
// Fairchild Channel F
// Cartridge Pinout
// 22P (27P Width) 2.54mm pitch connector
//
// TOP BOTTOM
// SIDE SIDE
// +-------+
// | == |
// | 1 |- GND
// | 2 |- GND
// | 3 |- D0
// | 4 |- D1
// | 5 |- /INTREQ
// | 6 |- ROMC0
// | 7 |- ROMC1
// | 8 |- ROMC2
// | 9 |- D2
// | 10 |- ROMC3
// | 11 |- D3
// | == |
// | == |
// | == |
// | 12 |- ROMC4
// | 13 |- PHI
// | 14 |- D4
// | 15 |- WRITE
// | 16 |- D5
// | 17 |- D6
// | 18 |- D7
// | 19 |- VDD(+5V)
// | 20 |- VDD(+5V)
// | 21 |- NC
// | 22 |- VGG(+12V)
// | == |
// +-------+
//
// TOP
// +----------------------------------------------------------------------------------+
// | |
// LEFT | | RIGHT
// | == 22 21 20 19 18 17 16 15 14 13 12 == == == 11 10 9 8 7 6 5 4 3 2 1 == |
// +----------------------------------------------------------------------------------+
// BOTTOM
//
// CONTROL PINS:
// PHI(PH3) - SNES /CS
// /INTREQ(PH4) - SNES /IRQ
// WRITE(PH5) - SNES /WR
// ROMC0(PF0) - SNES A0
// ROMC1(PF1) - SNES A1
// ROMC2(PF2) - SNES A2
// ROMC3(PF3) - SNES A3
// ROMC4(PF4) - SNES A4
/******************************************
Defines
*****************************************/
#define PHI_HI PORTH |= (1 << 3)
#define PHI_LOW PORTH &= ~(1 << 3)
#define WRITE_HI PORTH |= (1 << 5)
#define WRITE_LOW PORTH &= ~(1 << 5)
byte FAIRCHILD[] = { 2, 3, 4, 6 };
byte fairchildlo = 0; // Lowest Entry
byte fairchildhi = 3; // Highest Entry
byte fairchildsize;
byte newfairchildsize;
// EEPROM MAPPING
// 08 ROM SIZE
//******************************************
// Menu
//******************************************
// Base Menu
static const char fairchildMenuItem1[] PROGMEM = "Select Cart";
static const char fairchildMenuItem2[] PROGMEM = "Read ROM";
static const char fairchildMenuItem3[] PROGMEM = "Set Size";
static const char fairchildMenuItem4[] PROGMEM = "Read 16K";
static const char fairchildMenuItem5[] PROGMEM = "Reset";
static const char* const menuOptionsFAIRCHILD[] PROGMEM = { fairchildMenuItem1, fairchildMenuItem2, fairchildMenuItem3, fairchildMenuItem4, fairchildMenuItem5 };
void setup_FAIRCHILD() {
// Set Address Pins to Output
// Channel F uses A0-A4 [A5-A23 UNUSED]
//A0-A7
DDRF = 0xFF;
//A8-A15
DDRK = 0xFF;
//A16-A23
DDRL = 0xFF;
// Set Control Pins to Output
// ---(PH0) ---(PH1) PHI(PH3) /INTREQ(PH4) WRITE(PH5) ---(PH6)
DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// Set TIME(PJ0) to Output (UNUSED)
DDRJ |= (1 << 0);
// Set Pins (D0-D7) to Input
DDRC = 0x00;
// Setting Unused Control Pins to HIGH
// ---(PH0) ---(PH1) PHI(PH3) /INTREQ(PH4) WRITE(PH5) ---(PH6)
PORTH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// Set Unused Data Pins (PA0-PA7) to Output
DDRA = 0xFF;
// Set Unused Pins HIGH
PORTA = 0xFF;
PORTK = 0xFF; // A8-A15
PORTL = 0xFF; // A16-A23
PORTJ |= (1 << 0); // TIME(PJ0)
checkStatus_FAIRCHILD();
strcpy(romName, "FAIRCHILD");
mode = mode_FAIRCHILD;
}
void fairchildMenu() {
convertPgm(menuOptionsFAIRCHILD, 5);
uint8_t mainMenu = question_box(F("CHANNEL F MENU"), menuOptions, 5, 0);
switch (mainMenu) {
case 0:
// Select Cart
setCart_FAIRCHILD();
wait();
setup_FAIRCHILD();
break;
case 1:
// Read ROM
sd.chdir("/");
readROM_FAIRCHILD();
sd.chdir("/");
break;
case 2:
// Set Size
setROMSize_FAIRCHILD();
break;
case 3:
// Read 16K
sd.chdir("/");
read16K_FAIRCHILD();
sd.chdir("/");
break;
case 4:
// reset
resetArduino();
break;
}
}
//******************************************
// READ CODE
//******************************************
// Sean Riddle Dumper Routine
// clear PC0 with ROMC state 8
// loop 256 times
// fetch 16 bytes into buffer with ROMC state 0
// dump buffer to serial port
// clear PC0
// Clear PC0
void clearRegister_FAIRCHILD() {
PHI_LOW;
WRITE_LOW;
PORTF = 0; // ROMC3 LOW
delay(2000);
PHI_HI;
WRITE_HI;
NOP;
NOP;
NOP;
PHI_LOW;
NOP;
NOP;
NOP;
WRITE_LOW;
PHI_HI;
PORTF = 0; // ROMC3 LOW
NOP;
NOP;
NOP;
PHI_LOW;
NOP;
NOP;
NOP;
PORTF = 0; // ROMC3 LOW
PHI_HI;
PORTF = 0x8; // this puts us in ROMC state 8 - clear PC0
NOP;
NOP;
NOP;
PHI_LOW;
NOP;
NOP;
NOP;
PORTF = 0x08; // ROMC3 HIGH
PHI_HI;
PORTF = 0x08; // ROMC3 HIGH
NOP;
NOP;
NOP;
PHI_LOW;
NOP;
NOP;
NOP;
PHI_HI;
WRITE_HI;
NOP;
NOP;
NOP;
PHI_LOW;
NOP;
NOP;
NOP;
WRITE_LOW;
PHI_HI;
PORTF = 0; // ROMC3 LOW
NOP;
NOP;
NOP;
PHI_LOW;
NOP;
NOP;
NOP;
WRITE_LOW;
PHI_HI;
PORTF = 0; // ROMC3 LOW
NOP;
NOP;
NOP;
PHI_LOW;
NOP;
NOP;
NOP;
WRITE_LOW;
}
void setROMC_FAIRCHILD(uint8_t command) {
PHI_LOW;
WRITE_LOW;
NOP;
WRITE_HI;
PHI_HI;
NOP;
PHI_LOW;
NOP;
NOP;
WRITE_LOW;
PHI_HI;
NOP;
NOP;
// PWs = 4 PHI Cycles
// PWl = 6 PHI Cycles
for (int x = 0; x < 2; x++) { // 2 PHI
PHI_LOW;
NOP;
NOP;
PHI_HI;
NOP;
NOP;
}
PORTF = command; // ROMC3 = command
for (int x = 0; x < 3; x++) { // 4 PHI
PHI_LOW;
NOP;
NOP;
PHI_HI;
NOP;
NOP;
}
PHI_LOW;
NOP;
NOP;
PHI_HI;
WRITE_HI;
NOP;
PHI_LOW;
NOP;
NOP;
PHI_HI;
WRITE_LOW;
NOP;
PHI_LOW;
NOP;
NOP;
}
void setREAD_FAIRCHILD() {
PHI_LOW;
WRITE_LOW;
NOP;
WRITE_HI;
PHI_HI;
NOP;
PHI_LOW;
NOP;
NOP;
WRITE_LOW;
PHI_HI;
NOP;
NOP;
// PWs = 4 PHI Cycles
// PWl = 6 PHI Cycles
for (int x = 0; x < 2; x++) { // 2 PHI
PHI_LOW;
NOP;
NOP;
PHI_HI;
NOP;
NOP;
}
PORTF = 0; // ROMC3 = 0 = Fetch Data
}
uint8_t readData_FAIRCHILD() {
for (int x = 0; x < 3; x++) { // 4 PHI
PHI_LOW;
NOP;
NOP;
PHI_HI;
NOP;
NOP;
}
PHI_LOW;
NOP;
NOP;
PHI_HI;
WRITE_HI;
NOP;
PHI_LOW;
NOP;
NOP;
PHI_HI;
WRITE_LOW;
NOP;
uint8_t ret = PINC; // read databus into buffer
PHI_LOW;
NOP;
NOP;
return ret;
}
void readROM_FAIRCHILD() {
strcpy(fileName, romName);
strcat(fileName, ".bin");
// create a new folder for storing rom file
EEPROM_readAnything(0, foldern);
sprintf(folder, "FAIRCHILD/ROM/%d", foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
display_Clear();
print_Msg(F("Saving to "));
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
// open file on sdcard
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
print_FatalError(create_file_STR);
}
// write new folder number back to EEPROM
foldern++;
EEPROM_writeAnything(0, foldern);
unsigned long cartsize = FAIRCHILD[fairchildsize] * 0x400;
uint8_t blocks = cartsize / 0x200;
setROMC_FAIRCHILD(0x8); // Clear PC0
setREAD_FAIRCHILD();
// ROM Start Bytes
// 0x55,0x08 - desert fox, muehle, space war, tic-tac-toe (all 2K)
// 0x55,0x2B - most carts
// 0x55,0xAA - alien invasion (4K)
// 0x55,0xBB - video whizball (3K)
for (int y = 0; y < 0x4800; y++) {
uint8_t startbyte = readData_FAIRCHILD();
if (startbyte == 0x55) { // Start Byte
sdBuffer[0] = startbyte;
startbyte = readData_FAIRCHILD();
if ((startbyte == 0x08) || (startbyte == 0x2B) || (startbyte == 0xAA) || (startbyte == 0xBB)) {
sdBuffer[1] = startbyte;
for (int w = 2; w < 512; w++) {
startbyte = readData_FAIRCHILD();
sdBuffer[w] = startbyte;
}
myFile.write(sdBuffer, 512);
delay(1); // Added delay
for (int z = 1; z < blocks; z++) {
// Skip BIOS/Blocks Code for 4K Cart
if (cartsize == 0x1000) { // Pro Football 4K
setROMC_FAIRCHILD(0x8); // Clear PC0
setREAD_FAIRCHILD();
uint16_t offset = z * 0x200;
for (int x = 0; x < 0x800 + offset; x++) { // Skip BIOS/Previous Blocks
readData_FAIRCHILD();
}
}
for (int w = 0; w < 512; w++) {
uint8_t temp = readData_FAIRCHILD();
sdBuffer[w] = temp;
}
myFile.write(sdBuffer, 512);
delay(1); // Added delay
}
break;
}
}
}
myFile.close();
calcCRC(fileName, cartsize, NULL, 0);
println_Msg(F(""));
println_Msg(F("Press Button..."));
display_Update();
wait();
}
void read16K_FAIRCHILD() // Read 16K Bytes
{
strcpy(fileName, romName);
strcat(fileName, ".bin");
// create a new folder for storing rom file
EEPROM_readAnything(0, foldern);
sprintf(folder, "FAIRCHILD/ROM/%d", foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
display_Clear();
print_Msg(F("Saving to "));
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
// open file on sdcard
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
print_FatalError(create_file_STR);
}
// write new folder number back to EEPROM
foldern++;
EEPROM_writeAnything(0, foldern);
unsigned long cartsize = FAIRCHILD[fairchildsize] * 0x400;
for (int y = 0; y < 0x20; y++) {
// Skip BIOS/Blocks Code for 4K Cart
// 3K/6K CARTS MAY NEED THE SKIP BIOS/BLOCKS ROUTINE USED FOR THE 4K CART
// TEST 3K/6K CARTS BY SETTING ROM SIZE TO 2K/4K AND COMPARE 16K DUMPS
if (cartsize == 0x1000) { // Pro Football 4K
setROMC_FAIRCHILD(0x8); // Clear PC0
setREAD_FAIRCHILD();
uint16_t offset = y * 0x200;
for (int x = 0; x < 0x800 + offset; x++) { // Skip BIOS/Previous Blocks
readData_FAIRCHILD();
}
}
for (int w = 0; w < 512; w++) {
uint8_t temp = readData_FAIRCHILD();
sdBuffer[w] = temp;
}
myFile.write(sdBuffer, 512);
delay(1); // Added delay
}
myFile.close();
calcCRC(fileName, 0x4000, NULL, 0);
println_Msg(F(""));
println_Msg(F("Press Button..."));
display_Update();
wait();
}
//******************************************
// ROM SIZE
//******************************************
void setROMSize_FAIRCHILD() {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
if (fairchildlo == fairchildhi)
newfairchildsize = fairchildlo;
else {
int b = 0;
int i = fairchildlo;
while (1) {
display_Clear();
print_Msg(F("ROM Size: "));
println_Msg(FAIRCHILD[i]);
println_Msg(F(""));
println_Msg(F("Press to Change"));
println_Msg(F("Hold to Select"));
display_Update();
b = checkButton();
if (b == 2) { // Previous (doubleclick)
if (i == fairchildlo)
i = fairchildhi;
else
i--;
}
if (b == 1) { // Next (press)
if (i == fairchildhi)
i = fairchildlo;
else
i++;
}
if (b == 3) { // Long Press - Execute (hold)
newfairchildsize = i;
break;
}
}
display.setCursor(0, 56); // Display selection at bottom
}
print_Msg(F("ROM SIZE "));
print_Msg(FAIRCHILD[newfairchildsize]);
println_Msg(F("K"));
display_Update();
delay(1000);
#else
if (fairchildlo == fairchildhi)
newfairchildsize = fairchildlo;
else {
setrom:
String sizeROM;
for (int i = 0; i < (fairchildhi - fairchildlo + 1); i++) {
Serial.print(F("Select ROM Size: "));
Serial.print(i);
Serial.print(F(" = "));
Serial.print(FAIRCHILD[i + fairchildlo]);
Serial.println(F("K"));
}
Serial.print(F("Enter ROM Size: "));
while (Serial.available() == 0) {}
sizeROM = Serial.readStringUntil('\n');
Serial.println(sizeROM);
newfairchildsize = sizeROM.toInt() + fairchildlo;
if (newfairchildsize > fairchildhi) {
Serial.println(F("SIZE NOT SUPPORTED"));
Serial.println(F(""));
goto setrom;
}
}
Serial.print(F("ROM Size = "));
Serial.print(FAIRCHILD[newfairchildsize]);
Serial.println(F("K"));
#endif
EEPROM_writeAnything(8, newfairchildsize);
fairchildsize = newfairchildsize;
}
void checkStatus_FAIRCHILD() {
EEPROM_readAnything(8, fairchildsize);
if (fairchildsize > 3) {
fairchildsize = 0;
EEPROM_writeAnything(8, fairchildsize);
}
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CHANNEL F READER"));
println_Msg(F("CURRENT SETTINGS"));
println_Msg(F(""));
print_Msg(F("ROM SIZE: "));
print_Msg(FAIRCHILD[fairchildsize]);
println_Msg(F("K"));
display_Update();
wait();
#else
Serial.print(F("CURRENT ROM SIZE: "));
Serial.print(FAIRCHILD[fairchildsize]);
Serial.println(F("K"));
Serial.println(F(""));
#endif
}
//******************************************
// CART SELECT CODE
//******************************************
FsFile fairchildcsvFile;
char fairchildgame[33]; // title
char fairchildrr[3]; // romsize
char fairchildll[4]; // linelength (previous line)
unsigned long fairchildcsvpos; // CSV File Position
char fairchildcartCSV[] = "fairchildcart.txt"; // CSV List
char fairchildcsvEND[] = "EOF"; // CSV End Marker for scrolling
bool readLine_FAIRCHILD(FsFile& f, char* line, size_t maxLen) {
for (size_t n = 0; n < maxLen; n++) {
int c = f.read();
if (c < 0 && n == 0) return false; // EOF
if (c < 0 || c == '\n') {
line[n] = 0;
return true;
}
line[n] = c;
}
return false; // line too long
}
bool readVals_FAIRCHILD(char* fairchildgame, char* fairchildrr, char* fairchildll) {
char line[39];
fairchildcsvpos = fairchildcsvFile.position();
if (!readLine_FAIRCHILD(fairchildcsvFile, line, sizeof(line))) {
return false; // EOF or too long
}
char* comma = strtok(line, ",");
int x = 0;
while (comma != NULL) {
if (x == 0)
strcpy(fairchildgame, comma);
else if (x == 1)
strcpy(fairchildrr, comma);
else if (x == 2)
strcpy(fairchildll, comma);
comma = strtok(NULL, ",");
x += 1;
}
return true;
}
bool getCartListInfo_FAIRCHILD() {
bool buttonreleased = 0;
bool cartselected = 0;
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F(" HOLD TO FAST CYCLE"));
display_Update();
#else
Serial.println(F("HOLD BUTTON TO FAST CYCLE"));
#endif
delay(2000);
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
boolean buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == LOW) { // Button Held - Fast Cycle
while (1) { // Scroll Game List
while (readVals_FAIRCHILD(fairchildgame, fairchildrr, fairchildll)) {
if (strcmp(fairchildcsvEND, fairchildgame) == 0) {
fairchildcsvFile.seek(0); // Restart
} else {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART TITLE:"));
println_Msg(F(""));
println_Msg(fairchildgame);
display_Update();
#else
Serial.print(F("CART TITLE:"));
Serial.println(fairchildgame);
#endif
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == HIGH) { // Button Released
buttonreleased = 1;
break;
}
if (buttonreleased) {
buttonreleased = 0; // Reset Flag
break;
}
}
}
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == HIGH) // Button Released
break;
}
}
#if (defined(enable_OLED) || defined(enable_LCD))
display.setCursor(0, 56);
println_Msg(F("FAST CYCLE OFF"));
display_Update();
#else
Serial.println(F(""));
Serial.println(F("FAST CYCLE OFF"));
Serial.println(F("PRESS BUTTON TO STEP FORWARD"));
Serial.println(F("DOUBLE CLICK TO STEP BACK"));
Serial.println(F("HOLD TO SELECT"));
Serial.println(F(""));
#endif
while (readVals_FAIRCHILD(fairchildgame, fairchildrr, fairchildll)) {
if (strcmp(fairchildcsvEND, fairchildgame) == 0) {
fairchildcsvFile.seek(0); // Restart
} else {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART TITLE:"));
println_Msg(F(""));
println_Msg(fairchildgame);
display.setCursor(0, 48);
println_Msg(F("Press to Change"));
println_Msg(F("Hold to Select"));
display_Update();
#else
Serial.print(F("CART TITLE:"));
Serial.println(fairchildgame);
#endif
while (1) { // Single Step
int b = checkButton();
if (b == 1) { // Continue (press)
break;
}
if (b == 2) { // Reset to Start of List (doubleclick)
byte prevline = strtol(fairchildll, NULL, 10);
fairchildcsvpos -= prevline;
fairchildcsvFile.seek(fairchildcsvpos);
break;
}
if (b == 3) { // Long Press - Select Cart (hold)
newfairchildsize = strtol(fairchildrr, NULL, 10);
EEPROM_writeAnything(8, newfairchildsize);
cartselected = 1; // SELECTION MADE
#if (defined(enable_OLED) || defined(enable_LCD))
println_Msg(F("SELECTION MADE"));
display_Update();
#else
Serial.println(F("SELECTION MADE"));
#endif
break;
}
}
if (cartselected) {
cartselected = 0; // Reset Flag
return true;
}
}
}
#if (defined(enable_OLED) || defined(enable_LCD))
println_Msg(F(""));
println_Msg(F("END OF FILE"));
display_Update();
#else
Serial.println(F("END OF FILE"));
#endif
return false;
}
void checkCSV_FAIRCHILD() {
if (getCartListInfo_FAIRCHILD()) {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART SELECTED"));
println_Msg(F(""));
println_Msg(fairchildgame);
display_Update();
// Display Settings
display.setCursor(0, 56);
print_Msg(F("CODE: R"));
println_Msg(newfairchildsize);
display_Update();
#else
Serial.println(F(""));
Serial.println(F("CART SELECTED"));
Serial.println(fairchildgame);
// Display Settings
Serial.print(F("CODE: R"));
Serial.println(newfairchildsize);
Serial.println(F(""));
#endif
} else {
#if (defined(enable_OLED) || defined(enable_LCD))
display.setCursor(0, 56);
println_Msg(F("NO SELECTION"));
display_Update();
#else
Serial.println(F("NO SELECTION"));
#endif
}
}
void setCart_FAIRCHILD() {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(fairchildcartCSV);
display_Update();
#endif
sd.chdir();
sprintf(folder, "FAIRCHILD/CSV");
sd.chdir(folder); // Switch Folder
fairchildcsvFile = sd.open(fairchildcartCSV, O_READ);
if (!fairchildcsvFile) {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CSV FILE NOT FOUND!"));
display_Update();
#else
Serial.println(F("CSV FILE NOT FOUND!"));
#endif
while (1) {
if (checkButton() != 0)
setup_FAIRCHILD();
}
}
checkCSV_FAIRCHILD();
fairchildcsvFile.close();
}
#endif

View File

@ -2797,7 +2797,7 @@ void readPRG(boolean readrom) {
}
}
break;
case 31:
banks = int_pow(2, prgsize) * 4;
for (int i = 0; i < banks; i += 8) {
@ -2960,7 +2960,7 @@ void readPRG(boolean readrom) {
}
}
break;
case 56:
banks = int_pow(2, prgsize) * 2;
for (int i = 0; i < banks; i++) {
@ -2971,7 +2971,7 @@ void readPRG(boolean readrom) {
}
}
break;
case 57:
banks = int_pow(2, prgsize);
for (int i = 0; i < banks; i++) {
@ -3060,7 +3060,7 @@ void readPRG(boolean readrom) {
case 68:
case 73: // 128K
banks = int_pow(2, prgsize);
for (int i = 0; i < banks; i++) { // 128K
for (int i = 0; i < banks; i++) { // 128K
write_prg_byte(0xF000, i);
for (word address = 0x0; address < 0x4000; address += 512) { // 16K Banks ($8000-$BFFF)
dumpPRG(base, address);
@ -3230,7 +3230,7 @@ void readPRG(boolean readrom) {
case 94: // bus conflicts - fixed last bank
banks = int_pow(2, prgsize);
busConflict = true;
for (int i = 0; i < banks-1; i++) {
for (int i = 0; i < banks - 1; i++) {
for (int x = 0; x < 0x4000; x++) {
if (read_prg_byte(0xC000 + x) == (i << 2)) {
write_prg_byte(0xC000 + x, i << 2);
@ -3250,13 +3250,13 @@ void readPRG(boolean readrom) {
}
break;
case 97: // fixed first bank
case 97: // fixed first bank
case 180: // bus conflicts - fixed fist bank
banks = int_pow(2, prgsize);
busConflict = true;
for (word address = 0x0; address < 0x4000; address += 512) {
dumpPRG(base, address);
}
dumpPRG(base, address);
}
for (int i = 1; i < banks; i++) {
for (int x = 0; x < 0x4000; x++) {
if (read_prg_byte(0x8000 + x) == i) {
@ -3504,7 +3504,7 @@ void readPRG(boolean readrom) {
}
}
break;
case 214:
banks = int_pow(2, prgsize);
for (int i = 0; i < banks; i++) {
@ -3621,12 +3621,12 @@ void readPRG(boolean readrom) {
}
}
break;
case 236:
banks = int_pow(2, prgsize);
for (int i = 0; i < banks; i++) {
write_prg_byte(0x8000 | ((i & 0x38) >> 3), 0); // A19-A17
write_prg_byte(0xC030 | (i & 0x0F), 0); // A17-A14
write_prg_byte(0xC030 | (i & 0x0F), 0); // A17-A14
for (word address = 0x0; address < 0x4000; address += 512) {
dumpPRG(base, address);
}
@ -3920,8 +3920,8 @@ void readCHR(boolean readrom) {
case 25: // 128K/256K
banks = int_pow(2, chrsize) * 4;
for (int i = 0; i < banks; i++) {
write_prg_byte(0xB000, i & 0xF); // CHR Bank Lower 4 bits
write_prg_byte(0xB00A, (i >> 4) & 0xF); // Combine VRC2c and VRC4b, VRC4d reg
write_prg_byte(0xB000, i & 0xF); // CHR Bank Lower 4 bits
write_prg_byte(0xB00A, (i >> 4) & 0xF); // Combine VRC2c and VRC4b, VRC4d reg
for (word address = 0x0; address < 0x400; address += 512) {
dumpCHR(address);
}
@ -4076,7 +4076,7 @@ void readCHR(boolean readrom) {
}
}
break;
case 56:
banks = int_pow(2, chrsize) * 4;
for (int i = 0; i < banks; i++) {
@ -4086,11 +4086,11 @@ void readCHR(boolean readrom) {
}
}
break;
case 57:
banks = int_pow(2, chrsize) / 2;
for (int i = 0; i < banks; i++) {
write_prg_byte(0x8800, i & 0x07); // A15-A13
write_prg_byte(0x8800, i & 0x07); // A15-A13
write_prg_byte(0x8000, 0x80 | ((i & 0x08) << 3)); // A16
for (word address = 0x0; address < 0x2000; address += 512) {
dumpCHR(address);
@ -4530,7 +4530,7 @@ void readCHR(boolean readrom) {
}
}
break;
case 214:
banks = int_pow(2, chrsize) / 2;
for (int i = 0; i < banks; i++) {
@ -4570,7 +4570,7 @@ void readCHR(boolean readrom) {
}
}
break;
case 236:
banks = int_pow(2, chrsize) / 2;
for (int i = 0; i < banks; i++) {

632
Cart_Reader/ODY2.ino Normal file
View File

@ -0,0 +1,632 @@
//******************************************
// MAGNAVOX ODYSSEY 2 MODULE
//******************************************
#if defined(enable_ODY2)
// Magnavox Odyssey 2
// Philips Videopac/Videopac+
// Cartridge Pinout
// 30P 3.96mm pitch connector
//
// FRONT BACK
// SIDE SIDE
// +------+
// T0 -| 1 A |- /WR
// D0 -| 2 B |- GND
// D1 -| 3 C |- GND
// D2 -| 4 D |- VCC (+5V)
// D3 -| 5 E |- CS3
// D4 -| 6 F |- /PSEN (/CE)
// D5 -| 7 G |- A0
// D6 -| 8 H |- A1
// D7 -| 9 J |- A2
// A10 (P22) -| 10 K |- A3
// /CS1 (P14) -| 11 L |- A4
// P11 -| 12 M |- A5
// P10 -| 13 N |- A7
// A11 (P23) -| 14 P |- A6
// A9 (P21) -| 15 R |- A8 (P20)
// +------+
//
// NOTE: ADDRESS A7/A6 PIN ORDER ON PINS N & P.
// NOTE: MOST CARTS DO NOT CONNECT A10 ON PIN 10.
//
// BACK
// +---------------------------------------------+
// | A B C D E F G H J K L M N P R |
// LEFT | | RIGHT
// | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// +---------------------------------------------+
// FRONT
//
// CONTROL PINS:
// T0(PH0) - SNES RESET
// CS3(PH3) - SNES /CS
// /CS1(PH4) - SNES /IRQ
// /WR(PH5) - SNES /WR
// /PSEN(PH6) - SNES /RD
byte ODY2[] = { 2, 4, 8, 12, 16 };
byte ody2lo = 0; // Lowest Entry
byte ody2hi = 4; // Highest Entry
byte ody2mapper;
byte newody2mapper;
byte ody2size;
byte newody2size;
// EEPROM MAPPING
// 07 MAPPER
// 08 ROM SIZE
//******************************************
// Menu
//******************************************
// Base Menu
static const char ody2MenuItem1[] PROGMEM = "Select Cart";
static const char ody2MenuItem2[] PROGMEM = "Read ROM";
static const char ody2MenuItem3[] PROGMEM = "Set Size";
static const char ody2MenuItem4[] PROGMEM = "Reset";
static const char* const menuOptionsODY2[] PROGMEM = { ody2MenuItem1, ody2MenuItem2, ody2MenuItem3, ody2MenuItem4 };
void setup_ODY2() {
// Set Address Pins to Output
// Odyssey 2 uses A0-A13 [A14-A23 UNUSED]
//A0-A7
DDRF = 0xFF;
//A8-A15
DDRK = 0xFF;
//A16-A23
DDRL = 0xFF;
// Set Control Pins to Output
// T0(PH0) ---(PH1) CS3(PH3) /CS1(PH4) /WR(PH5) /RD(PH6)
DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// Set TIME(PJ0) to Output (UNUSED)
DDRJ |= (1 << 0);
// Set Pins (D0-D7) to Input
DDRC = 0x00;
// Setting Control Pins to HIGH
// T0(PH0) ---(PH1) /CS1(PH4) /WR(PH5) /RD(PH6)
PORTH |= (1 << 0) | (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6);
// Set CS3(PH3) to LOW
PORTH &= ~(1 << 3);
// Set Unused Data Pins (PA0-PA7) to Output
DDRA = 0xFF;
// Set Unused Pins HIGH
PORTA = 0xFF;
PORTL = 0xFF; // A16-A23
PORTJ |= (1 << 0); // TIME(PJ0)
checkStatus_ODY2();
strcpy(romName, "ODYSSEY2");
mode = mode_ODY2;
}
void ody2Menu() {
convertPgm(menuOptionsODY2, 4);
uint8_t mainMenu = question_box(F("ODYSSEY 2 MENU"), menuOptions, 4, 0);
switch (mainMenu) {
case 0:
// Select Cart
setCart_ODY2();
wait();
setup_ODY2();
break;
case 1:
// Read ROM
sd.chdir("/");
readROM_ODY2();
sd.chdir("/");
break;
case 2:
// Set Size
setROMSize_ODY2();
break;
case 3:
// reset
resetArduino();
break;
}
}
//******************************************
// READ CODE
//******************************************
uint8_t readData_ODY2(uint16_t addr) {
PORTF = addr & 0xFF; // A0-A7
PORTK = (addr >> 8) & 0xFF; // A8-A13
// Set /PSEN (/CE) to LOW
PORTH &= ~(1 << 6); // /PSEN LOW (ENABLE)
NOP;
NOP;
NOP;
NOP;
NOP;
uint8_t ret = PINC;
// Pull /PSEN (/CE) to HIGH
PORTH |= (1 << 6); // /PSEN HIGH (DISABLE)
return ret;
}
void readSegment_ODY2(uint16_t startaddr, uint16_t endaddr) {
for (uint16_t addr = startaddr; addr < endaddr; addr += 512) {
for (int w = 0; w < 512; w++) {
uint8_t temp = readData_ODY2(addr + w);
sdBuffer[w] = temp;
}
myFile.write(sdBuffer, 512);
}
}
void bankSwitch_ODY2(uint16_t addr, uint8_t data) {
PORTF = addr & 0xFF; // A0-A7
PORTK = (addr >> 8) & 0xFF; // A8-A13
NOP;
NOP;
NOP;
NOP;
NOP;
// Set /CS1(PH4) to LOW
PORTH &= ~(1 << 4);
// Set /WR(PH5) to LOW
PORTH &= ~(1 << 5);
NOP;
NOP;
NOP;
NOP;
NOP;
DDRC = 0xFF; // Set to Output
NOP;
NOP;
NOP;
NOP;
NOP;
PORTC = data;
NOP;
NOP;
NOP;
NOP;
NOP;
// Set /WR(PH5) to HIGH
PORTH |= (1 << 5);
// Set /CS1(PH4) to HIGH
PORTH |= (1 << 4);
DDRC = 0x00; // Reset to Input
}
void readROM_ODY2() {
strcpy(fileName, romName);
strcat(fileName, ".bin");
// create a new folder for storing rom file
EEPROM_readAnything(0, foldern);
sprintf(folder, "ODY2/ROM/%d", foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
display_Clear();
print_Msg(F("Saving to "));
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
// open file on sdcard
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
print_FatalError(create_file_STR);
}
// write new folder number back to EEPROM
foldern++;
EEPROM_writeAnything(0, foldern);
if (ody2mapper == 1) { // A10 CONNECTED
// Videopac 31: Musician
// Videopac 40: 4 in 1 Row/4 en 1 Ligne
readSegment_ODY2(0x0000, 0x1000); // 4K
}
// A10 NOT CONNECTED
else if (ody2size > 2) { // 12K/16K (BANKSWITCH)
// Videopac+ 55: Neutron Star 12K = 2K x 6 Banks
// Videopac+ 58: Norseman 12K = 2K x 6 Banks
// Videopac+ 59: Helicopter Rescue 16K = 2K x 8 Banks
// Videopac+ 60: Trans American Rally 16K = 2K x 8 Banks
uint8_t ody2banks = (ody2size * 4) / 2;
for (int x = (ody2banks - 1); x >= 0; x--) {
bankSwitch_ODY2(0x80, ~x);
readSegment_ODY2(0x0400, 0x0C00); // 2K x 6/8 = 12K/16K
}
} else { // STANDARD SIZES
readSegment_ODY2(0x0400, 0x0C00); // 2K
if (ody2size > 0) {
readSegment_ODY2(0x1400, 0x1C00); // +2K = 4K
if (ody2size > 1) {
readSegment_ODY2(0x2400, 0x2C00); // +2K = 6K
readSegment_ODY2(0x3400, 0x3C00); // +2K = 8K
}
}
}
myFile.close();
unsigned long crcsize = ODY2[ody2size] * 0x400;
calcCRC(fileName, crcsize, NULL, 0);
println_Msg(F(""));
println_Msg(F("Press Button..."));
display_Update();
wait();
}
//******************************************
// ROM SIZE
//******************************************
void setROMSize_ODY2() {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
if (ody2lo == ody2hi)
newody2size = ody2lo;
else {
int b = 0;
int i = ody2lo;
while (1) {
display_Clear();
print_Msg(F("ROM Size: "));
println_Msg(ODY2[i]);
println_Msg(F(""));
println_Msg(F("Press to Change"));
println_Msg(F("Hold to Select"));
display_Update();
b = checkButton();
if (b == 2) { // Previous (doubleclick)
if (i == ody2lo)
i = ody2hi;
else
i--;
}
if (b == 1) { // Next (press)
if (i == ody2hi)
i = ody2lo;
else
i++;
}
if (b == 3) { // Long Press - Execute (hold)
newody2size = i;
break;
}
}
display.setCursor(0, 56); // Display selection at bottom
}
print_Msg(F("ROM SIZE "));
print_Msg(ODY2[newody2size]);
println_Msg(F("K"));
display_Update();
delay(1000);
#else
if (ody2lo == ody2hi)
newody2size = ody2lo;
else {
setrom:
String sizeROM;
for (int i = 0; i < (ody2hi - ody2lo + 1); i++) {
Serial.print(F("Select ROM Size: "));
Serial.print(i);
Serial.print(F(" = "));
Serial.print(ODY2[i + ody2lo]);
Serial.println(F("K"));
}
Serial.print(F("Enter ROM Size: "));
while (Serial.available() == 0) {}
sizeROM = Serial.readStringUntil('\n');
Serial.println(sizeROM);
newody2size = sizeROM.toInt() + ody2lo;
if (newody2size > ody2hi) {
Serial.println(F("SIZE NOT SUPPORTED"));
Serial.println(F(""));
goto setrom;
}
}
Serial.print(F("ROM Size = "));
Serial.print(ODY2[newody2size]);
Serial.println(F("K"));
#endif
EEPROM_writeAnything(8, newody2size);
ody2size = newody2size;
}
void checkStatus_ODY2() {
EEPROM_readAnything(7, ody2mapper);
EEPROM_readAnything(8, ody2size);
if (ody2mapper > 1) {
ody2mapper = 0;
EEPROM_writeAnything(7, ody2mapper);
}
if (ody2size > 4) {
ody2size = 0;
EEPROM_writeAnything(8, ody2size);
}
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("ODYSSEY 2 READER"));
println_Msg(F("CURRENT SETTINGS"));
println_Msg(F(""));
print_Msg(F("MAPPER: "));
println_Msg(ody2mapper);
print_Msg(F("ROM SIZE: "));
print_Msg(ODY2[ody2size]);
println_Msg(F("K"));
display_Update();
wait();
#else
Serial.print(F("CURRENT MAPPER: "));
Serial.println(ody2mapper);
Serial.print(F("CURRENT ROM SIZE: "));
Serial.print(ODY2[ody2size]);
Serial.println(F("K"));
Serial.println(F(""));
#endif
}
//******************************************
// CART SELECT CODE
//******************************************
FsFile ody2csvFile;
char ody2game[51]; // title
char ody2mm[3]; // mapper (A10)
char ody2rr[3]; // romsize
char ody2ll[4]; // linelength (previous line)
unsigned long ody2csvpos; // CSV File Position
char ody2cartCSV[] = "ody2cart.txt"; // CSV List
char ody2csvEND[] = "EOF"; // CSV End Marker for scrolling
bool readLine_ODY2(FsFile& f, char* line, size_t maxLen) {
for (size_t n = 0; n < maxLen; n++) {
int c = f.read();
if (c < 0 && n == 0) return false; // EOF
if (c < 0 || c == '\n') {
line[n] = 0;
return true;
}
line[n] = c;
}
return false; // line too long
}
bool readVals_ODY2(char* ody2game, char* ody2mm, char* ody2rr, char* ody2ll) {
char line[59];
ody2csvpos = ody2csvFile.position();
if (!readLine_ODY2(ody2csvFile, line, sizeof(line))) {
return false; // EOF or too long
}
char* comma = strtok(line, ",");
int x = 0;
while (comma != NULL) {
if (x == 0)
strcpy(ody2game, comma);
else if (x == 1)
strcpy(ody2mm, comma);
else if (x == 2)
strcpy(ody2rr, comma);
else if (x == 3)
strcpy(ody2ll, comma);
comma = strtok(NULL, ",");
x += 1;
}
return true;
}
bool getCartListInfo_ODY2() {
bool buttonreleased = 0;
bool cartselected = 0;
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F(" HOLD TO FAST CYCLE"));
display_Update();
#else
Serial.println(F("HOLD BUTTON TO FAST CYCLE"));
#endif
delay(2000);
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
boolean buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == LOW) { // Button Held - Fast Cycle
while (1) { // Scroll Game List
while (readVals_ODY2(ody2game, ody2mm, ody2rr, ody2ll)) {
if (strcmp(ody2csvEND, ody2game) == 0) {
ody2csvFile.seek(0); // Restart
} else {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART TITLE:"));
println_Msg(F(""));
println_Msg(ody2game);
display_Update();
#else
Serial.print(F("CART TITLE:"));
Serial.println(ody2game);
#endif
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == HIGH) { // Button Released
buttonreleased = 1;
break;
}
if (buttonreleased) {
buttonreleased = 0; // Reset Flag
break;
}
}
}
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == HIGH) // Button Released
break;
}
}
#if (defined(enable_OLED) || defined(enable_LCD))
display.setCursor(0, 56);
println_Msg(F("FAST CYCLE OFF"));
display_Update();
#else
Serial.println(F(""));
Serial.println(F("FAST CYCLE OFF"));
Serial.println(F("PRESS BUTTON TO STEP FORWARD"));
Serial.println(F("DOUBLE CLICK TO STEP BACK"));
Serial.println(F("HOLD TO SELECT"));
Serial.println(F(""));
#endif
while (readVals_ODY2(ody2game, ody2mm, ody2rr, ody2ll)) {
if (strcmp(ody2csvEND, ody2game) == 0) {
ody2csvFile.seek(0); // Restart
} else {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART TITLE:"));
println_Msg(F(""));
println_Msg(ody2game);
display.setCursor(0, 48);
println_Msg(F("Press to Change"));
println_Msg(F("Hold to Select"));
display_Update();
#else
Serial.print(F("CART TITLE:"));
Serial.println(ody2game);
#endif
while (1) { // Single Step
int b = checkButton();
if (b == 1) { // Continue (press)
break;
}
if (b == 2) { // Reset to Start of List (doubleclick)
byte prevline = strtol(ody2ll, NULL, 10);
ody2csvpos -= prevline;
ody2csvFile.seek(ody2csvpos);
break;
}
if (b == 3) { // Long Press - Select Cart (hold)
newody2mapper = strtol(ody2mm, NULL, 10);
newody2size = strtol(ody2rr, NULL, 10);
EEPROM_writeAnything(7, newody2mapper);
EEPROM_writeAnything(8, newody2size);
cartselected = 1; // SELECTION MADE
#if (defined(enable_OLED) || defined(enable_LCD))
println_Msg(F("SELECTION MADE"));
display_Update();
#else
Serial.println(F("SELECTION MADE"));
#endif
break;
}
}
if (cartselected) {
cartselected = 0; // Reset Flag
return true;
}
}
}
#if (defined(enable_OLED) || defined(enable_LCD))
println_Msg(F(""));
println_Msg(F("END OF FILE"));
display_Update();
#else
Serial.println(F("END OF FILE"));
#endif
return false;
}
void checkCSV_ODY2() {
if (getCartListInfo_ODY2()) {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART SELECTED"));
println_Msg(F(""));
println_Msg(ody2game);
display_Update();
// Display Settings
display.setCursor(0, 56);
print_Msg(F("CODE: M"));
print_Msg(newody2mapper);
print_Msg(F("/R"));
println_Msg(newody2size);
display_Update();
#else
Serial.println(F(""));
Serial.println(F("CART SELECTED"));
Serial.println(ody2game);
// Display Settings
Serial.print(F("CODE: M"));
Serial.print(newody2mapper);
Serial.print(F("/R"));
Serial.println(newody2size);
Serial.println(F(""));
#endif
} else {
#if (defined(enable_OLED) || defined(enable_LCD))
display.setCursor(0, 56);
println_Msg(F("NO SELECTION"));
display_Update();
#else
Serial.println(F("NO SELECTION"));
#endif
}
}
void setCart_ODY2() {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(ody2cartCSV);
display_Update();
#endif
sd.chdir();
sprintf(folder, "ODY2/CSV");
sd.chdir(folder); // Switch Folder
ody2csvFile = sd.open(ody2cartCSV, O_READ);
if (!ody2csvFile) {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CSV FILE NOT FOUND!"));
display_Update();
#else
Serial.println(F("CSV FILE NOT FOUND!"));
#endif
while (1) {
if (checkButton() != 0)
setup_ODY2();
}
}
checkCSV_ODY2();
ody2csvFile.close();
}
#endif

49
sd/arccart.txt Normal file
View File

@ -0,0 +1,49 @@
3D Bowling,1,0
3D Soccer,3,16
Alien Invaders,1,16
American Football,2,21
Astro Invader,3,24
Autorace,3,20
Baseball,1,15
Basketball,1,15
Blackjack and Poker,1,17
Boxing,1,26
Brain Quiz,1,13
Breakaway,1,17
Capture,0,16
Cat Trax,1,14
Circus,1,15
Combat,3,13
Crazy Climber,1,13
Crazy Gobbler,0,20
Escape,1,20
Funky Fish,3,13
Golf,3,17
Grand Slam Tennis,3,11
Hobo,3,24
Horse Racing,1,11
Jump Bug,3,19
Jungler,3,15
Missile War,1,14
Monaco Grand Prix,1,18
Nibblemen,1,24
Ocean Battle,1,16
Parashooter,1,19
Pleiades,3,18
R2D Tank,1,15
Red Clash,3,15
Robot Killer,1,16
Route 16,3,19
Soccer,3,15
Space Attack,1,13
Space Mission,1,19
Space Raiders,1,20
Space Squadron,1,20
Space Vultures,2,21
Spiders,3,21
Star Chess,1,14
Super Gobbler,1,17
Tanks a Lot,1,20
The End,1,18
Turtles,3,14
EOF,0,0

494
sd/ataricart.txt Normal file
View File

@ -0,0 +1,494 @@
32 in 1,240,0
3-D Tic-Tac-Toe,32,15
Acid Drop,246,23
Action Force,64,18
Adventure,64,20
Adventures of TRON,64,17
Air Raid,64,26
Air Raiders,64,16
Airlock,64,19
Air-Sea Battle,32,15
AKA Space Adventure,64,22
Alien,64,27
Alpha Beam with Ernie,248,13
Amidar,64,30
Armor Ambush,64,14
Artillery Duel,248,20
Assault,64,23
Asterix,248,15
Asteroids,248,16
Astroblast,64,18
Atari Video Cube,64,18
Atlantis,64,24
Atlantis II,64,16
Bachelor Party,64,19
Bachelorette Party,64,22
Backgammon,64,26
Bank Heist,64,18
Barnstorming,64,18
Base Attack,64,20
Basic Math,32,19
BASIC Programming,64,18
Basketball,32,25
Battlezone,248,18
Beamrider,248,19
Beany Bopper,64,18
Beat 'Em & Eat 'Em,64,20
Berenstain Bears,248,26
Bermuda,64,25
Bermuda Triangle,64,15
Berzerk,64,24
Big Bird's Egg Catch,248,15
Blackjack,32,29
Blueprint,248,17
BMX Air Master,246,18
Bobby Is Going Home,64,23
Boing!,64,27
Bowling,32,14
Boxing,32,15
Brain Games,32,14
Breakout,32,19
Bridge,64,16
Buck Rogers - Planet of Zoom,248,14
Bugs,64,37
Bump 'n' Jump,231,12
Bumper Bash,64,22
BurgerTime,231,19
Burning Desire,64,19
Cakewalk,64,22
California Games,246,16
Canyon Bomber,32,25
Carnival,64,21
Casino,64,16
Cat Trax,64,14
Cathouse Blues,64,16
Centipede,248,22
Challenge,248,18
Challenge of.... Nexar,64,18
Chase the Chuckwagon,64,30
Checkers,32,28
China Syndrome,64,16
Chopper Command,64,22
Chuck Norris Superkicks,248,23
Circus Atari,64,32
Coco Nuts,64,20
Codebreaker,32,17
Combat,32,19
Commando,246,14
Commando Raid,64,17
Condor Attack,64,21
Confrontation,64,21
Congo Bongo,248,21
Cookie Monster Munch,248,20
Cosmic Ark,64,29
Cosmic Commuter,64,18
Cosmic Corridor,64,23
Cosmic Creeps,64,23
Cosmic Swarm,32,21
Crackpots,64,20
Crash Dive,64,17
Crazy Climber,248,18
Cross Force,64,22
Crossbow,246,19
Crypts of Chaos,64,17
Crystal Castles,246,23
Cubicolor,64,24
Cubo Magico,64,17
Custer's Revenge,64,19
Dancing Plate,64,24
Dark Cavern,64,21
Dark Chambers,246,19
Dark Mage,248,22
Deadly Duck,64,18
Death Trap,64,19
Decathlon,254,18
Defender,64,18
Defender II,248,16
Demolition Herby,64,20
Demon Attack,64,24
Demons to Diamonds,64,20
Desert Falcon,246,26
Dice Puzzle,64,22
Dig Dug,246,19
Dishaster,64,16
Dodge 'Em,64,17
Dolphin,64,17
Donald Duck's Speedboat,248,15
Donkey Kong,64,32
Donkey Kong Jr,248,19
Double Dragon,246,23
Double Dunk,246,22
Dragon Defender,64,20
Dragonfire,64,23
Dragster,32,18
Dukes of Hazzard,246,16
E.T.,248,25
Earth Dies Screaming,64,13
Eggomania,64,28
Elevator Action,248,17
Eli's Ladder,64,24
Encounter at L-5,64,20
Enduro,64,24
Entombed,64,14
Espial,63,16
Exocet,64,14
Extra Terrestrials,64,14
Fantastic Voyage,64,26
Farmyard Fun,64,24
Fast Eddie,64,20
Fast Food,64,18
Fatal Run,244,17
Fathom,248,18
Fighter Pilot,246,15
Final Approach,64,22
Fire Fighter,64,22
Fire Fly,64,20
Fishing Derby,32,16
Flag Capture,32,21
Flash Gordon,64,20
Football,32,20
Forest,64,16
Frankenstein's Monster,64,14
Freeway,32,30
Frisco,64,15
Frog Pond,248,14
Frogger,64,18
Frogger II,224,15
Frogs and Flies,64,19
Front Line,248,23
Frostbite,64,19
G.I. Joe,64,17
Galaxian,248,16
Gamma Attack,32,17
Gangster Alley,64,20
Gas Hog,64,22
Gauntlet,64,15
Ghost Manor,248,16
Ghostbusters,248,20
Ghostbusters II,246,21
Gigolo,64,24
Glacier Patrol,64,14
Glib,64,22
Golf,32,12
Gopher,64,12
Gorf,64,14
Grand Prix,246,12
Gravitar,248,19
Great Escape,64,17
Gremlins,248,20
Guardian,64,17
Gyruss,224,16
H.E.R.O.,248,15
Halloween,64,17
Hangman,64,17
Harbor Escape,64,15
Haunted House,64,21
Hole Hunter,64,21
Home Run,32,19
Human Cannonball,32,16
Hunt & Score,32,24
I Want My Mommy,64,20
Ice Hockey,64,23
Ikari Warriors,246,18
Indy 500,32,23
Infiltrate,64,16
International Soccer,64,18
IQ 180,64,28
James Bond 007,224,14
Jaw Breaker,64,23
Journey Escape,64,19
Joust,248,22
JoustPong,64,14
Jr. Pac-Man,246,17
Jungle Fever,64,20
Jungle Hunt,248,20
Kaboom!,32,20
Kangaroo,248,15
Karate,64,17
Keystone Kapers,64,14
King Kong,64,23
Klax,246,17
Knight on the Town,64,13
Kool-Aid Man,64,26
Krull,248,20
Kung Fu Superkicks,248,14
Kung-Fu Master,248,27
Lady in Wading,64,23
Laser Blast,32,22
Laser Gates,64,19
Lilly Advenure,64,19
Lochjaw,64,22
Lock 'n' Chase,64,15
London Blitz,64,22
Lost Luggage,64,20
M.A.D.,64,20
M.A.S.H,64,14
MagiCard,192,15
Malagai,64,17
Mangia',64,15
Marauder,64,15
Marine Wars,64,16
Mario Bros.,248,19
Master Builder,64,20
Masters of the Universe,231,22
Math Gran Prix,64,32
Maze Craze,64,22
Mega Force,64,18
MegaBoy,240,18
MegaMania,64,16
Midnight Magic,246,17
Millipede,246,23
Mind Maze,248,18
Miner 2049er,63,18
Miner 2049er Volume II,63,20
Mines of Minos,64,30
Miniature Golf,32,22
Miss Piggy's Wedding,248,22
Missile Command,64,29
Missile Control,64,23
Mission 3000 A.D.,64,23
Mission Survive,64,25
Mogul Maniac,64,23
Montezuma's Revenge,224,20
Moon Patrol,248,28
Moonsweeper,248,20
Motocross,64,20
Motocross Racer,248,17
MotoRodeo,246,24
Mountain King,250,18
Mouse Trap,64,22
Mr. Do!,248,18
Mr. Do!'s Castle,224,16
Mr. Postman,64,25
Ms. Pac-Man,248,19
Music Machine,64,20
My Golf,248,21
Name This Game,64,16
Night Driver,32,22
Night Stalker,64,20
Nightmare,64,21
No Escape!,64,17
Nuts,64,18
Obelix,248,12
Off the Wall,246,15
Oink!,64,21
Omega Race,250,13
Open Sesame,64,19
Oscar's Trash Race,248,19
Othello,32,27
Out of Control,64,15
Outlaw,32,22
Pac-Kong,64,14
Pac-Man,64,16
Panda Chase,64,15
Parachute,64,19
Pele's Soccer,64,17
Pengo,248,21
Pepsi Invaders,64,14
Pete Rose Baseball,246,22
Phantom Tank,64,27
Pharaoh's Curse,64,20
Philly Flasher,64,23
Phoenix,248,22
Pick 'n' Pile,246,16
Picnic,64,22
Piece o' Cake,64,14
Pigs in Space,248,21
Pitfall II,208,22
Pitfall!,64,19
Planet Patrol,64,16
Plaque Attack,64,21
Polaris,63,21
Pole Position,248,15
Polo,32,22
Pompeii,64,12
Pooyan,64,15
Popeye,224,14
Porky's,248,15
Pressure Cooker,248,16
Private Eye,248,24
Pyramid War,64,20
Q-bert,64,19
Q-bert's Qubes,224,14
Quadrun,248,23
Quest for Quintana Roo,248,16
Quick Step!,64,31
Rabbit Transit,248,19
Racing Car,64,23
Racquetball,64,18
Radar Lock,246,19
Raft Rider,64,19
Raiders of the Lost Ark,248,18
Ram It,64,32
Rampage!,246,14
Raumpatrouille,64,17
Reactor,64,22
RealSports Baseball,248,15
RealSports Boxing,246,28
RealSports Football,248,26
RealSports Soccer,248,28
RealSports Tennis,248,26
RealSports Volleyball,64,26
Rescue Terra I,64,29
Resgate Espacial,248,22
Revenge of the Beefsteak Tomatoes,64,25
Riddle of the Sphinx,64,41
River Patrol,63,28
River Raid,64,20
River Raid II,246,18
Road Runner,246,22
Robin Hood,248,20
Robot Tank,254,19
Roc 'n Rope,248,19
Room of Doom,64,20
Rubik's Cube,64,20
Save Our Ship,64,20
Scuba Diver,64,21
Sea Hawk,64,19
Sea Hunt,64,16
Sea Monster,64,16
Seaquest,64,19
Secret Quest,246,16
Sentinel,246,21
Shark Attack,64,17
Shootin' Gallery,64,20
Shuttle Orbiter,64,24
Sir Lancelot,248,23
Skate Boardin',248,21
Skeet Shoot,32,23
Ski Hunt,64,19
Ski Run,64,16
Skiing,32,15
Sky Diver,32,14
Sky Jinks,32,17
Sky Skipper,64,17
Slot Machine,32,19
Slot Racers,32,20
Smurf - Rescue in Gargamel's Castle,248,19
Smurfs Save the Day,248,44
Sneak 'n Peek,64,28
Snoopy and the Red Baron,248,21
Solar Fox,248,33
Solar Storm,64,18
Solaris,246,19
Sorcerer,64,16
Sorcerer's Apprentice,248,16
Space Attack,64,30
Space Canyon,64,20
Space Cavern,64,20
Space Invaders,64,20
Space Jockey,32,22
Space Shuttle,248,20
Space Tunnel,64,22
Space War,32,20
Spacechase,64,17
SpaceMaster X-7,64,18
Spider Fighter,64,23
Spider Maze,64,22
Spider-Man,64,19
Spike's Peak,248,18
Spitfire Attack,64,21
Springer,63,23
Sprint Master,246,16
Spy Hunter,248,22
Squeeze Box,64,19
Squirrel,64,19
Sssnake,64,16
Stampede,32,15
Star Fox,64,16
Star Raiders,248,16
Star Ship,32,21
Star Strike,64,17
Star Trek,248,19
Star Voyager,64,18
Star Wars - Arcade Game,224,20
Star Wars - Death Star Battle,224,32
Star Wars - Empire Strikes Back,64,38
Star Wars - Jedi Arena,64,39
Stargate,246,30
Stargunner,64,17
StarMaster,64,18
Steeplechase,32,18
Stellar Track,64,20
Strategy X,64,21
Strawberry Shortcake,64,18
Street Racer,32,28
Stronghold,64,20
Stunt Man,64,18
Submarine Commander,64,17
Sub-Scan,64,27
Subterranea,248,16
Summer Games,246,20
Super Baseball,246,21
Super Breakout,64,23
Super Challenge Baseball,64,22
Super Challenge Football,64,32
Super Cobra,224,32
Super Football,248,20
Superman,64,23
Surfer's Paradise,64,16
Surround,32,25
Survival Run,64,16
SwordQuest - EarthWorld,248,20
SwordQuest - FireWorld,248,32
SwordQuest - WaterWorld,248,31
Tac-Scan,64,32
Tanks But No Tanks,64,16
Tapeworm,64,26
Tapper,248,16
Tax Avoiders,248,15
Taz,248,21
Tempest,248,12
Tennis,32,16
Texas Chainsaw Massacre,64,14
Threshold,64,31
Thunderground,64,17
Time Pilot,248,21
Time Warp,64,19
Title Match Pro Wrestling,248,17
Tomarc the Barbarian,248,34
Tomcat,246,29
Tooth Protectors,224,15
Towering Inferno,64,25
Track and Field,246,24
Treasure Below,64,24
Treasure Island,64,22
Trick Shot,64,23
TRON - Deadly Discs,64,18
Tunnel Runner,250,27
Turmoil,64,22
Tutankham,224,15
Universal Chaos,64,18
Up 'n Down,248,23
Vanguard,248,19
Venture,64,17
Video Checkers,64,15
Video Chess,64,22
Video Jogger,64,19
Video Life,32,20
Video Olympics,32,18
Video Pinball,64,22
Video Reflex,64,21
Vogel Flieh,64,20
Vulture Attack,64,19
Wabbit,64,22
Walker,64,14
Wall Ball,64,14
Wall Break,64,17
Wall-Defender,64,18
Warlords,64,21
Warplock,64,16
Wing War,248,16
Wings,250,17
Winter Games,246,14
Wizard of Wor,64,21
Word Zapper,64,21
World End,64,19
Worm War I,64,17
Xenophobe,246,18
X-Man,64,18
Yars' Revenge,64,13
Zaxxon,248,21
Zoo Fun,64,15
Z-Tack,64,15
EOF,0,0

33
sd/fairchildcart.txt Normal file
View File

@ -0,0 +1,33 @@
Alien Invasion,2,0
Backgammon/Acey-Deucey,0,20
Baseball,0,29
Bowling,0,15
Casino Poker,2,14
Checkers,0,19
Demonstration Cartridge,0,15
Demonstration Cartridge 2,0,30
Desert Fox/Shooting Gallery,0,32
Dodge It,0,34
Drag Race,0,15
Galactic Space Wars/Lunar Lander,0,16
Hangman,1,39
Magic Numbers,0,14
Math Quiz I,0,20
Math Quiz II,0,18
Maze (Multi),0,19
Memory Match 1 & 2,0,19
Muehle (Multi),0,25
Ordtaevling,1,21
Pinball Challenge,0,18
Pro Football,2,24
Rat' Mal,1,19
Robot War/Torpedo Alley,0,15
Schach,3,30
Slot Machine,0,13
Sonar Search,0,19
Space War,0,19
Spitfire,0,16
Tic-Tac-Toe (Multi),0,15
Video Blackjack,0,26
Video Whizball,1,22
EOF,0,0

134
sd/ody2cart.txt Normal file
View File

@ -0,0 +1,134 @@
4 en 1 Ligne,1,1,0
4 in 1 Row,1,1,20
Air Battle (V+),0,3,19
Alien Invaders - Plus,0,0,24
Alpine Skiing,0,0,30
Armored Encounter + Sub Chase,0,0,22
Atlantis,0,1,38
Attack of the Timelord,0,1,17
Baseball,0,0,31
Billard Americain,0,0,17
Billard Americain (V+),0,2,26
Billard Electrique,0,0,31
Billard Electrique (V+),0,2,27
Blackjack,0,0,32
Blobbers,0,2,18
Blockout + Breakdown,0,0,17
Bombardeio Submarino + Tiro ao Alvo,0,0,29
Bowling + Basketball,0,0,44
Buraco Negro,0,2,29
Casino Slot Machine,0,0,21
Catch the Ball + Noughts and Crosses,0,0,28
Chat et Souris,0,1,45
Chess,0,0,23
Chez Maxime (V+),0,1,14
Chinese Logic,0,0,25
Clay Pigeon,0,1,22
Comando Noturno,0,2,20
Computer Golf,0,0,24
Computer Intro,0,0,22
Computer Programmer,0,0,23
Conflit Cosmique,0,0,28
Conquest of the World,0,1,25
Cosmic Conflict,0,0,30
Cosmic Conflict (V+),0,2,24
Course de Voitures + Autodrome + Cryptogramme,0,0,29
Course de Voitures + Autodrome + Cryptogramme (V+),0,2,54
Demon Attack,0,1,59
Demon Attack (V+),0,2,21
Depth Charge + Marksman,0,0,26
Desafio Chines,0,0,32
Dynasty,0,0,23
Electronic Table Soccer,0,0,16
Exojet (V+),0,2,32
Football,0,0,20
Football Electronique + Hockey Electronique,0,0,17
Freedom Fighters,0,1,52
Freedom Fighters (V+),0,2,25
Frogger,0,2,30
Golf,0,0,16
Great Wall Street Fortune Hunt,0,1,13
Guerre de l'Espace (V+),0,2,39
Guerre Laser,0,0,32
Gunfighter,0,0,21
Helicopter Rescue (V+),0,4,19
Hockey + Soccer,0,0,31
I've Got Your Number,0,0,24
Invaders from Hyperspace,0,0,29
Jeu de Quilles + Basketball,0,0,33
Jeu de Quilles + Basketball (V+),0,2,36
Jumping Acrobats,0,0,41
K.C. Munchkin,0,1,25
K.C.'s Krazy Chase,0,1,22
Keyboard Creations,0,0,27
Killer Bees,0,2,27
Killer Bees (V+),0,2,20
Kinder im Verkehr 1,0,0,25
La Quete des Anneaux,0,1,28
La Ruche Infernale (V+),0,2,29
Labyrinth Game + Supermind,0,0,32
Las Vegas,0,0,35
Las Vegas Blackjack,0,0,18
Laser War,0,0,28
Le Mur Magique,0,0,18
Le Tresor Englouti (V+),0,2,23
Loony Balloon (V+),0,2,32
Matchmaker + Logix + Buzzword,0,0,27
Math-A-Magic + Echo,0,0,38
Mathematicien + Echo,0,0,28
Monkeyshines,0,1,29
Morse,0,0,21
Moto-Crash (V+),0,2,14
Mousing Cat,0,1,24
Munchkin,0,1,20
Musician,1,1,17
Neutron Star (V+),0,3,17
Newscaster,0,0,26
Nightmare,0,2,19
Nightmare (V+),0,2,18
Nimble Numbers Ned,0,1,23
Norseman (V+),0,2,27
O Malabarista + Jogo da Velha,0,0,22
O Segredo do Farao,0,1,38
Out of this World + Helicopter Rescue,0,0,27
P.T. Barnum's Acrobats,0,1,46
Pachinko,0,0,31
Pairs + Space Rendezvous + Logic,0,0,17
Pick Axe Pete,0,1,41
Pickaxe Pete (V+),0,2,22
Pocket Billiards,0,0,26
Popeye,0,2,25
Power Lords,0,1,15
Power Lords (Alt),0,2,20
Prendre l'Argent et Fuir,0,0,26
Q-Bert,0,2,33
Quest for the Rings,0,1,15
Race + Spin-out + Cryptogram (V+),0,2,28
Samurai,0,0,42
Satellite Attack,0,0,16
Satellite Attack (V+),0,2,25
Secret of the Pharaohs,0,0,30
Showdown in 2100 A.D.,0,0,31
Sid the Spellbinder,0,1,30
Skiing,0,0,28
Smithereens,0,1,15
Speedway + Spin-out + Crypto-logic,0,0,20
Stone Sling,0,0,43
Stone Sling (V+),0,2,20
Super Bee,0,1,25
Super Cobra,0,2,18
Syracuse (V+),0,1,20
Take the Money and Run,0,0,22
Terrahawks (V+),0,2,31
Thunderball,0,0,24
Trans American Rally (V+),0,4,20
Turtles,0,2,34
Type & Tell,0,1,16
UFO,0,0,20
Verkehrsspiele 1,0,0,12
Verkehrsspiele 2,0,0,25
Volleyball,0,0,25
Volleyball Electronique,0,0,19
Wall Street,0,1,32
War of Nerves,0,0,20
EOF,0,0,22