mirror of
https://github.com/sanni/cartreader.git
synced 2024-12-30 15:01:53 +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.
649 lines
14 KiB
C++
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
|