//****************************************** // NGP MODULE //****************************************** #ifdef ENABLE_NGP static const char ngpMenuItem2[] PROGMEM = "Read chip info"; static const char* const menuOptionsNGP[] PROGMEM = { FSTRING_READ_ROM, ngpMenuItem2, FSTRING_SET_SIZE, FSTRING_RESET }; 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() { // Request 3.3V setVoltage(VOLTS_SET_3V3); // 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_FatalError(F("Cartridge read error")); } void ngpMenu() { uint8_t mainMenu; convertPgm(menuOptionsNGP, 4); mainMenu = question_box(F("NGP Menu"), menuOptions, 4, 0); switch (mainMenu) { case 0: sd.chdir("/"); readROM_NGP(filePath, FILEPATH_LENGTH); sd.chdir("/"); break; case 1: scanChip_NGP(); break; case 2: changeSize_NGP(); break; case 3: resetArduino(); break; } println_Msg(FS(FSTRING_EMPTY)); // Prints string out of the common strings array either with or without newline print_STR(press_button_STR, 1); 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) { // 4 Mbits case 0x98ab: // Toshiba case 0x204c: // STMicroelectronics ? cartSize = 524288; break; // 8 Mbits case 0x982c: // Toshiba case 0xec2c: // Samsung cartSize = 1048576; break; // 16 Mbits case 0x982f: // Toshiba case 0xec2f: // Samsung case 0x4c7: // Fujitsu (FlashMasta USB) cartSize = 2097152; break; // detection error (no cart inserted or hw problem) case 0xffff: return false; break; } // 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); // replace '/' chars in game name to avoid path errors if (romName[i] == '/') romName[i] = '_'; } return true; } void printCartInfo_NGP() { display_Clear(); println_Msg(F("NGP Cart Info")); print_Msg(FS(FSTRING_NAME)); println_Msg(romName); print_Msg(F("App ID: ")); println_Msg(cartID); print_Msg(F("App 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")); } // Prints string out of the common strings array either with or without newline print_STR(press_button_STR, 1); display_Update(); wait(); } void readROM_NGP(char* outPathBuf, size_t bufferSize) { // Set rom size manually if chip ID is unknown if (cartSize == 0) changeSize_NGP(); // generate fullname of rom file createFolder("NGP", "ROM", romName, "ngp"); // filling output file path to buffer if (outPathBuf != NULL && bufferSize > 0) snprintf(outPathBuf, bufferSize, "%s/%s", folder, fileName); printAndIncrementFolder(true); // open file on sdcard if (!myFile.open(fileName, O_RDWR | O_CREAT)) print_FatalError(create_file_STR); // back to read mode dataOut(); writeByte_NGP(0x0, 0xf0); // read rom dataIn(); uint32_t progress = 0; draw_progressbar(0, cartSize); 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); progress += 512; draw_progressbar(progress, cartSize); } myFile.close(); } void scanChip_NGP() { display_Clear(); // generate name of report file createFolder("NGP", "ROM", romName, "txt"); 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_FatalError(create_file_STR); // write new folder number back to EEPROM foldern++; EEPROM_writeAnything(0, foldern); // write software info to report file myFile.println("Name: " + String(romName)); myFile.println("App ID: " + String(cartID)); myFile.println("App 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; // For 32Mbits carts, 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 changeSize_NGP() { 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; } } 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 //****************************************** // End of File //******************************************