mirror of
https://github.com/sanni/cartreader.git
synced 2024-11-26 22:44:15 +01:00
2cf7f5dbe7
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.
895 lines
24 KiB
C++
895 lines
24 KiB
C++
//******************************************
|
|
// 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
|
|
//
|
|
// Special thanks
|
|
// sanni - Arduino cart reader
|
|
// skaman - ROM size detection
|
|
// NO-INTRO - CRC list for game name detection
|
|
// Chris Covell - Tennokoe bank support
|
|
//
|
|
//******************************************
|
|
#ifdef enable_PCE
|
|
|
|
/******************************************
|
|
Defines
|
|
*****************************************/
|
|
#define HUCARD 0
|
|
#define TURBOCHIP 1
|
|
#define HUCARD_NOSWAP 2
|
|
#define DETECTION_SIZE 64
|
|
#define FORCED_SIZE 1024
|
|
#define CHKSUM_SKIP 0
|
|
#define CHKSUM_OK 1
|
|
#define CHKSUM_ERROR 2
|
|
|
|
/******************************************
|
|
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
|
|
uint16_t pce_force_rom_size = 0;
|
|
uint8_t tennokoe_bank_index = 0;
|
|
|
|
/******************************************
|
|
Menu
|
|
*****************************************/
|
|
// PCE start menu
|
|
static const char pceMenuItem1[] PROGMEM = "HuCARD (swapped)";
|
|
static const char pceMenuItem2[] PROGMEM = "HuCARD(not swapped)";
|
|
static const char pceMenuItem3[] PROGMEM = "Turbochip";
|
|
static const char *const menuOptionspce[] PROGMEM = { pceMenuItem1, pceMenuItem2, pceMenuItem3, string_reset2 };
|
|
|
|
// PCE card menu items
|
|
static const char menuOptionspceCart_0[] PROGMEM = "Read ROM";
|
|
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";
|
|
|
|
// Turbochip menu items
|
|
static const char pceTCMenuItem1[] PROGMEM = "Read ROM";
|
|
static const char *const menuOptionspceTC[] PROGMEM = { pceTCMenuItem1, string_reset2 };
|
|
|
|
// PCE start menu
|
|
void pcsMenu(void) {
|
|
// create menu with title and 3 options to choose from
|
|
unsigned char pceDev;
|
|
// Copy menuOptions out of progmem
|
|
convertPgm(menuOptionspce, 3);
|
|
pceDev = question_box(F("Select device"), menuOptions, 3, 0);
|
|
|
|
// wait for user choice to come back from the question box menu
|
|
switch (pceDev) {
|
|
case 0:
|
|
//Hucard
|
|
display_Clear();
|
|
display_Update();
|
|
pce_internal_mode = HUCARD;
|
|
setup_cart_PCE();
|
|
mode = mode_PCE;
|
|
break;
|
|
|
|
case 1:
|
|
//Hucard not swapped
|
|
display_Clear();
|
|
display_Update();
|
|
pce_internal_mode = HUCARD_NOSWAP;
|
|
setup_cart_PCE();
|
|
mode = mode_PCE;
|
|
break;
|
|
|
|
case 2:
|
|
//Turbografx
|
|
display_Clear();
|
|
display_Update();
|
|
pce_internal_mode = TURBOCHIP;
|
|
setup_cart_PCE();
|
|
mode = mode_PCE;
|
|
break;
|
|
|
|
case 3:
|
|
resetArduino();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void pin_read_write_PCE(void) {
|
|
// 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) {
|
|
//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);
|
|
|
|
// 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) {
|
|
//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) {
|
|
//Set address
|
|
PORTF = address & 0xFF;
|
|
PORTK = (address >> 8) & 0xFF;
|
|
PORTL = (PORTL & 0xF0) | ((address >> 16) & 0x0F);
|
|
}
|
|
|
|
void set_cs_rd_low_PCE() {
|
|
// Set CS(PL4) and RD(PH3) as LOW
|
|
PORTL &= ~(1 << 4);
|
|
PORTH &= ~(1 << 3);
|
|
}
|
|
|
|
uint8_t read_byte_PCE(uint32_t address) {
|
|
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");
|
|
|
|
//read byte
|
|
ret = PINC;
|
|
|
|
//Swap bit order for PC Engine HuCARD
|
|
if (pce_internal_mode == HUCARD) {
|
|
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() {
|
|
// Set Data Pins (D0-D7) to Output
|
|
DDRC = 0xFF;
|
|
}
|
|
|
|
void data_input_PCE() {
|
|
// 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) {
|
|
//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");
|
|
|
|
//Swap bit order for PC Engine HuCARD
|
|
if (pce_internal_mode == HUCARD) {
|
|
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");
|
|
|
|
// 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) {
|
|
uint32_t rom_size;
|
|
uint8_t read_byte;
|
|
uint8_t current_byte;
|
|
uint8_t detect_32, detect_128, detect_256, detect_512, detect_768;
|
|
|
|
//Initialize variables
|
|
detect_32 = 0;
|
|
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 (32KB, 128KB, 256KB, 512KB, 768KB, or 1024KB)
|
|
for (current_byte = 0; current_byte < DETECTION_SIZE; current_byte++) {
|
|
if ((current_byte != detect_32) && (current_byte != detect_128) && (current_byte != detect_256) && (current_byte != detect_512) && (current_byte != detect_768)) {
|
|
//If none matched, it is 1024KB
|
|
break;
|
|
}
|
|
|
|
//read byte for 32KB, 128KB, 256KB, 512KB detection
|
|
read_byte = read_byte_PCE(current_byte);
|
|
|
|
//32KB detection
|
|
if (current_byte == detect_32) {
|
|
if (read_byte_PCE(current_byte + 32UL * 1024UL) == read_byte) {
|
|
detect_32++;
|
|
}
|
|
}
|
|
|
|
//128KB detection
|
|
if (current_byte == detect_128) {
|
|
if (read_byte_PCE(current_byte + 128UL * 1024UL) == read_byte) {
|
|
detect_128++;
|
|
}
|
|
}
|
|
|
|
//256KB detection
|
|
if (current_byte == detect_256) {
|
|
if (read_byte_PCE(current_byte + 256UL * 1024UL) == read_byte) {
|
|
detect_256++;
|
|
}
|
|
}
|
|
|
|
//512KB detection
|
|
if (current_byte == detect_512) {
|
|
if (read_byte_PCE(current_byte + 512UL * 1024UL) == read_byte) {
|
|
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) {
|
|
detect_768++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//debug
|
|
//sprintf(fileName, "%d %d %d %d %d", detect_32, detect_128, detect_256, detect_512, detect_768); //using filename global variable as string. Initialzed in below anyways.
|
|
//println_Msg(fileName);
|
|
|
|
//ROM size detection by result
|
|
if (detect_32 == DETECTION_SIZE) {
|
|
rom_size = 32;
|
|
} else if (detect_128 == DETECTION_SIZE) {
|
|
rom_size = 128;
|
|
} else if (detect_256 == DETECTION_SIZE) {
|
|
if (detect_512 == DETECTION_SIZE) {
|
|
rom_size = 256;
|
|
} else {
|
|
//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;
|
|
}
|
|
} else if (detect_512 == DETECTION_SIZE) {
|
|
rom_size = 512;
|
|
} else if (detect_768 == DETECTION_SIZE) {
|
|
rom_size = 768;
|
|
} else {
|
|
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') {
|
|
rom_size = 2560;
|
|
}
|
|
//Populous (Japan)
|
|
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 (World)
|
|
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;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
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) {
|
|
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) {
|
|
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;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
void crc_search(char *file_p, char *folder_p, uint32_t rom_size __attribute__((unused)), uint32_t crc) {
|
|
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)) {
|
|
//Calculate CRC of ROM file
|
|
sd.chdir(folder_p);
|
|
if (rom.open(file_p, O_READ)) {
|
|
//Initialize flag as error
|
|
flag = CHKSUM_ERROR;
|
|
crc = crc ^ 0xFFFFFFFFUL; //Finish CRC calculation and progress bar
|
|
//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
|
|
|
|
//if checksum search successful, rename the file and end search
|
|
if (strcmp(crc_search, crc_file) == 0) {
|
|
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;
|
|
rom.rename(gamename);
|
|
break;
|
|
}
|
|
}
|
|
rom.close();
|
|
}
|
|
}
|
|
|
|
if (flag == CHKSUM_SKIP) {
|
|
print_Msg(F("Saved to "));
|
|
print_Msg(folder_p);
|
|
print_Msg(F("/"));
|
|
print_Msg(file_p);
|
|
} else if (flag == CHKSUM_ERROR) {
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
void read_tennokoe_bank_PCE(int bank_index) {
|
|
//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);
|
|
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);
|
|
}
|
|
|
|
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(F(" "));
|
|
// }
|
|
// println_Msg(F(""));
|
|
// }
|
|
|
|
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(F(""));
|
|
}
|
|
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
|
|
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(F(""));
|
|
print_STR(press_button_STR, 1);
|
|
display_Update();
|
|
wait();
|
|
}
|
|
|
|
void write_tennokoe_bank_PCE(int bank_index) {
|
|
//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);
|
|
}
|
|
|
|
pin_init_PCE();
|
|
|
|
// Close the file:
|
|
myFile.close();
|
|
println_Msg(F("Finished"));
|
|
|
|
} else {
|
|
print_Error(F("File doesn't exist"));
|
|
}
|
|
|
|
println_Msg(F(""));
|
|
print_STR(press_button_STR, 1);
|
|
display_Update();
|
|
wait();
|
|
}
|
|
|
|
void read_rom_PCE(void) {
|
|
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
|
|
strcpy(fileName, "PCEROM");
|
|
strcat(fileName, ".pce");
|
|
|
|
// create a new folder for the save file
|
|
EEPROM_readAnything(0, foldern);
|
|
sd.chdir("/");
|
|
sprintf(folder, "PCE/ROM/%d", foldern);
|
|
sd.mkdir(folder, true);
|
|
sd.chdir(folder);
|
|
|
|
print_Msg(F("Saving ROM 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);
|
|
}
|
|
|
|
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) {
|
|
//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) {
|
|
//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
|
|
data_input_PCE();
|
|
read_bank_PCE_ROM(0x80000, 0x100000, &processed_size, rom_size * 1024UL, &crc); //Read second bank
|
|
data_output_PCE();
|
|
write_byte_PCE(0x1FF1, 0xFF); //Display third bank
|
|
data_input_PCE();
|
|
read_bank_PCE_ROM(0x80000, 0x100000, &processed_size, rom_size * 1024UL, &crc); //Read third bank
|
|
data_output_PCE();
|
|
write_byte_PCE(0x1FF2, 0xFF); //Display forth bank
|
|
data_input_PCE();
|
|
read_bank_PCE_ROM(0x80000, 0x100000, &processed_size, rom_size * 1024UL, &crc); //Read forth bank
|
|
data_output_PCE();
|
|
write_byte_PCE(0x1FF3, 0xFF); //Display fifth bank
|
|
data_input_PCE();
|
|
read_bank_PCE_ROM(0x80000, 0x100000, &processed_size, rom_size * 1024UL, &crc); //Read fifth bank
|
|
} else {
|
|
//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(F(""));
|
|
print_STR(press_button_STR, 1);
|
|
display_Update();
|
|
wait();
|
|
}
|
|
|
|
// 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], menuOptionspceCart_0);
|
|
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);
|
|
if (pce_force_rom_size > 0) {
|
|
sprintf_P(menuOptions[5], menuOptionspceCart_5_fmt, pce_force_rom_size);
|
|
} else {
|
|
sprintf_P(menuOptions[5], menuOptionspceCart_5, FORCED_SIZE);
|
|
}
|
|
strcpy_P(menuOptions[6], string_reset2);
|
|
mainMenu = question_box(F("PCE HuCARD menu"), menuOptions, 7, 0);
|
|
|
|
// wait for user choice to come back from the question box menu
|
|
switch (mainMenu) {
|
|
case 0:
|
|
read_rom_PCE();
|
|
break;
|
|
|
|
case 1:
|
|
read_tennokoe_bank_PCE(tennokoe_bank_index);
|
|
break;
|
|
|
|
case 2:
|
|
write_tennokoe_bank_PCE(tennokoe_bank_index);
|
|
break;
|
|
|
|
case 3:
|
|
if (tennokoe_bank_index < 3) {
|
|
tennokoe_bank_index++;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
if (tennokoe_bank_index > 0) {
|
|
tennokoe_bank_index--;
|
|
}
|
|
break;
|
|
|
|
case 5:
|
|
pce_force_rom_size = FORCED_SIZE;
|
|
break;
|
|
|
|
case 6:
|
|
resetArduino();
|
|
break;
|
|
}
|
|
} else {
|
|
// 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) {
|
|
case 0:
|
|
read_rom_PCE();
|
|
break;
|
|
|
|
case 1:
|
|
resetArduino();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
//******************************************
|
|
// End of File
|
|
//******************************************
|