Skip to content

Commit

Permalink
Merge pull request #977 from partlyhuman/pce-tg16-flashing
Browse files Browse the repository at this point in the history
Adds support for flashing reproduction HuCards
  • Loading branch information
sanni authored Jun 30, 2024
2 parents 0e48720 + ae52422 commit c11690b
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 15 deletions.
2 changes: 1 addition & 1 deletion Cart_Reader/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@

/****/

/* [ Flashrom Programmer for SNES repros -------------------------- ]
/* [ Flashrom Programmer for repro carts -------------------------- ]
*/

#define ENABLE_FLASH
Expand Down
220 changes: 206 additions & 14 deletions Cart_Reader/PCE.ino
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,30 @@
// 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
// June 29th 2024 Revision 1.0.3 Added repro HuCard flashing
//
// Special thanks
// sanni - Arduino cart reader
// skaman - ROM size detection
// NO-INTRO - CRC list for game name detection
// Chris Covell - Tennokoe bank support
// partlyhuman - repro HuCard 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
enum PCE_MODE_T : uint8_t { HUCARD,
TURBOCHIP,
HUCARD_NOSWAP,
PCE_FLASH };

/******************************************
Prototype Declarations
Expand Down Expand Up @@ -53,7 +56,8 @@ uint8_t tennokoe_bank_index = 0;
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, FSTRING_RESET };
static const char pceMenuItem4[] PROGMEM = "HuCARD Flash Repro";
static const char *const menuOptionspce[] PROGMEM = { pceMenuItem1, pceMenuItem2, pceMenuItem3, pceMenuItem4, FSTRING_RESET };

// PCE card menu items
static const char menuOptionspceCart_1[] PROGMEM = "Read RAM Bank %d";
Expand All @@ -66,15 +70,18 @@ static const char menuOptionspceCart_5_fmt[] PROGMEM = "ROM size now %dK";
// Turbochip menu items
static const char *const menuOptionspceTC[] PROGMEM = { FSTRING_READ_ROM, FSTRING_RESET };

// PCE start menu
#ifdef ENABLE_FLASH
// Flash repro menu items
// 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
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);
convertPgm(menuOptionspce, 4);
pceDev = question_box(F("Select device"), menuOptions, 4, 0);

// wait for user choice to come back from the question box menu
switch (pceDev) {
case 0:
//Hucard
Expand Down Expand Up @@ -103,9 +110,23 @@ void pcsMenu(void) {
mode = CORE_PCE;
break;

#ifdef ENABLE_FLASH
case 3:
//Flash Repro
display_Clear();
display_Update();
pce_internal_mode = PCE_FLASH;
setup_cart_PCE();
mode = CORE_PCE;
break;
#endif

case 4:
resetArduino();
break;

default:
print_MissingModule(); // does not return
}
}

Expand Down Expand Up @@ -228,7 +249,7 @@ uint8_t read_byte_PCE(uint32_t address) {
ret = PINC;

//Swap bit order for PC Engine HuCARD
if (pce_internal_mode == HUCARD) {
if (pce_internal_mode == HUCARD || pce_internal_mode == PCE_FLASH) {
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);
}

Expand Down Expand Up @@ -273,7 +294,7 @@ void write_byte_PCE(uint32_t address, uint8_t data) {
"nop\n\t");

//Swap bit order for PC Engine HuCARD
if (pce_internal_mode == HUCARD) {
if (pce_internal_mode == HUCARD || pce_internal_mode == PCE_FLASH) {
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);
}

Expand Down Expand Up @@ -803,10 +824,161 @@ void read_rom_PCE(void) {
crc_search(fileName, folder, rom_size, crc);

println_Msg(FS(FSTRING_EMPTY));
print_STR(press_button_STR, 1);
print_STR(press_button_STR, true);
display_Update();
wait();
}

#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
// We only look at D7, or the highest bit of expected
void flash_wait_status_PCE(uint8_t expected) {
set_cs_rd_low_PCE();
data_input_PCE();

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 >> 7));

data_output_PCE();
// leave RD high on exit
}

// Flashes a reproduction HuCard that's directly wired to a flash chip
// Supported flash: SST39SF0x0, MX29F0x0 1Mbit-8Mbit
// Developed against Ichigobankai's design https://github.com/partlyhuman/HuCARD-repro
void flash_PCE() {
println_Msg(F("Detecting..."));
display_Update();

// 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;
// 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:
// SST39SF010 = 1Mbit
flashSize = 131072UL;
break;
case 0xBFB6:
// SST39SF020 = 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;
}

if (flashSize <= 0) {
print_Msg(F("UNKNOWN "));
println_Msg(deviceId);
display_Update();
wait();
resetArduino();
return;
} else {
print_Msg(FS(FSTRING_SIZE));
print_Msg(flashSize / 131072UL);
println_Msg(F("Mbit"));
display_Update();
wait();
}

filePath[0] = '\0';
sd.chdir("/");
fileBrowser(FS(FSTRING_SELECT_FILE));
display_Clear();

if (openFlashFile()) {

println_Msg(F("Erasing..."));
display_Update();

// CHIP ERASE PROGRAM
flash_mode_PCE();
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);

print_STR(flashing_file_STR, true);
display_Update();
uint32_t processedProgressBar = 0;
uint32_t totalProgressBar = (uint32_t)fileSize;
draw_progressbar(0, totalProgressBar);

flash_mode_PCE();
const size_t BUFSIZE = 512;
for (unsigned long currAddr = 0; currAddr < fileSize; currAddr += BUFSIZE) {
myFile.read(sdBuffer, BUFSIZE);

if (currAddr % 4096 == 0) {
blinkLED();
}

for (int currByte = 0; currByte < BUFSIZE; currByte++) {
// BYTE PROGRAM
byte b = sdBuffer[currByte];
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);
}

// update progress bar
processedProgressBar += BUFSIZE;
draw_progressbar(processedProgressBar, totalProgressBar);
}
myFile.close();
pin_init_PCE();
print_STR(done_STR, true);
}
display_Update();
wait();
}
#endif

// PC Engine Menu
void pceMenu() {
Expand Down Expand Up @@ -861,7 +1033,7 @@ void pceMenu() {
resetArduino();
break;
}
} else {
} else if (pce_internal_mode == TURBOCHIP) {
// Copy menuOptions out of progmem
convertPgm(menuOptionspceTC, 2);
mainMenu = question_box(F("TG TurboChip menu"), menuOptions, 2, 0);
Expand All @@ -877,6 +1049,26 @@ void pceMenu() {
break;
}
}
#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:
flash_PCE();
break;

case 1:
resetArduino();
break;
}
}
#endif
else {
print_MissingModule(); // does not return
}
}

#endif
Expand Down
5 changes: 5 additions & 0 deletions Cart_Reader/SFM.ino
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,19 @@ void sfmMenu() {
case 0:
sfmGameMenu();
break;
#ifdef CORE_SFM_FLASH
// Flash menu
case 1:
mode = CORE_SFM_FLASH;
break;
#endif
// Reset
case 2:
resetArduino();
break;
default:
print_MissingModule();
break;
}
}

Expand Down

0 comments on commit c11690b

Please sign in to comment.