cartreader/Cart_Reader/NGP.ino
2021-10-26 17:13:42 +02:00

374 lines
9.0 KiB
C++

//******************************************
// NGP MODULE
//******************************************
#include "options.h"
#ifdef enable_NGP
static const char ngpMenuItem1[] PROGMEM = "Read Rom";
static const char ngpMenuItem2[] PROGMEM = "Read chip info";
static const char ngpMenuItemReset[] PROGMEM = "Reset";
static const char* const menuOptionsNGP[] PROGMEM = {ngpMenuItem1, ngpMenuItem2, ngpMenuItemReset};
static const char ngpRomItem1[] PROGMEM = "4 Mbits / 512 KB";
static const char ngpRomItem2[] PROGMEM = "8 Mbits / 1 MB";
static const char ngpRomItem3[] PROGMEM = "16 Mbits / 2 MB";
static const char ngpRomItem4[] PROGMEM = "32 Mbits / 4 MB";
static const char* const ngpRomOptions[] PROGMEM = {ngpRomItem1, ngpRomItem2, ngpRomItem3, ngpRomItem4};
char ngpRomVersion[3];
uint8_t ngpSystemType;
uint8_t manufacturerID;
uint8_t deviceID;
void setup_NGP() {
// A0 - A7
DDRF = 0xff;
// A8 - A15
DDRK = 0xff;
// A16 - A20
DDRL = 0xff;
// D0 - D7
DDRC = 0x00;
// controls
// /CE0: PH3
// /CE1: PH0
// /OE: PH6
// /WE: PH5
// PWR: PH4
DDRH |= ((1 << 0) | (1 << 3) | (1 << 5) | (1 << 6));
DDRH &= ~(1 << 4);
PORTH |= ((1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6));
println_Msg(F("Initializing..."));
display_Update();
if (getCartInfo_NGP())
printCartInfo_NGP();
else
print_Error(F("Cartridge read error"), true);
}
void ngpMenu() {
uint8_t mainMenu;
convertPgm(menuOptionsNGP, 3);
mainMenu = question_box(F("NGP Menu"), menuOptions, 3, 0);
switch (mainMenu) {
case 0:
sd.chdir("/");
readROM_NGP(filePath, FILEPATH_LENGTH);
sd.chdir("/");
break;
case 1:
scanChip_NGP();
break;
case 2:
resetArduino();
break;
}
println_Msg(F(""));
println_Msg(F("Press Button..."));
display_Update();
wait();
}
bool getCartInfo_NGP() {
uint8_t *tmp;
// enter autoselect mode
dataOut();
writeByte_NGP(0x555, 0xaa);
writeByte_NGP(0x2aa, 0x55);
writeByte_NGP(0x555, 0x90);
dataIn();
cartSize = 0;
// get chip manufacturer and device IDs
manufacturerID = readByte_NGP(0);
deviceID = readByte_NGP(1);
tmp = (uint8_t*)&romSize;
*(tmp + 0) = deviceID;
*(tmp + 1) = manufacturerID;
switch (romSize) {
case 0xffff: return false; break; // detection error (no cart inserted or hw problem)
case 0x98ab: cartSize = 524288; break; // 4 Mbits - Toshiba
case 0x204c: cartSize = 524288; break; // 4 Mbits - STMicroelectronics ?
case 0x982c: cartSize = 1048576; break; // 8 Mbits - Toshiba
case 0xec2c: cartSize = 1048576; break; // 8 Mbits - Samsung
case 0x982f: cartSize = 2097152; break; // 16 Mbits - Toshiba
case 0xec2f: cartSize = 2097152; break; // 16 Mbits - Samsung
}
// reset to read mode
dataOut();
writeByte_NGP(0x0, 0xf0);
// confirm NGP cart recognition
dataIn();
for (uint32_t addr = 0; addr < 28; addr++)
sdBuffer[addr] = readByte_NGP(addr);
if (memcmp_P(sdBuffer, PSTR("COPYRIGHT BY SNK CORPORATION"), 28) != 0 && memcmp_P(sdBuffer, PSTR(" LICENSED BY SNK CORPORATION"), 28) != 0)
return false;
// get app ID
snprintf(cartID, 5, "%02X%02X", readByte_NGP(0x21), readByte_NGP(0x20));
// force rom size to 32 Mbits for few titles
if (strcmp(cartID, "0060") == 0 || strcmp(cartID, "0061") == 0 || strcmp(cartID, "0069") == 0 )
cartSize = 4194304;
// get app version
snprintf(ngpRomVersion, 3, "%02X", readByte_NGP(0x22));
// get app system compatibility
ngpSystemType = readByte_NGP(0x23);
// get app name
for (uint32_t i = 0; i < 17; i++)
romName[i] = readByte_NGP(0x24 + i);
return true;
}
void printCartInfo_NGP() {
display_Clear();
println_Msg(F("NGP Cart Info"));
print_Msg(F("Name: "));
println_Msg(romName);
print_Msg(F("ID: "));
println_Msg(cartID);
print_Msg(F("Version: "));
println_Msg(ngpRomVersion);
print_Msg(F("System: "));
if (ngpSystemType == 0x0)
println_Msg(F("NGPMonochrome"));
else if (ngpSystemType == 0x10)
println_Msg(F("NGPColor"));
else
println_Msg(F("Unknown"));
print_Msg(F("Rom Size: "));
if (cartSize == 0) {
println_Msg(F("Unknown"));
}
else {
print_Msg((cartSize >> 17));
println_Msg(F(" Mbits"));
}
println_Msg(F("Press Button..."));
display_Update();
wait();
}
void readROM_NGP(char *outPathBuf, size_t bufferSize) {
// Set cartsize manually if chip ID is unknown
if (cartSize == 0) {
unsigned char ngpRomMenu;
// Copy menuOptions out of progmem
convertPgm(ngpRomOptions, 4);
ngpRomMenu = question_box(F("Select ROM size"), menuOptions, 4, 0);
// wait for user choice to come back from the question box menu
switch (ngpRomMenu) {
case 0: cartSize = 524288; break;
case 1: cartSize = 1048576; break;
case 2: cartSize = 2097152; break;
case 3: cartSize = 4194304; break;
}
}
// generate fullname of rom file
snprintf(fileName, FILENAME_LENGTH, "%s.ngp", romName);
// create a new folder for storing rom file
EEPROM_readAnything(0, foldern);
snprintf(folder, sizeof(folder), "NGP/ROM/%s/%d", romName, foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
// filling output file path to buffer
if (outPathBuf != NULL && bufferSize > 0)
snprintf(outPathBuf, bufferSize, "%s/%s", folder, fileName);
display_Clear();
print_Msg(F("Saving to "));
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
// open file on sdcard
if (!myFile.open(fileName, O_RDWR | O_CREAT))
print_Error(F("Can't create file on SD"), true);
// write new folder number back to EEPROM
foldern++;
EEPROM_writeAnything(0, foldern);
// back to read mode
dataOut();
writeByte_NGP(0x0, 0xf0);
// read rom
dataIn();
for (uint32_t addr = 0; addr < cartSize; addr += 512) {
// blink LED
if ((addr & ((1 << 14) - 1)) == 0)
blinkLED();
// read block
for (uint32_t i = 0; i < 512; i++)
sdBuffer[i] = readByte_NGP(addr + i);
myFile.write(sdBuffer, 512);
}
myFile.close();
}
void scanChip_NGP() {
display_Clear();
//uint32_t block_addr = 0;
uint32_t block_addr = 0;
// generate name of report file
snprintf(fileName, FILENAME_LENGTH, "%s.txt", romName);
// create a new folder to save report file
EEPROM_readAnything(0, foldern);
snprintf(folder, sizeof(folder), "NGP/ROM/%s/%d", romName, foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
print_Msg(F("Saving chip report to "));
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
// open file on sdcard
if (!myFile.open(fileName, O_RDWR | O_CREAT))
print_Error(F("Can't create file on SD"), true);
// write new folder number back to EEPROM
foldern++;
EEPROM_writeAnything(0, foldern);
// write software info to report file
myFile.println("Game: " + String(romName));
myFile.println("ID: " + String(cartID));
myFile.println("Version: " + String(ngpRomVersion));
myFile.println("");
// write chip info to report file
myFile.println("Chip manufacturer ID : 0x" + String(manufacturerID, HEX));
myFile.println("Chip device ID : 0x" + String(deviceID, HEX));
myFile.println("");
if (cartSize == 0)
myFile.println("Cart size unknown");
else {
// enter autoselect mode
dataOut();
writeByte_NGP(0x555, 0xaa);
writeByte_NGP(0x2aa, 0x55);
writeByte_NGP(0x555, 0x90);
dataIn();
uint32_t addrMax;
uint8_t sectorID = 0;
// skip the 2nd 16Mbits chip
if (cartSize == 4194304) {
myFile.println("Warning: this cart is 32Mbits. Only the first 16Mbits chip will be scanned.");
myFile.println("");
addrMax = 2097152;
}
else
addrMax = cartSize;
myFile.println("Sector | Start address | Status");
// browse sectors
for (uint32_t addr = 0; addr < addrMax; addr += 0x1000) {
if ( (addr % 0x10000 == 0) || (addr == addrMax - 0x8000) || (addr == addrMax - 0x6000) || (addr == addrMax - 0x4000)) {
myFile.print("#" + String(sectorID) + " | 0x" + String(addr, HEX) + " | ");
// check the protection status
if (readByte_NGP(addr + 0x2) == 0)
myFile.println("unprotected");
else
myFile.println("protected");
sectorID += 1;
}
}
myFile.close();
writeByte_NGP(0x00, 0xf0);
}
}
void writeByte_NGP(uint32_t addr, uint8_t data) {
PORTF = addr & 0xff;
PORTK = (addr >> 8) & 0xff;
PORTL = (addr >> 16) & 0x1f;
PORTC = data;
// which chip to select
// 0x000000 - 0x1fffff -> /CE0
// 0x200000 - 0x3fffff -> /CE1
data = (addr & 0x00200000 ? (1 << 0) : (1 << 3));
PORTH &= ~data;
PORTH &= ~(1 << 5);
NOP;
PORTH |= data;
PORTH |= (1 << 5);
NOP; NOP;
}
uint8_t readByte_NGP(uint32_t addr) {
uint8_t data;
PORTF = addr & 0xff;
PORTK = (addr >> 8) & 0xff;
PORTL = (addr >> 16) & 0x1f;
// which chip to select
// 0x000000 - 0x1fffff -> /CE0
// 0x200000 - 0x3fffff -> /CE1
data = (addr & 0x00200000 ? (1 << 0) : (1 << 3));
PORTH &= ~data;
PORTH &= ~(1 << 6);
NOP; NOP; NOP;
data = PINC;
PORTH |= data;
PORTH |= (1 << 6);
return data;
}
#endif