cartreader/Cart_Reader/PCW.ino
Ancyker 2cf7f5dbe7 Cleanup voltage requests
The `setVoltage()` function should be called even when `ENABLE_VSELECT` is disabled because `ENABLE_3V3FIX` also uses it. There is no resource cost to do this as when both options are disabled the compiler will optimize this function out. This just "future proofs" the code so if that function ever does more it doesn't need updated everywhere. This applies to `setup_FlashVoltage()` as well.

The changes to OSCR.cpp are just for code formatting and additional comments to clarify this.
2023-06-26 15:25:54 -04:00

744 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
boolean multipack = 0; // Multi-Pack Cart
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)
// Multi-Pack Cart Check
check_multi_PCW();
strcpy(romName, "PCW");
mode = mode_PCW;
}
//******************************************
// MENU
//******************************************
static const char pcwmenuItem1[] PROGMEM = "Read ROM";
static const char pcwmenuItem2[] PROGMEM = "Read SRAM";
static const char pcwmenuItem3[] PROGMEM = "Write SRAM";
static const char* const menuOptionsPCW[] PROGMEM = { pcwmenuItem1, pcwmenuItem2, pcwmenuItem3, string_reset2 };
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("/");
if (multipack)
readMultiROM_PCW();
else
readROM_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;
}
//==============================================================================
// Overload Multi-Pack Bank Switch
//
// 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
// MULTI-PACK CART CHECK
void check_multi_PCW() {
read_setup_PCW();
byte tempbyte = read_rom_byte_PCW(0x3FFC); // Bank 0 Switch
if (tempbyte) {
bank0 = tempbyte; // Store Bank 0 Switch
tempbyte = read_rom_byte_PCW(0x3FFD); // Bank 1 Switch
if (tempbyte) {
bank1 = tempbyte; // Store Bank 1 Switch
// Check for 00s
tempbyte = read_rom_byte_PCW(0x3FFB); // Should be 00
if (!tempbyte) {
tempbyte = read_rom_byte_PCW(0x3FFF); // Should be 00
if (!tempbyte)
multipack = 1; // Flag Multi-Cart
else {
bank0 = 0;
bank1 = 0;
}
}
}
}
}
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;
__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;
}
void switchBank_PCW(int bank) {
if (bank == 1) { // Upper Half
write_bank_byte_PCW(bank1);
} else { // Lower Half (default)
write_bank_byte_PCW(bank0);
}
}
//******************************************
// READ ROM FUNCTIONS
//******************************************
void readROM_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(F(""));
display_Update();
// Create file
strcpy(fileName, romName);
strcat(fileName, ".pcw");
EEPROM_readAnything(0, foldern);
sprintf(folder, "PCW/ROM/%d", foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
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(F(""));
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
display_Update();
wait();
}
void readMultiROM_PCW() {
strcpy(fileName, romName);
strcat(fileName, ".pcw");
EEPROM_readAnything(0, foldern);
sprintf(folder, "PCW/ROM/%d", foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
display_Clear();
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);
}
display_Clear();
println_Msg(F("READING MULTI-PACK"));
println_Msg(F(""));
display_Update();
// Init progress bar
uint32_t progress = 0;
draw_progressbar(0, 0x400000);
read_setup_PCW();
// Lower Half
switchBank_PCW(0);
for (unsigned long address = 0; address < 0x200000; address += 512) { // 2MB
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, 0x400000);
}
read_setup_PCW();
// Upper Half
switchBank_PCW(1);
for (unsigned long address = 0x200000; address < 0x400000; address += 512) { // 2MB
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, 0x400000);
}
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(F(""));
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
display_Update();
wait();
}
uint32_t detect_rom_size_PCW(void) {
uint32_t rom_size;
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;
}
//******************************************
// SRAM FUNCTIONS
//******************************************
void readSRAM_PCW() { // readSRAM_1A()
strcpy(fileName, romName);
strcat(fileName, ".srm");
EEPROM_readAnything(0, foldern);
sprintf(folder, "PCW/SAVE/%d", foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
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
//******************************************