cartreader/Cart_Reader/SUPRACAN.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

649 lines
14 KiB
C++

//******************************************
// Super A'can MODULE
// Only tested with HW3 and HW5
//******************************************
#ifdef enable_SUPRACAN
/******************************************
Menu
*****************************************/
static const char acanMenuItem1[] PROGMEM = "Read Rom";
static const char acanMenuItem2[] PROGMEM = "Read Save";
static const char acanMenuItem3[] PROGMEM = "Write Save";
static const char acanMenuItem4[] PROGMEM = "Read UM6650";
static const char acanMenuItem5[] PROGMEM = "Write UM6650";
static const char acanMenuItem6[] PROGMEM = "Flash repro";
static const char *const menuOptionsAcan[] PROGMEM = { acanMenuItem1, acanMenuItem2, acanMenuItem3, acanMenuItem4, acanMenuItem5, string_reset2, acanMenuItem6 };
void setup_SuprAcan() {
// Request 5V
setVoltage(VOLTS_SET_5V);
// addr as output
DDRF = 0xff; // A0 - A7
DDRK = 0xff; // A8 - A15
DDRL = 0xff; // A16 - A23
// data as input
DDRC = 0xff;
DDRA = 0xff;
PORTC = 0x00; // disable internal pull-up
PORTA = 0x00;
DDRC = 0x00; // D0 - D7
DDRA = 0x00; // D8 - D15
// set /RST(PH0), /CS(PH3), C27(PH4), R/W(PH5), /AS(PH6) output
DDRH |= ((1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6));
PORTH |= ((1 << 3) | (1 << 5) | (1 << 6));
PORTH &= ~((1 << 0) | (1 << 4));
// set 6619_124(PE4) input
DDRE &= ~(1 << 4);
// detect if flash chip exists
PORTG |= (1 << 5);
DDRG |= (1 << 5);
PORTG |= (1 << 5);
dataOut_MD();
writeWord_Acan(0xaaaa, 0xaaaa);
writeWord_Acan(0x5555, 0x5555);
writeWord_Acan(0xaaaa, 0x9090);
dataIn_MD();
eepbit[0] = readWord_Acan(0x2);
eepbit[1] = readWord_Acan(0x0);
dataOut_MD();
writeWord_Acan(0x0, 0xf0f0);
dataIn_MD();
// set /CARTIN(PG5) input
PORTG &= ~(1 << 5);
DDRG &= ~(1 << 5);
*((uint32_t *)(eepbit + 4)) = getFlashChipSize_Acan(*((uint16_t *)eepbit));
display_Clear();
initializeClockOffset();
// clockgen
if (i2c_found) {
clockgen.set_freq(1073863500ULL, SI5351_CLK1); // cpu
clockgen.set_freq(357954500ULL, SI5351_CLK2); // subcarrier
clockgen.set_freq(5369317500ULL, SI5351_CLK0); // master clock
clockgen.output_enable(SI5351_CLK1, 1);
clockgen.output_enable(SI5351_CLK2, 1);
clockgen.output_enable(SI5351_CLK0, 0);
// Wait for clock generator
clockgen.update_status();
delay(500);
}
#ifdef clockgen_installed
else {
print_FatalError(F("Clock Generator not found"));
}
#endif
checkRomExist_Acan();
wait();
}
void suprAcanMenu() {
uint8_t mainMenu = 6;
if (*((uint32_t *)(eepbit + 4)) > 0)
mainMenu = 7;
convertPgm(menuOptionsAcan, mainMenu);
mainMenu = question_box(F("Super A'can Menu"), menuOptions, mainMenu, 0);
switch (mainMenu) {
case 0:
{
readROM_Acan();
break;
}
case 1:
{
readSRAM_Acan();
break;
}
case 2:
{
writeSRAM_Acan();
verifySRAM_Acan();
break;
}
case 3:
{
readUM6650();
break;
}
case 4:
{
writeUM6650();
verifyUM6650();
break;
}
case 6:
{
flashCart_Acan();
break;
}
default:
{
resetCart_Acan();
resetArduino();
break;
}
}
println_Msg(F(""));
print_STR(press_button_STR, 1);
display_Update();
wait();
}
static void readROM_Acan() {
uint32_t crc32 = 0xffffffff;
EEPROM_readAnything(0, foldern);
snprintf(folder, FILEPATH_LENGTH, "/ACAN/ROM/%d", foldern);
display_Clear();
print_STR(saving_to_STR, 0);
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
sd.mkdir(folder, true);
sd.chdir(folder);
if (!myFile.open("rom.bin", O_RDWR | O_CREAT))
print_FatalError(create_file_STR);
foldern++;
EEPROM_writeAnything(0, foldern);
draw_progressbar(0, cartSize);
dataIn_MD();
for (uint32_t addr = 0; addr < cartSize; addr += 512, draw_progressbar(addr, cartSize)) {
for (uint32_t i = 0; i < 512; i += 2) {
*((uint16_t *)(sdBuffer + i)) = readWord_Acan(addr + i);
UPDATE_CRC(crc32, sdBuffer[i]);
UPDATE_CRC(crc32, sdBuffer[i + 1]);
}
myFile.write(sdBuffer, 512);
if ((addr & ((1 << 14) - 1)) == 0)
blinkLED();
}
crc32 = ~crc32;
myFile.close();
print_Msg(F("CRC32: "));
print_Msg_PaddedHex32(crc32);
println_Msg(F(""));
print_STR(done_STR, 1);
}
static void readSRAM_Acan() {
// create a new folder for storing rom file
EEPROM_readAnything(0, foldern);
snprintf(folder, FILEPATH_LENGTH, "/ACAN/SAVE/%d", foldern);
display_Clear();
print_STR(saving_to_STR, 0);
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
sd.mkdir(folder, true);
sd.chdir(folder);
if (!myFile.open("save.bin", O_RDWR | O_CREAT))
print_FatalError(create_file_STR);
foldern++;
EEPROM_writeAnything(0, foldern);
dataIn_MD();
for (uint32_t i = 0; i < 0x10000; i += 1024) {
for (uint32_t j = 0; j < 1024; j += 2)
sdBuffer[(j >> 1)] = readWord_Acan(0xec0000 + i + j);
myFile.write(sdBuffer, 512);
}
myFile.close();
print_STR(done_STR, 1);
}
static void writeSRAM_Acan() {
filePath[0] = 0;
sd.chdir();
fileBrowser(F("Select a file"));
snprintf(filePath, FILEPATH_LENGTH, "%s/%s", filePath, fileName);
display_Clear();
if (!myFile.open(filePath, O_READ)) {
print_Error(F("File doesn't exist"));
return;
}
print_Msg(F("Writing "));
print_Msg(filePath);
println_Msg(F("..."));
display_Update();
dataOut_MD();
for (uint32_t i = 0; i < 0x10000 && myFile.available(); i += 1024) {
myFile.read(sdBuffer, 512);
for (uint32_t j = 0; j < 1024; j += 2)
writeWord_Acan(0xec0000 + i + j, sdBuffer[(j >> 1)]);
}
myFile.close();
dataIn_MD();
print_STR(done_STR, 1);
}
static void verifySRAM_Acan() {
print_STR(verifying_STR, 0);
display_Update();
if (!myFile.open(filePath, O_READ)) {
print_Error(F("File doesn't exist"));
return;
}
uint16_t write_errors = 0;
dataIn_MD();
for (uint32_t i = 0; i < 0x10000 && myFile.available(); i += 1024) {
myFile.read(sdBuffer, 512);
for (uint32_t j = 0; j < 1024; j += 2) {
if (readWord_Acan(0xec0000 + i + j) != sdBuffer[(j >> 1)])
write_errors++;
}
}
myFile.close();
if (write_errors == 0) {
println_Msg(F("passed"));
} else {
println_Msg(F("failed"));
print_Msg(F("Error: "));
print_Msg(write_errors);
println_Msg(F(" bytes "));
print_Error(did_not_verify_STR);
}
}
static void readUM6650() {
// create a new folder for storing rom file
EEPROM_readAnything(0, foldern);
snprintf(folder, sizeof(folder), "/ACAN/UM6650/%d", foldern);
display_Clear();
print_STR(saving_to_STR, 0);
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
sd.mkdir(folder, true);
sd.chdir(folder);
if (!myFile.open("UM6650.bin", O_RDWR | O_CREAT))
print_FatalError(create_file_STR);
foldern++;
EEPROM_writeAnything(0, foldern);
for (uint16_t i = 0; i < 256; i++) {
dataOut_MD();
writeWord_Acan(0xeb0d03, i);
dataIn_MD();
sdBuffer[i] = readWord_Acan(0xeb0d01);
}
myFile.write(sdBuffer, 256);
myFile.close();
print_STR(done_STR, 1);
}
static void verifyUM6650() {
print_STR(verifying_STR, 0);
display_Update();
if (!myFile.open(filePath, O_READ)) {
print_Error(F("File doesn't exist"));
return;
}
uint16_t write_errors = 0;
uint16_t len = myFile.read(sdBuffer, 256);
myFile.close();
for (uint16_t i = 0; i < len; i++) {
dataOut_MD();
writeWord_Acan(0xeb0d03, i);
dataIn_MD();
if (readWord_Acan(0xeb0d01) != sdBuffer[i])
write_errors++;
}
if (write_errors) {
println_Msg(F("failed"));
print_Msg(F("Error: "));
print_Msg(write_errors);
println_Msg(F(" bytes "));
print_Error(did_not_verify_STR);
} else {
println_Msg(F("passed"));
}
}
static void writeUM6650() {
filePath[0] = 0;
sd.chdir("/");
fileBrowser(F("Select a file"));
snprintf(filePath, FILEPATH_LENGTH, "%s/%s", filePath, fileName);
display_Clear();
if (!myFile.open(filePath, O_READ)) {
print_Error(F("File doesn't exist"));
return;
}
uint16_t len = myFile.read(sdBuffer, 256);
myFile.close();
print_Msg(F("Writing "));
print_Msg(filePath);
println_Msg(F("..."));
display_Update();
dataOut_MD();
for (uint16_t i = 0; i < len; i++) {
writeWord_Acan(0xeb0d03, i);
writeWord_Acan(0xeb0d01, sdBuffer[i]);
delay(10); // for AT28C64B write
}
dataIn_MD();
print_STR(done_STR, 1);
}
static void flashCart_Acan() {
uint32_t *flash_size = (uint32_t *)(eepbit + 4);
filePath[0] = 0;
sd.chdir();
fileBrowser(F("Select a file"));
snprintf(filePath, FILEPATH_LENGTH, "%s/%s", filePath, fileName);
display_Clear();
if (!myFile.open(filePath, O_READ)) {
print_Error(F("File doesn't exist"));
return;
}
print_Msg(F("Writing "));
print_Msg(filePath + 1);
println_Msg(F("..."));
display_Update();
uint32_t i, j, k, file_length = myFile.fileSize();
uint16_t data;
DDRG |= (1 << 5);
PORTG |= (1 << 5);
draw_progressbar(0, file_length);
for (i = 0; i < file_length; i += *flash_size) {
// erase chip
dataOut_MD();
writeWord_Acan(i + 0xaaaa, 0xaaaa);
writeWord_Acan(i + 0x5555, 0x5555);
writeWord_Acan(i + 0xaaaa, 0x8080);
writeWord_Acan(i + 0xaaaa, 0xaaaa);
writeWord_Acan(i + 0x5555, 0x5555);
writeWord_Acan(i + 0xaaaa, 0x1010);
dataIn_MD();
while (readWord_Acan(i) != 0xffff)
;
for (j = 0; j < *flash_size; j += 512) {
myFile.read(sdBuffer, 512);
for (k = 0; k < 512; k += 2) {
data = *((uint16_t *)(sdBuffer + k));
dataOut_MD();
writeWord_Acan(i + 0xaaaa, 0xaaaa);
writeWord_Acan(i + 0x5555, 0x5555);
writeWord_Acan(i + 0xaaaa, 0xa0a0);
writeWord_Acan(i + j + k, data);
dataIn_MD();
while (readWord_Acan(i + j + k) != data)
;
}
draw_progressbar(i + j + k, file_length);
if ((j & 0xfff) == 0)
blinkLED();
}
}
PORTG &= ~(1 << 5);
DDRG &= ~(1 << 5);
myFile.close();
print_STR(done_STR, 1);
checkRomExist_Acan();
}
static void checkRomExist_Acan() {
// RST to 0
PORTH &= ~(1 << 0);
NOP;
NOP;
NOP;
NOP;
NOP;
NOP;
// /RST to 1
PORTH |= (1 << 0);
cartSize = getRomSize_Acan();
romSize = cartSize >> 17;
mode = mode_SUPRACAN;
if (cartSize == 0)
print_Error(F("Unable to find rom signature..."));
else {
print_Msg(F("ROM Size: "));
print_Msg(romSize);
println_Msg(F(" Mb"));
}
display_Update();
}
static uint32_t getRomSize_Acan() {
uint32_t addr = 0;
uint32_t crc32;
do {
// check if there is rom chip exists
// pull-up enable
DDRC = 0xff;
DDRA = 0xff;
PORTC = 0xff;
PORTA = 0xff;
DDRC = 0x00;
DDRA = 0x00;
*((uint16_t *)sdBuffer) = readWord_Acan(addr);
// pull-up disable
DDRC = 0xff;
DDRA = 0xff;
PORTC = 0x00;
PORTA = 0x00;
DDRC = 0x00;
DDRA = 0x00;
*((uint16_t *)(sdBuffer + 2)) = readWord_Acan(addr);
// should be them same if chip exists
if (sdBuffer[0] != sdBuffer[2] || sdBuffer[1] != sdBuffer[3])
break;
crc32 = 0xffffffff;
for (uint32_t i = 0x2000; i < 0x2080; i += 2) {
*((uint16_t *)sdBuffer) = readWord_Acan(addr + i);
UPDATE_CRC(crc32, sdBuffer[0]);
UPDATE_CRC(crc32, sdBuffer[1]);
}
crc32 = ~crc32;
if (crc32 == 0xa2bc9d7a) {
if (addr > 0)
break;
} else {
if (addr == 0)
break;
}
addr += 0x80000;
} while (addr < 0x800000);
return addr;
}
static void resetCart_Acan() {
// set /CS(PH3), R/W(PH5), /AS(PH6) high
// /RST(PH0) and C27(PH4) low
PORTH |= ((1 << 3) | (1 << 5) | (1 << 6));
PORTH &= ~((1 << 0) | (1 << 4));
if (i2c_found) {
clockgen.output_enable(SI5351_CLK1, 0); // CPU clock
clockgen.output_enable(SI5351_CLK2, 0); // CIC clock
clockgen.output_enable(SI5351_CLK0, 0); // master clock
}
}
static void writeWord_Acan(uint32_t addr, uint16_t data) {
uint8_t *ptr = (uint8_t *)&addr;
PORTF = *ptr++;
PORTK = *ptr++;
PORTL = *ptr;
if (*ptr < 0xe0) {
// ROM area
// /CS(PH3), C27(PH4), R/W(PH5), /AS(PH6) to L
PORTH &= ~((1 << 3) | (1 << 4) | (1 << 5) | (1 << 6));
} else if (*ptr == 0xec) {
// save area
// /CS(PH3) to H, C27(PH4), R/W(PH5), /AS(PH6) to L
PORTH |= (1 << 3);
PORTH &= ~((1 << 4) | (1 << 5) | (1 << 6));
} else if (addr == 0x00eb0d03 || addr == 0x00eb0d01) {
// UM6650 area
// /CS(PH3), C27(PH4) to H, R/W(PH5), /AS(PH6) to L
PORTH |= ((1 << 3) | (1 << 4));
PORTH &= ~((1 << 5) | (1 << 6));
}
ptr = (uint8_t *)&data;
PORTC = *ptr++;
PORTA = *ptr;
NOP;
NOP;
NOP;
PORTH &= ~(1 << 4);
PORTH |= ((1 << 3) | (1 << 5) | (1 << 6));
}
static uint16_t readWord_Acan(uint32_t addr) {
uint8_t *ptr = (uint8_t *)&addr;
uint16_t data;
PORTF = *ptr++;
PORTK = *ptr++;
PORTL = *ptr;
if (*ptr < 0xe0) {
// ROM area
// /CS(PH3), C27(PH4), /AS(PH6) to L
PORTH &= ~((1 << 3) | (1 << 4) | (1 << 6));
} else if (*ptr == 0xec) {
// save area
// /CS(PH3) to H, C27(PH4), /AS(PH6) to L
PORTH |= (1 << 3);
PORTH &= ~((1 << 4) | (1 << 6));
} else if (addr == 0x00eb0d03 || addr == 0x00eb0d01) {
// UM6650 area
// /CS(PH3), C27(PH4) to H, /AS(PH6) to L
PORTH |= ((1 << 3) | (1 << 4));
PORTH &= ~(1 << 6);
}
ptr = (uint8_t *)&data;
NOP;
NOP;
NOP;
*ptr++ = PINC;
*ptr = PINA;
PORTH &= ~(1 << 4);
PORTH |= ((1 << 3) | (1 << 5) | (1 << 6));
return data;
}
static uint32_t getFlashChipSize_Acan(uint16_t chip_id) {
// 0x0458 (8M), 0x01ab (4M), 0x01d8 (16M)
switch (chip_id) {
case 0x01ab: return 524288;
case 0x0458: return 1048576;
case 0x01d8: return 2097152;
}
return 0;
}
#endif