cartreader/Cart_Reader/PCW.ino

719 lines
18 KiB
C++

//******************************************
// BENESSE POCKET CHALLENGE W MODULE
//******************************************
#ifdef ENABLE_PCW
// Benesse Pocket Challenge W
// Cartridge Pinout
// 38P 1.27mm pitch connector
//
// CART CART
// TOP EDGE
// +---------+
// | 1 |-- VCC
// | 2 |-- GND
// | 3 |-- AD0 (A0 IN/D0 OUT)
// | 4 |-- AD1 (A1 IN/D1 OUT)
// | 5 |-- AD2 (A2 IN/D2 OUT)
// | 6 |-- AD3 (A3 IN/D3 OUT)
// | 7 |-- AD4 (A4 IN/D4 OUT)
// | 8 |-- AD5 (A5 IN/D5 OUT)
// | 9 |-- AD6 (A6 IN/D6 OUT)
// | 10 |-- AD7 (A7 IN/D7 OUT)
// | 11 |-- A08
// | 12 |-- A09
// | 13 |-- A10
// | 14 |-- A11
// | 15 |-- A12
// | 16 |-- A13
// | 17 |-- A14
// | 18 |-- A15
// | 19 |-- A16
// | 20 |-- A17
// | 21 |-- A18
// | 22 |-- A19
// | 23 |-- A20
// | 24 |-- A21
// | 25 |-- NC [CPU-INT2/T15(P47)]
// | 26 |-- 1B (7W00F) -> HIGH -+
// | 27 |-- 1A (7W00F) -> HIGH -+-> OE (ROM) = LOW
// | 28 |-- OE (LH5164A)
// | 29 |-- WE (LH5164A)
// | 30 |-- LE (HC573) [LATCH ENABLE FOR AD0-AD7]
// | 31 |-- NC [CPU-TO5(P43)]
// | 32 |-- NC [CPU-RESET]
// | 33 |-- VCC
// | 34 |-- GND
// | 35 |-- NC
// | 36 |-- NC [CPU-VCC]
// | 37 |-- NC [CONSOLE-GND]
// | 38 |-- NC
// +---------+
// CONTROL PINS:
// LE - (PH0) - PCW PIN 30 - SNES RST
// 1A - (PH3) - PCW PIN 27 - SNES /CS
// 1B - (PH4) - PCW PIN 26 - SNES /IRQ
// WE - (PH5) - PCW PIN 29 - SNES /WR
// OE - (PH6) - PCW PIN 28 - SNES /RD
// NOT CONNECTED:
// CLK(PH1) - N/C
// ADDRESS PINS:
// ADDR/DATA [AD0-AD7] - PORTC
// ADDR A8-A15 - PORTK
// ADDR A16-A21 - PORTL
//******************************************
// DEFINES
//******************************************
// CONTROL PINS - LE/1A/1B/WE/OE
#define LE_HIGH PORTH |= (1 << 0)
#define LE_LOW PORTH &= ~(1 << 0)
#define NAND_1A_HIGH PORTH |= (1 << 3)
#define NAND_1A_LOW PORTH &= ~(1 << 3)
#define NAND_1B_HIGH PORTH |= (1 << 4)
#define NAND_1B_LOW PORTH &= ~(1 << 4) // Built-in RAM + I/O
#define WE_HIGH PORTH |= (1 << 5)
#define WE_LOW PORTH &= ~(1 << 5)
#define OE_HIGH PORTH |= (1 << 6)
#define OE_LOW PORTH &= ~(1 << 6)
#define MODE_READ DDRC = 0 // [INPUT]
#define MODE_WRITE DDRC = 0xFF //[OUTPUT]
#define DATA_READ \
{ \
DDRC = 0; \
PORTC = 0xFF; \
} // [INPUT PULLUP]
#define ADDR_WRITE DDRC = 0xFF // [OUTPUT]
#define DETECTION_SIZE 64
uint32_t rom_size;
boolean multipack;
byte bank0;
byte bank1;
//******************************************
// SETUP
//******************************************
void setup_PCW() {
// Request 5V
setVoltage(VOLTS_SET_5V);
// Set Address Pins to Output
//A8-A15
DDRK = 0xFF;
//A16-A21
DDRL = 0xFF;
// Set Control Pins to Output
// LE(PH0) --(PH1) 1A(PH3) 1B(PH4) WE(PH5) OE(PH6)
DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// Set TIME(PJ0) to Output (UNUSED)
DDRJ |= (1 << 0);
// Set Address/Data Pins AD0-AD7 (PC0-PC7) to Input
MODE_READ; // DDRC = 0
// Setting Control Pins to HIGH
// LE(PH0) ---(PH1) 1A(PH3) 1B(PH4) WE(PH5) OE(PH6)
PORTH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// Set Unused Pins HIGH
PORTJ |= (1 << 0); // TIME(PJ0)
strcpy(romName, "PCW");
mode = CORE_PCW;
}
//******************************************
// MENU
//******************************************
static const char* const menuOptionsPCW[] PROGMEM = { FSTRING_READ_ROM, FSTRING_READ_SAVE, FSTRING_WRITE_SAVE, FSTRING_RESET };
void pcwMenu() {
convertPgm(menuOptionsPCW, 4);
uint8_t mainMenu = question_box(F(" POCKET CHALLENGE W"), menuOptions, 4, 0);
switch (mainMenu) {
case 0:
// Read ROM
sd.chdir("/");
check_multi_PCW();
if (multipack)
readMultiROM_PCW();
else
readSingleROM_PCW();
sd.chdir("/");
break;
case 1:
// Read SRAM
sd.chdir("/");
display_Clear();
println_Msg(F("Reading SRAM..."));
display_Update();
readSRAM_PCW();
sd.chdir("/");
// Wait for user input
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
display_Update();
wait();
break;
case 2:
// Write SRAM
sd.chdir("/");
fileBrowser(F("Select SRM file"));
display_Clear();
writeSRAM_PCW();
display_Clear();
writeErrors = verifySRAM_PCW();
if (writeErrors == 0) {
println_Msg(F("SRAM verified OK"));
display_Update();
} else {
print_STR(error_STR, 0);
print_Msg(writeErrors);
print_STR(_bytes_STR, 1);
print_Error(did_not_verify_STR);
}
break;
case 3:
// Reset
resetArduino();
break;
}
}
//******************************************
// LOW LEVEL FUNCTIONS
//******************************************
// Max ROM Size 0x400000 (Highest Address = 0x3FFFFF) - 3F FFFF
// NAND 1A + 1B HIGH = LOW = ROM Output Enabled
void read_setup_PCW() {
NAND_1A_HIGH;
NAND_1B_HIGH;
OE_HIGH;
WE_HIGH;
LE_LOW;
}
// READ ROM BYTE WITH ADDITIONAL DELAY
// NEEDED FOR PROBLEM CARTS TO SWITCH FROM ADDRESS TO DATA
unsigned char read_rom_byte_PCW(unsigned long address) {
PORTL = (address >> 16) & 0xFF;
PORTK = (address >> 8) & 0xFF;
// Latch Address on AD0-AD7
ADDR_WRITE;
LE_HIGH; // Latch Enable
PORTC = address & 0xFF; // A0-A7
LE_LOW; // Address Latched
__asm__("nop\n\t"
"nop\n\t");
// Read Data on AD0-AD7
OE_LOW;
DATA_READ;
delayMicroseconds(5); // 3+ Microseconds for Problem Carts
unsigned char data = PINC;
OE_HIGH;
return data;
}
// SRAM Size 0x8000 (Highest Address = 0x7FFF)
// NAND 1A LOW = SRAM Enabled [ROM DISABLED]
unsigned char read_ram_byte_1A_PCW(unsigned long address) {
NAND_1A_LOW;
PORTL = (address >> 16) & 0xFF;
PORTK = (address >> 8) & 0xFF;
// Latch Address on AD0-AD7
ADDR_WRITE;
LE_HIGH; // Latch Enable
PORTC = address & 0xFF; // A0-A7
LE_LOW; // Address Latched
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
// Read Data on AD0-AD7
OE_LOW;
DATA_READ;
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
unsigned char data = PINC;
OE_HIGH;
NAND_1A_HIGH;
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
return data;
}
// Toshiba TMP90C845A
// 0xFEC0-0xFFBF Built-in RAM (256 bytes)
// 0xFFC0-0xFFF7 Built-in I/O (56 bytes)
// 0xFF00-0xFFBF (192 byte area available in direct addressing mode)
// 0xFF18-0xFF68 (Micro DMA parameters (if used))
// TEST CODE TO READ THE CPU BUILT-IN RAM + I/O
// NAND 1B LOW = Built-In RAM + I/O Enabled [ROM DISABLED]
unsigned char read_ram_byte_1B_PCW(unsigned long address) {
NAND_1B_LOW;
PORTL = (address >> 16) & 0xFF;
PORTK = (address >> 8) & 0xFF;
// Latch Address on AD0-AD7
ADDR_WRITE;
LE_HIGH; // Latch Enable
PORTC = address & 0xFF; // A0-A7
LE_LOW; // Address Latched
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
// Read Data on AD0-AD7
OE_LOW;
DATA_READ;
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
unsigned char data = PINC;
OE_HIGH;
NAND_1B_HIGH;
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
return data;
}
// WRITE SRAM 32K
void write_ram_byte_1A_PCW(unsigned long address, unsigned char data) {
NAND_1A_LOW;
PORTL = (address >> 16) & 0xFF;
PORTK = (address >> 8) & 0xFF;
// Latch Address on AD0-AD7
ADDR_WRITE;
LE_HIGH; // Latch Enable
PORTC = address & 0xFF; // A0-A7
LE_LOW; // Address Latched
// Write Data on AD0-AD7 - WE LOW ~240-248ns
WE_LOW;
PORTC = data;
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
WE_HIGH;
NAND_1A_HIGH;
}
// WRITE CPU BUILT-IN RAM + I/O AREA
// MODIFIED TO MATCH WORKING BANK SWITCH ROUTINE
void write_ram_byte_1B_PCW(unsigned long address, unsigned char data) {
NAND_1A_LOW;
NAND_1A_HIGH;
NAND_1B_LOW;
PORTL = (address >> 16) & 0xFF;
PORTK = (address >> 8) & 0xFF;
// Latch Address on AD0-AD7
ADDR_WRITE;
LE_HIGH; // Latch Enable
PORTC = address & 0xFF; // A0-A7
LE_LOW; // Address Latched
// Write Data on AD0-AD7 - WE LOW ~740ns
WE_LOW;
PORTC = data;
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
WE_HIGH;
NAND_1B_HIGH;
}
//******************************************
// SINGLE-PACK FUNCTIONS
//******************************************
uint32_t detect_rom_size_PCW(void) {
uint8_t read_byte;
uint8_t current_byte;
uint8_t detect_1m, detect_2m;
//Initialize variables
detect_1m = 0;
detect_2m = 0;
//Confirm where mirror address starts from (1MB, 2MB or 4MB)
for (current_byte = 0; current_byte < DETECTION_SIZE; current_byte++) {
if ((current_byte != detect_1m) && (current_byte != detect_2m)) {
//If none matched, size is 4MB
break;
}
read_byte = read_rom_byte_PCW(current_byte);
if (current_byte == detect_1m) {
if (read_rom_byte_PCW(0x100000 + current_byte) == read_byte) {
detect_1m++;
}
}
if (current_byte == detect_2m) {
if (read_rom_byte_PCW(0x200000 + current_byte) == read_byte) {
detect_2m++;
}
}
}
//ROM size detection
if (detect_1m == DETECTION_SIZE) {
rom_size = 0x100000;
} else if (detect_2m == DETECTION_SIZE) {
rom_size = 0x200000;
} else {
rom_size = 0x400000;
}
return rom_size;
}
void readSingleROM_PCW() {
// Setup read mode
read_setup_PCW();
// Detect rom size
uint32_t rom_size = detect_rom_size_PCW();
display_Clear();
print_Msg(F("READING "));
print_Msg(rom_size / 1024 / 1024);
print_Msg("MB SINGLE-PACK");
println_Msg(FS(FSTRING_EMPTY));
// Create file
createFolder("PCW", "ROM", romName, "pcw");
print_STR(saving_to_STR, 0);
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
foldern = foldern + 1;
EEPROM_writeAnything(0, foldern);
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
print_FatalError(sd_error_STR);
}
// Init progress bar
uint32_t progress = 0;
draw_progressbar(0, rom_size);
for (unsigned long address = 0; address < rom_size; address += 512) { // 4MB
for (unsigned int x = 0; x < 512; x++) {
sdBuffer[x] = read_rom_byte_PCW(address + x);
}
myFile.write(sdBuffer, 512);
progress += 512;
draw_progressbar(progress, rom_size);
}
myFile.flush();
myFile.close();
// Compare CRC32 to database and rename ROM if found
// Arguments: database name, precalculated crc string or 0 to calculate, rename rom or not, starting offset
compareCRC("pcw.txt", 0, 1, 0);
// Wait for user input
println_Msg(FS(FSTRING_EMPTY));
print_STR(press_button_STR, 1);
display_Update();
wait();
}
//******************************************
// MULTI-PACK FUNCTIONS
//******************************************
// Known Multi-Pack Carts (Yellow Label Carts)
// 0BD400 [PS] (2MB Version)
// 0BD400 [PS] (4MB Version)
// 0BF400 [PL]
// 1BF400 [PZ]
// 8BD400 [CR]
// 8BF400 [LP]
// 9BF400 [SLP] (Undumped)
// Per Overload, identify multi-pack cart by reading 0x3FFA-0x3FFE. Multi-Pack carts are non-zero.
// 0x3FFA - Current Cartridge Bank
// 0x3FFC - Value to Switch to Cartridge Bank 0
// 0x3FFD - Value to Switch to Cartridge Bank 1
// 0x3FFE - Last Value written to 0xFFFF
// Bank Settings for 2MB
// Write 0x28 to 0xFFFF to read 1st half of ROM
// Write 0x2E to 0xFFFF to read 2nd half of ROM
// Bank Settings for 4MB
// Write 0x20 to 0xFFFF to read 1st half of ROM
// Write 0x31 to 0xFFFF to read 2nd half of ROM
void check_multi_PCW() {
// init variables
read_setup_PCW();
multipack = 0;
bank0 = 0;
bank1 = 0;
byte tempbyte = read_rom_byte_PCW(0x3FFC); // Check for a bank 0 switch value
if (tempbyte) {
bank0 = tempbyte; // Store bank 0 switch value
tempbyte = read_rom_byte_PCW(0x3FFD); // Check for a bank 1 switch value
if (tempbyte) {
bank1 = tempbyte; // Store bank 1 switch value
if (!read_rom_byte_PCW(0x3FFB) && !read_rom_byte_PCW(0x3FFF)) { // Check for 00s
multipack = 1; // Flag as multi-pack
display_Clear();
if ((bank0 == 0x28) && (bank1 == 0x2E)) // 2MB multi-pack cart
rom_size = 0x200000;
else if ((bank0 == 0x20) && (bank1 == 0x31)) // 4MB multi-pack cart
rom_size = 0x400000;
else { // Warn for unknown bank switch values, size set to 4MB
println_Msg(F("Warning: Unknown cart size"));
rom_size = 0x400000;
}
}
}
}
}
void write_bank_byte_PCW(unsigned char data) {
NAND_1A_LOW;
NAND_1A_HIGH;
NAND_1B_LOW;
// Write to Address 0xFFFF
PORTL = 0x00;
PORTK = 0xFF; // A8-A15
// Latch Address on AD0-AD7
ADDR_WRITE;
LE_HIGH; // Latch Enable
PORTC = 0xFF; // A0-A7
LE_LOW; // Address Latched
// Write Data on AD0-AD7 - WE LOW ~728-736ns
WE_LOW;
PORTC = data;
for (unsigned int x = 0; x < 40; x++)
__asm__("nop\n\t");
WE_HIGH;
NAND_1B_HIGH;
}
void switchBank_PCW(int bank) {
if (bank == 1) { // Upper Half
write_bank_byte_PCW(bank1);
} else { // Lower Half (default)
write_bank_byte_PCW(bank0);
}
}
void readMultiROM_PCW() {
print_Msg(F("READING "));
print_Msg(rom_size / 1024 / 1024);
print_Msg("MB MULTI-PACK");
println_Msg(FS(FSTRING_EMPTY));
// Create file
createFolder("PCW", "ROM", romName, "pcw");
print_STR(saving_to_STR, 0);
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
foldern = foldern + 1;
EEPROM_writeAnything(0, foldern);
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
print_FatalError(sd_error_STR);
}
// Init progress bar
uint32_t progress = 0;
draw_progressbar(0, rom_size);
// Lower Half
read_setup_PCW();
switchBank_PCW(0);
for (unsigned long address = 0; address < (rom_size / 2); address += 512) {
for (unsigned int x = 0; x < 512; x++) {
sdBuffer[x] = read_rom_byte_PCW(address + x);
}
myFile.write(sdBuffer, 512);
progress += 512;
draw_progressbar(progress, rom_size);
}
// Upper Half
read_setup_PCW();
switchBank_PCW(1);
for (unsigned long address = 0x200000; address < (0x200000 + (rom_size / 2)); address += 512) {
for (unsigned int x = 0; x < 512; x++) {
sdBuffer[x] = read_rom_byte_PCW(address + x);
}
myFile.write(sdBuffer, 512);
progress += 512;
draw_progressbar(progress, rom_size);
}
myFile.flush();
myFile.close();
// Reset Bank
switchBank_PCW(0);
// Compare CRC32 to database and rename ROM if found
// Arguments: database name, precalculated crc string or 0 to calculate, rename rom or not, starting offset
compareCRC("pcw.txt", 0, 1, 0);
// Wait for user input
println_Msg(FS(FSTRING_EMPTY));
print_STR(press_button_STR, 1);
display_Update();
wait();
}
//******************************************
// SRAM FUNCTIONS
//******************************************
void readSRAM_PCW() { // readSRAM_1A()
createFolder("PCW", "SAVE", romName, "srm");
foldern = foldern + 1;
EEPROM_writeAnything(0, foldern);
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
print_FatalError(sd_error_STR);
}
display_Clear();
read_setup_PCW();
for (unsigned long address = 0x0; address < 0x8000; address += 512) { // 32K
for (unsigned int x = 0; x < 512; x++) {
sdBuffer[x] = read_ram_byte_1A_PCW(address + x);
}
myFile.write(sdBuffer, 512);
}
myFile.flush();
myFile.close();
print_Msg(F("Saved to "));
print_Msg(folder);
println_Msg(F("/"));
display_Update();
// calcCRC(fileName, 0x8000, NULL, 0); // 32K
}
// SRAM
void writeSRAM_PCW() {
sprintf(filePath, "%s/%s", filePath, fileName);
println_Msg(F("Writing..."));
println_Msg(filePath);
display_Update();
if (myFile.open(filePath, O_READ)) {
sd.chdir();
sprintf(filePath, "%s/%s", filePath, fileName);
display_Clear();
println_Msg(F("Writing File: "));
println_Msg(filePath);
println_Msg(fileName);
display_Update();
//open file on sd card
if (myFile.open(filePath, O_READ)) {
read_setup_PCW();
for (unsigned int address = 0x0; address < 0x8000; address += 512) { // 32K
myFile.read(sdBuffer, 512);
for (unsigned int x = 0; x < 512; x++) {
write_ram_byte_1A_PCW(address + x, sdBuffer[x]);
}
}
myFile.close();
print_STR(done_STR, 1);
display_Update();
} else {
print_FatalError(sd_error_STR);
}
} else {
print_FatalError(sd_error_STR);
}
display_Clear();
}
unsigned long verifySRAM_PCW() {
writeErrors = 0;
if (myFile.open(filePath, O_READ)) {
read_setup_PCW();
for (unsigned int address = 0x0; address < 0x8000; address += 512) { // 32K
for (unsigned int x = 0; x < 512; x++) {
byte myByte = read_ram_byte_1A_PCW(address + x);
sdBuffer[x] = myByte;
}
for (int i = 0; i < 512; i++) {
if (myFile.read() != sdBuffer[i]) {
writeErrors++;
}
}
}
myFile.close();
} else {
print_FatalError(sd_error_STR);
}
return writeErrors;
}
// avoid warnings
#undef MODE_READ
#undef MODE_WRITE
#endif
//******************************************
// End of File
//******************************************