diff --git a/Cart_Reader/Cart_Reader.ino b/Cart_Reader/Cart_Reader.ino index 71f4214..e692266 100644 --- a/Cart_Reader/Cart_Reader.ino +++ b/Cart_Reader/Cart_Reader.ino @@ -2,8 +2,8 @@ Cartridge Reader for Arduino Mega2560 Author: sanni - Date: 01-09-2019 - Version: 3.5 + Date: 05-09-2019 + Version: 3.6 SD lib: https://github.com/greiman/SdFat LCD lib: https://github.com/adafruit/Adafruit_SSD1306 @@ -39,46 +39,28 @@ Gens-gs - Megadrive checksum **********************************************************************************/ - -#include - -char ver[5] = "3.5"; - -/****************************************** - Define Starting Point -******************************************/ -// mainMenu, n64Menu, snsMenu, gbxMenu, nesMenu, mdMenu, flashMenu, pceMenu -#define startMenu mainMenu - -/****************************************** - Define Output -******************************************/ -// To use the Serial Monitor change -// enable_OLED to 0 and enable_Serial to 1 -#define enable_OLED 1 -#define enable_Serial 0 - -/****************************************** - Define Input -******************************************/ -// If you are using the old version with only one button add // in front of the next line -#define enable_Button2 - -/****************************************** - Define SD Speed -******************************************/ -// Change to half speed if you get an sd error or it hangs when writing -#define sdSpeed SPI_FULL_SPEED -//#define sdSpeed SPI_HALF_SPEED +char ver[5] = "3.6"; /****************************************** Options ******************************************/ -// Enable 16bit flash adapter menu -//#define enable_flash16 -// Disable splash screen +// Change mainMenu to snsMenu, mdMenu, n64Menu, gbxMenu, pcsMenu, +// flashMenu, nesMenu or smsMenu for single slot Cart Readers +#define startMenu mainMenu + +// Comment out to change to Serial Ouput +// be sure to change the Arduino Serial Monitor to no line ending +#define enable_OLED + +// Skip OLED start-up animation #define fast_start +// Enable the second button +#define enable_Button2 + +// Enable old 16-bit flash adapter menu +//#define enable_flash16 + /****************************************** Libraries *****************************************/ @@ -88,14 +70,21 @@ char ver[5] = "3.5"; #include #include +// SD Card +#include +#define sdSpeed SPI_FULL_SPEED +// SD Card (Pin 50 = MISO, Pin 51 = MOSI, Pin 52 = SCK, Pin 53 = SS) +#define chipSelectPin 53 +SdFat sd; +SdFile myFile; + // AVR Eeprom #include // forward declarations for "T" (for non Arduino IDE) template int EEPROM_writeAnything(int ee, const T& value); template int EEPROM_readAnything(int ee, T& value); -template int EEPROM_writeAnything(int ee, const T& value) -{ +template int EEPROM_writeAnything(int ee, const T& value) { const byte* p = (const byte*)(const void*)&value; unsigned int i; for (i = 0; i < sizeof(value); i++) @@ -103,8 +92,7 @@ template int EEPROM_writeAnything(int ee, const T& value) return i; } -template int EEPROM_readAnything(int ee, T& value) -{ +template int EEPROM_readAnything(int ee, T& value) { byte* p = (byte*)(void*)&value; unsigned int i; for (i = 0; i < sizeof(value); i++) @@ -141,11 +129,6 @@ typedef enum COLOR_T { white_color, } color_t; -// SD Card (Pin 50 = MISO, Pin 51 = MOSI, Pin 52 = SCK, Pin 53 = SS) -#define chipSelectPin 53 -SdFat sd; -SdFile myFile; - /****************************************** Defines *****************************************/ @@ -169,20 +152,20 @@ SdFile myFile; #define mode_SMS 16 #define mode_SEGA_CD 17 -/****************************************** - optimization-safe nop delay - *****************************************/ +// optimization-safe nop delay #define NOP __asm__ __volatile__ ("nop\n\t") +// Button timing +#define debounce 20 // ms debounce period to prevent flickering when pressing or releasing the button +#define DCgap 250 // max ms between clicks for a double click event +#define holdTime 2000 // ms hold period: how long to wait for press+hold event +#define longHoldTime 5000 // ms long hold period: how long to wait for press+hold event + /****************************************** Variables *****************************************/ -// Button timing -static int debounce = 20; // ms debounce period to prevent flickering when pressing or releasing the button -static int DCgap = 250; // max ms between clicks for a double click event -static int holdTime = 2000; // ms hold period: how long to wait for press+hold event -static int longHoldTime = 5000; // ms long hold period: how long to wait for press+hold event -// Variables for button 1 +#ifdef enable_OLED +// Button 1 boolean buttonVal1 = HIGH; // value read from button boolean buttonLast1 = HIGH; // buffered value of the button's previous state boolean DCwaiting1 = false; // whether we're waiting for a double click (down) @@ -194,7 +177,7 @@ boolean ignoreUp1 = false; // whether to ignore the button release because the c boolean waitForUp1 = false; // when held, whether to wait for the up event boolean holdEventPast1 = false; // whether or not the hold event happened already boolean longholdEventPast1 = false;// whether or not the long hold event happened already -// Variables for button 2 +// Button 2 boolean buttonVal2 = HIGH; // value read from button boolean buttonLast2 = HIGH; // buffered value of the button's previous state boolean DCwaiting2 = false; // whether we're waiting for a double click (down) @@ -206,9 +189,10 @@ boolean ignoreUp2 = false; // whether to ignore the button release because the c boolean waitForUp2 = false; // when held, whether to wait for the up event boolean holdEventPast2 = false; // whether or not the hold event happened already boolean longholdEventPast2 = false;// whether or not the long hold event happened already - +#else // For incoming serial data int incomingByte; +#endif // Variables for the menu int choice = 0; @@ -260,7 +244,7 @@ byte sdBuffer[512]; // using the watchdog timer would be more elegant but some Mega2560 bootloaders are buggy with it void(*resetArduino) (void) = 0; -/* Hoping that sanni will use this progressbar function */ +// Progressbar void draw_progressbar(uint32_t processedsize, uint32_t totalsize); //****************************************** @@ -393,6 +377,7 @@ static const char addonsItem4[] PROGMEM = "Sega Master System"; static const char addonsItem5[] PROGMEM = "Reset"; static const char* const addonsOptions[] PROGMEM = {addonsItem1, addonsItem2, addonsItem3, addonsItem4, addonsItem5}; +// Info Screen void aboutScreen() { display_Clear(); // Draw the Logo @@ -409,40 +394,40 @@ void aboutScreen() { display_Update(); while (1) { - if (enable_OLED) { - // get input button - int b = checkButton(); +#ifdef enable_OLED + // get input button + int b = checkButton(); - // if the cart readers input button is pressed shortly - if (b == 1) { - resetArduino(); - } - - // if the cart readers input button is pressed long - if (b == 3) { - resetArduino(); - } - - // if the button is pressed super long - if (b == 4) { - display_Clear(); - println_Msg(F("Resetting folder...")); - display_Update(); - delay(2000); - foldern = 0; - EEPROM_writeAnything(10, foldern); - resetArduino(); - } - } - if (enable_Serial) { - wait_serial(); + // if the cart readers input button is pressed shortly + if (b == 1) { resetArduino(); } + + // if the cart readers input button is pressed long + if (b == 3) { + resetArduino(); + } + + // if the button is pressed super long + if (b == 4) { + display_Clear(); + println_Msg(F("Resetting folder...")); + display_Update(); + delay(2000); + foldern = 0; + EEPROM_writeAnything(10, foldern); + resetArduino(); + } +#else + wait_serial(); + resetArduino(); +#endif rgb.setColor(random(0, 255), random(0, 255), random(0, 255)); delay(random(50, 100)); } } +// All included slots void mainMenu() { // create menu with title and 6 options to choose from unsigned char modeMenu; @@ -483,12 +468,13 @@ void mainMenu() { } } +// Everything that needs an adapter void addonsMenu() { // create menu with title and 5 options to choose from unsigned char addonsMenu; // Copy menuOptions out of progmem - convertPgm(addonsOptions, 2); - addonsMenu = question_box(F("Choose Adapter"), menuOptions, 2, 0); + convertPgm(addonsOptions, 5); + addonsMenu = question_box(F("Choose Adapter"), menuOptions, 5, 0); // wait for user choice to come back from the question box menu switch (addonsMenu) @@ -567,71 +553,66 @@ void setup() { // Read current folder number out of eeprom EEPROM_readAnything(10, foldern); - if (enable_OLED) { - // GLCD - display.begin(SSD1306_SWITCHCAPVCC, 0x3C); - display.setTextSize(1); - display.setTextColor(WHITE); +#ifdef enable_OLED + // GLCD + display.begin(SSD1306_SWITCHCAPVCC, 0x3C); + display.setTextSize(1); + display.setTextColor(WHITE); - // Clear the screen buffer. - display_Clear(); + // Clear the screen buffer. + display_Clear(); #ifndef fast_start - delay(100); + delay(100); - // Draw line - display.drawLine(0, 32, 127, 32, WHITE); - display_Update(); - delay(100); + // Draw line + display.drawLine(0, 32, 127, 32, WHITE); + display_Update(); + delay(100); - // Initialize LED - rgb.setColor(0, 0, 0); + // Initialize LED + rgb.setColor(0, 0, 0); - // Clear the screen. - display_Clear(); - display_Update(); - delay(25); + // Clear the screen. + display_Clear(); + display_Update(); + delay(25); - // Draw the Logo - display.drawBitmap(28, 0, icon, 72, 64, 1); - for (int s = 1; s < 64; s += 2) { - // Draw Scanlines - display.drawLine(0, s, 127, s, BLACK); - } - display_Update(); - delay(50); + // Draw the Logo + display.drawBitmap(28, 0, icon, 72, 64, 1); + for (int s = 1; s < 64; s += 2) { + // Draw Scanlines + display.drawLine(0, s, 127, s, BLACK); + } + display_Update(); + delay(50); - // Clear the screen. - display_Clear(); - display_Update(); - delay(25); + // Clear the screen. + display_Clear(); + display_Update(); + delay(25); - // Draw the Logo - display.drawBitmap(28, 0, icon, 72, 64, 1); - for (int s = 1; s < 64; s += 2) { - // Draw Scanlines - display.drawLine(0, s, 127, s, BLACK); - } - display.setCursor(100, 55); - display.println(ver); - display_Update(); - delay(200); + // Draw the Logo + display.drawBitmap(28, 0, icon, 72, 64, 1); + for (int s = 1; s < 64; s += 2) { + // Draw Scanlines + display.drawLine(0, s, 127, s, BLACK); + } + display.setCursor(100, 55); + display.println(ver); + display_Update(); + delay(200); #endif - } - if (enable_Serial) { - // Serial Begin - Serial.begin(9600); - Serial.println(F("Cartridge Reader")); - Serial.println(F("2019 sanni")); - Serial.println(""); - // LED Error - rgb.setColor(0, 0, 255); - } - else { - // LED Off - rgb.setColor(0, 0, 0); - } +#else + // Serial Begin + Serial.begin(9600); + Serial.println(F("Cartridge Reader")); + Serial.println(F("2019 sanni")); + Serial.println(""); + // LED Error + rgb.setColor(0, 0, 255); +#endif // Init SD card if (!sd.begin(chipSelectPin, sdSpeed)) { @@ -639,13 +620,13 @@ void setup() { print_Error(F("SD Error"), true); } - if (enable_Serial) { - // Print SD Info - Serial.print(F("SD Card: ")); - Serial.print(sd.card()->cardSize() * 512E-9); - Serial.print(F("GB FAT")); - Serial.println(int(sd.vol()->fatType())); - } +#ifndef enable_OLED + // Print SD Info + Serial.print(F("SD Card: ")); + Serial.print(sd.card()->cardSize() * 512E-9); + Serial.print(F("GB FAT")); + Serial.println(int(sd.vol()->fatType())); +#endif startMenu(); } @@ -683,137 +664,146 @@ void print_Error(const __FlashStringHelper *errorMessage, boolean forceReset) { display_Update(); if (forceReset) { - if (enable_Serial) { - println_Msg(F("Fatal Error, please reset")); - while (1); +#ifdef enable_OLED + println_Msg(F("")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); + if (ignoreError == 0) { + resetArduino(); } else { + ignoreError = 0; + display_Clear(); println_Msg(F("")); - println_Msg(F("Press Button...")); + println_Msg(F("")); + println_Msg(F("")); + println_Msg(F(" Error Overwrite")); display_Update(); - wait(); - if (ignoreError == 0) { - resetArduino(); - } - else { - ignoreError = 0; - display_Clear(); - println_Msg(F("")); - println_Msg(F("")); - println_Msg(F("")); - println_Msg(F(" Error Overwrite")); - display_Update(); - delay(2000); - } + delay(2000); } +#else + println_Msg(F("Fatal Error, please reset")); + while (1); +#endif } } void wait() { - if (enable_OLED) { - wait_btn(); - } - if (enable_Serial) { - wait_serial(); - } +#ifdef enable_OLED + wait_btn(); +#else + wait_serial(); +#endif } void print_Msg(const __FlashStringHelper *string) { - if (enable_OLED) - display.print(string); - if (enable_Serial) - Serial.print(string); +#ifdef enable_OLED + display.print(string); +#else + Serial.print(string); +#endif } void print_Msg(const char string[]) { - if (enable_OLED) - display.print(string); - if (enable_Serial) - Serial.print(string); +#ifdef enable_OLED + display.print(string); +#else + Serial.print(string); +#endif } void print_Msg(long unsigned int message) { - if (enable_OLED) - display.print(message); - if (enable_Serial) - Serial.print(message); +#ifdef enable_OLED + display.print(message); +#else + Serial.print(message); +#endif } void print_Msg(byte message, int outputFormat) { - if (enable_OLED) - display.print(message, outputFormat); - if (enable_Serial) - Serial.print(message, outputFormat); +#ifdef enable_OLED + display.print(message, outputFormat); +#else + Serial.print(message, outputFormat); +#endif } void print_Msg(String string) { - if (enable_OLED) - display.print(string); - if (enable_Serial) - Serial.print(string); +#ifdef enable_OLED + display.print(string); +#else + Serial.print(string); +#endif } void println_Msg(String string) { - if (enable_OLED) - display.println(string); - if (enable_Serial) - Serial.println(string); +#ifdef enable_OLED + display.println(string); +#else + Serial.println(string); +#endif } void println_Msg(byte message, int outputFormat) { - if (enable_OLED) - display.println(message, outputFormat); - if (enable_Serial) - Serial.println(message, outputFormat); +#ifdef enable_OLED + display.println(message, outputFormat); +#else + Serial.println(message, outputFormat); +#endif } void println_Msg(const char message[]) { - if (enable_OLED) - display.println(message); - if (enable_Serial) - Serial.println(message); +#ifdef enable_OLED + display.println(message); +#else + Serial.println(message); +#endif } void println_Msg(const __FlashStringHelper *string) { - if (enable_OLED) - display.println(string); - if (enable_Serial) - Serial.println(string); +#ifdef enable_OLED + display.println(string); +#else + Serial.println(string); +#endif } void println_Msg(long unsigned int message) { - if (enable_OLED) - display.println(message); - if (enable_Serial) - Serial.println(message); +#ifdef enable_OLED + display.println(message); +#else + Serial.println(message); +#endif } void display_Update() { - if (enable_OLED) - display.display(); - if (enable_Serial) - delay(100); +#ifdef enable_OLED + display.display(); +#else + delay(100); +#endif } void display_Clear() { - if (enable_OLED) { - display.clearDisplay(); - display.setCursor(0, 0); - } +#ifdef enable_OLED + display.clearDisplay(); + display.setCursor(0, 0); +#endif } unsigned char question_box(const __FlashStringHelper* question, char answers[7][20], int num_answers, int default_choice) { - if (enable_OLED) { - return questionBox_OLED(question, answers, num_answers, default_choice); - } - else if (enable_Serial) { - return questionBox_Serial(question, answers, num_answers, default_choice); - } +#ifdef enable_OLED + return questionBox_OLED(question, answers, num_answers, default_choice); +#else + return questionBox_Serial(question, answers, num_answers, default_choice); +#endif } /****************************************** Serial Out *****************************************/ +#ifndef enable_OLED void wait_serial() { while (Serial.available() == 0) { } @@ -931,6 +921,7 @@ byte questionBox_Serial(const __FlashStringHelper* question, char answers[7][20] //Serial.println(""); return incomingByte; } +#endif /****************************************** RGB LED @@ -964,6 +955,7 @@ void rgbLed(byte Color) { /****************************************** OLED Menu Module *****************************************/ +#ifdef enable_OLED // Read button state int checkButton() { #ifdef enable_Button2 @@ -979,6 +971,7 @@ int checkButton() { // Read button 1 int checkButton1() { int event = 0; + // Read the state of the button (PD7) buttonVal1 = (PIND & (1 << 7)); // Button pressed down @@ -1038,6 +1031,7 @@ int checkButton1() { // Read button 2 int checkButton2() { int event = 0; + // Read the state of the button (PD7) buttonVal2 = (PING & (1 << 2)); // Button pressed down @@ -1129,7 +1123,6 @@ void wait_btn() { // Display a question box with selectable answers. Make sure default choice is in (0, num_answers] unsigned char questionBox_OLED(const __FlashStringHelper* question, char answers[7][20], int num_answers, int default_choice) { - //clear the screen display.clearDisplay(); display.display(); @@ -1251,6 +1244,7 @@ unsigned char questionBox_OLED(const __FlashStringHelper* question, char answers rgb.setColor(0, 0, 0); return choice; } +#endif /****************************************** Filebrowser Module diff --git a/Cart_Reader/MD.ino b/Cart_Reader/MD.ino index 4ef21ef..b10841e 100644 --- a/Cart_Reader/MD.ino +++ b/Cart_Reader/MD.ino @@ -702,11 +702,11 @@ void getCartInfo_MD() { println_Msg(F(" ")); // Wait for user input - if (enable_OLED) { - println_Msg(F("Press Button...")); - display_Update(); - wait(); - } +#ifdef enable_OLED + println_Msg(F("Press Button...")); + display_Update(); + wait(); +#endif } void writeSSF2Map(unsigned long myAddress, word myData) { diff --git a/Cart_Reader/NES.ino b/Cart_Reader/NES.ino index ed5b44d..8dd9869 100644 --- a/Cart_Reader/NES.ino +++ b/Cart_Reader/NES.ino @@ -3,27 +3,2802 @@ //****************************************** // unfinished and untested -/****************************************** - Variables - *****************************************/ +// mostly copy&pasted from "Famicom Dumper" 2019-08-31 by skaman +// also based on "CoolArduino" by HardWareMan +// Pinout changes: LED and CIRAM_A10 + +//Line Content +//25 Supported Mappers +//99 Defines +//129 Variables +//180 Menu +//265 Setup +//294 Low Level Functions +//541 CRC Functions +//596 File Functions +//758 Config Functions +//1252 ROM Functions +//2332 RAM Functions +//2614 Eeprom Functions /****************************************** - Menu + Supported Mappers *****************************************/ +// Supported Mapper Array (iNES Mapper #s) +// Format = {mapper,prglo,prghi,chrlo,chrhi,ramlo,ramhi} +static const byte PROGMEM mapsize [] = { + 0, 0, 1, 0, 1, 0, 0, // nrom + 1, 1, 5, 0, 5, 0, 3, // mmc1 [sram r/w] + 2, 3, 4, 0, 0, 0, 0, // uxrom + 3, 0, 1, 0, 3, 0, 0, // cnrom + 4, 1, 5, 0, 6, 0, 1, // mmc3 [sram r/w] + 5, 3, 5, 5, 7, 0, 3, // mmc5 [sram r/w] + 7, 3, 4, 0, 0, 0, 0, // axrom + 9, 3, 3, 5, 5, 0, 0, // mmc2 (punch out) + 10, 3, 4, 4, 5, 1, 1, // mmc4 [sram r/w] + 13, 1, 1, 0, 0, 0, 0, // cprom (videomation) + 16, 3, 4, 5, 6, 0, 1, // bandai x24c02 [eep r/w] + 18, 3, 4, 5, 6, 0, 1, // jaleco ss8806 [sram r/w] + 19, 3, 4, 5, 6, 0, 1, // namco 106/163 [sram r/w] + 21, 4, 4, 5, 6, 0, 1, // vrc4a/vrc4c [sram r/w] + 22, 3, 3, 5, 5, 0, 0, // vrc2a + 23, 3, 3, 5, 6, 0, 0, // vrc2b/vrc4e + 24, 4, 4, 5, 5, 0, 0, // vrc6a (akumajou densetsu) + 25, 3, 4, 5, 6, 0, 1, // vrc2c/vrc4b/vrc4d [sram r/w] + 26, 4, 4, 5, 6, 1, 1, // vrc6b [sram r/w] + 32, 3, 4, 5, 5, 0, 0, // irem g-101 + 33, 3, 4, 5, 6, 0, 0, // taito tc0190 + 34, 3, 3, 0, 0, 0, 0, // bnrom [nina-1 NOT SUPPORTED] + 37, 4, 4, 6, 6, 0, 0, // (super mario bros + tetris + world cup) + 47, 4, 4, 6, 6, 0, 0, // (super spike vball + world cup) + 48, 3, 4, 6, 6, 0, 0, // taito tc0690 + 65, 3, 4, 5, 6, 0, 0, // irem h-3001 + 66, 2, 3, 2, 3, 0, 0, // gxrom/mhrom + 67, 3, 3, 5, 5, 0, 0, // sunsoft 3 + 68, 3, 3, 5, 6, 0, 1, // sunsoft 4 [sram r/w] + 69, 3, 4, 5, 6, 0, 1, // sunsoft fme-7/5a/5b [sram r/w] + 70, 3, 3, 5, 5, 0, 0, // bandai + 71, 2, 4, 0, 0, 0, 0, // camerica/codemasters [UNLICENSED] + 72, 3, 3, 5, 5, 0, 0, // jaleco jf-17 + 73, 3, 3, 0, 0, 0, 0, // vrc3 (salamander) + 75, 3, 3, 5, 5, 0, 0, // vrc1 + 76, 3, 3, 5, 5, 0, 0, // namco 109 variant (megami tensei: digital devil story) + 77, 3, 3, 3, 3, 0, 0, // (napoleon senki) + 78, 3, 3, 5, 5, 0, 0, // irem 74hc161/32 + 80, 3, 3, 5, 6, 0, 0, // taito x1-005 + 82, 3, 3, 5, 6, 0, 0, // taito x1-017 + 85, 3, 5, 0, 5, 0, 1, // vrc7 [sram r/w] + 86, 3, 3, 4, 4, 0, 0, // jaleco jf-13 (moero pro yakyuu) + 87, 0, 1, 2, 3, 0, 0, + 88, 3, 3, 5, 5, 0, 0, // namco (dxrom variant) + 89, 3, 3, 5, 5, 0, 0, // sunsoft 2 variant (tenka no goikenban: mito koumon) + 92, 4, 4, 5, 5, 0, 0, // jaleco jf-19/jf-21 + 93, 3, 3, 0, 0, 0, 0, // sunsoft 2 + 94, 3, 3, 0, 0, 0, 0, // hvc-un1rom (senjou no ookami) + 95, 3, 3, 3, 3, 0, 0, // namcot-3425 (dragon buster) + 96, 3, 3, 0, 0, 0, 0, // (oeka kids) + 97, 4, 4, 0, 0, 0, 0, // irem tam-s1 (kaiketsu yanchamaru) + 105, 4, 4, 0, 0, 0, 0, // (nintendo world Championships 1990) [UNTESTED] + 118, 3, 4, 5, 5, 0, 1, // txsrom/mmc3 [sram r/w] + 119, 3, 3, 4, 4, 0, 0, // tqrom/mmc3 + 140, 3, 3, 3, 5, 0, 0, // jaleco jf-11/jf-14 + 152, 2, 3, 5, 5, 0, 0, + 153, 5, 5, 0, 0, 1, 1, // (famicom jump ii) [sram r/w] + 154, 3, 3, 5, 5, 0, 0, // namcot-3453 (devil man) + 155, 3, 3, 3, 5, 0, 1, // mmc1 variant [sram r/w] + 159, 3, 4, 5, 6, 1, 1, // bandai x24c01 [eep r/w] + 180, 3, 3, 0, 0, 0, 0, // unrom variant (crazy climber) + 184, 1, 1, 2, 3, 0, 0, // sunsoft 1 + 185, 0, 1, 1, 1, 0, 0, // cnrom lockout + 206, 1, 3, 2, 4, 0, 0, // dxrom + 207, 4, 4, 5, 5, 0, 0, // taito x1-005 variant (fudou myouou den) + 210, 3, 5, 5, 6, 0, 0, // namco 175/340 +}; + +/****************************************** + Defines + *****************************************/ +#define ROMSEL_HI PORTF |= (1<<1) +#define ROMSEL_LOW PORTF &= ~(1<<1) +#define PHI2_HI PORTF |= (1<<0) +#define PHI2_LOW PORTF &= ~(1<<0) +#define PRG_READ PORTF |= (1<<7) +#define PRG_WRITE PORTF &= ~(1<<7) +#define CHR_READ_HI PORTF |= (1<<5) +#define CHR_READ_LOW PORTF &= ~(1<<5) +#define CHR_WRITE_HI PORTF |= (1<<2) +#define CHR_WRITE_LOW PORTF &= ~(1<<2) + +// RGB LED COMMON ANODE +#define LED_RED_OFF PORTB |= (1<<6) +#define LED_RED_ON PORTB &= ~(1<<6) +#define LED_GREEN_OFF PORTB |= (1<<5) +#define LED_GREEN_ON PORTB &= ~(1<<5) +#define LED_BLUE_OFF PORTB |= (1<<4) +#define LED_BLUE_ON PORTB &= ~(1<<4) + +#define MODE_READ { PORTK = 0xFF; DDRK = 0; } +#define MODE_WRITE DDRK = 0xFF + +#define press 1 +#define doubleclick 2 +#define hold 3 +#define longhold 4 + +/****************************************** + Variables +*****************************************/ +// Mapper +byte mapcount = (sizeof(mapsize) / sizeof(mapsize[0])) / 7; +boolean mapfound = false; +byte mapselect; + +int PRG[] = {16, 32, 64, 128, 256, 512}; +byte prglo = 0; // Lowest Entry +byte prghi = 5; // Highest Entry + +int CHR[] = {0, 8, 16, 32, 64, 128, 256, 512}; +byte chrlo = 0; // Lowest Entry +byte chrhi = 7; // Highest Entry + +byte RAM[] = {0, 8, 16, 32}; +byte ramlo = 0; // Lowest Entry +byte ramhi = 3; // Highest Entry + +int banks; +int prg; +int chr; +byte ram; +boolean vrc4e = false; +byte prgchk0; +byte prgchk1; +int eepsize; +byte bytecheck; + +// Files +File sdFile; +char fileCount[3]; +File nesFile; +char filePRG[] = "PRG.bin"; +char fileCHR[] = "CHR.bin"; +char fileNES[] = "CART.nes"; + +// Cartridge Config +byte mapper; +byte newmapper; +byte prgsize; +byte newprgsize; +byte chrsize; +byte newchrsize; +byte ramsize; +byte newramsize; + +// Button +int b = 0; + +/****************************************** + Menu +*****************************************/ +static const char menuItem1[] PROGMEM = "Select Mapper"; +static const char menuItem2[] PROGMEM = "Read Complete Cart"; +static const char menuItem3[] PROGMEM = "Read PRG"; +static const char menuItem4[] PROGMEM = "Read CHR"; +static const char menuItem5[] PROGMEM = "Read RAM"; +static const char menuItem6[] PROGMEM = "Write RAM"; +static const char* const baseMenu[] PROGMEM = {menuItem1, menuItem2, menuItem3, menuItem4, menuItem5, menuItem6}; + +// NES start menu void nesMenu() { + display_Clear(); + display_Update(); + setup_NES(); + checkStatus_NES(); + nesCartMenu(); + mode = mode_NES; +} + +void nesCartMenu() { + // create menu with title " FAMICOM CART READER" and 6 options to choose from + convertPgm(baseMenu, 6); + unsigned char answer = question_box(F("NES CART READER"), menuOptions, 6, 0); + + // wait for user choice to come back from the question box menu + switch (answer) { + // Select Mapper + case 0: + setMapper(); + checkMapperSize(); + setPRGSize(); + setCHRSize(); + setRAMSize(); + break; + + // Read Complete Cart + case 1: + CartStart(); + readPRG(); + delay(2000); + readCHR(); + delay(2000); + outputNES(); + delay(2000); + readRAM(); + delay(2000); + resetROM(); + CartFinish(); + break; + + // Read PRG + case 2: + CreateROMFolderInSD(); + readPRG(); + resetROM(); + wait(); + break; + + // Read CHR + case 3: + CreateROMFolderInSD(); + readCHR(); + resetROM(); + wait(); + break; + + // Read RAM + case 4: + CreateROMFolderInSD(); + readRAM(); + resetROM(); + wait(); + break; + + // Write RAM + case 5: + writeRAM(); + resetROM(); + wait(); + break; + } } /****************************************** Setup *****************************************/ +void setup_NES() { + // CPU R/W, IRQ, PPU /RD, PPU /A13, CIRAM /CE, PPU /WR, /ROMSEL, PHI2 + DDRF = 0b10110111; + // CPU R/W, IRQ, PPU /RD, PPU /A13, CIRAM /CE, PPU /WR, /ROMSEL, PHI2 + PORTF = 0b11111111; + + // A0-A7 to Output + DDRL = 0xFF; + // A8-A14 to Output + DDRA = 0xFF; + + // Set CIRAM A10 to Input + DDRC &= ~(1 << 2); + // Activate Internal Pullup Resistors + PORTC |= (1 << 2); + + // Set D0-D7 to Input + PORTK = 0xFF; + DDRK = 0; + + set_address(0); + LED_RED_OFF; + LED_GREEN_OFF; + LED_BLUE_OFF; +} /****************************************** - Low level functions -*****************************************/ + Low Level Functions + *****************************************/ +static void phi2_init() { + int i = 0x80; + unsigned char h = PORTF |= (1 << 0); + unsigned char l = PORTF &= ~(1 << 0); + while (i != 0) { + PORTL = l; + PORTL = h; + i--; + } +} + +static void set_address(unsigned int address) { + unsigned char l = address & 0xFF; + unsigned char h = address >> 8; + PORTL = l; + PORTA = h; + + // PPU /A13 + if ((address >> 13) & 1) + PORTF &= ~(1 << 4); + else + PORTF |= 1 << 4; +} + +static void set_romsel(unsigned int address) { + if (address & 0x8000) { + ROMSEL_LOW; + } else { + ROMSEL_HI; + } +} + +static unsigned char read_prg_byte(unsigned int address) { + MODE_READ; + PRG_READ; + set_address(address); + PHI2_HI; + set_romsel(address); + _delay_us(1); + return PINK; +} + +static unsigned char read_chr_byte(unsigned int address) { + MODE_READ; + PHI2_HI; + ROMSEL_HI; + set_address(address); + CHR_READ_LOW; + _delay_us(1); + uint8_t result = PINK; + CHR_READ_HI; + return result; +} + +static void write_prg_byte(unsigned int address, uint8_t data) { + PHI2_LOW; + ROMSEL_HI; + MODE_WRITE; + PRG_WRITE; + PORTK = data; + + set_address(address); // PHI2 low, ROMSEL always HIGH + // _delay_us(1); + PHI2_HI; + //_delay_us(10); + set_romsel(address); // ROMSEL is low if need, PHI2 high + _delay_us(1); // WRITING + //_delay_ms(1); // WRITING + // PHI2 low, ROMSEL high + PHI2_LOW; + _delay_us(1); + ROMSEL_HI; + // Back to read mode + // _delay_us(1); + PRG_READ; + MODE_READ; + set_address(0); + // Set phi2 to high state to keep cartridge unreseted + // _delay_us(1); + PHI2_HI; + // _delay_us(1); +} + +static void write_chr_byte(unsigned int address, uint8_t data) { + PHI2_LOW; + ROMSEL_HI; + MODE_WRITE; + PORTK = data; + + set_address(address); // PHI2 low, ROMSEL always HIGH + //_delay_us(10); + CHR_WRITE_LOW; + _delay_us(1); // WRITING + //_delay_ms(1); // WRITING + CHR_WRITE_HI; + //_delay_us(1); + MODE_READ; + set_address(0); + PHI2_HI; + //_delay_us(1); +} + +static void write_prg(unsigned int address, unsigned int len, uint8_t* data) { + LED_RED_ON; + while (len > 0) { + write_prg_byte(address, *data); + address++; + len--; + data++; + } + //_delay_ms(1); + LED_RED_OFF; +} + +static void write_chr(unsigned int address, unsigned int len, uint8_t* data) { + LED_RED_ON; + while (len > 0) { + write_chr_byte(address, *data); + address++; + len--; + data++; + } + //_delay_ms(1); + LED_RED_OFF; +} + +static void reset_phi2() { + LED_RED_ON; + LED_GREEN_ON; + PHI2_LOW; + ROMSEL_HI; + _delay_ms(100); + PHI2_HI; + LED_RED_OFF; + LED_GREEN_OFF; +} + +void resetROM() { + set_address(0); + PHI2_HI; + ROMSEL_HI; +} + +void write_mmc1_byte(unsigned int address, uint8_t data) { // write loop for 5 bit register + if (address >= 0xE000) { + for (int i = 0; i < 5; i++) { + write_reg_byte(address, data >> i); // shift 1 bit into temp register [WRITE RAM SAFE] + } + } + else { + for (int j = 0; j < 5; j++) { + write_prg_byte(address, data >> j); // shift 1 bit into temp register + } + } +} + +// REFERENCE FOR REGISTER WRITE TO 0xE000/0xF000 +// PORTF 7 = CPU R/W = 0 +// PORTF 6 = /IRQ = 1 +// PORTF 5 = PPU /RD = 1 +// PORTF 4 = PPU /A13 = 1 +// PORTF 3 = CIRAM /CE = 1 +// PORTF 2 = PPU /WR = 1 +// PORTF 1 = /ROMSEL +// PORTF 0 = PHI2 (M2) + +// WRITE RAM SAFE TO REGISTERS 0xE000/0xF000 +static void write_reg_byte(unsigned int address, uint8_t data) { // FIX FOR MMC1 RAM CORRUPTION + PHI2_LOW; + ROMSEL_HI; // A15 HI = E000 + MODE_WRITE; + PRG_WRITE; // CPU R/W LO + PORTK = data; + + set_address(address); // PHI2 low, ROMSEL always HIGH + // DIRECT PIN TO PREVENT RAM CORRUPTION + // DIFFERENCE BETWEEN M2 LO AND ROMSEL HI MUST BE AROUND 33ns + // IF TIME IS GREATER THAN 33ns THEN WRITES TO 0xE000/0xF000 WILL CORRUPT RAM AT 0x6000/0x7000 + PORTF = 0b01111101; // ROMSEL LO/M2 HI + PORTF = 0b01111110; // ROMSEL HI/M2 LO + _delay_us(1); + // Back to read mode + PRG_READ; + MODE_READ; + set_address(0); + // Set phi2 to high state to keep cartridge unreseted + PHI2_HI; +} + +static void write_ram_byte(unsigned int address, uint8_t data) { // Mapper 19 (Namco 106/163) WRITE RAM SAFE ($E000-$FFFF) + PHI2_LOW; + ROMSEL_HI; + MODE_WRITE; + PRG_WRITE; + PORTK = data; + + set_address(address); // PHI2 low, ROMSEL always HIGH + PHI2_HI; + ROMSEL_LOW; // SET /ROMSEL LOW OTHERWISE CORRUPTS RAM + _delay_us(1); // WRITING + // PHI2 low, ROMSEL high + PHI2_LOW; + _delay_us(1); + ROMSEL_HI; + // Back to read mode + PRG_READ; + MODE_READ; + set_address(0); + // Set phi2 to high state to keep cartridge unreseted + PHI2_HI; +} + +static void write_wram_byte(unsigned int address, uint8_t data) { // Mapper 5 (MMC5) RAM + PHI2_LOW; + ROMSEL_HI; + set_address(address); + PORTK = data; + + _delay_us(1); + MODE_WRITE; + PRG_WRITE; + PHI2_HI; + _delay_us(1); // WRITING + PHI2_LOW; + ROMSEL_HI; + // Back to read mode + PRG_READ; + MODE_READ; + set_address(0); + // Set phi2 to high state to keep cartridge unreseted + PHI2_HI; +} + +int int_pow(int base, int exp) { // Power for int + int result = 1; + while (exp) { + if (exp & 1) + result *= base; + exp /= 2; + base *= base; + } + return result; +} /****************************************** - NES functions -*****************************************/ + CRC Functions + *****************************************/ +File crcFile; +char tempCRC[9]; + +inline uint32_t updateCRC32(uint8_t ch, uint32_t crc) { + uint32_t idx = ((crc) ^ (ch)) & 0xff; + uint32_t tab_value = pgm_read_dword(crc_32_tab + idx); + return tab_value ^ ((crc) >> 8); +} + +uint32_t crc32(File &file, uint32_t &charcnt) { + uint32_t oldcrc32 = 0xFFFFFFFF; + charcnt = 0; + while (file.available()) { + crcFile.read(sdBuffer, 512); + for (int x = 0; x < 512; x++) { + uint8_t c = sdBuffer[x]; + charcnt++; + oldcrc32 = updateCRC32(c, oldcrc32); + } + } + return ~oldcrc32; +} + +uint32_t crc32EEP(File &file, uint32_t &charcnt) { + uint32_t oldcrc32 = 0xFFFFFFFF; + charcnt = 0; + while (file.available()) { + crcFile.read(sdBuffer, 128); + for (int x = 0; x < 128; x++) { + uint8_t c = sdBuffer[x]; + charcnt++; + oldcrc32 = updateCRC32(c, oldcrc32); + } + } + return ~oldcrc32; +} + +void calcCRC(char* checkFile, unsigned long filesize) { + uint32_t crc; + crcFile = sd.open(checkFile); + if (filesize < 1024) + crc = crc32EEP(crcFile, filesize); + else + crc = crc32(crcFile, filesize); + crcFile.close(); + sprintf(tempCRC, "%08lX", crc); + + print_Msg(F("CRC: ")); + println_Msg(tempCRC); + display_Update(); +} + +/****************************************** + File Functions + *****************************************/ +void CreateROMFolderInSD() { + sd.chdir(); + sprintf(folder, "NES/ROM"); + sd.mkdir(folder, true); + sd.chdir(folder); +} + +void CreatePRGFileInSD() { + strcpy(fileName, "PRG"); + strcat(fileName, ".bin"); + for (byte i = 0; i < 100; i++) { + if (!sd.exists(fileName)) { + sdFile = sd.open(fileName, O_RDWR | O_CREAT); + break; + } + sprintf(fileCount, "%02d", i); + strcpy(fileName, "PRG."); + strcat(fileName, fileCount); + strcat(fileName, ".bin"); + } + if (!sdFile) { + LED_RED_ON; + + display_Clear(); + println_Msg(F("PRG FILE FAILED!")); + display_Update(); + print_Error(F("SD Error"), true); + + LED_RED_OFF; + } +} + +void CreateCHRFileInSD() { + strcpy(fileName, "CHR"); + strcat(fileName, ".bin"); + for (byte i = 0; i < 100; i++) { + if (!sd.exists(fileName)) { + sdFile = sd.open(fileName, O_RDWR | O_CREAT); + break; + } + sprintf(fileCount, "%02d", i); + strcpy(fileName, "CHR."); + strcat(fileName, fileCount); + strcat(fileName, ".bin"); + } + if (!sdFile) { + LED_RED_ON; + + display_Clear(); + println_Msg(F("CHR FILE FAILED!")); + display_Update(); + print_Error(F("SD Error"), true); + + LED_RED_OFF; + } +} + +void CreateRAMFileInSD() { + strcpy(fileName, "RAM"); + strcat(fileName, ".bin"); + for (byte i = 0; i < 100; i++) { + if (!sd.exists(fileName)) { + sdFile = sd.open(fileName, O_RDWR | O_CREAT); + break; + } + sprintf(fileCount, "%02d", i); + strcpy(fileName, "RAM."); + strcat(fileName, fileCount); + strcat(fileName, ".bin"); + } + if (!sdFile) { + LED_RED_ON; + + display_Clear(); + println_Msg(F("RAM FILE FAILED!")); + display_Update(); + print_Error(F("SD Error"), true); + + LED_RED_OFF; + } +} + +void outputNES() { + display_Clear(); + + LED_RED_ON; + LED_GREEN_ON; + LED_BLUE_ON; + if (!sdFile.open(filePRG, FILE_READ)) { + LED_GREEN_OFF; + LED_BLUE_OFF; + + display_Clear(); + println_Msg(F("PRG FILE FAILED!")); + display_Update(); + print_Error(F("SD Error"), true); + + } + if (!sd.exists(fileNES)) { + nesFile = sd.open(fileNES, O_RDWR | O_CREAT); + } + if (!nesFile) { + LED_GREEN_OFF; + LED_BLUE_OFF; + + display_Clear(); + println_Msg(F("NES FILE FAILED!")); + display_Update(); + print_Error(F("SD Error"), true); + + } + size_t n; + while ((n = sdFile.read(sdBuffer, sizeof(sdBuffer))) > 0) { + nesFile.write(sdBuffer, n); + } + sdFile.close(); + if (sd.exists(fileCHR)) { + if (!sdFile.open(fileCHR, FILE_READ)) { + LED_GREEN_OFF; + LED_BLUE_OFF; + + display_Clear(); + println_Msg(F("CHR FILE FAILED!")); + display_Update(); + print_Error(F("SD Error"), true); + + } + while ((n = sdFile.read(sdBuffer, sizeof(sdBuffer))) > 0) { + nesFile.write(sdBuffer, n); + } + sdFile.close(); + } + nesFile.flush(); + nesFile.close(); + + println_Msg(F("NES FILE OUTPUT!")); + println_Msg(F("")); + display_Update(); + + calcCRC(fileNES, (prg + chr) * 1024); + LED_RED_OFF; + LED_GREEN_OFF; + LED_BLUE_OFF; +} + +void CartStart() { + sd.chdir(); + EEPROM_readAnything(0, foldern); // FOLDER # + sprintf(folder, "NES/CART/%d", foldern); + sd.mkdir(folder, true); + sd.chdir(folder); +} + +void CartFinish() { + foldern += 1; + EEPROM_writeAnything(0, foldern); // FOLDER # + sd.chdir(); +} + +/****************************************** + Config Functions + *****************************************/ +void setMapper() { +#ifdef enable_OLED +chooseMapper: + // Read stored mapper + EEPROM_readAnything(7, newmapper); + if (newmapper > 220) + newmapper = 0; + // Split into digits + byte hundreds = newmapper / 100; + byte tens = newmapper / 10 - hundreds * 10; + byte units = newmapper - hundreds * 100 - tens * 10; + + // Cycle through al 3 digits + for (byte digit = 0; digit < 3; digit++) { + while (1) { + display.clearDisplay(); + display.setCursor(0, 0); + display.println("Select Mapper:"); + display.setCursor(23, 20); + display.println(hundreds); + display.setCursor(43, 20); + display.println(tens); + display.setCursor(63, 20); + display.println(units); + display.println(""); + display.println(F("Press to change")); + display.println(F("Hold to select")); + if (digit == 0) { + display.drawLine(20, 30, 30, 30, WHITE); + display.drawLine(40, 30, 50, 30, BLACK); + display.drawLine(60, 30, 70, 30, BLACK); + } + else if (digit == 1) { + display.drawLine(20, 30, 30, 30, BLACK); + display.drawLine(40, 30, 50, 30, WHITE); + display.drawLine(60, 30, 70, 30, BLACK); + } + else if (digit == 2) { + display.drawLine(20, 30, 30, 30, BLACK); + display.drawLine(40, 30, 50, 30, BLACK); + display.drawLine(60, 30, 70, 30, WHITE); + } + /* Check Button + 1 click + 2 doubleClick + 3 hold + 4 longHold */ + int b = checkButton(); + + if (b == 1) { + if (digit == 0) { + if (hundreds < 2) + hundreds++; + else + hundreds = 0; + } + else if (digit == 1) { + if (hundreds == 2) { + if (tens < 1) + tens++; + else + tens = 0; + } + else { + if (tens < 9) + tens++; + else + tens = 0; + } + } + else if (digit == 2) { + if (units < 9) + units++; + else + units = 0; + } + } + else if (b == 2) { + if (digit == 0) { + if (hundreds > 0) + hundreds--; + else + hundreds = 2; + } + else if (digit == 1) { + if (hundreds == 2) { + if (tens > 0) + tens--; + else + tens = 1; + } + else { + if (tens > 0) + tens--; + else + tens = 9; + } + } + else if (digit == 2) { + if (units > 0) + units--; + else + units = 9; + } + } + else if (b == 3) { + break; + } + display.display(); + } + } + display.clearDisplay(); + display.setCursor(0, 0); + + newmapper = hundreds * 100 + tens * 10 + units; + + // Check if valid + boolean validMapper = 0; + byte mapcount = (sizeof(mapsize) / sizeof(mapsize[0])) / 7; + for (byte currMaplist = 0; currMaplist < mapcount; currMaplist++) { + if (pgm_read_byte(mapsize + currMaplist * 7) == newmapper) + validMapper = 1; + } + + if (!validMapper) { + errorLvl = 1; + display.println("Mapper not supported"); + display.display(); + wait_btn(); + goto chooseMapper; + } +#else +setmapper: + String newmap; + mapfound = false; + Serial.println(F("SUPPORTED MAPPERS:")); + for (int i = 0; i < mapcount; i++) { + index = i * 7; + mapselect = pgm_read_byte(mapsize + index); + Serial.print(mapselect); + if (i < mapcount - 1) { + if ((i != 0) && ((i + 1) % 10 == 0)) + Serial.println(F("")); + else + Serial.print(F("\t")); + } + else + Serial.println(F("")); + } + Serial.print(F("Enter Mapper: ")); + while (Serial.available() == 0) {} + newmap = Serial.readStringUntil('\n'); + Serial.println(newmap); + newmapper = newmap.toInt(); + for (int i = 0; i < mapcount; i++) { + index = i * 7; + mapselect = pgm_read_byte(mapsize + index); + if (newmapper == mapselect) + mapfound = true; + } + if (mapfound == false) { + Serial.println(F("MAPPER NOT SUPPORTED!")); + Serial.println(F("")); + newmapper = 0; + goto setmapper; + } +#endif + EEPROM_writeAnything(7, newmapper); + mapper = newmapper; +} + +void checkMapperSize() { + for (int i = 0; i < mapcount; i++) { + index = i * 7; + byte mapcheck = pgm_read_byte(mapsize + index); + if (mapcheck == mapper) { + prglo = pgm_read_byte(mapsize + index + 1); + prghi = pgm_read_byte(mapsize + index + 2); + chrlo = pgm_read_byte(mapsize + index + 3); + chrhi = pgm_read_byte(mapsize + index + 4); + ramlo = pgm_read_byte(mapsize + index + 5); + ramhi = pgm_read_byte(mapsize + index + 6); + break; + } + } +} + +void setPRGSize() { +#ifdef enable_OLED + display_Clear(); + if (prglo == prghi) + newprgsize = prglo; + else { + b = 0; + int i = prglo; + while (1) { + display_Clear(); + print_Msg(F("PRG Size: ")); + println_Msg(PRG[i]); + println_Msg(F("")); + println_Msg(F("Press to Change")); + println_Msg(F("Hold to Select")); + display_Update(); + b = checkButton(); + if (b == doubleclick) { // Previous + if (i == prglo) + i = prghi; + else + i--; + } + if (b == press) { // Next + if (i == prghi) + i = prglo; + else + i++; + } + if (b == hold) { // Long Press - Execute + newprgsize = i; + break; + } + } + + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(F("PRG SIZE ")); + print_Msg(PRG[newprgsize]); + println_Msg(F("K")); + display_Update(); + delay(1000); +#else + if (prglo == prghi) + newprgsize = prglo; + else { +setprg: + String sizePRG; + for (int i = 0; i < (prghi - prglo + 1); i++) { + Serial.print(F("Select PRG Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(PRG[i + prglo]); + Serial.println(F("K")); + } + Serial.print(F("Enter PRG Size: ")); + while (Serial.available() == 0) {} + sizePRG = Serial.readStringUntil('\n'); + Serial.println(sizePRG); + newprgsize = sizePRG.toInt() + prglo; + if (newprgsize > prghi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(F("")); + goto setprg; + } + } + Serial.print(F("PRG Size = ")); + Serial.print(PRG[newprgsize]); + Serial.println(F("K")); +#endif + EEPROM_writeAnything(8, newprgsize); + prgsize = newprgsize; +} + +void setCHRSize() { +#ifdef enable_OLED + display_Clear(); + if (chrlo == chrhi) + newchrsize = chrlo; + else { + b = 0; + int i = chrlo; + while (1) { + display_Clear(); + print_Msg(F("CHR Size: ")); + println_Msg(CHR[i]); + println_Msg(F("")); + println_Msg(F("Press to Change")); + println_Msg(F("Hold to Select")); + display_Update(); + b = checkButton(); + if (b == doubleclick) { // Previous + if (i == chrlo) + i = chrhi; + else + i--; + } + if (b == press) { // Next + if (i == chrhi) + i = chrlo; + else + i++; + } + if (b == hold) { // Long Press - Execute + newchrsize = i; + break; + } + } + display.setCursor(0, 56); // Display selection at bottom + } + print_Msg(F("CHR SIZE ")); + print_Msg(CHR[newchrsize]); + println_Msg(F("K")); + display_Update(); + delay(1000); +#else + if (chrlo == chrhi) + newchrsize = chrlo; + else { +setchr: + String sizeCHR; + for (int i = 0; i < (chrhi - chrlo + 1); i++) { + Serial.print(F("Select CHR Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + Serial.print(CHR[i + chrlo]); + Serial.println(F("K")); + } + Serial.print(F("Enter CHR Size: ")); + while (Serial.available() == 0) {} + sizeCHR = Serial.readStringUntil('\n'); + Serial.println(sizeCHR); + newchrsize = sizeCHR.toInt() + chrlo; + if (newchrsize > chrhi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(F("")); + goto setchr; + } + } + Serial.print(F("CHR Size = ")); + Serial.print(CHR[newchrsize]); + Serial.println(F("K")); +#endif + EEPROM_writeAnything(9, newchrsize); + chrsize = newchrsize; +} + +void setRAMSize() { +#ifdef enable_OLED + display_Clear(); + if (ramlo == ramhi) + newramsize = ramlo; + else { + b = 0; + int i = 0; + while (1) { + display_Clear(); + print_Msg(F("RAM Size: ")); + if (mapper == 16) + println_Msg(RAM[i] * 32); + else if (mapper == 159) + println_Msg(RAM[i] * 16); + else + println_Msg(RAM[i]); + println_Msg(F("")); + println_Msg(F("Press to Change")); + println_Msg(F("Hold to Select")); + display_Update(); + b = checkButton(); + if (b == doubleclick) { // Previous Mapper + if (i == 0) + i = ramhi; + else + i--; + } + if (b == press) { // Next + if (i == ramhi) + i = 0; + else + i++; + } + if (b == hold) { // Long Press - Execute + newramsize = i; + break; + } + } + + display.setCursor(0, 56); // Display selection at bottom + } + if ((mapper == 16) || (mapper == 159)) { + int sizeEEP = 0; + print_Msg(F("EEPROM SIZE ")); + if (mapper == 16) + sizeEEP = RAM[newramsize] * 32; + else + sizeEEP = RAM[newramsize] * 16; + print_Msg(sizeEEP); + println_Msg(F("B")); + } + else { + print_Msg(F("RAM SIZE ")); + print_Msg(RAM[newramsize]); + println_Msg(F("K")); + } + display_Update(); + delay(1000); +#else + if (ramlo == ramhi) + newramsize = ramlo; + else { +setram: + String sizeRAM; + for (int i = 0; i < (ramhi - ramlo + 1); i++) { + Serial.print(F("Select RAM Size: ")); + Serial.print(i); + Serial.print(F(" = ")); + if ((mapper == 16) || (mapper == 159)) { + if (mapper == 16) + Serial.print(RAM[i + ramlo] * 32); + else + Serial.print(RAM[i + ramlo] * 16); + Serial.println(F("B")); + } + else { + Serial.print(RAM[i + ramlo]); + Serial.println(F("K")); + } + } + Serial.print(F("Enter RAM Size: ")); + while (Serial.available() == 0) {} + sizeRAM = Serial.readStringUntil('\n'); + Serial.println(sizeRAM); + newramsize = sizeRAM.toInt() + ramlo; + if (newramsize > ramhi) { + Serial.println(F("SIZE NOT SUPPORTED")); + Serial.println(F("")); + goto setram; + } + } + if ((mapper == 16) || (mapper == 159)) { + int sizeEEP = 0; + Serial.print(F("EEPROM Size = ")); + if (mapper == 16) + sizeEEP = RAM[newramsize] * 32; + else + sizeEEP = RAM[newramsize] * 16; + Serial.print(sizeEEP); + Serial.println(F("B")); + Serial.println(F("")); + } + else { + Serial.print(F("RAM Size = ")); + Serial.print(RAM[newramsize]); + Serial.println(F("K")); + Serial.println(F("")); + } +#endif + EEPROM_writeAnything(10, newramsize); + ramsize = newramsize; +} + +void checkStatus_NES() { + EEPROM_readAnything(7, mapper); + EEPROM_readAnything(8, prgsize); + EEPROM_readAnything(9, chrsize); + EEPROM_readAnything(10, ramsize); + prg = (int_pow(2, prgsize)) * 16; + if (chrsize == 0) + chr = 0; // 0K + else + chr = (int_pow(2, chrsize)) * 4; + if (ramsize == 0) + ram = 0; // 0K + else + ram = (int_pow(2, ramsize)) * 4; + + display_Clear(); + println_Msg(F("NES CART READER")); + println_Msg(F("CURRENT SETTINGS")); + println_Msg(F("")); + print_Msg(F("MAPPER: ")); + println_Msg(mapper); + print_Msg(F("PRG SIZE: ")); + print_Msg(prg); + println_Msg(F("K")); + print_Msg(F("CHR SIZE: ")); + print_Msg(chr); + println_Msg(F("K")); + print_Msg(F("RAM SIZE: ")); + if ((mapper == 16) || (mapper == 159)) { + if (mapper == 16) + print_Msg(ram * 32); + else + print_Msg(ram * 16); + println_Msg(F("B")); + } + else { + print_Msg(ram); + println_Msg(F("K")); + } + display_Update(); + wait(); +} + +/****************************************** + ROM Functions + *****************************************/ +void dumpPRG(word base, word address) { + for (int x = 0; x < 512; x++) { + sdBuffer[x] = read_prg_byte(base + address + x); + } + sdFile.write(sdBuffer, 512); +} + +void dumpCHR(word address) { + for (int x = 0; x < 512; x++) { + sdBuffer[x] = read_chr_byte(address + x); + } + sdFile.write(sdBuffer, 512); +} + +void dumpMMC5RAM(word base, word address) { // MMC5 SRAM DUMP - PULSE M2 LO/HI + for (int x = 0; x < 512; x++) { + PHI2_LOW; + sdBuffer[x] = read_prg_byte(base + address + x); + } + sdFile.write(sdBuffer, 512); +} + +void writeMMC5RAM(word base, word address) { // MMC5 SRAM WRITE + sdFile.read(sdBuffer, 512); + for (int x = 0; x < 512; x++) { + do { + write_prg_byte(0x5102, 2); // PRG RAM PROTECT1 + write_prg_byte(0x5103, 1); // PRG RAM PROTECT2 + write_wram_byte(base + address + x, sdBuffer[x]); + bytecheck = read_prg_byte(base + address + x); + } + while (bytecheck != sdBuffer[x]); // CHECK WRITTEN BYTE + } + write_prg_byte(0x5102, 0); // PRG RAM PROTECT1 + write_prg_byte(0x5103, 0); // PRG RAM PROTECT2 +} + +void readPRG() { + display_Clear(); + display_Update(); + + LED_BLUE_ON; + set_address(0); + _delay_us(1); + CreatePRGFileInSD(); + word base = 0x8000; + if (sdFile) { + switch (mapper) { + case 0: + case 3: + case 13: + case 87: // 16K/32K + case 184: // 32K + case 185: // 16K/32K + for (word address = 0; address < ((prgsize * 0x4000) + 0x4000); address += 512) { // 16K or 32K + dumpPRG(base, address); + } + break; + + case 1: + case 155: // 32K/64K/128K/256K/512K + banks = int_pow(2, prgsize) - 1; + for (int i = 0; i < banks; i++) { // 16K Banks ($8000-$BFFF) + write_prg_byte(0x8000, 0x80); // Clear Register + write_mmc1_byte(0x8000, 0x0C); // Switch 16K Bank ($8000-$BFFF) + Fixed Last Bank ($C000-$FFFF) + if (prgsize > 4) // 512K + write_mmc1_byte(0xA000, 0x00); // Reset 512K Flag for Lower 256K + if (i > 15) // Switch Upper 256K + write_mmc1_byte(0xA000, 0x10); // Set 512K Flag + write_mmc1_byte(0xE000, i); + for (word address = 0x0; address < 0x4000; address += 512) { + dumpPRG(base, address); + } + } + for (word address = 0x4000; address < 0x8000; address += 512) { // Final Bank ($C000-$FFFF) + dumpPRG(base, address); + } + break; + + case 2: + for (int i = 0; i < 8; i++) { // 128K/256K + write_prg_byte(0x8000, i); + for (word address = 0x0; address < (((prgsize - 3) * 0x4000) + 0x4000); address += 512) { + dumpPRG(base, address); + } + } + break; + + case 4: + case 47: + case 118: + case 119: + banks = ((int_pow(2, prgsize) * 2)) - 2; // Set Number of Banks + if (mapper == 47) + write_prg_byte(0xA001, 0x80); // Block Register - PRG RAM Chip Enable, Writable + for (int i = 0; i < banks; i += 2) { // 32K/64K/128K/256K/512K + if (mapper == 47) { + if (i == 0) + write_prg_byte(0x6000, 0); // Switch to Lower Block + else if (i == 16) + write_prg_byte(0x6000, 1); // Switch to Upper Block + } + write_prg_byte(0x8000, 6); // PRG Bank 0 ($8000-$9FFF) + write_prg_byte(0x8001, i); + write_prg_byte(0x8000, 7); // PRG Bank 1 ($A000-$BFFF) + write_prg_byte(0x8001, i + 1); + for (word address = 0x0; address < 0x4000; address += 512) { + dumpPRG(base, address); + } + } + for (word address = 0x4000; address < 0x8000; address += 512) { // Final 2 Banks ($C000-$FFFF) + dumpPRG(base, address); + } + break; + + case 5: // 128K/256K/512K + banks = int_pow(2, prgsize) * 2; + write_prg_byte(0x5100, 3); // 8K PRG Banks + for (int i = 0; i < banks; i += 2) { // 128K/256K/512K + write_prg_byte(0x5114, i | 0x80); + write_prg_byte(0x5115, (i + 1) | 0x80); + for (word address = 0x0; address < 0x4000; address += 512) { + dumpPRG(base, address); + } + } + break; + + case 7: // 128K/256K + case 34: + case 77: + case 96: // 128K + banks = int_pow(2, prgsize) / 2; + for (int i = 0; i < banks; i++) { // 32K Banks + write_prg_byte(0x8000, i); + for (word address = 0x0; address < 0x8000; address += 512) { // 32K Banks ($8000-$FFFF) + dumpPRG(base, address); + } + } + break; + + case 9: // 128K + for (int i = 0; i < 13; i++) { // 16-3 = 13 = 128K + write_prg_byte(0xA000, i); // $8000-$9FFF + for (word address = 0x0; address < 0x2000; address += 512) { // Switch Bank ($8000-$9FFF) + dumpPRG(base, address); + } + } + for (word address = 0x2000; address < 0x8000; address += 512) { // Final 3 Banks ($A000-$FFFF) + dumpPRG(base, address); + } + break; + + case 10: // 128K/256K + for (int i = 0; i < (((prgsize - 3) * 8) + 7); i++) { + write_prg_byte(0xA000, i); // $8000-$BFFF + for (word address = 0x0; address < 0x4000; address += 512) { // Switch Bank ($8000-$BFFF) + dumpPRG(base, address); + } + } + for (word address = 0x4000; address < 0x8000; address += 512) { // Final Bank ($C000-$FFFF) + dumpPRG(base, address); + } + break; + + case 16: + case 159: // 128K/256K + banks = int_pow(2, prgsize); + for (int i = 0; i < banks; i++) { + write_prg_byte(0x6008, i); // Submapper 4 + write_prg_byte(0x8008, i); // Submapper 5 + for (word address = 0x0; address < 0x4000; address += 512) { // 16K Banks ($8000-$BFFF) + dumpPRG(base, address); + } + } + break; + + case 18: // 128K/256K + banks = int_pow(2, prgsize) * 2; + for (int i = 0; i < banks; i += 2) { + write_prg_byte(0x8000, i & 0xF); + write_prg_byte(0x8001, (i >> 4) & 0xF); + write_prg_byte(0x8002, (i + 1) & 0xF); + write_prg_byte(0x8003, ((i + 1) >> 4) & 0xF); + for (word address = 0x0; address < 0x4000; address += 512) { + dumpPRG(base, address); + } + } + break; + + case 19: // 128K/256K + for (int j = 0; j < 64; j++) { // Init Register + write_ram_byte(0xE000, 0); // PRG Bank 0 ($8000-$9FFF) + } + banks = int_pow(2, prgsize) * 2; + for (int i = 0; i < banks; i++) { + write_ram_byte(0xE000, i); // PRG Bank 0 ($8000-$9FFF) + for (word address = 0x0; address < 0x2000; address += 512) { + dumpPRG(base, address); + } + } + break; + + case 21: + case 22: + case 23: + case 25: + case 65: + case 75: // 128K/256K + banks = int_pow(2, prgsize) * 2; + for (int i = 0; i < banks; i += 2) { + write_prg_byte(0x8000, i); + write_prg_byte(0xA000, i + 1); + for (word address = 0x0; address < 0x4000; address += 512) { + dumpPRG(base, address); + } + } + break; + + case 24: + case 26: // 256K + case 78: // 128K + banks = int_pow(2, prgsize); + for (int i = 0; i < banks; i++) { // 128K + write_prg_byte(0x8000, i); + for (word address = 0x0; address < 0x4000; address += 512) { // 16K Banks ($8000-$BFFF) + dumpPRG(base, address); + } + } + break; + + case 32: // 128K/256K + banks = int_pow(2, prgsize) * 2; + for (int i = 0; i < banks; i++) { // 128K/256K + write_prg_byte(0x9000, 1); // PRG Mode 0 - Read $A000-$BFFF to avoid difference between Modes 0 and 1 + write_prg_byte(0xA000, i); // PRG Bank + for (word address = 0x2000; address < 0x4000; address += 512) { // 8K Banks ($A000-$BFFF) + dumpPRG(base, address); + } + } + break; + + case 33: + case 48: // 128K/256K + banks = int_pow(2, prgsize) * 2; + for (int i = 0; i < banks; i += 2) { + write_prg_byte(0x8000, i); // PRG Bank 0 ($8000-$9FFF) + write_prg_byte(0x8001, i + 1); // PRG Bank 1 ($A000-$BFFF) + for (word address = 0x0; address < 0x4000; address += 512) { // 8K Banks ($8000-$BFFF) + dumpPRG(base, address); + } + } + break; + + case 37: + banks = ((int_pow(2, prgsize) * 2)) - 2; // Set Number of Banks + write_prg_byte(0xA001, 0x80); // Block Register - PRG RAM Chip Enable, Writable + for (int i = 0; i < banks; i += 2) { // 256K + if (i == 0) + write_prg_byte(0x6000, 0); // Switch to Lower Block ($0000-$FFFF) + else if (i == 8) + write_prg_byte(0x6000, 3); // Switch to 2nd 64K Block ($10000-$1FFFF) + else if (i == 16) + write_prg_byte(0x6000, 4); // Switch to 128K Block ($20000-$3FFFF) + write_prg_byte(0x8000, 6); // PRG Bank 0 ($8000-$9FFF) + write_prg_byte(0x8001, i); + write_prg_byte(0x8000, 7); // PRG Bank 1 ($A000-$BFFF) + write_prg_byte(0x8001, i + 1); + for (word address = 0x0; address < 0x4000; address += 512) { + dumpPRG(base, address); + } + } + for (word address = 0x4000; address < 0x8000; address += 512) { // Final 2 Banks ($C000-$FFFF) + dumpPRG(base, address); + } + break; + + case 66: // 64K/128K + banks = int_pow(2, prgsize) / 2; + for (int i = 0; i < banks; i++) { // 64K/128K + write_prg_byte(0x8000, i << 4); // bits 4-5 + for (word address = 0x0; address < 0x8000; address += 512) { // 32K Banks ($8000-$FFFF) + dumpPRG(base, address); + } + } + break; + + case 67: // 128K + banks = int_pow(2, prgsize); + for (int i = 0; i < banks; i++) { // 128K + write_reg_byte(0xF800, i); // [WRITE RAM SAFE] + for (word address = 0x0; address < 0x4000; address += 512) { // 16K Banks ($8000-$BFFF) + dumpPRG(base, address); + } + } + break; + + case 68: + case 73: // 128K + banks = int_pow(2, prgsize); + for (int i = 0; i < banks; i++) { // 128K + write_reg_byte(0xF000, i); // [WRITE RAM SAFE] + for (word address = 0x0; address < 0x4000; address += 512) { // 16K Banks ($8000-$BFFF) + dumpPRG(base, address); + } + } + break; + + case 69: // 128K/256K + banks = int_pow(2, prgsize) * 2; + write_prg_byte(0x8000, 8); // Command Register - PRG Bank 0 + write_prg_byte(0xA000, 0); // Parameter Register - PRG RAM Disabled, PRG ROM, Bank 0 to $6000-$7FFF + for (int i = 0; i < banks; i++) { // 128K/256K + write_prg_byte(0x8000, 9); // Command Register - PRG Bank 1 + write_prg_byte(0xA000, i); // Parameter Register - ($8000-$9FFF) + for (word address = 0x0000; address < 0x2000; address += 512) { // 8K Banks ($8000-$9FFF) + dumpPRG(base, address); + } + } + break; + + case 70: + case 89: + case 93: // 128K + case 152: // 64K/128K + banks = int_pow(2, prgsize); + for (int i = 0; i < banks; i++) { // 128K + write_prg_byte(0x8000, i << 4); + for (word address = 0x0; address < 0x4000; address += 512) { // 16K Banks ($8000-$BFFF) + dumpPRG(base, address); + } + } + break; + + case 71: // 64K/128K/256K + banks = int_pow(2, prgsize); + for (int i = 0; i < banks; i++) { + write_prg_byte(0xC000, i); + for (word address = 0x0; address < 0x4000; address += 512) { // 16K Banks ($8000-$BFFF) + dumpPRG(base, address); + } + } + break; + + case 72: // 128K + banks = int_pow(2, prgsize); + write_prg_byte(0x8000, 0); // Reset Register + for (int i = 0; i < banks; i++) { // 128K + write_prg_byte(0x8000, i | 0x80); // PRG Command + Bank + write_prg_byte(0x8000, i); // PRG Bank + for (word address = 0x0; address < 0x4000; address += 512) { // 16K Banks ($8000-$BFFF) + dumpPRG(base, address); + } + } + break; + + case 76: + case 88: + case 95: + case 154: // 128K + case 206: // 32K/64K/128K + banks = int_pow(2, prgsize) * 2; + for (int i = 0; i < banks; i += 2) { + write_prg_byte(0x8000, 6); // PRG ROM Command ($8000-$9FFF) + write_prg_byte(0x8001, i); // PRG Bank + write_prg_byte(0x8000, 7); // PRG ROM Command ($A000-$BFFF) + write_prg_byte(0x8001, i + 1); // PRG Bank + for (word address = 0x0; address < 0x4000; address += 512) { // 8K Banks ($8000-$BFFF) + dumpPRG(base, address); + } + } + break; + + case 80: // 128K + case 207: // 256K [CART SOMETIMES NEEDS POWERCYCLE] + banks = int_pow(2, prgsize) * 2; + for (int i = 0; i < banks; i += 2) { + write_prg_byte(0x7EFA, i); // PRG Bank 0 ($8000-$9FFF) + write_prg_byte(0x7EFC, i + 1); // PRG Bank 1 ($A000-$BFFF) + for (word address = 0x0; address < 0x4000; address += 512) { + dumpPRG(base, address); + } + } + break; + + case 82: // 128K + banks = int_pow(2, prgsize) * 2; + for (int i = 0; i < banks; i += 2) { + write_prg_byte(0x7EFA, i << 2); // PRG Bank 0 ($8000-$9FFF) + write_prg_byte(0x7EFB, (i + 1) << 2); // PRG Bank 1 ($A000-$BFFF) + for (word address = 0x0; address < 0x4000; address += 512) { // 8K Banks ($8000-$BFFF) + dumpPRG(base, address); + } + } + break; + + case 85: // 128K/512K + banks = int_pow(2, prgsize) * 2; + for (int i = 0; i < banks; i++) { + write_prg_byte(0x8000, i); // PRG Bank 0 ($8000-$9FFF) + for (word address = 0x0; address < 0x2000; address += 512) { // 8K Banks ($8000-$9FFF) + dumpPRG(base, address); + } + } + break; + + case 86: + case 140: // 128K + banks = int_pow(2, prgsize) / 2; + for (int i = 0; i < banks; i++) { // 128K + write_prg_byte(0x6000, i << 4); // bits 4-5 + for (word address = 0x0; address < 0x8000; address += 512) { // 32K Banks ($8000-$FFFF) + dumpPRG(base, address); + } + } + break; + + case 92: // 256K + banks = int_pow(2, prgsize); + write_prg_byte(0x8000, 0); // Reset Register + for (int i = 0; i < banks; i++) { // 256K + write_prg_byte(0x8000, i | 0x80); // PRG Command + Bank + write_prg_byte(0x8000, i); // PRG Bank + for (word address = 0x4000; address < 0x8000; address += 512) { // 16K Banks ($C000-$FFFF) + dumpPRG(base, address); + } + } + break; + + case 94: + banks = int_pow(2, prgsize); + for (int i = 0; i < banks; i++) { // 128K + write_prg_byte(0x8000, i << 2); + for (word address = 0x0; address < 0x4000; address += 512) { + dumpPRG(base, address); + } + } + break; + + case 97: // 256K + banks = int_pow(2, prgsize); + for (int i = 0; i < banks; i++) { // 256K + write_prg_byte(0x8000, i); // PRG Bank + for (word address = 0x4000; address < 0x8000; address += 512) { // 16K Banks ($C000-$FFFF) + dumpPRG(base, address); + } + } + break; + + case 105: // 256K + write_mmc1_byte(0xA000, 0x00); // Clear PRG Init/IRQ (Bit 4) + write_mmc1_byte(0xA000, 0x10); // Set PRG Init/IRQ (Bit 4) to enable bank swapping + for (int i = 0; i < 4; i++) { // PRG CHIP 1 128K + write_mmc1_byte(0xA000, i << 1); + for (word address = 0x0; address < 0x8000; address += 512) { // 32K Banks ($8000-$FFFF) + dumpPRG(base, address); + } + } + write_mmc1_byte(0x8000, 0x0C); // Switch 16K Bank ($8000-$BFFF) + Fixed Last Bank ($C000-$FFFF) + write_mmc1_byte(0xA000, 0x08); // Select PRG CHIP 2 (Bit 3) + for (int j = 0; j < 8; j++) { // PRG CHIP 2 128K + write_mmc1_byte(0xE000, j); + for (word address = 0x0; address < 0x4000; address += 512) { // 16K Banks ($8000-$BFFF) + dumpPRG(base, address); + } + } + break; + + case 180: // 128K + banks = int_pow(2, prgsize); + for (int i = 0; i < banks; i++) { + write_prg_byte(0x8000, i); + for (word address = 0x4000; address < 0x8000; address += 512) { // 16K Banks ($C000-$FFFF) + dumpPRG(base, address); + } + } + break; + + case 153: // 512K + banks = int_pow(2, prgsize); + for (int i = 0; i < banks; i++) { // 512K + write_prg_byte(0x8000, i >> 4); // PRG Outer Bank (Documentation says duplicate over $8000-$8003 registers) + write_prg_byte(0x8001, i >> 4); // PRG Outer Bank + write_prg_byte(0x8002, i >> 4); // PRG Outer Bank + write_prg_byte(0x8003, i >> 4); // PRG Outer Bank + write_prg_byte(0x8008, i & 0xF); // PRG Inner Bank + for (word address = 0x0000; address < 0x4000; address += 512) { // 16K Banks ($8000-$BFFF) + dumpPRG(base, address); + } + } + break; + + case 210: // 128K/256K + banks = int_pow(2, prgsize) * 2; + for (int i = 0; i < banks; i += 2) { + write_prg_byte(0xE000, i); // PRG Bank 0 ($8000-$9FFF) [WRITE NO RAM] + write_prg_byte(0xE800, i + 1); // PRG Bank 1 ($A000-$BFFF) [WRITE NO RAM] + for (word address = 0x0; address < 0x4000; address += 512) { + dumpPRG(base, address); + } + } + break; + } + sdFile.flush(); + sdFile.close(); + + println_Msg(F("PRG FILE DUMPED!")); + println_Msg(F("")); + display_Update(); + + calcCRC(fileName, prg * 1024); + } + set_address(0); + PHI2_HI; + ROMSEL_HI; + LED_BLUE_OFF; +} + +void readCHR() { + + display_Clear(); + display_Update(); + + LED_GREEN_ON; + set_address(0); + _delay_us(1); + if (chrsize == 0) { + println_Msg(F("CHR SIZE 0K")); + display_Update(); + } + else { + CreateCHRFileInSD(); + if (sdFile) { + switch (mapper) { + case 0: // 8K + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + break; + + case 1: + case 155: + banks = int_pow(2, chrsize); + for (int i = 0; i < banks; i += 2) { // 8K/16K/32K/64K/128K (Bank #s are based on 4K Banks) + write_prg_byte(0x8000, 0x80); // Clear Register + write_mmc1_byte(0xA000, i); + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + + case 3: // 8K/16K/32K + case 66: // 16K/32K + case 70: + case 152: // 128K + banks = int_pow(2, chrsize) / 2; + for (int i = 0; i < banks; i++) { // 8K Banks + write_prg_byte(0x8000, i); // CHR Bank 0 + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + + case 4: + case 47: + case 118: + case 119: + banks = int_pow(2, chrsize) * 4; + if (mapper == 47) + write_prg_byte(0xA001, 0x80); // Block Register - PRG RAM Chip Enable, Writable + for (int i = 0; i < banks; i += 4) { // 8K/16K/32K/64K/128K/256K + if (mapper == 47) { + if (i == 0) + write_prg_byte(0x6000, 0); // Switch to Lower Block + else if (i == 128) + write_prg_byte(0x6000, 1); // Switch to Upper Block + } + write_prg_byte(0x8000, 0); // CHR Bank 0 ($0000-$07FF) + write_prg_byte(0x8001, i); + write_prg_byte(0x8000, 1); // CHR Bank 1 ($0800-$0FFF) + write_prg_byte(0x8001, i + 2); + for (word address = 0x0; address < 0x1000; address += 512) { + dumpCHR(address); + } + } + break; + + case 5: // 128K/256K/512K + banks = int_pow(2, chrsize) / 2; + write_prg_byte(0x5101, 0); // 8K CHR Banks + for (int i = 0; i < banks; i++) { + if (i == 0) + write_prg_byte(0x5130, 0); // Set Upper 2 bits + else if (i == 8) + write_prg_byte(0x5130, 1); // Set Upper 2 bits + else if (i == 16) + write_prg_byte(0x5130, 2); // Set Upper 2 bits + else if (i == 24) + write_prg_byte(0x5130, 3); // Set Upper 2 bits + write_prg_byte(0x5127, i); + for (word address = 0x0; address < 0x2000; address += 512) { // ($0000-$1FFF) + dumpCHR(address); + } + } + break; + + case 9: + case 10: // Mapper 9: 128K, Mapper 10: 64K/128K + if (mapper == 9) + banks = 32; + else // Mapper 10 + banks = int_pow(2, chrsize); + for (int i = 0; i < banks; i++) { // 64K/128K + write_prg_byte(0xB000, i); + write_prg_byte(0xC000, i); + for (word address = 0x0; address < 0x1000; address += 512) { + dumpCHR(address); + } + } + break; + + case 16: + case 159: // 128K/256K + banks = int_pow(2, chrsize) * 4; + for (int i = 0; i < banks; i++) { + write_prg_byte(0x6000, i); // Submapper 4 + write_prg_byte(0x8000, i); // Submapper 5 + for (word address = 0x0; address < 0x400; address += 512) { + dumpCHR(address); + } + } + break; + + case 18: // 128K/256K + banks = int_pow(2, chrsize) * 4; + for (int i = 0; i < banks; i++) { + write_prg_byte(0xA000, i & 0xF); // CHR Bank Lower 4 bits + write_prg_byte(0xA001, (i >> 4) & 0xF); // CHR Bank Upper 4 bits + for (word address = 0x0; address < 0x400; address += 512) { + dumpCHR(address); + } + } + break; + + case 19: // 128K/256K + for (int j = 0; j < 64; j++) { // Init Register + write_ram_byte(0xE800, 0xC0); // CHR RAM High/Low Disable (ROM Enable) + } + banks = int_pow(2, chrsize) * 4; + write_ram_byte(0xE800, 0xC0); // CHR RAM High/Low Disable (ROM Enable) + for (int i = 0; i < banks; i += 8) { + write_prg_byte(0x8000, i); // CHR Bank 0 + write_prg_byte(0x8800, i + 1); // CHR Bank 1 + write_prg_byte(0x9000, i + 2); // CHR Bank 2 + write_prg_byte(0x9800, i + 3); // CHR Bank 3 + write_prg_byte(0xA000, i + 4); // CHR Bank 4 + write_prg_byte(0xA800, i + 5); // CHR Bank 5 + write_prg_byte(0xB000, i + 6); // CHR Bank 6 + write_prg_byte(0xB800, i + 7); // CHR Bank 7 + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + + case 21: // 128K/256K + banks = int_pow(2, chrsize) * 4; + for (int i = 0; i < banks; i++) { + write_prg_byte(0xB000, i & 0xF); // CHR Bank Lower 4 bits + if (banks == 128) + write_prg_byte(0xB002, (i >> 4) & 0xF); // CHR Bank Upper 4 bits VRC4a (Wai Wai World 2) + else // banks == 256 + write_prg_byte(0xB040, (i >> 4) & 0xF); // CHR Bank Upper 4 bits VRC4c (Ganbare Goemon Gaiden 2) + for (word address = 0x0; address < 0x400; address += 512) { + dumpCHR(address); + } + } + break; + + case 22: // 128K + banks = int_pow(2, chrsize) * 4; + for (int i = 0; i < banks; i++) { + write_prg_byte(0xB000, (i << 1) & 0xF); // CHR Bank Lower 4 bits + write_prg_byte(0xB002, (i >> 3) & 0xF); // CHR Bank Upper 4 bits + for (word address = 0x0; address < 0x400; address += 512) { + dumpCHR(address); + } + } + break; + + case 23: // 128K + // Detect VRC4e Carts - read PRG 0x1FFF6 (DATE) + // Boku Dracula-kun = 890810, Tiny Toon = 910809 + write_prg_byte(0x8000, 15); + prgchk0 = read_prg_byte(0x9FF6); + prgchk1 = read_prg_byte(0x9FF7); + if ((prgchk0 == 0x30) && (prgchk1 == 0x38)) { // Check for "08" in middle of date + vrc4e = true; // VRC4e Cart + } + banks = int_pow(2, chrsize) * 4; + for (int i = 0; i < banks; i++) { + write_prg_byte(0xB000, i & 0xF); // CHR Bank Lower 4 bits + if (vrc4e == true) + write_prg_byte(0xB004, (i >> 4) & 0xF); // CHR Bank Upper 4 bits VRC4e (Boku Dracula-kun/Tiny Toon) + else + write_prg_byte(0xB001, (i >> 4) & 0xF); // CHR Bank Upper 4 bits VRC2b/VRC4f + for (word address = 0x0; address < 0x400; address += 512) { + dumpCHR(address); + } + } + break; + + case 24: // 128K + banks = int_pow(2, chrsize) * 4; + write_prg_byte(0xB003, 0); // PPU Banking Mode 0 + for (int i = 0; i < banks; i += 8) { + write_prg_byte(0xD000, i); // CHR Bank 0 + write_prg_byte(0xD001, i + 1); // CHR Bank 1 + write_prg_byte(0xD002, i + 2); // CHR Bank 2 + write_prg_byte(0xD003, i + 3); // CHR Bank 3 + write_prg_byte(0xE000, i + 4); // CHR Bank 4 [WRITE NO RAM] + write_prg_byte(0xE001, i + 5); // CHR Bank 5 [WRITE NO RAM] + write_prg_byte(0xE002, i + 6); // CHR Bank 6 [WRITE NO RAM] + write_prg_byte(0xE003, i + 7); // CHR Bank 7 [WRITE NO RAM] + for (word address = 0x0; address < 0x2000; address += 512) { // 1K Banks + dumpCHR(address); + } + } + break; + + case 25: // 128K/256K + banks = int_pow(2, chrsize) * 4; + for (int i = 0; i < banks; i++) { + write_prg_byte(0xB000, i & 0xF); // CHR Bank Lower 4 bits + if ((ramsize > 0) || (banks == 128)) // VRC2c (Ganbare Goemon Gaiden)/VRC4b (Bio Miracle/Gradius 2/Racer Mini) + write_prg_byte(0xB002, (i >> 4) & 0xF); // CHR Bank Upper 4 bits VRC2c/VRC4b + else + write_prg_byte(0xB008, (i >> 4) & 0xF); // CHR Bank Upper 4 bits VRC4d (Teenage Mutant Ninja Turtles) + for (word address = 0x0; address < 0x400; address += 512) { + dumpCHR(address); + } + } + break; + + case 26: // 128K/256K + banks = int_pow(2, chrsize) * 4; + write_prg_byte(0xB003, 0); // PPU Banking Mode 0 + for (int i = 0; i < banks; i += 8) { + write_prg_byte(0xD000, i); // CHR Bank 0 + write_prg_byte(0xD002, i + 1); // CHR Bank 1 + write_prg_byte(0xD001, i + 2); // CHR Bank 2 + write_prg_byte(0xD003, i + 3); // CHR Bank 3 + write_reg_byte(0xE000, i + 4); // CHR Bank 4 [WRITE RAM SAFE] + write_reg_byte(0xE002, i + 5); // CHR Bank 5 [WRITE RAM SAFE] + write_reg_byte(0xE001, i + 6); // CHR Bank 6 [WRITE RAM SAFE] + write_reg_byte(0xE003, i + 7); // CHR Bank 7 [WRITE RAM SAFE] + for (word address = 0x0; address < 0x2000; address += 512) { // 1K Banks + dumpCHR(address); + } + } + break; + + case 32: // 128K + case 65: // 128K/256K + banks = int_pow(2, chrsize) * 4; + for (int i = 0; i < banks; i += 8) { + write_prg_byte(0xB000, i); // CHR Bank 0 + write_prg_byte(0xB001, i + 1); // CHR Bank 1 + write_prg_byte(0xB002, i + 2); // CHR Bank 2 + write_prg_byte(0xB003, i + 3); // CHR Bank 3 + write_prg_byte(0xB004, i + 4); // CHR Bank 4 + write_prg_byte(0xB005, i + 5); // CHR Bank 5 + write_prg_byte(0xB006, i + 6); // CHR Bank 6 + write_prg_byte(0xB007, i + 7); // CHR Bank 7 + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + + case 33: // 128K/256K + case 48: // 256K + banks = int_pow(2, chrsize) * 2; + for (int i = 0; i < banks; i += 2) { // 2K Banks + write_prg_byte(0x8002, i); // CHR Bank 0 + write_prg_byte(0x8003, i + 1); // CHR Bank 1 + for (word address = 0x0; address < 0x1000; address += 512) { + dumpCHR(address); + } + } + break; + + case 37: + banks = int_pow(2, chrsize) * 4; + write_prg_byte(0xA001, 0x80); // Block Register - PRG RAM Chip Enable, Writable + for (int i = 0; i < banks; i += 4) { // 256K + if (i == 0) + write_prg_byte(0x6000, 0); // Switch to Lower Block ($00000-$1FFFF) + else if (i == 128) + write_prg_byte(0x6000, 4); // Switch to Upper Block ($20000-$3FFFF) + write_prg_byte(0x8000, 0); // CHR Bank 0 ($0000-$07FF) + write_prg_byte(0x8001, i); + write_prg_byte(0x8000, 1); // CHR Bank 1 ($0800-$0FFF) + write_prg_byte(0x8001, i + 2); + for (word address = 0x0; address < 0x1000; address += 512) { + dumpCHR(address); + } + } + break; + + case 67: // 128K + banks = int_pow(2, chrsize) * 2; + for (int i = 0; i < banks; i += 4) { // 2K Banks + write_prg_byte(0x8800, i); // CHR Bank 0 + write_prg_byte(0x9800, i + 1); // CHR Bank 1 + write_prg_byte(0xA800, i + 2); // CHR Bank 2 + write_prg_byte(0xB800, i + 3); // CHR Bank 3 + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + + case 68: // 128K/256K + banks = int_pow(2, chrsize) * 2; + for (int i = 0; i < banks; i += 4) { // 2K Banks + write_prg_byte(0x8000, i); // CHR Bank 0 + write_prg_byte(0x9000, i + 1); // CHR Bank 1 + write_prg_byte(0xA000, i + 2); // CHR Bank 2 + write_prg_byte(0xB000, i + 3); // CHR Bank 3 + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + + case 69: // 128K/256K + banks = int_pow(2, chrsize) * 4; + for (int i = 0; i < banks; i++) { + write_prg_byte(0x8000, 0); // Command Register - CHR Bank 0 + write_prg_byte(0xA000, i); // Parameter Register - ($0000-$03FF) + for (word address = 0x0; address < 0x400; address += 512) { // 1K Banks + dumpCHR(address); + } + } + break; + + case 72: // 128K + banks = int_pow(2, chrsize) / 2; + write_prg_byte(0x8000, 0); // Reset Register + for (int i = 0; i < banks; i++) { // 8K Banks + write_prg_byte(0x8000, i | 0x40); // CHR Command + Bank + write_prg_byte(0x8000, i); // CHR Bank + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + + case 75: // 128K + banks = int_pow(2, chrsize); + for (int i = 0; i < banks; i++) { // 4K Banks + write_reg_byte(0xE000, i); // CHR Bank Low Bits [WRITE RAM SAFE] + write_prg_byte(0x9000, (i & 0x10) >> 3); // High Bit + for (word address = 0x0; address < 0x1000; address += 512) { + dumpCHR(address); + } + } + break; + + case 76: // 128K + banks = int_pow(2, chrsize) * 2; + for (int i = 0; i < banks; i += 2) { // 2K Banks + write_prg_byte(0x8000, 2); // CHR Command ($0000-$07FF) 2K Bank + write_prg_byte(0x8001, i); // CHR Bank + write_prg_byte(0x8000, 3); // CHR Command ($0800-$0FFF) 2K Bank + write_prg_byte(0x8001, i + 1); // CHR Bank + for (word address = 0x0000; address < 0x1000; address += 512) { + dumpCHR(address); + } + } + break; + + case 77: // 32K + banks = int_pow(2, chrsize) * 2; + for (int i = 0; i < banks; i++) { // 2K Banks + write_prg_byte(0x8000, i << 4); // CHR Bank 0 + for (word address = 0x0; address < 0x800; address += 512) { + dumpCHR(address); + } + } + break; + + case 78: // 128K + banks = int_pow(2, chrsize) / 2; + for (int i = 0; i < banks; i++) { // 8K Banks + write_prg_byte(0x8000, i << 4); // CHR Bank 0 + for (word address = 0x0; address < 0x2000; address += 512) { // 8K Banks ($0000-$1FFF) + dumpCHR(address); + } + } + break; + + case 80: // 128K/256K + case 82: // 128K/256K + case 207: // 128K [CART SOMETIMES NEEDS POWERCYCLE] + banks = int_pow(2, chrsize) * 4; + for (int i = 0; i < banks; i += 4) { + write_prg_byte(0x7EF2, i); // CHR Bank 2 [REGISTERS 0x7EF0/0x7EF1 WON'T WORK] + write_prg_byte(0x7EF3, i + 1); // CHR Bank 3 + write_prg_byte(0x7EF4, i + 2); // CHR Bank 4 + write_prg_byte(0x7EF5, i + 3); // CHR Bank 5 + for (word address = 0x1000; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + + case 85: // 128K + banks = int_pow(2, chrsize) * 4; + for (int i = 0; i < banks; i += 8) { + write_prg_byte(0xA000, i); // CHR Bank 0 + write_prg_byte(0xA008, i + 1); // CHR Bank 1 + write_prg_byte(0xB000, i + 2); // CHR Bank 2 + write_prg_byte(0xB008, i + 3); // CHR Bank 3 + write_prg_byte(0xC000, i + 4); // CHR Bank 4 + write_prg_byte(0xC008, i + 5); // CHR Bank 5 + write_prg_byte(0xD000, i + 6); // CHR Bank 6 + write_prg_byte(0xD008, i + 7); // CHR Bank 7 + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + + case 86: // 64K + banks = int_pow(2, chrsize) / 2; + for (int i = 0; i < banks; i++) { // 8K Banks + if (i < 4) + write_prg_byte(0x6000, i & 0x3); + else + write_prg_byte(0x6000, (i | 0x40) & 0x43); + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + + case 87: // 16K/32K + banks = int_pow(2, chrsize) / 2; + for (int i = 0; i < banks; i++) { // 16K/32K + write_prg_byte(0x6000, (((i & 0x1) << 1) | ((i & 0x2) >> 1))); + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + + case 88: // 128K + case 95: // 32K + case 154: // 128K + case 206: // 16K/32K/64K + banks = int_pow(2, chrsize) * 4; + for (int i = 0; i < banks; i += 2) { // 1K Banks + if (i < 64) { + write_prg_byte(0x8000, 0); // CHR Command ($0000-$07FF) 2K Bank + write_prg_byte(0x8001, i & 0x3F); // CHR Bank + for (word address = 0x0; address < 0x800; address += 512) { + dumpCHR(address); + } + } + else { + write_prg_byte(0x8000, 2); // CHR Command ($1000-$13FF) 1K Bank + write_prg_byte(0x8001, i); // CHR Bank + write_prg_byte(0x8000, 3); // CHR Command ($1400-$17FF) 1K Bank + write_prg_byte(0x8001, i + 1); // CHR Bank + for (word address = 0x1000; address < 0x1800; address += 512) { + dumpCHR(address); + } + } + } + break; + + case 89: // 128K + banks = int_pow(2, chrsize) / 2; + for (int i = 0; i < banks; i++) { // 8K Banks + if (i < 8) + write_prg_byte(0x8000, i & 0x7); + else + write_prg_byte(0x8000, (i | 0x80) & 0x87); + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + + case 92: // 128K + banks = int_pow(2, chrsize) / 2; + write_prg_byte(0x8000, 0); // Reset Register + for (int i = 0; i < banks; i++) { // 8K Banks + write_prg_byte(0x8000, i | 0x40); // CHR Command + Bank + write_prg_byte(0x8000, i); // CHR Bank + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + + case 140: // 32K/128K + banks = int_pow(2, chrsize) / 2; + for (int i = 0; i < banks; i++) { // 8K Banks + write_prg_byte(0x6000, i); + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + + case 184: // 16K/32K + banks = int_pow(2, chrsize); + for (int i = 0; i < banks; i++) { // 4K Banks + write_prg_byte(0x6000, i); // CHR LOW (Bits 0-2) ($0000-$0FFF) + for (word address = 0x0; address < 0x1000; address += 512) { // 4K Banks ($0000-$0FFF) + dumpCHR(address); + } + } + break; + + case 185: // 8K [READ 32K TO OVERRIDE LOCKOUT] + for (int i = 0; i < 4; i++) { // Read 32K to locate valid 8K + write_prg_byte(0x8000, i); + byte chrcheck = read_chr_byte(0); + for (word address = 0x0; address < 0x2000; address += 512) { + for (int x = 0; x < 512; x++) { + sdBuffer[x] = read_chr_byte(address + x); + } + if (chrcheck != 0xFF) + sdFile.write(sdBuffer, 512); + } + } + break; + + case 210: // 128K/256K + banks = int_pow(2, chrsize) * 4; + write_prg_byte(0xE800, 0xC0); // CHR RAM DISABLE (Bit 6 and 7) [WRITE NO RAM] + for (int i = 0; i < banks; i += 8) { + write_prg_byte(0x8000, i); // CHR Bank 0 + write_prg_byte(0x8800, i + 1); // CHR Bank 1 + write_prg_byte(0x9000, i + 2); // CHR Bank 2 + write_prg_byte(0x9800, i + 3); // CHR Bank 3 + write_prg_byte(0xA000, i + 4); // CHR Bank 4 + write_prg_byte(0xA800, i + 5); // CHR Bank 5 + write_prg_byte(0xB000, i + 6); // CHR Bank 6 + write_prg_byte(0xB800, i + 7); // CHR Bank 7 + for (word address = 0x0; address < 0x2000; address += 512) { + dumpCHR(address); + } + } + break; + } + sdFile.flush(); + sdFile.close(); + + println_Msg(F("CHR FILE DUMPED!")); + println_Msg(F("")); + display_Update(); + + calcCRC(fileName, chr * 1024); + } + } + set_address(0); + PHI2_HI; + ROMSEL_HI; + LED_GREEN_OFF; +} + +/****************************************** + RAM Functions + *****************************************/ +void readRAM() { + display_Clear(); + display_Update(); + + LED_BLUE_ON; + LED_GREEN_ON; + set_address(0); + _delay_us(1); + if (ramsize == 0) { + + println_Msg(F("RAM SIZE 0K")); + display_Update(); + } + else { + CreateRAMFileInSD(); + word base = 0x6000; + if (sdFile) { + switch (mapper) { + case 1: + case 155: // 8K/16K/32K + banks = int_pow(2, ramsize) / 2; // banks = 1,2,4 + for (int i = 0; i < banks; i++) { // 8K Banks ($6000-$7FFF) + write_prg_byte(0x8000, 0x80); // Clear Register + write_mmc1_byte(0x8000, 1 << 3); + write_mmc1_byte(0xE000, 0); + if (banks == 4) // 32K + write_mmc1_byte(0xA000, i << 2); + else + write_mmc1_byte(0xA000, i << 3); + for (word address = 0x0; address < 0x2000; address += 512) { // 8K + dumpPRG(base, address); + } + } + break; + + case 5: // 8K/16K/32K + write_prg_byte(0x5100, 3); // 8K PRG Banks + banks = int_pow(2, ramsize) / 2; // banks = 1,2,4 + if (banks == 2) { // 16K - Split SRAM Chips 8K/8K + for (int i = 0; i < (banks / 2); i++) { // Chip 1 + write_prg_byte(0x5113, i); + for (word address = 0; address < 0x2000; address += 512) { // 8K + dumpMMC5RAM(base, address); + } + } + for (int j = 4; j < (banks / 2) + 4; j++) { // Chip 2 + write_prg_byte(0x5113, j); + for (word address = 0; address < 0x2000; address += 512) { // 8K + dumpMMC5RAM(base, address); + } + } + } + else { // 8K/32K Single SRAM Chip + for (int i = 0; i < banks; i++) { // banks = 1 or 4 + write_prg_byte(0x5113, i); + for (word address = 0; address < 0x2000; address += 512) { // 8K + dumpMMC5RAM(base, address); + } + } + } + break; + + case 16: // 256-byte EEPROM 24C02 + case 159: // 128-byte EEPROM 24C01 [Little Endian] + if (mapper == 159) + eepsize = 128; + else + eepsize = 256; + for (word address = 0; address < eepsize; address++) { + EepromREAD(address); + } + sdFile.write(sdBuffer, eepsize); + // display_Clear(); // TEST PURPOSES - DISPLAY EEPROM DATA + break; + + default: + if ((mapper == 4) || (mapper == 118)) // 8K + write_prg_byte(0xA001, 0xC0); // PRG RAM CHIP ENABLE - Chip Enable, Write Protect + else if (mapper == 19) { + for (int i = 0; i < 64; i++) { // Init Register + write_ram_byte(0xE000, 0); + } + } + else if ((mapper == 21) || (mapper == 25)) // 8K + write_prg_byte(0x8000, 0); + else if (mapper == 26) // 8K + write_prg_byte(0xB003, 0x80); // PRG RAM ENABLE + else if (mapper == 68) // 8K + write_reg_byte(0xF000, 0x10); // PRG RAM ENABLE [WRITE RAM SAFE] + else if (mapper == 69) { // 8K + write_prg_byte(0x8000, 8); // Command Register - PRG Bank 0 + write_prg_byte(0xA000, 0xC0); // Parameter Register - PRG RAM Enabled, PRG RAM, Bank 0 to $6000-$7FFF + } + else if (mapper == 85) // 8K + write_ram_byte(0xE000, 0x80); // PRG RAM ENABLE + else if (mapper == 153) // 8K + write_prg_byte(0x800D, 0x20); // PRG RAM Chip Enable + for (word address = 0; address < 0x2000; address += 512) { // 8K + dumpPRG(base, address); + } + if (mapper == 85) // 8K + write_reg_byte(0xE000, 0); // PRG RAM DISABLE [WRITE RAM SAFE] + break; + } + sdFile.flush(); + sdFile.close(); + + println_Msg(F("RAM FILE DUMPED!")); + println_Msg(F("")); + display_Update(); + + if ((mapper == 16) || (mapper == 159)) + calcCRC(fileName, eepsize); + else + calcCRC(fileName, ram * 1024); + } + } + set_address(0); + PHI2_HI; + ROMSEL_HI; + LED_BLUE_OFF; + LED_GREEN_OFF; +} + +void writeRAM () { + display_Clear(); + + if (ramsize == 0) { + LED_RED_ON; + + println_Msg(F("RAM SIZE 0K")); + display_Update(); + + } + else { + fileBrowser(F("Select RAM File")); + word base = 0x6000; + + sd.chdir(); + sprintf(filePath, "%s/%s", filePath, fileName); + + println_Msg(F("Writing File: ")); + println_Msg(filePath); + println_Msg(fileName); + display_Update(); + + //open file on sd card + if (sdFile.open(filePath, O_READ)) { + switch (mapper) { + case 1: + case 155: + banks = int_pow(2, ramsize) / 2; // banks = 1,2,4 + for (int i = 0; i < banks; i++) { // 8K Banks ($6000-$7FFF) + write_prg_byte(0x8000, 0x80); // Clear Register + write_mmc1_byte(0x8000, 1 << 3); // PRG ROM MODE 32K + write_mmc1_byte(0xE000, 0); // PRG RAM ENABLED + if (banks == 4) // 32K + write_mmc1_byte(0xA000, i << 2); + else + write_mmc1_byte(0xA000, i << 3); + for (word address = 0x0; address < 0x2000; address += 512) { // 8K + sdFile.read(sdBuffer, 512); + for (int x = 0; x < 512; x++) { + write_prg_byte(base + address + x, sdBuffer[x]); + } + } + } + break; + + case 5: // 8K/16K/32K + write_prg_byte(0x5100, 3); // 8K PRG Banks + banks = int_pow(2, ramsize) / 2; // banks = 1,2,4 + if (banks == 2) { // 16K - Split SRAM Chips 8K/8K [ETROM = 16K (ONLY 1ST 8K BATTERY BACKED)] + for (int i = 0; i < (banks / 2); i++) { // Chip 1 + write_prg_byte(0x5113, i); + for (word address = 0; address < 0x2000; address += 512) { // 8K + writeMMC5RAM(base, address); + } + } + for (int j = 4; j < (banks / 2) + 4; j++) { // Chip 2 + write_prg_byte(0x5113, j); + for (word address = 0; address < 0x2000; address += 512) { // 8K + writeMMC5RAM(base, address); + } + } + } + else { // 8K/32K Single SRAM Chip [EKROM = 8K BATTERY BACKED, EWROM = 32K BATTERY BACKED] + for (int i = 0; i < banks; i++) { // banks = 1 or 4 + write_prg_byte(0x5113, i); + for (word address = 0; address < 0x2000; address += 512) { // 8K + writeMMC5RAM(base, address); + } + } + } + break; + + case 16: // 256-byte EEPROM 24C02 + case 159: // 128-byte EEPROM 24C01 [Little Endian] + if (mapper == 159) + eepsize = 128; + else + eepsize = 256; + sdFile.read(sdBuffer, eepsize); + for (word address = 0; address < eepsize; address++) { + EepromWRITE(address); + if ((address % 128) == 0) + display_Clear(); + print_Msg(F(".")); + display_Update(); + } + break; + + default: + if ((mapper == 4) || (mapper == 118)) // 8K + write_prg_byte(0xA001, 0x80); // PRG RAM CHIP ENABLE - Chip Enable, Allow Writes + else if (mapper == 19) { + for (int i = 0; i < 64; i++) { // Init Register + write_ram_byte(0xF800, 0x40); // PRG RAM WRITE ENABLE + } + write_ram_byte(0xF800, 0x40); // PRG RAM WRITE ENABLE + } + else if ((mapper == 21) || (mapper == 25)) // 8K + write_prg_byte(0x8000, 0); + else if (mapper == 26) // 8K + write_prg_byte(0xB003, 0x80); // PRG RAM ENABLE + // else if (mapper == 68) // 8K + // write_reg_byte(0xF000, 0x10); // PRG RAM ENABLE [WRITE RAM SAFE] + else if (mapper == 69) { // 8K + write_prg_byte(0x8000, 8); // Command Register - PRG Bank 0 + write_prg_byte(0xA000, 0xC0); // Parameter Register - PRG RAM Enabled, PRG RAM, Bank 0 to $6000-$7FFF + } + else if (mapper == 85) // 8K + write_ram_byte(0xE000, 0x80); // PRG RAM ENABLE + else if (mapper == 153) // 8K + write_prg_byte(0x800D, 0x20); // PRG RAM Chip Enable + for (word address = 0; address < 0x2000; address += 512) { // 8K + sdFile.read(sdBuffer, 512); + for (int x = 0; x < 512; x++) { + write_prg_byte(base + address + x, sdBuffer[x]); + } + } + if ((mapper == 4) || (mapper == 118)) // 8K + write_prg_byte(0xA001, 0xC0); // PRG RAM CHIP ENABLE - Chip Enable, Write Protect + else if (mapper == 19) + write_ram_byte(0xF800, 0x0F); // PRG RAM WRITE PROTECT + else if (mapper == 26) // 8K + write_prg_byte(0xB003, 0); // PRG RAM DISABLE + // else if (mapper == 68) // 8K + // write_reg_byte(0xF000, 0x00); // PRG RAM DISABLE [WRITE RAM SAFE] + else if (mapper == 69) { // 8K + write_prg_byte(0x8000, 8); // Command Register - PRG Bank 0 + write_prg_byte(0xA000, 0); // Parameter Register - PRG RAM Disabled, PRG ROM, Bank 0 to $6000-$7FFF + } + else if (mapper == 85) // 8K + write_reg_byte(0xE000, 0); // PRG RAM DISABLE [WRITE RAM SAFE] + break; + } + sdFile.close(); + LED_GREEN_ON; + + println_Msg(F("")); + println_Msg(F("RAM FILE WRITTEN!")); + display_Update(); + + } + else { + LED_RED_ON; + println_Msg(F("SD ERROR")); + display_Update(); + } + } + + display_Clear(); + + LED_RED_OFF; + LED_GREEN_OFF; + sd.chdir(); // root + filePath[0] = '\0'; // Reset filePath +} +/****************************************** + Eeprom Functions + *****************************************/ +// EEPROM MAPPING +// 00-01 FOLDER # +// 02-05 SNES/GB READER SETTINGS +// 06 LED - ON/OFF [SNES/GB] +// 07 MAPPER +// 08 PRG SIZE +// 09 CHR SIZE +// 10 RAM SIZE + +void resetEEPROM() { + EEPROM_writeAnything(0, 0); // FOLDER # + EEPROM_writeAnything(2, 0); // CARTMODE + EEPROM_writeAnything(3, 0); // RETRY + EEPROM_writeAnything(4, 0); // STATUS + EEPROM_writeAnything(5, 0); // UNKNOWNCRC + EEPROM_writeAnything(6, 1); // LED (RESET TO ON) + EEPROM_writeAnything(7, 0); // MAPPER + EEPROM_writeAnything(8, 0); // PRG SIZE + EEPROM_writeAnything(9, 0); // CHR SIZE + EEPROM_writeAnything(10, 0); // RAM SIZE +} + +void EepromStart_NES() { + write_prg_byte(0x800D, 0x00); // sda low, scl low + write_prg_byte(0x800D, 0x60); // sda, scl high + write_prg_byte(0x800D, 0x20); // sda low, scl high + write_prg_byte(0x800D, 0x00); // START +} + +void EepromStop_NES() { + write_prg_byte(0x800D, 0x00); // sda, scl low + write_prg_byte(0x800D, 0x20); // sda low, scl high + write_prg_byte(0x800D, 0x60); // sda, scl high + write_prg_byte(0x800D, 0x40); // sda high, scl low + write_prg_byte(0x800D, 0x00); // STOP +} + +void EepromSet0_NES() { + write_prg_byte(0x800D, 0x00); // sda low, scl low + write_prg_byte(0x800D, 0x20); // sda low, scl high // 0 + write_prg_byte(0x800D, 0x00); // sda low, scl low +} + +void EepromSet1_NES() { + write_prg_byte(0x800D, 0x40); // sda high, scl low + write_prg_byte(0x800D, 0x60); // sda high, scl high // 1 + write_prg_byte(0x800D, 0x40); // sda high, scl low + write_prg_byte(0x800D, 0x00); // sda low, scl low +} + +void EepromStatus_NES() { // ACK + write_prg_byte(0x800D, 0x40); // sda high, scl low + write_prg_byte(0x800D, 0x60); // sda high, scl high + write_prg_byte(0x800D, 0xE0); // sda high, scl high, read high + byte eepStatus = 1; + do { + eepStatus = (read_prg_byte(0x6000) & 0x10) >> 4; + delayMicroseconds(4); + } + while (eepStatus == 1); + write_prg_byte(0x800D, 0x40); // sda high, scl low +} + +void EepromReadData_NES() { + // read serial data into buffer + for (int i = 0; i < 8; i++) { + write_prg_byte(0x800D, 0x60); // sda high, scl high, read low + write_prg_byte(0x800D, 0xE0); // sda high, scl high, read high + eepbit[i] = (read_prg_byte(0x6000) & 0x10) >> 4; // Read 0x6000 with Mask 0x10 (bit 4) + write_prg_byte(0x800D, 0x40); // sda high, scl low + } +} + +void EepromDevice_NES() { // 24C02 ONLY + EepromSet1_NES(); + EepromSet0_NES(); + EepromSet1_NES(); + EepromSet0_NES(); + EepromSet0_NES(); // A2 + EepromSet0_NES(); // A1 + EepromSet0_NES(); // A0 +} + +void EepromReadMode_NES() { + EepromSet1_NES(); // READ + EepromStatus_NES(); // ACK +} +void EepromWriteMode_NES() { + EepromSet0_NES(); // WRITE + EepromStatus_NES(); // ACK +} + +void EepromFinish_NES() { + write_prg_byte(0x800D, 0x00); // sda low, scl low + write_prg_byte(0x800D, 0x40); // sda high, scl low + write_prg_byte(0x800D, 0x60); // sda high, scl high + write_prg_byte(0x800D, 0x40); // sda high, scl low + write_prg_byte(0x800D, 0x00); // sda low, scl low +} + +void EepromSetAddress01(byte address) { // 24C01 [Little Endian] + for (int i = 0; i < 7; i++) { + if (address & 0x1) // Bit is HIGH + EepromSet1_NES(); + else // Bit is LOW + EepromSet0_NES(); + address >>= 1; // rotate to the next bit + } +} + +void EepromSetAddress02(byte address) { // 24C02 + for (int i = 0; i < 8; i++) { + if ((address >> 7) & 0x1) // Bit is HIGH + EepromSet1_NES(); + else // Bit is LOW + EepromSet0_NES(); + address <<= 1; // rotate to the next bit + } + EepromStatus_NES(); // ACK +} + +void EepromWriteData01() { // 24C01 [Little Endian] + for (int i = 0; i < 8; i++) { + if (eeptemp & 0x1) // Bit is HIGH + EepromSet1_NES(); + else // Bit is LOW + EepromSet0_NES(); + eeptemp >>= 1; // rotate to the next bit + } + EepromStatus_NES(); // ACK +} + +void EepromWriteData02() { // 24C02 + for (int i = 0; i < 8; i++) { + if ((eeptemp >> 7) & 0x1) // Bit is HIGH + EepromSet1_NES(); + else // Bit is LOW + EepromSet0_NES(); + eeptemp <<= 1; // rotate to the next bit + } + EepromStatus_NES(); // ACK +} + +void EepromREAD(byte address) { + EepromStart_NES(); // START + if (mapper == 159) { // 24C01 + EepromSetAddress01(address); // 24C01 [Little Endian] + EepromReadMode_NES(); + EepromReadData_NES(); + EepromFinish_NES(); + EepromStop_NES(); // STOP + // OR 8 bits into byte + eeptemp = eepbit[7] << 7 | eepbit[6] << 6 | eepbit[5] << 5 | eepbit[4] << 4 | eepbit[3] << 3 | eepbit[2] << 2 | eepbit[1] << 1 | eepbit[0]; + } + else { // 24C02 + EepromDevice_NES(); // DEVICE [1010] + ADDR [A2-A0] + EepromWriteMode_NES(); + EepromSetAddress02(address); + EepromStart_NES(); // START + EepromDevice_NES(); // DEVICE [1010] + ADDR [A2-A0] + EepromReadMode_NES(); + EepromReadData_NES(); + EepromFinish_NES(); + EepromStop_NES(); // STOP + // OR 8 bits into byte + eeptemp = eepbit[0] << 7 | eepbit[1] << 6 | eepbit[2] << 5 | eepbit[3] << 4 | eepbit[4] << 3 | eepbit[5] << 2 | eepbit[6] << 1 | eepbit[7]; + } + sdBuffer[address] = eeptemp; +} + +void EepromWRITE(byte address) { + eeptemp = sdBuffer[address]; + EepromStart_NES(); // START + if (mapper == 159) { // 24C01 + EepromSetAddress01(address); // 24C01 [Little Endian] + EepromWriteMode_NES(); + EepromWriteData01(); // 24C01 [Little Endian] + } + else { // 24C02 + EepromDevice_NES(); // DEVICE [1010] + ADDR [A2-A0] + EepromWriteMode_NES(); + EepromSetAddress02(address); + EepromWriteData02(); + } + EepromStop_NES(); // STOP +} //****************************************** // End of File diff --git a/Cart_Reader/NP.ino b/Cart_Reader/NP.ino index 222b960..544a324 100644 --- a/Cart_Reader/NP.ino +++ b/Cart_Reader/NP.ino @@ -1,7 +1,7 @@ //****************************************** // NINTENDO POWER MODULE -// (GB Memory starts at around line 1460) //****************************************** +// (GB Memory starts at around line 1460) /****************************************** SF Memory Cassette diff --git a/Cart_Reader/SMS.ino b/Cart_Reader/SMS.ino index 18875ef..cd4700e 100644 --- a/Cart_Reader/SMS.ino +++ b/Cart_Reader/SMS.ino @@ -196,11 +196,11 @@ void getCartInfo_SMS() { println_Msg(F(" ")); // Wait for user input - if (enable_OLED) { - println_Msg(F("Press Button...")); - display_Update(); - wait(); - } +#ifdef enable_OLED + println_Msg(F("Press Button...")); + display_Update(); + wait(); +#endif } // Read rom and save to the SD card diff --git a/Cart_Reader/SNES.ino b/Cart_Reader/SNES.ino index 989fe9f..4df2440 100644 --- a/Cart_Reader/SNES.ino +++ b/Cart_Reader/SNES.ino @@ -626,17 +626,15 @@ void getCartInfo_SNES() { display_Update(); // Wait for user input - if (enable_OLED) { - println_Msg(F(" ")); - println_Msg(F(" ")); - println_Msg(F("Press Button...")); - display_Update(); - wait(); - } - - else if (enable_Serial) { - println_Msg(F(" ")); - } +#ifdef enable_OLED + println_Msg(F(" ")); + println_Msg(F(" ")); + println_Msg(F("Press Button...")); + display_Update(); + wait(); +#else + println_Msg(F(" ")); +#endif // Start manual config if (manualConfig == 1) {