cartreader/Cart_Reader/PCE.ino

1084 lines
29 KiB
Arduino
Raw Normal View History

2022-02-25 00:59:25 +01:00
//******************************************
// PC Engine & TurboGrafx dump code by tamanegi_taro
// April 18th 2018 Revision 1.0.1 Initial version
// August 12th 2019 Revision 1.0.2 Added Tennokoe Bank support
2024-06-30 02:05:05 +02:00
// June 29th 2024 Revision 1.0.3 Added ichigobankai repro HuCard writing
2022-02-25 00:59:25 +01:00
//
// Special thanks
// sanni - Arduino cart reader
// skaman - ROM size detection
// NO-INTRO - CRC list for game name detection
// Chris Covell - Tennokoe bank support
2024-06-30 02:05:05 +02:00
// partlyhuman - repro HuCard support
2022-02-25 00:59:25 +01:00
//
//******************************************
#ifdef ENABLE_PCE
2022-02-25 00:59:25 +01:00
/******************************************
Defines
*****************************************/
#define DETECTION_SIZE 64
#define FORCED_SIZE 1024
2022-02-25 00:59:25 +01:00
#define CHKSUM_SKIP 0
#define CHKSUM_OK 1
#define CHKSUM_ERROR 2
2024-06-30 02:05:05 +02:00
enum PCE_MODE_T : uint8_t { HUCARD,
TURBOCHIP,
HUCARD_NOSWAP,
PCE_FLASH };
2022-02-25 00:59:25 +01:00
/******************************************
Prototype Declarations
*****************************************/
/* Several PCE dedicated functions */
void pin_read_write_PCE(void);
void pin_init_PCE(void);
void setup_cart_PCE(void);
void reset_cart_PCE(void);
uint8_t read_byte_PCE(uint32_t address);
void write_byte_PCE(uint32_t address, uint8_t data);
uint32_t detect_rom_size_PCE(void);
void read_bank_PCE_ROM(uint32_t address_start, uint32_t address_end, uint32_t *processed_size, uint32_t total_size);
void read_bank_PCE_RAM(uint32_t address_start);
void read_rom_PCE(void);
/******************************************
Variables
*****************************************/
uint8_t pce_internal_mode; //0 - HuCARD, 1 - TurboChip
2022-02-25 00:59:25 +01:00
uint16_t pce_force_rom_size = 0;
uint8_t tennokoe_bank_index = 0;
/******************************************
Menu
*****************************************/
// PCE start menu
2022-04-27 12:34:32 +02:00
static const char pceMenuItem1[] PROGMEM = "HuCARD (swapped)";
static const char pceMenuItem2[] PROGMEM = "HuCARD(not swapped)";
static const char pceMenuItem3[] PROGMEM = "Turbochip";
2024-06-30 02:05:05 +02:00
static const char pceMenuItem4[] PROGMEM = "HuCARD Flash Repro";
static const char *const menuOptionspce[] PROGMEM = { pceMenuItem1, pceMenuItem2, pceMenuItem3, pceMenuItem4, FSTRING_RESET };
2022-02-25 00:59:25 +01:00
// PCE card menu items
static const char menuOptionspceCart_1[] PROGMEM = "Read RAM Bank %d";
static const char menuOptionspceCart_2[] PROGMEM = "Write RAM Bank %d";
static const char menuOptionspceCart_3[] PROGMEM = "Inc Bank Number";
static const char menuOptionspceCart_4[] PROGMEM = "Dec Bank Number";
static const char menuOptionspceCart_5[] PROGMEM = "Set %dK ROM size";
static const char menuOptionspceCart_5_fmt[] PROGMEM = "ROM size now %dK";
2022-02-25 00:59:25 +01:00
// Turbochip menu items
static const char *const menuOptionspceTC[] PROGMEM = { FSTRING_READ_ROM, FSTRING_RESET };
2022-02-25 00:59:25 +01:00
#ifdef ENABLE_FLASH
// Flash repro menu items
2024-06-30 02:05:05 +02:00
// static const char menuOptionspceFlash1[] PROGMEM = "Program";
static const char *const menuOptionspceFlash[] PROGMEM = { flashMenuItemWrite, FSTRING_RESET };
#endif
// PCE start menu, first a device type is selected and set in pce_internal_mode
2022-02-25 00:59:25 +01:00
void pcsMenu(void) {
unsigned char pceDev;
convertPgm(menuOptionspce, 4);
pceDev = question_box(F("Select device"), menuOptions, 4, 0);
2022-02-25 00:59:25 +01:00
switch (pceDev) {
2022-02-25 00:59:25 +01:00
case 0:
//Hucard
display_Clear();
display_Update();
pce_internal_mode = HUCARD;
setup_cart_PCE();
mode = CORE_PCE;
2022-02-25 00:59:25 +01:00
break;
case 1:
2022-04-27 12:34:32 +02:00
//Hucard not swapped
2022-02-25 00:59:25 +01:00
display_Clear();
display_Update();
2022-04-27 12:34:32 +02:00
pce_internal_mode = HUCARD_NOSWAP;
2022-02-25 00:59:25 +01:00
setup_cart_PCE();
mode = CORE_PCE;
2022-02-25 00:59:25 +01:00
break;
case 2:
2022-04-27 12:34:32 +02:00
//Turbografx
2022-02-25 00:59:25 +01:00
display_Clear();
display_Update();
2022-04-27 12:34:32 +02:00
pce_internal_mode = TURBOCHIP;
2022-02-25 00:59:25 +01:00
setup_cart_PCE();
mode = CORE_PCE;
2022-02-25 00:59:25 +01:00
break;
#ifdef ENABLE_FLASH
2022-02-25 00:59:25 +01:00
case 3:
//Flash Repro
display_Clear();
display_Update();
pce_internal_mode = PCE_FLASH;
setup_cart_PCE();
mode = CORE_PCE;
break;
#endif
case 4:
2022-02-25 00:59:25 +01:00
resetArduino();
break;
default:
print_MissingModule(); // does not return
2022-02-25 00:59:25 +01:00
}
}
void pin_read_write_PCE(void) {
2022-02-25 00:59:25 +01:00
// Set Address Pins to Output
//A0-A7
DDRF = 0xFF;
//A8-A15
DDRK = 0xFF;
//A16-A19
DDRL = (DDRL & 0xF0) | 0x0F;
//Set Control Pin to Output CS(PL4)
DDRL |= (1 << 4);
//Set CS(PL4) to HIGH
PORTL |= (1 << 4);
// Set Control Pins to Output RST(PH0) RD(PH3) WR(PH5)
DDRH |= (1 << 0) | (1 << 3) | (1 << 5);
// Switch all of above to HIGH
PORTH |= (1 << 0) | (1 << 3) | (1 << 5);
// Set IRQ(PH4) to Input
DDRH &= ~(1 << 4);
// Activate Internal Pullup Resistors
PORTH |= (1 << 4);
// Set Data Pins (D0-D7) to Input
DDRC = 0x00;
// Enable Internal Pullups
PORTC = 0xFF;
set_cs_rd_low_PCE();
reset_cart_PCE();
}
void pin_init_PCE(void) {
2022-02-25 00:59:25 +01:00
//Set Address Pins to input and pull up
DDRF = 0x00;
PORTF = 0xFF;
DDRK = 0x00;
PORTK = 0xFF;
DDRL = 0x00;
PORTL = 0xFF;
DDRH &= ~((1 << 0) | (1 << 3) | (1 << 5) | (1 << 6));
PORTH = (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6);
// Set IRQ(PH4) to Input
DDRH &= ~(1 << 4);
// Activate Internal Pullup Resistors
PORTH |= (1 << 4);
// Set Data Pins (D0-D7) to Input
DDRC = 0x00;
// Enable Internal Pullups
PORTC = 0xFF;
}
void setup_cart_PCE(void) {
// Request 5V
setVoltage(VOLTS_SET_5V);
2022-02-25 00:59:25 +01:00
// Set cicrstPin(PG1) to Output
DDRG |= (1 << 1);
// Output a high to disable CIC
PORTG |= (1 << 1);
pin_init_PCE();
}
void reset_cart_PCE(void) {
2022-02-25 00:59:25 +01:00
//Set RESET as Low
PORTH &= ~(1 << 0);
delay(200);
//Set RESET as High
PORTH |= (1 << 0);
delay(200);
}
void set_address_PCE(uint32_t address) {
2022-02-25 00:59:25 +01:00
//Set address
PORTF = address & 0xFF;
PORTK = (address >> 8) & 0xFF;
PORTL = (PORTL & 0xF0) | ((address >> 16) & 0x0F);
}
void set_cs_rd_low_PCE() {
2022-02-25 00:59:25 +01:00
// Set CS(PL4) and RD(PH3) as LOW
PORTL &= ~(1 << 4);
PORTH &= ~(1 << 3);
}
uint8_t read_byte_PCE(uint32_t address) {
2022-02-25 00:59:25 +01:00
uint8_t ret;
set_address_PCE(address);
// Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
2022-02-25 00:59:25 +01:00
//read byte
ret = PINC;
//Swap bit order for PC Engine HuCARD
2024-06-30 02:05:05 +02:00
if (pce_internal_mode == HUCARD || pce_internal_mode == PCE_FLASH) {
2022-02-25 00:59:25 +01:00
ret = ((ret & 0x01) << 7) | ((ret & 0x02) << 5) | ((ret & 0x04) << 3) | ((ret & 0x08) << 1) | ((ret & 0x10) >> 1) | ((ret & 0x20) >> 3) | ((ret & 0x40) >> 5) | ((ret & 0x80) >> 7);
}
//return read data
return ret;
}
void data_output_PCE() {
2022-02-25 00:59:25 +01:00
// Set Data Pins (D0-D7) to Output
DDRC = 0xFF;
}
void data_input_PCE() {
2022-02-25 00:59:25 +01:00
// Set Data Pins (D0-D7) to Input
DDRC = 0x00;
// Enable Internal Pullups
PORTC = 0xFF;
set_cs_rd_low_PCE();
}
void write_byte_PCE(uint32_t address, uint8_t data) {
2022-02-25 00:59:25 +01:00
//PORTH |= (1 << 3); // RD HIGH
set_address_PCE(address);
// Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
2022-02-25 00:59:25 +01:00
//Swap bit order for PC Engine HuCARD
if (pce_internal_mode == HUCARD || pce_internal_mode == PCE_FLASH) {
2022-02-25 00:59:25 +01:00
data = ((data & 0x01) << 7) | ((data & 0x02) << 5) | ((data & 0x04) << 3) | ((data & 0x08) << 1) | ((data & 0x10) >> 1) | ((data & 0x20) >> 3) | ((data & 0x40) >> 5) | ((data & 0x80) >> 7);
}
//write byte
PORTC = data;
// Set CS(PL4) and WR(PH5) as LOW
PORTL &= ~(1 << 4);
PORTH &= ~(1 << 5);
// Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total
__asm__("nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t");
2022-02-25 00:59:25 +01:00
// Set CS(PL4) and WR(PH5) as HIGH
PORTL |= (1 << 4);
PORTH |= (1 << 5);
}
//Confirm the size of ROM
uint32_t detect_rom_size_PCE(void) {
2022-02-25 00:59:25 +01:00
uint32_t rom_size;
uint8_t read_byte;
uint8_t current_byte;
uint8_t detect_16, detect_32, detect_128, detect_256, detect_512, detect_768;
2022-02-25 00:59:25 +01:00
//Initialize variables
detect_16 = 0;
detect_32 = 0;
2022-02-25 00:59:25 +01:00
detect_128 = 0;
detect_256 = 0;
detect_512 = 0;
detect_768 = 0;
//Set pins to read PC Engine cart
pin_read_write_PCE();
//Confirm where mirror address start from (16KB, 32KB, 128KB, 256KB, 512KB, 768KB, or 1024KB)
2022-02-25 00:59:25 +01:00
for (current_byte = 0; current_byte < DETECTION_SIZE; current_byte++) {
if ((current_byte != detect_16) && (current_byte != detect_32) && (current_byte != detect_128) && (current_byte != detect_256) && (current_byte != detect_512) && (current_byte != detect_768)) {
2022-02-25 00:59:25 +01:00
//If none matched, it is 1024KB
break;
}
//read byte for 16KB, 32KB, 128KB, 256KB, 512KB detection
2022-02-25 00:59:25 +01:00
read_byte = read_byte_PCE(current_byte);
//16KB detection
if (current_byte == detect_16) {
if (read_byte_PCE(current_byte + 16UL * 1024UL) == read_byte) {
detect_16++;
}
}
//32KB detection
if (current_byte == detect_32) {
if (read_byte_PCE(current_byte + 32UL * 1024UL) == read_byte) {
detect_32++;
}
}
2023-01-16 12:35:48 +01:00
2022-02-25 00:59:25 +01:00
//128KB detection
if (current_byte == detect_128) {
if (read_byte_PCE(current_byte + 128UL * 1024UL) == read_byte) {
2022-02-25 00:59:25 +01:00
detect_128++;
}
}
//256KB detection
if (current_byte == detect_256) {
if (read_byte_PCE(current_byte + 256UL * 1024UL) == read_byte) {
2022-02-25 00:59:25 +01:00
detect_256++;
}
}
//512KB detection
if (current_byte == detect_512) {
if (read_byte_PCE(current_byte + 512UL * 1024UL) == read_byte) {
2022-02-25 00:59:25 +01:00
detect_512++;
}
}
//768KB detection
read_byte = read_byte_PCE(current_byte + 512UL * 1024UL);
if (current_byte == detect_768) {
if (read_byte_PCE(current_byte + 768UL * 1024UL) == read_byte) {
2022-02-25 00:59:25 +01:00
detect_768++;
}
}
}
//debug
//sprintf(fileName, "%d %d %d %d %d %d", detect_16, detect_32, detect_128, detect_256, detect_512, detect_768); //using filename global variable as string. Initialzed in below anyways.
2022-02-25 00:59:25 +01:00
//println_Msg(fileName);
//ROM size detection by result
if (detect_16 == DETECTION_SIZE) {
rom_size = 16;
} else if (detect_32 == DETECTION_SIZE) {
rom_size = 32;
2023-01-16 12:35:48 +01:00
} else if (detect_128 == DETECTION_SIZE) {
2022-02-25 00:59:25 +01:00
rom_size = 128;
2023-01-16 12:35:48 +01:00
} else if (detect_256 == DETECTION_SIZE) {
if (detect_512 == DETECTION_SIZE) {
2022-02-25 00:59:25 +01:00
rom_size = 256;
2023-01-16 12:35:48 +01:00
} else {
2022-02-25 00:59:25 +01:00
//rom_size = 1024;
//Another confirmation for 384KB because 384KB hucard has data in 0x0--0x40000 and 0x80000--0xA0000(0x40000 is mirror of 0x00000)
rom_size = 384;
}
2023-01-16 12:35:48 +01:00
} else if (detect_512 == DETECTION_SIZE) {
2022-02-25 00:59:25 +01:00
rom_size = 512;
2023-01-16 12:35:48 +01:00
} else if (detect_768 == DETECTION_SIZE) {
2022-02-25 00:59:25 +01:00
rom_size = 768;
2023-01-16 12:35:48 +01:00
} else {
2022-02-25 00:59:25 +01:00
rom_size = 1024;
}
//If rom size is more than or equal to 512KB, detect special cards
if (rom_size >= 512) {
//Street Fighter II' - Champion Edition (Japan)
if (read_byte_PCE(0x7FFF9) == 'N' && read_byte_PCE(0x7FFFA) == 'E' && read_byte_PCE(0x7FFFB) == 'C' && read_byte_PCE(0x7FFFC) == ' ' && read_byte_PCE(0x7FFFD) == 'H' && read_byte_PCE(0x7FFFE) == 'E') {
2022-02-25 00:59:25 +01:00
rom_size = 2560;
}
//Populous (Japan)
2023-01-16 12:35:48 +01:00
if (read_byte_PCE(0x1F26) == 'P' && read_byte_PCE(0x1F27) == 'O' && read_byte_PCE(0x1F28) == 'P' && read_byte_PCE(0x1F29) == 'U' && read_byte_PCE(0x1F2A) == 'L' && read_byte_PCE(0x1F2B) == 'O' && read_byte_PCE(0x1F2C) == 'U' && read_byte_PCE(0x1F2D) == 'S') {
rom_size = 512;
}
//Dinoforce (Japan)
2023-03-25 10:50:06 +01:00
if (read_byte_PCE(0x15A) == 'D' && read_byte_PCE(0x15B) == 'I' && read_byte_PCE(0x15C) == 'N' && read_byte_PCE(0x15D) == 'O' && read_byte_PCE(0x15E) == '-' && read_byte_PCE(0x15F) == 'F' && read_byte_PCE(0x160) == 'O' && read_byte_PCE(0x161) == 'R' && read_byte_PCE(0x162) == 'C' && read_byte_PCE(0x163) == 'E') {
rom_size = 512;
}
2022-02-25 00:59:25 +01:00
}
if (rom_size == 384) {
//"CD-ROM² Super System Card (v3.0)(Japan)" or "Arcade Card Pro CD-ROM²"
if (read_byte_PCE(0x29D1) == 'V' && read_byte_PCE(0x29D2) == 'E' && read_byte_PCE(0x29D3) == 'R' && read_byte_PCE(0x29D4) == '.' && read_byte_PCE(0x29D5) == ' ' && read_byte_PCE(0x29D6) == '3' && read_byte_PCE(0x29D7) == '.' && read_byte_PCE(0x29D8) == '0' && read_byte_PCE(0x29D9) == '0') {
rom_size = 256;
}
}
2022-02-25 00:59:25 +01:00
return rom_size;
}
/* Must be address_start and address_end should be 512 byte aligned */
void read_bank_PCE_ROM(uint32_t address_start, uint32_t address_end, uint32_t *processed_size, uint32_t total_size, uint32_t *crcp) {
2022-02-25 00:59:25 +01:00
uint32_t currByte;
uint16_t c;
for (currByte = address_start; currByte < address_end; currByte += 512) {
for (c = 0; c < 512; c++) {
sdBuffer[c] = read_byte_PCE(currByte + c);
}
if (crcp != NULL) {
*crcp = calculate_crc32(512, sdBuffer, *crcp);
}
myFile.write(sdBuffer, 512);
*processed_size += 512;
draw_progressbar(*processed_size, total_size);
}
}
void read_bank_PCE_RAM(uint32_t address_start, int block_index) {
2022-02-25 00:59:25 +01:00
uint32_t start = address_start + block_index * 512;
for (uint16_t c = 0; c < 512; c++) {
sdBuffer[c] = read_byte_PCE(start + c);
}
}
uint32_t calculate_crc32(int n, unsigned char c[], uint32_t r) {
2022-02-25 00:59:25 +01:00
int i, j;
for (i = 0; i < n; i++) {
r ^= c[i];
for (j = 0; j < 8; j++)
if (r & 1) r = (r >> 1) ^ 0xEDB88320UL;
else r >>= 1;
2022-02-25 00:59:25 +01:00
}
return r;
}
2022-10-28 15:02:51 +02:00
void crc_search(char *file_p, char *folder_p, uint32_t rom_size __attribute__((unused)), uint32_t crc) {
2022-02-25 00:59:25 +01:00
FsFile rom, script;
char gamename[100];
char crc_file[9], crc_search[9];
uint8_t flag;
flag = CHKSUM_SKIP;
//Open list file. If no list file found, just skip
sd.chdir("/"); //Set read directry to root
if (script.open("pce.txt", O_READ)) {
2022-02-25 00:59:25 +01:00
//Calculate CRC of ROM file
sd.chdir(folder_p);
if (rom.open(file_p, O_READ)) {
2022-02-25 00:59:25 +01:00
//Initialize flag as error
flag = CHKSUM_ERROR;
crc = crc ^ 0xFFFFFFFFUL; //Finish CRC calculation and progress bar
2022-02-25 00:59:25 +01:00
//Display calculated CRC
sprintf(crc_file, "%08lX", crc);
//Search for same CRC in list
while (script.available()) {
//Read 2 lines (game name and CRC)
get_line(gamename, &script, 96);
get_line(crc_search, &script, 9);
skip_line(&script); //Skip every 3rd line
2022-02-25 00:59:25 +01:00
//if checksum search successful, rename the file and end search
if (strcmp(crc_search, crc_file) == 0) {
2022-02-25 00:59:25 +01:00
print_Msg(F("Chksum OK "));
println_Msg(crc_file);
print_Msg(F("Saved to "));
print_Msg(folder_p);
print_Msg(F("/"));
print_Msg(gamename);
flag = CHKSUM_OK;
2022-04-15 12:52:59 +02:00
rom.rename(gamename);
2022-02-25 00:59:25 +01:00
break;
}
}
rom.close();
}
}
if (flag == CHKSUM_SKIP) {
2022-02-25 00:59:25 +01:00
print_Msg(F("Saved to "));
print_Msg(folder_p);
print_Msg(F("/"));
print_Msg(file_p);
} else if (flag == CHKSUM_ERROR) {
2022-02-25 00:59:25 +01:00
print_Msg(F("Chksum Error "));
println_Msg(crc_file);
print_Msg(F("Saved to "));
print_Msg(folder_p);
print_Msg(F("/"));
print_Msg(file_p);
}
script.close();
}
void unlock_tennokoe_bank_RAM() {
write_byte_PCE(0x0D0000, 0x68); //Unlock RAM sequence 1 Bank 68
write_byte_PCE(0x0F0000, 0x00); //Unlock RAM sequence 2 Bank 78
write_byte_PCE(0x0F0000, 0x73); //Unlock RAM sequence 3 Bank 78
write_byte_PCE(0x0F0000, 0x73); //Unlock RAM sequence 4 Bank 78
write_byte_PCE(0x0F0000, 0x73); //Unlock RAM sequence 5 Bank 78
2022-02-25 00:59:25 +01:00
}
void lock_tennokoe_bank_RAM() {
write_byte_PCE(0x0D0000, 0x68); //Lock RAM sequence 1 Bank 68
write_byte_PCE(0x0F0001, 0x00); //Lock RAM sequence 2 Bank 78
write_byte_PCE(0x0C0001, 0x60); //Lock RAM sequence 3 Bank 60
2022-02-25 00:59:25 +01:00
}
void read_tennokoe_bank_PCE(int bank_index) {
2022-02-25 00:59:25 +01:00
//clear the screen
display_Clear();
println_Msg(F("RAM bank size: 2KB"));
// Get name, add extension and convert to char array for sd lib
sprintf(fileName, "BANKRAM%d.sav", bank_index + 1);
// create a new folder for the save file
EEPROM_readAnything(0, foldern);
sd.chdir("/");
sprintf(folder, "PCE/RAM/%d", foldern);
2022-02-25 00:59:25 +01:00
sd.mkdir(folder, true);
sd.chdir(folder);
print_Msg(F("Saving RAM to "));
print_Msg(folder);
print_Msg(F("/"));
println_Msg(fileName);
display_Update();
// write new folder number back to eeprom
foldern = foldern + 1;
EEPROM_writeAnything(0, foldern);
//open file on sd card
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
print_FatalError(create_file_STR);
2022-02-25 00:59:25 +01:00
}
pin_read_write_PCE();
for (int block_index = 0; block_index < 4; block_index++) {
//Unlock Tennokoe Bank RAM
//Disable interrupts
noInterrupts();
data_output_PCE();
unlock_tennokoe_bank_RAM();
data_input_PCE();
//Read Tennokoe bank RAM
read_bank_PCE_RAM(0x080000 + 2048UL * bank_index, block_index);
//Lock Tennokoe Bank RAM
data_output_PCE();
lock_tennokoe_bank_RAM();
data_input_PCE();
//Enable interrupts
interrupts();
// hexdump:
// for (int c = 0; c < 512; c += 16) {
// for (int i = 0; i < 16; i++) {
// uint8_t b = sdBuffer[c + i];
// print_Msg_PaddedHexByte(b);
// //print_Msg(FS(FSTRING_SPACE));
2022-02-25 00:59:25 +01:00
// }
// println_Msg(FS(FSTRING_EMPTY));
2022-02-25 00:59:25 +01:00
// }
if (block_index == 0) {
print_Msg(F("header: "));
for (int i = 0; i < 4; i++) {
uint8_t b = sdBuffer[i];
print_Msg_PaddedHexByte(b);
}
println_Msg(FS(FSTRING_EMPTY));
2022-02-25 00:59:25 +01:00
}
if (block_index == 0 && sdBuffer[2] == 0x42 && sdBuffer[3] == 0x4D) {
if (sdBuffer[0] != 0x48 || sdBuffer[1] != 0x55) {
sdBuffer[0] = 0x48; // H
sdBuffer[1] = 0x55; // U
2022-02-25 00:59:25 +01:00
println_Msg(F("Corrected header"));
} else {
println_Msg(F("Header is correct"));
}
}
myFile.write(sdBuffer, 512);
}
pin_init_PCE();
//Close the file:
myFile.close();
println_Msg(FS(FSTRING_EMPTY));
print_STR(press_button_STR, 1);
display_Update();
wait();
2022-02-25 00:59:25 +01:00
}
void write_tennokoe_bank_PCE(int bank_index) {
2022-02-25 00:59:25 +01:00
//Display file Browser and wait user to select a file. Size must be 2KB.
filePath[0] = '\0';
sd.chdir("/");
fileBrowser(F("Select RAM file"));
// Create filepath
sprintf(filePath, "%s/%s", filePath, fileName);
display_Clear();
//open file on sd card
if (myFile.open(filePath, O_READ)) {
fileSize = myFile.fileSize();
if (fileSize != 2 * 1024UL) {
println_Msg(F("File must be 2KB"));
display_Update();
myFile.close();
wait();
return;
}
pin_read_write_PCE();
for (int block_index = 0; block_index < 4; block_index++) {
for (uint16_t c = 0; c < 512; c++) {
sdBuffer[c] = myFile.read();
}
//Unlock Tennokoe Bank RAM
//Disable interrupts
noInterrupts();
data_output_PCE();
unlock_tennokoe_bank_RAM();
data_input_PCE();
//Write file to Tennokoe BANK RAM
data_output_PCE();
uint32_t offset = 0x080000 + (bank_index * 2048UL) + (block_index * 512UL);
for (uint16_t c = 0; c < 512; c++) {
write_byte_PCE(offset + c, sdBuffer[c]);
}
//Lock Tennokoe Bank RAM
lock_tennokoe_bank_RAM();
data_input_PCE();
//Enable interrupts
interrupts();
}
// verify
int diffcnt = 0;
myFile.seekSet(0);
for (int block_index = 0; block_index < 4; block_index++) {
//Unlock Tennokoe Bank RAM
//Disable interrupts
noInterrupts();
data_output_PCE();
unlock_tennokoe_bank_RAM();
data_input_PCE();
//Read Tennokoe bank RAM
read_bank_PCE_RAM(0x080000 + 2048UL * bank_index, block_index);
//Lock Tennokoe Bank RAM
data_output_PCE();
lock_tennokoe_bank_RAM();
data_input_PCE();
//Enable interrupts
interrupts();
int diffcnt = 0;
for (int c = 0; c < 512; c += 16) {
uint8_t ram_b = sdBuffer[c];
uint8_t file_b = myFile.read();
if (ram_b != file_b) {
diffcnt++;
}
}
}
if (diffcnt == 0) {
println_Msg(F("Verify OK..."));
} else {
println_Msg(F("Verify failed..."));
print_Msg(diffcnt);
print_STR(_bytes_STR, 1);
print_Error(did_not_verify_STR);
2022-02-25 00:59:25 +01:00
}
pin_init_PCE();
// Close the file:
myFile.close();
println_Msg(F("Finished"));
} else {
print_Error(FS(FSTRING_FILE_DOESNT_EXIST));
2022-02-25 00:59:25 +01:00
}
println_Msg(FS(FSTRING_EMPTY));
print_STR(press_button_STR, 1);
display_Update();
wait();
2022-02-25 00:59:25 +01:00
}
void read_rom_PCE(void) {
2022-02-25 00:59:25 +01:00
uint32_t rom_size;
uint32_t processed_size = 0;
//clear the screen
display_Clear();
rom_size = detect_rom_size_PCE();
if (pce_force_rom_size > 0) {
rom_size = pce_force_rom_size;
print_Msg(F("Forced size: "));
} else {
print_Msg(F("Detected size: "));
}
print_Msg(rom_size);
println_Msg(F("KB"));
// Get name, add extension and convert to char array for sd lib
2024-05-12 15:37:11 +02:00
createFolder("PCE", "ROM", "PCEROM", "pce");
2022-02-25 00:59:25 +01:00
printAndIncrementFolder();
2022-02-25 00:59:25 +01:00
//open file on sd card
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
print_FatalError(create_file_STR);
2022-02-25 00:59:25 +01:00
}
pin_read_write_PCE();
//Initialize progress bar by setting processed size as 0
draw_progressbar(0, rom_size * 1024UL);
uint32_t crc = 0xFFFFFFFFUL; //Initialize CRC
if (rom_size == 384) {
2022-02-25 00:59:25 +01:00
//Read two sections. 0x000000--0x040000 and 0x080000--0x0A0000 for 384KB
read_bank_PCE_ROM(0, 0x40000, &processed_size, rom_size * 1024UL, &crc);
read_bank_PCE_ROM(0x80000, 0xA0000, &processed_size, rom_size * 1024UL, &crc);
} else if (rom_size == 2560) {
2022-02-25 00:59:25 +01:00
//Dump Street fighter II' Champion Edition
read_bank_PCE_ROM(0, 0x80000, &processed_size, rom_size * 1024UL, &crc); //Read first bank
data_output_PCE();
write_byte_PCE(0x1FF0, 0xFF); //Display second bank
2022-02-25 00:59:25 +01:00
data_input_PCE();
read_bank_PCE_ROM(0x80000, 0x100000, &processed_size, rom_size * 1024UL, &crc); //Read second bank
2022-02-25 00:59:25 +01:00
data_output_PCE();
write_byte_PCE(0x1FF1, 0xFF); //Display third bank
2022-02-25 00:59:25 +01:00
data_input_PCE();
read_bank_PCE_ROM(0x80000, 0x100000, &processed_size, rom_size * 1024UL, &crc); //Read third bank
2022-02-25 00:59:25 +01:00
data_output_PCE();
write_byte_PCE(0x1FF2, 0xFF); //Display forth bank
2022-02-25 00:59:25 +01:00
data_input_PCE();
read_bank_PCE_ROM(0x80000, 0x100000, &processed_size, rom_size * 1024UL, &crc); //Read forth bank
2022-02-25 00:59:25 +01:00
data_output_PCE();
write_byte_PCE(0x1FF3, 0xFF); //Display fifth bank
2022-02-25 00:59:25 +01:00
data_input_PCE();
read_bank_PCE_ROM(0x80000, 0x100000, &processed_size, rom_size * 1024UL, &crc); //Read fifth bank
} else {
2022-02-25 00:59:25 +01:00
//Read start form 0x000000 and keep reading until end of ROM
read_bank_PCE_ROM(0, rom_size * 1024UL, &processed_size, rom_size * 1024UL, &crc);
}
pin_init_PCE();
//Close the file:
myFile.close();
//CRC search and rename ROM
crc_search(fileName, folder, rom_size, crc);
println_Msg(FS(FSTRING_EMPTY));
2024-06-30 02:05:05 +02:00
print_STR(press_button_STR, true);
display_Update();
wait();
2022-02-25 00:59:25 +01:00
}
2024-06-30 02:05:05 +02:00
#ifdef ENABLE_FLASH
void flash_mode_PCE() {
pin_read_write_PCE();
data_output_PCE();
PORTH |= (1 << 3); // RD HIGH
// write_byte_PCE sets WR
}
// Implements data complement status checking
void flash_wait_status_PCE(uint8_t expected) {
set_cs_rd_low_PCE();
data_input_PCE();
// We only look at D7, or the highest bit of expected
expected >>= 7;
uint8_t status;
do {
PORTH &= ~(1 << 3); // RD low
// one nop = 62.5ns
// tOE = 30-50ns depending on flash
NOP;
status = PINC;
PORTH |= (1 << 3); // RD high
// reversed, bit 0 is the MSB
} while ((status & 0x1) != expected);
data_output_PCE();
// leave RD high on exit
}
2024-06-29 04:31:14 +02:00
void flash_PCE() {
println_Msg(F("Detecting..."));
display_Update();
2024-06-30 02:05:05 +02:00
// SOFTWARE ID PROGRAM
flash_mode_PCE();
write_byte_PCE(0x5555, 0xAA);
write_byte_PCE(0x2AAA, 0x55);
write_byte_PCE(0x5555, 0x90);
data_input_PCE();
// tIDA = 150ns
NOP;NOP;NOP;
2024-06-30 02:05:05 +02:00
// MFG,DEVICE
uint16_t deviceId = (read_byte_PCE(0x0) << 8) | read_byte_PCE(0x1);
// EXIT SOFTWARE ID PROGRAM
flash_mode_PCE();
write_byte_PCE(0x5555, 0xAA);
write_byte_PCE(0x2AAA, 0x55);
write_byte_PCE(0x5555, 0xF0);
flashSize = 0;
switch (deviceId) {
case 0xBFB5:
// SST39SF010A = 1Mbit
flashSize = 131072UL;
break;
case 0xBFB6:
// SST39SF020A = 2Mbit
flashSize = 262144UL;
break;
case 0xBFB7:
// SST39SF040 = 4Mbit
flashSize = 524288UL;
break;
case 0xC2A4:
// MX29F040 = 4Mbit
flashSize = 524288UL;
break;
case 0xC2D5:
// MX29F080 = 8Mbit
flashSize = 1048576UL;
break;
2024-06-30 02:05:05 +02:00
}
if (flashSize <= 0) {
print_Msg(F("UNKNOWN "));
println_Msg(deviceId);
2024-06-30 02:05:05 +02:00
display_Update();
wait();
resetArduino();
return;
} else {
print_Msg(FS(FSTRING_SIZE));
print_Msg(flashSize / 131072UL);
println_Msg(F("Mbit"));
display_Update();
wait();
}
2024-06-29 04:31:14 +02:00
filePath[0] = '\0';
sd.chdir("/");
fileBrowser(FS(FSTRING_SELECT_FILE));
display_Clear();
2024-06-29 04:31:14 +02:00
if (openFlashFile()) {
println_Msg(F("Erasing..."));
2024-06-29 04:31:14 +02:00
display_Update();
2024-06-30 02:05:05 +02:00
// CHIP ERASE PROGRAM
flash_mode_PCE();
2024-06-29 04:31:14 +02:00
write_byte_PCE(0x5555, 0xAA);
write_byte_PCE(0x2AAA, 0x55);
write_byte_PCE(0x5555, 0x80);
write_byte_PCE(0x5555, 0xAA);
write_byte_PCE(0x2AAA, 0x55);
write_byte_PCE(0x5555, 0x10);
// Data complement polling, wait until highest bit is 1
flash_wait_status_PCE(0xFF);
2024-06-29 04:31:14 +02:00
print_STR(flashing_file_STR, true);
display_Update();
2024-06-29 04:31:14 +02:00
uint32_t processedProgressBar = 0;
uint32_t totalProgressBar = (uint32_t)fileSize;
draw_progressbar(0, totalProgressBar);
unsigned long startMs = millis();
2024-06-30 02:05:05 +02:00
flash_mode_PCE();
const size_t BUFSIZE = 512;
for (unsigned long currAddr = 0; currAddr < fileSize; currAddr += BUFSIZE) {
myFile.read(sdBuffer, BUFSIZE);
2024-06-29 04:31:14 +02:00
if (currAddr % 4096 == 0) {
blinkLED();
}
2024-06-30 02:05:05 +02:00
for (int currByte = 0; currByte < BUFSIZE; currByte++) {
// BYTE PROGRAM
byte b = sdBuffer[currByte];
2024-06-29 04:31:14 +02:00
write_byte_PCE(0x5555, 0xAA);
write_byte_PCE(0x2AAA, 0x55);
write_byte_PCE(0x5555, 0xA0);
write_byte_PCE(currAddr + currByte, b);
flash_wait_status_PCE(b);
2024-06-30 02:05:05 +02:00
// tBP = 20us
// delayMicroseconds(20);
// status polling = 38s
2024-06-29 04:31:14 +02:00
}
// update progress bar
2024-06-30 02:05:05 +02:00
processedProgressBar += BUFSIZE;
2024-06-29 04:31:14 +02:00
draw_progressbar(processedProgressBar, totalProgressBar);
}
myFile.close();
pin_init_PCE();
println_Msg((millis() - startMs) / 1000);
2024-06-30 02:05:05 +02:00
print_STR(done_STR, true);
2024-06-29 04:31:14 +02:00
}
display_Update();
wait();
}
2024-06-29 04:31:14 +02:00
#endif
2022-02-25 00:59:25 +01:00
// PC Engine Menu
void pceMenu() {
// create menu with title and 7 options to choose from
unsigned char mainMenu;
if (pce_internal_mode == HUCARD || pce_internal_mode == HUCARD_NOSWAP) {
strcpy_P(menuOptions[0], FSTRING_READ_ROM);
sprintf_P(menuOptions[1], menuOptionspceCart_1, tennokoe_bank_index + 1);
sprintf_P(menuOptions[2], menuOptionspceCart_2, tennokoe_bank_index + 1);
strcpy_P(menuOptions[3], menuOptionspceCart_3);
strcpy_P(menuOptions[4], menuOptionspceCart_4);
2022-02-25 00:59:25 +01:00
if (pce_force_rom_size > 0) {
sprintf_P(menuOptions[5], menuOptionspceCart_5_fmt, pce_force_rom_size);
2022-02-25 00:59:25 +01:00
} else {
sprintf_P(menuOptions[5], menuOptionspceCart_5, FORCED_SIZE);
2022-02-25 00:59:25 +01:00
}
strcpy_P(menuOptions[6], FSTRING_RESET);
mainMenu = question_box(F("PCE HuCARD menu"), menuOptions, 7, 0);
2022-02-25 00:59:25 +01:00
// wait for user choice to come back from the question box menu
switch (mainMenu) {
2022-02-25 00:59:25 +01:00
case 0:
read_rom_PCE();
break;
2022-02-25 00:59:25 +01:00
case 1:
read_tennokoe_bank_PCE(tennokoe_bank_index);
break;
2022-02-25 00:59:25 +01:00
case 2:
write_tennokoe_bank_PCE(tennokoe_bank_index);
break;
2022-02-25 00:59:25 +01:00
case 3:
if (tennokoe_bank_index < 3) {
tennokoe_bank_index++;
}
break;
case 4:
2022-02-25 00:59:25 +01:00
if (tennokoe_bank_index > 0) {
tennokoe_bank_index--;
}
break;
case 5:
pce_force_rom_size = FORCED_SIZE;
2022-02-25 00:59:25 +01:00
break;
2022-02-25 00:59:25 +01:00
case 6:
resetArduino();
2022-02-25 00:59:25 +01:00
break;
}
2024-06-30 02:05:05 +02:00
} else if (pce_internal_mode == TURBOCHIP) {
2022-02-25 00:59:25 +01:00
// Copy menuOptions out of progmem
convertPgm(menuOptionspceTC, 2);
mainMenu = question_box(F("TG TurboChip menu"), menuOptions, 2, 0);
// wait for user choice to come back from the question box menu
switch (mainMenu) {
2022-02-25 00:59:25 +01:00
case 0:
read_rom_PCE();
break;
case 1:
resetArduino();
break;
}
}
2024-06-30 02:05:05 +02:00
#ifdef ENABLE_FLASH
else if (pce_internal_mode == PCE_FLASH) {
const int max = 2;
convertPgm(menuOptionspceFlash, max);
mainMenu = question_box(F("Flash Repro menu"), menuOptions, max, 0);
switch (mainMenu) {
case 0:
2024-06-29 04:31:14 +02:00
flash_PCE();
break;
2024-06-30 02:05:05 +02:00
case 1:
resetArduino();
break;
}
}
#endif
else {
print_MissingModule(); // does not return
}
2022-02-25 00:59:25 +01:00
}
#endif
//******************************************
// End of File
//******************************************