mirror of
https://github.com/sanni/cartreader.git
synced 2024-11-14 17:05:08 +01:00
0c75bf8aed
Thanks to skaman for the Sega CD Ram Cart code. Writes to the Sega CD Backup RAM Cart require an extra wire from MRES (B02) to VRES (B27).
846 lines
22 KiB
C++
846 lines
22 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
|
|
//
|
|
//******************************************
|
|
|
|
/******************************************
|
|
Defines
|
|
*****************************************/
|
|
#define HUCARD 0
|
|
#define TURBOCHIP 1
|
|
|
|
#define DETECTION_SIZE 64
|
|
#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(uint32_t address_start, uint32_t address_end, uint32_t *processed_size, uint32_t total_size);
|
|
void read_rom_PCE(void);
|
|
|
|
/******************************************
|
|
Variables
|
|
*****************************************/
|
|
uint8_t pce_internal_mode; //0 - HuCARD, 1 - TurboChip
|
|
|
|
/******************************************
|
|
Menu
|
|
*****************************************/
|
|
// PCE start menu
|
|
static const char pceMenuItem1[] PROGMEM = "HuCARD";
|
|
static const char pceMenuItem2[] PROGMEM = "Turbochip";
|
|
static const char* const menuOptionspce[] PROGMEM = {pceMenuItem1, pceMenuItem2};
|
|
|
|
// PCE card menu items
|
|
static const char pceCartMenuItem1[] PROGMEM = "Read Rom";
|
|
static const char pceCartMenuItem2[] PROGMEM = "Read Tennokoe Bank";
|
|
static const char pceCartMenuItem3[] PROGMEM = "Write Tennokoe Bank";
|
|
static const char pceCartMenuItem4[] PROGMEM = "Reset";
|
|
static const char* const menuOptionspceCart[] PROGMEM = {pceCartMenuItem1, pceCartMenuItem2, pceCartMenuItem3, pceCartMenuItem4};
|
|
|
|
// Turbochip menu items
|
|
static const char pceTCMenuItem1[] PROGMEM = "Read Rom";
|
|
static const char pceTCMenuItem2[] PROGMEM = "Reset";
|
|
static const char* const menuOptionspceTC[] PROGMEM = {pceTCMenuItem1, pceTCMenuItem2};
|
|
|
|
// 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, 2);
|
|
pceDev = question_box(F("Select device"), menuOptions, 2, 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:
|
|
//Turbografx
|
|
display_Clear();
|
|
display_Update();
|
|
pce_internal_mode = TURBOCHIP;
|
|
setup_cart_PCE();
|
|
mode = mode_PCE;
|
|
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;
|
|
|
|
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)
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
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");
|
|
|
|
// Set CS(PL4) and RD(PH3) as LOW
|
|
PORTL &= ~(1 << 4);
|
|
PORTH &= ~(1 << 3);
|
|
|
|
// 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);
|
|
}
|
|
|
|
// Set CS(PL4) and RD(PH3) as HIGH
|
|
PORTL |= (1 << 4);
|
|
PORTH |= (1 << 3);
|
|
|
|
//return read data
|
|
return ret;
|
|
|
|
}
|
|
|
|
void write_byte_PCE(uint32_t address, uint8_t data)
|
|
{
|
|
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 Data Pins (D0-D7) to Output
|
|
DDRC = 0xFF;
|
|
|
|
// 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);
|
|
|
|
// Set Data Pins (D0-D7) to Input
|
|
DDRC = 0x00;
|
|
|
|
// Enable Internal Pullups
|
|
PORTC = 0xFF;
|
|
}
|
|
|
|
//Confirm the size of ROM - 128Kb, 256Kb, 384Kb, 512Kb, 768Kb or 1024Kb
|
|
uint32_t detect_rom_size_PCE(void)
|
|
{
|
|
uint32_t rom_size;
|
|
uint8_t read_byte;
|
|
uint8_t current_byte;
|
|
uint8_t detect_128, detect_256, detect_512, detect_768;
|
|
|
|
//Initialize variables
|
|
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(128KB, 256KB, 512KB, 768, or 1024KB)
|
|
for (current_byte = 0; current_byte < DETECTION_SIZE; current_byte++) {
|
|
if ((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 128KB, 256KB, 512KB detection
|
|
read_byte = read_byte_PCE(current_byte);
|
|
|
|
//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", 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_128 == DETECTION_SIZE)
|
|
{
|
|
rom_size = 128;
|
|
}
|
|
else if (detect_256 == DETECTION_SIZE)
|
|
{
|
|
if (detect_512 == DETECTION_SIZE)
|
|
{
|
|
rom_size = 256;
|
|
}
|
|
else
|
|
{
|
|
//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 Street fighter II'
|
|
if (rom_size >= 512)
|
|
{
|
|
//Look for "NEC HE "
|
|
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;
|
|
}
|
|
}
|
|
|
|
return rom_size;
|
|
}
|
|
|
|
/* Must be address_start and address_end should be 512 byte aligned */
|
|
void read_bank_PCE(uint32_t address_start, uint32_t address_end, uint32_t *processed_size, uint32_t total_size)
|
|
{
|
|
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);
|
|
}
|
|
myFile.write(sdBuffer, 512);
|
|
*processed_size += 512;
|
|
draw_progressbar(*processed_size, total_size);
|
|
}
|
|
}
|
|
|
|
//Get line from file and convert upper case to lower case
|
|
void skip_line(SdFile* readfile)
|
|
{
|
|
int i = 0;
|
|
char str_buf;
|
|
|
|
while (readfile->available())
|
|
{
|
|
//Read 1 byte from file
|
|
str_buf = readfile->read();
|
|
|
|
//if end of file or newline found, execute command
|
|
if (str_buf == '\r')
|
|
{
|
|
readfile->read(); //dispose \n because \r\n
|
|
break;
|
|
}
|
|
i++;
|
|
}//End while
|
|
}
|
|
|
|
//Get line from file and convert upper case to lower case
|
|
void get_line(char* str_buf, SdFile* readfile, uint8_t maxi)
|
|
{
|
|
int i = 0;
|
|
|
|
while (readfile->available())
|
|
{
|
|
//If line size is more than maximum array, limit it.
|
|
if (i >= maxi)
|
|
{
|
|
i = maxi - 1;
|
|
}
|
|
|
|
//Read 1 byte from file
|
|
str_buf[i] = readfile->read();
|
|
|
|
//if end of file or newline found, execute command
|
|
if (str_buf[i] == '\r')
|
|
{
|
|
str_buf[i] = '\0';
|
|
readfile->read(); //dispose \n because \r\n
|
|
break;
|
|
}
|
|
i++;
|
|
}//End while
|
|
}
|
|
|
|
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)
|
|
{
|
|
SdFile rom, script;
|
|
uint32_t r, crc, processedsize;
|
|
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_CRC_LIST.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 = 0xFFFFFFFFUL; //Initialize CRC
|
|
display_Clear();
|
|
println_Msg(F("Calculating chksum..."));
|
|
processedsize = 0;
|
|
draw_progressbar(0, rom_size * 1024UL); //Initialize progress bar
|
|
|
|
while (rom.available())
|
|
{
|
|
r = rom.read(sdBuffer, 512);
|
|
crc = calculate_crc32(r, sdBuffer, crc);
|
|
processedsize += r;
|
|
draw_progressbar(processedsize, rom_size * 1024UL);
|
|
}
|
|
|
|
crc = crc ^ 0xFFFFFFFFUL; //Finish CRC calculation and progress bar
|
|
draw_progressbar(rom_size * 1024UL, rom_size * 1024UL);
|
|
|
|
//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);
|
|
print_Msg(F(".pce"));
|
|
flag = CHKSUM_OK;
|
|
strcat(gamename, ".pce");
|
|
rom.rename(sd.vwd(), 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 read_tennokoe_bank_PCE(void)
|
|
{
|
|
uint32_t processed_size = 0;
|
|
uint32_t verify_loop;
|
|
uint8_t verify_flag = 1;
|
|
|
|
//clear the screen
|
|
display_Clear();
|
|
|
|
println_Msg(F("RAM size: 8KB"));
|
|
|
|
// Get name, add extension and convert to char array for sd lib
|
|
strcpy(fileName, "BANKRAM");
|
|
strcat(fileName, ".sav");
|
|
|
|
// create a new folder for the save file
|
|
EEPROM_readAnything(10, foldern);
|
|
sprintf(folder, "PCE/ROM/%s/%d", romName, foldern);
|
|
sd.mkdir(folder, true);
|
|
sd.chdir(folder);
|
|
|
|
println_Msg(F("Saving RAM..."));
|
|
display_Update();
|
|
|
|
// write new folder number back to eeprom
|
|
foldern = foldern + 1;
|
|
EEPROM_writeAnything(10, foldern);
|
|
|
|
//open file on sd card
|
|
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
|
|
print_Error(F("Can't create file on SD"), true);
|
|
}
|
|
|
|
pin_read_write_PCE();
|
|
|
|
//Initialize progress bar by setting processed size as 0
|
|
draw_progressbar(0, 8 * 1024UL);
|
|
|
|
//Unlock Tennokoe Bank RAM
|
|
write_byte_PCE(0x0D0000, 0x68); //Unlock RAM sequence 1 Bank 68
|
|
write_byte_PCE(0x0F0000, 0x0); //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
|
|
|
|
//Read Tennokoe bank RAM
|
|
read_bank_PCE(0x080000, 0x080000 + 8 * 1024UL, &processed_size, 8 * 1024UL);
|
|
|
|
myFile.seekSet(0); // Go back to file beginning
|
|
processed_size = 0;
|
|
|
|
//Verify Tennokoe bank RAM
|
|
for (verify_loop = 0; verify_loop < 8 * 1024UL; verify_loop++)
|
|
{
|
|
if (myFile.read() != read_byte_PCE(verify_loop + 0x080000))
|
|
{
|
|
verify_flag = 0;
|
|
draw_progressbar(8 * 1024UL, 8 * 1024UL);
|
|
break;
|
|
}
|
|
draw_progressbar(verify_loop, 8 * 1024UL);
|
|
}
|
|
|
|
//If verify flag is 0, verify failed
|
|
if (verify_flag == 1)
|
|
{
|
|
println_Msg(F("Verify OK..."));
|
|
}
|
|
else
|
|
{
|
|
println_Msg(F("Verify failed..."));
|
|
}
|
|
|
|
//Lock Tennokoe Bank RAM
|
|
write_byte_PCE(0x0D0000, 0x68); //Lock RAM sequence 1 Bank 68
|
|
write_byte_PCE(0x0F0001, 0x0); //Lock RAM sequence 2 Bank 78
|
|
write_byte_PCE(0x0C0001, 0x60); //Lock RAM sequence 3 Bank 60
|
|
|
|
pin_init_PCE();
|
|
|
|
//Close the file:
|
|
myFile.close();
|
|
|
|
}
|
|
|
|
void write_tennokoe_bank_PCE(void)
|
|
{
|
|
uint32_t readwrite_loop, verify_loop;
|
|
uint32_t verify_flag = 1;
|
|
|
|
//Display file Browser and wait user to select a file. Size must be 8KB.
|
|
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 != 8 * 1024UL) {
|
|
println_Msg(F("File must be 1MB"));
|
|
display_Update();
|
|
myFile.close();
|
|
wait();
|
|
return;
|
|
}
|
|
|
|
pin_read_write_PCE();
|
|
|
|
//Unlock Tennokoe Bank RAM
|
|
write_byte_PCE(0x0D0000, 0x68); //Unlock RAM sequence 1 Bank 68
|
|
write_byte_PCE(0x0F0000, 0x0); //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
|
|
|
|
//Write file to Tennokoe BANK RAM
|
|
for (readwrite_loop = 0; readwrite_loop < 8 * 1024UL; readwrite_loop++)
|
|
{
|
|
write_byte_PCE(0x080000 + readwrite_loop, myFile.read());
|
|
draw_progressbar(readwrite_loop, 8 * 1024UL);
|
|
}
|
|
|
|
myFile.seekSet(0); // Go back to file beginning
|
|
|
|
for (verify_loop = 0; verify_loop < 8 * 1024UL; verify_loop++)
|
|
{
|
|
if (myFile.read() != read_byte_PCE(verify_loop + 0x080000))
|
|
{
|
|
draw_progressbar(2 * 1024UL, 8 * 1024UL);
|
|
verify_flag = 0;
|
|
break;
|
|
}
|
|
draw_progressbar(verify_loop, 8 * 1024UL);
|
|
}
|
|
|
|
//If verify flag is 0, verify failed
|
|
if (verify_flag == 1)
|
|
{
|
|
println_Msg(F("Verify OK..."));
|
|
}
|
|
else
|
|
{
|
|
println_Msg(F("Verify failed..."));
|
|
}
|
|
|
|
//Lock Tennokoe Bank RAM
|
|
write_byte_PCE(0x0D0000, 0x68); //Lock RAM sequence 1 Bank 68
|
|
write_byte_PCE(0x0F0001, 0x0); //Lock RAM sequence 2 Bank 78
|
|
write_byte_PCE(0x0C0001, 0x60); //Lock RAM sequence 3 Bank 60
|
|
|
|
pin_init_PCE();
|
|
|
|
// Close the file:
|
|
myFile.close();
|
|
println_Msg(F("Finished"));
|
|
display_Update();
|
|
wait();
|
|
}
|
|
else {
|
|
print_Error(F("File doesn't exist"), false);
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
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(10, foldern);
|
|
sprintf(folder, "PCE/ROM/%s/%d", romName, foldern);
|
|
sd.mkdir(folder, true);
|
|
sd.chdir(folder);
|
|
|
|
println_Msg(F("Saving ROM..."));
|
|
display_Update();
|
|
|
|
// write new folder number back to eeprom
|
|
foldern = foldern + 1;
|
|
EEPROM_writeAnything(10, foldern);
|
|
|
|
//open file on sd card
|
|
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
|
|
print_Error(F("Can't create file on SD"), true);
|
|
}
|
|
|
|
pin_read_write_PCE();
|
|
|
|
//Initialize progress bar by setting processed size as 0
|
|
draw_progressbar(0, rom_size * 1024UL);
|
|
|
|
if (rom_size == 384)
|
|
{
|
|
//Read two sections. 0x000000--0x040000 and 0x080000--0x0A0000 for 384KB
|
|
read_bank_PCE(0, 0x40000, &processed_size, rom_size * 1024UL);
|
|
read_bank_PCE(0x80000, 0xA0000, &processed_size, rom_size * 1024UL);
|
|
}
|
|
else if (rom_size == 2560)
|
|
{
|
|
//Dump Street fighter II' Champion Edition
|
|
read_bank_PCE(0, 0x80000, &processed_size, rom_size * 1024UL); //Read first bank
|
|
write_byte_PCE(0x1FF0, 0xFF); //Display second bank
|
|
read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL); //Read second bank
|
|
write_byte_PCE(0x1FF1, 0xFF); //Display third bank
|
|
read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL); //Read third bank
|
|
write_byte_PCE(0x1FF2, 0xFF); //Display forth bank
|
|
read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL); //Read forth bank
|
|
write_byte_PCE(0x1FF3, 0xFF); //Display fifth bank
|
|
read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL); //Read fifth bank
|
|
}
|
|
else
|
|
{
|
|
//Read start form 0x000000 and keep reading until end of ROM
|
|
read_bank_PCE(0, rom_size * 1024UL, &processed_size, rom_size * 1024UL);
|
|
}
|
|
|
|
pin_init_PCE();
|
|
|
|
//Close the file:
|
|
myFile.close();
|
|
|
|
//CRC search and rename ROM
|
|
crc_search(fileName, folder, rom_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PC Engine Menu
|
|
void pceMenu() {
|
|
// create menu with title and 7 options to choose from
|
|
unsigned char mainMenu;
|
|
|
|
if (pce_internal_mode == HUCARD)
|
|
{
|
|
// Copy menuOptions out of progmem
|
|
convertPgm(menuOptionspceCart, 4);
|
|
mainMenu = question_box(F("PCE HuCARD menu"), menuOptions, 4, 0);
|
|
|
|
// wait for user choice to come back from the question box menu
|
|
switch (mainMenu)
|
|
{
|
|
case 0:
|
|
display_Clear();
|
|
// Change working dir to root
|
|
sd.chdir("/");
|
|
read_rom_PCE();
|
|
break;
|
|
case 1:
|
|
display_Clear();
|
|
read_tennokoe_bank_PCE();
|
|
break;
|
|
case 2:
|
|
display_Clear();
|
|
write_tennokoe_bank_PCE();
|
|
break;
|
|
case 3:
|
|
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:
|
|
display_Clear();
|
|
// Change working dir to root
|
|
sd.chdir("/");
|
|
read_rom_PCE();
|
|
break;
|
|
|
|
case 1:
|
|
resetArduino();
|
|
break;
|
|
}
|
|
}
|
|
|
|
println_Msg(F(""));
|
|
println_Msg(F("Press Button..."));
|
|
display_Update();
|
|
wait();
|
|
}
|
|
|
|
|
|
//******************************************
|
|
// End of File
|
|
//******************************************
|