mirror of
https://github.com/sanni/cartreader.git
synced 2024-11-30 00:14:15 +01:00
649 lines
14 KiB
C++
649 lines
14 KiB
C++
//******************************************
|
|
// 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;
|
|
|
|
// EEPROM MAPPING
|
|
// 08 ROM SIZE
|
|
|
|
//******************************************
|
|
// Menu
|
|
//******************************************
|
|
// Base Menu
|
|
static const char fairchildMenuItem4[] PROGMEM = "Read 16K";
|
|
static const char* const menuOptionsFAIRCHILD[] PROGMEM = { FSTRING_SELECT_CART, FSTRING_READ_ROM, FSTRING_SET_SIZE, fairchildMenuItem4, FSTRING_RESET };
|
|
|
|
void setup_FAIRCHILD() {
|
|
// Request 5V
|
|
setVoltage(VOLTS_SET_5V);
|
|
|
|
// 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 = CORE_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 (uint16_t 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++) {
|
|
if (cartsize == 0x0C00) { // 3K
|
|
// Skip SRAM Code for 3K Carts - Tested with Hangman 3K
|
|
// Hangman uses an F21022PC 1K SRAM Chip at 0x0400
|
|
// SRAM is NOT Battery Backed so contents change
|
|
// Chips are organized: 1K ROM + 1K SRAM + 1K ROM + 1K ROM
|
|
if (z == 2) {
|
|
for (int x = 0; x < 0x0A00; x++) { // Skip 1K SRAM at 0x0400
|
|
readData_FAIRCHILD();
|
|
}
|
|
}
|
|
} else if (cartsize == 0x1000) { // 4K
|
|
// Skip BIOS/Blocks Code for 4K Carts - Tested with Alien Invasion/Pro Football
|
|
// Alien Invasion/Pro Football both use a DM74LS02N (Quad 2-Input NOR Gate) with two 2K ROM Chips
|
|
uint16_t offset = z * 0x200;
|
|
for (uint16_t 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();
|
|
|
|
printCRC(fileName, NULL, 0);
|
|
|
|
println_Msg(FS(FSTRING_EMPTY));
|
|
print_STR(press_button_STR, 1);
|
|
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 (uint16_t y = 0; y < 0x20; y++) {
|
|
if (cartsize == 0x1000) { // 4K
|
|
// Skip BIOS/Blocks Code for 4K Carts - Tested with Alien Invasion/Pro Football
|
|
// Alien Invasion/Pro Football both use a DM74LS02N (Quad 2-Input NOR Gate) with two 2K ROM Chips
|
|
// IF CASINO POKER DOES NOT DUMP PROPERLY USING READROM
|
|
// TEST BY SETTING ROM SIZE TO 2K AND 4K THEN COMPARE 16K DUMPS
|
|
uint16_t offset = y * 0x200;
|
|
for (uint16_t 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();
|
|
|
|
printCRC(fileName, NULL, 0);
|
|
|
|
println_Msg(FS(FSTRING_EMPTY));
|
|
print_STR(press_button_STR, 1);
|
|
display_Update();
|
|
wait();
|
|
}
|
|
|
|
//******************************************
|
|
// ROM SIZE
|
|
//******************************************
|
|
|
|
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
|
|
void printRomSize_FAIRCHILD(int index) {
|
|
display_Clear();
|
|
print_Msg(F("ROM Size: "));
|
|
println_Msg(FAIRCHILD[index]);
|
|
}
|
|
#endif
|
|
|
|
void setROMSize_FAIRCHILD() {
|
|
byte newfairchildsize;
|
|
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
|
|
display_Clear();
|
|
if (fairchildlo == fairchildhi)
|
|
newfairchildsize = fairchildlo;
|
|
else {
|
|
newfairchildsize = navigateMenu(fairchildlo, fairchildhi, &printRomSize_FAIRCHILD);
|
|
|
|
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(FS(FSTRING_EMPTY));
|
|
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(FS(FSTRING_CURRENT_SETTINGS));
|
|
println_Msg(FS(FSTRING_EMPTY));
|
|
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(FS(FSTRING_EMPTY));
|
|
#endif
|
|
}
|
|
|
|
//******************************************
|
|
// CART SELECT CODE
|
|
//******************************************
|
|
void setCart_FAIRCHILD() {
|
|
//go to root
|
|
sd.chdir();
|
|
|
|
byte gameSize;
|
|
|
|
// Select starting letter
|
|
//byte myLetter = starting_letter();
|
|
|
|
// Open database
|
|
if (myFile.open("fairchildcart.txt", O_READ)) {
|
|
// seek_first_letter_in_database(myFile, myLetter);
|
|
|
|
if(checkCartSelection(myFile, &readDataLineSingleDigit, &gameSize)) {
|
|
EEPROM_writeAnything(8, gameSize);
|
|
}
|
|
} else {
|
|
print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND));
|
|
}
|
|
}
|
|
#endif
|
|
//******************************************
|
|
// End of File
|
|
//******************************************
|