sakman55 9d02ed296d
Update NES.ino
Updated Mapper 45 to handle Dynavision 101 Games and 106 Games carts (4096K PRG).  Added Mappers 237, 289, 319, and 332.  Mappers 289 and 332 need pulsing M2 code to dump properly.
2025-01-20 22:32:51 -10:00

4971 lines
158 KiB
C++

//******************************************
// NES MODULE
//******************************************
// mostly copy&pasted from "Famicom Dumper" 2019-08-31 by skaman
// also based on "CoolArduino" by HardWareMan
// Pinout changes: LED and CIRAM_A10
#ifdef ENABLE_NES
//Line Content
//37 Supported Mappers
//185 Defines
//211 Variables
//242 Menus
//456 Setup
//486 No-Intro SD Database Functions
//803 Low Level Functions
//1012 File Functions
//1083 Config Functions
//1704 ROM Functions
//3534 RAM Functions
//3934 Eeprom Functions
//4122 NESmaker Flash Cart Functions
struct mapper_NES {
uint16_t mapper;
uint8_t prglo;
uint8_t prghi;
uint8_t chrlo;
uint8_t chrhi;
uint8_t ramlo;
uint8_t ramhi;
};
/******************************************
Supported Mappers
*****************************************/
// Supported Mapper Array (iNES Mapper #s)
// Format = {mapper,prglo,prghi,chrlo,chrhi,ramlo,ramhi}
static const struct mapper_NES PROGMEM mapsize[] = {
{ 0, 0, 1, 0, 1, 0, 2 }, // Nintendo NROM [sram r/w]
{ 1, 1, 5, 0, 5, 0, 3 }, // Nintendo SxROM (MMC1B) [sram r/w]
{ 2, 2, 4, 0, 0, 0, 0 }, // Nintendo UxROM
{ 3, 0, 1, 0, 9, 0, 0 }, // Nintendo CNROM
{ 4, 1, 5, 0, 6, 0, 1 }, // Nintendo TxROM/HKROM (MMC3) [sram/prgram r/w]
{ 5, 3, 5, 5, 7, 0, 3 }, // Nintendo ExROM (MMC5) [sram r/w]
// 6 - irrelevant (FFE RAM cartridges)
{ 7, 2, 4, 0, 0, 0, 0 }, // Nintendo AxROM
// 8 - irrelevant (FFE RAM cartridges)
{ 9, 0, 3, 0, 5, 0, 0 }, // Nintendo PNROM (MMC2)
{ 10, 0, 4, 4, 5, 1, 1 }, // Nintendo FJROM/FKROM (MMC4) [sram r/w]
{ 11, 1, 3, 1, 5, 0, 0 }, // Color Dreams
{ 12, 0, 5, 0, 7, 0, 0 }, // 哥德 [Gēdé] SL-5020B
{ 13, 1, 1, 0, 0, 0, 0 }, // Nintendo CPROM (Videomation)
// 14 哥德 [Gēdé] SL-1632 (武士魂/Samurai Spirits 8 characters version) [TODO]
{ 15, 6, 6, 0, 0, 0, 0 }, // K-1029/K-1030P
{ 16, 3, 4, 5, 6, 0, 1 }, // Bandai FCG [eep r/w]
// 17 - irrelevant (FFE RAM cartridges)
{ 18, 3, 4, 5, 6, 0, 1 }, // Jaleco SS 8806 [sram r/w]
{ 19, 3, 4, 5, 6, 0, 1 }, // Namco N129/N163 [sram/prgram r/w]
// 20 - irrelenvant (FDS emulation)
{ 21, 4, 4, 5, 6, 0, 1 }, // Konami VRC4a/VRC4c [sram r/w]
{ 22, 3, 3, 5, 5, 0, 0 }, // Konami 351618 (VRC2a)
{ 23, 3, 3, 5, 6, 0, 0 }, // Konami VRC2b/VRC4e/VRC4f
{ 24, 4, 4, 5, 5, 0, 0 }, // Konami 351951 (VRC6a)
{ 25, 3, 4, 5, 6, 0, 1 }, // Konami VRC2c/VRC4b/VRC4d [sram r/w]
{ 26, 4, 4, 5, 6, 1, 1 }, // Konami 351949A (VRC6b) [sram r/w]
// 27 - CC-21, not used
{ 28, 5, 7, 0, 0, 0, 0 }, // Action 53
{ 29, 0, 3, 0, 3, 0, 0 }, // RET-CUFROM (Sealie Computing)
{ 30, 4, 5, 0, 0, 0, 0 }, // UNROM-512
{ 31, 6, 6, 0, 0, 0, 0 }, // 2A03 Puritans Album
{ 32, 3, 4, 5, 5, 0, 0 }, // Irem G-101
{ 33, 3, 4, 5, 6, 0, 0 }, // Taito TC0190/TC0390
{ 34, 1, 9, 0, 4, 0, 0 }, // AVE NINA-001/Nintendo BNROM
{ 35, 0, 7, 1, 8, 0, 0 }, // 晶太 [Jīngtài] EL870914C
{ 36, 0, 3, 1, 5, 0, 0 }, // TXC 01-22000-200/400
{ 37, 4, 4, 6, 6, 0, 0 }, // Nintendo ZZ (3-in-1)
{ 38, 1, 3, 0, 3, 0, 0 }, // 普澤 [Bit Corp.] PCI556 (Crime Busters)
{ 39, 3, 5, 0, 0, 0, 0 }, // duplicate of 241
// 40 - NTDEC 2722/2752 [TODO]
{ 41, 4, 4, 5, 5, 0, 0 }, // NTDEC 2399 (Caltron 6-in-1)
{ 42, 0, 3, 0, 5, 0, 0 }, // AC08/LH09 (FDS games hacked to cartridges)
{ 43, 2, 2, 0, 0, 0, 0 }, // TONY-I / YS-612 [88KiB]
// { 44, 5, 6, 7, 8, 0, 0 }, Super HiK 7-in-1 (MMC3) [TODO]
{ 45, 3, 8, 0, 8, 0, 0 }, // TC3294/GA23C
{ 46, 1, 6, 0, 8, 0, 0 }, // GameStation/RumbleStation
{ 47, 4, 5, 6, 7, 0, 0 }, // Nintendo NES-QJ (2-in-1)
{ 48, 3, 4, 6, 6, 0, 0 }, // Taito TC0690/TC0190+PAL16R4
{ 49, 0, 5, 0, 7, 0, 0 }, // 820401/T-217 (Super HIK 4-in-1 MMC3 multicart)
{ 50, 3, 3, 0, 0, 0, 0 }, // N-32 (761214)
// 51 - 820718C [TODO]
{ 52, 0, 5, 0, 7, 0, 0 }, // Realtec 8213
// 53 - Supervision 16-in-1 [TODO]
// 54 - not used
{ 55, 1, 1, 0, 0, 0, 0 }, // NCN-35A/BTL-MARIO1-MALEE2 [56KiB]
{ 56, 0, 7, 0, 6, 0, 0 }, // Kaiser KS202 (SMB3)
{ 57, 0, 3, 0, 5, 0, 0 }, // GK 6-in-1
{ 58, 1, 6, 1, 6, 0, 0 }, // GK-192, duplicate of mapper 213
{ 59, 0, 3, 0, 4, 0, 0 }, // BS-01/VT1512A (BMC-T3H53 + BMC-D1038)
{ 60, 2, 2, 3, 3, 0, 0 }, // Reset-based NROM-128 (4-in-1 multicarts)
{ 61, 0, 5, 0, 5, 0, 0 }, // NTDEC GS-2017/0324
{ 62, 7, 7, 8, 8, 0, 0 }, // N-190B/K-1016/K-1017P (Super 700-in-1)
{ 63, 8, 8, 0, 0, 0, 0 }, // NTDEC 2291 (Powerful multicart) / CH-011 / 82AB
{ 64, 2, 3, 4, 5, 0, 0 }, // Tengen 800032 (RAMBO-1)
{ 65, 3, 4, 5, 6, 0, 0 }, // Irem H-3001
{ 66, 2, 3, 2, 3, 0, 0 }, // Nintendo GNROM/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-5 (FME-5A, FME-5B, FME-7) [sram r/w]
{ 70, 3, 3, 5, 5, 0, 0 }, // Bandai UOROM
{ 71, 2, 4, 0, 0, 0, 0 }, // Codemasters BIC BF9093/BF9097
{ 72, 3, 3, 5, 5, 0, 0 }, // Jaleco JF-17
{ 73, 3, 3, 0, 0, 0, 0 }, // Konami VRC3
// 74 - 43-393/43-406/860908C [TODO]
{ 75, 3, 3, 5, 5, 0, 0 }, // Konami VRC1
{ 76, 3, 3, 5, 5, 0, 0 }, // Namco 3446
{ 77, 3, 3, 3, 3, 0, 0 }, // Irem LROG017 (Napoleon Senki)
{ 78, 3, 3, 5, 5, 0, 0 }, // Jaleco JF-16/Irem IF-12
{ 79, 1, 2, 2, 3, 0, 0 }, // AVE NINA-03, NINA-06, MB-91 / 聖謙 [Sachen] 3015, SA-016
{ 80, 3, 3, 5, 6, 0, 1 }, // Taito P3-33/34/36 (X1-005) [prgram r/w]
// 81 - NTDEC N715021 (Super Gun) [TODO]
{ 82, 3, 3, 5, 6, 0, 1 }, // Taito P3-044 (X1-017, wrong PRG order) [prgram r/w]
// 83 - Cony [TODO]
// 84 - not used
{ 85, 3, 5, 0, 5, 0, 1 }, // Konami VRC7 [sram r/w]
{ 86, 3, 3, 4, 4, 0, 0 }, // Jaleco JF-13
{ 87, 0, 1, 2, 3, 0, 0 }, // Jaleco/Konami CNROM
{ 88, 3, 3, 5, 5, 0, 0 }, // Namco 3433
{ 89, 3, 3, 5, 5, 0, 0 }, // Sunsoft-2
{ 90, 0, 7, 1, 8, 0, 0 }, // 晶太 [Jīngtài] EL861226C
{ 91, 3, 5, 7, 8, 0, 0 }, // EJ-006-1/晶太 [Jīngtài] YY830624C/JY830848C
{ 92, 4, 4, 5, 5, 0, 0 }, // Jaleco JF-19
{ 93, 3, 3, 0, 0, 0, 0 }, // Sunsoft-3R
{ 94, 3, 3, 0, 0, 0, 0 }, // Nintendo UN1ROM
{ 95, 3, 3, 3, 3, 0, 0 }, // Namco 3425 (Dragon Buster)
{ 96, 3, 3, 0, 0, 0, 0 }, // Oeka Kids
{ 97, 4, 4, 0, 0, 0, 0 }, // Irem TAM-S1 (Kaiketsu Yanchamaru)
// 98 - not used
// 99 - irrelevant (Nintendo Vs. System)
// 100 - irrelevant (Nesticle MMC3)
// 101 - irrelevant (Jaleco/Konami CNROM with wrong bit order)
// 102 - not used
// 103 - Whirlwind Manu LH30 (Doki Doki Panic) [TODO]
// 104 - Pegasus 5-in-1 [TODO]
{ 105, 4, 4, 0, 0, 0, 0 }, // NES-EVENT (Nintendo World Championships)
// 106 - 890418 (Super Mario Bros 3 bootleg) [TODO]
// 107 - Magic Dragon [TODO]
// 108 - DH-08 (Whirlwind Manu FDS-to-cartridge conversions) [TODO]
// 109 - not used (duplicate of 137)
// 110 - not used (duplicate of 243)
{ 111, 5, 5, 0, 0, 0, 0 }, // GTROM
// 112 - NTDEC MMC3 [TODO]
{ 113, 1, 4, 0, 5, 0, 0 }, // HES NTD-8
{ 114, 3, 4, 5, 6, 0, 0 }, // 6122 (SuperGame MMC3-clone)
{ 115, 0, 5, 0, 7, 0, 0 }, // 卡聖 [Kǎshèng] SFC-02B/-03/-004
// 116 - 哥德 [Gēdé] SOMARI-P [TODO]
{ 117, 0, 7, 0, 6, 0, 0 }, // Future Media
{ 118, 3, 4, 5, 5, 0, 1 }, // Nintendo TKSROM/TLSROM [sram r/w]
{ 119, 3, 3, 4, 4, 0, 0 }, // Nintendo TQROM
{ 120, 3, 3, 0, 0, 0, 0 }, // Whirlwind Manu LH15 (Tobidase Daisakusen) [96KiB]
// 121 - 卡聖 [Kǎshèng] A9711/A9713 [TODO]
{ 122, 1, 1, 2, 3, 0, 0 }, // JY043 (duplicate of 184)
// 123 - 卡聖 [Kǎshèng] H2288 [TODO]
// 124 - irrelevant (Super Game Mega Type III pirate arcade board)
{ 125, 3, 3, 0, 0, 0, 0 }, // Whirlwind Manu LH32 (Montyのドキドキ大脱走)
{ 126, 1, 8, 0, 8, 0, 0 }, // MMC3-based multicart (PJ-008, AT-207) [UNLICENSED]
{ 134, 1, 8, 0, 8, 0, 0 }, // T4A54A, WX-KB4K, or BS-5652 [UNLICENSED]
{ 140, 3, 3, 3, 5, 0, 0 }, // jaleco jf-11/jf-14
{ 142, 1, 3, 0, 0, 0, 0 }, // UNL-KS7032 [UNLICENSED]
{ 144, 2, 2, 4, 4, 0, 0 }, // AGCI-50282 (Death Race)
{ 146, 1, 2, 2, 3, 0, 0 }, // duplicated of 79
{ 148, 1, 2, 0, 4, 0, 0 }, // Sachen SA-0037 & Tengen 800008 [UNLICENSED]
// 151 - not used
{ 152, 2, 3, 5, 5, 0, 0 }, // BANDAI-74*161/161/32
{ 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 }, // Nintendo SxROM (MMC1A) [sram r/w]
{ 157, 4, 4, 0, 0, 0, 0 }, // Datach
{ 158, 3, 3, 5, 5, 0, 0 }, // Tengen 800037 (RAMBO-1 variant)
{ 159, 3, 4, 5, 6, 1, 1 }, // Bandai FCG with 24C01 EEPROM [eep r/w]
{ 162, 6, 7, 0, 0, 0, 0 }, // Waixing FS304 [UNLICENSED]
{ 163, 6, 7, 0, 0, 0, 0 }, // Nanjing FC-001 [UNLICENSED]
// 171 - 步步高 (Bùbùgāo/BBK) [TODO]
{ 174, 3, 3, 4, 4, 0, 0 }, // NTDEC 5-in-1 [UNLICENSED]
{ 176, 4, 4, 5, 5, 0, 0 }, // 8025 enhanced MMC3 [UNLICENSED]
{ 177, 1, 7, 0, 0, 0, 0 }, // Henggedianzi Super Rich PCB [UNLICENSED]
{ 178, 5, 5, 0, 0, 0, 0 }, // some Waixing PCBs [UNLICENSED]
{ 180, 3, 3, 0, 0, 0, 0 }, // unrom variant (crazy climber)
// 181
{ 182, 3, 4, 5, 6, 0, 0 }, // YH-001 (duplicate of 114)
// 183
{ 184, 1, 1, 2, 3, 0, 0 }, // Sunsoft-1
{ 185, 0, 1, 1, 1, 0, 0 }, // cnrom lockout
// 186 - not used
{ 200, 1, 4, 1, 4, 0, 0 }, // HN-02 multicarts [UNLICENSED]
{ 201, 1, 8, 1, 9, 0, 0 }, // NROM-256 multicarts [UNLICENSED]
{ 202, 0, 3, 1, 4, 0, 0 }, // BMC-150IN1 multicarts [UNLICENSED]
{ 203, 1, 4, 1, 4, 0, 0 }, // various NROM-128 multicarts [UNLICENSED]
{ 206, 1, 3, 2, 4, 0, 0 }, // dxrom
{ 207, 4, 4, 5, 5, 0, 0 }, // Taito Ashura
{ 209, 0, 7, 1, 8, 0, 0 }, // 晶太 YY850629C
{ 210, 3, 5, 5, 6, 0, 0 }, // namco 175/340
{ 211, 0, 7, 1, 8, 0, 0 }, // 晶太 EL860339C
{ 212, 0, 3, 0, 4, 0, 0 }, // BMC Super HiK 300-in-1 [UNLICENSED]
{ 213, 1, 6, 1, 6, 0, 0 }, // GK-192, duplicate of mapper 58
{ 214, 0, 3, 0, 4, 0, 0 }, // BMC-SUPERGUN-20IN1, BMC-190IN1 [UNLICENSED]
{ 225, 4, 7, 5, 8, 0, 0 }, // ET-4310 (FC) + K-1010 (NES) [UNLICENSED]
{ 226, 6, 7, 0, 0, 0, 0 }, // BMC-76IN1, BMC-SUPER42IN1, BMC-GHOSTBUSTERS63IN1 [UNLICENSED]
{ 227, 1, 5, 0, 0, 0, 0 }, // 810449-C-A1 / FW-01 [UNLICENSED]
{ 228, 4, 7, 5, 7, 0, 0 }, // Action 52 + Cheetahmen II [UNLICENSED]
{ 229, 5, 5, 6, 6, 0, 0 }, // BMC 31-IN-1 [UNLICENSED]
{ 232, 4, 4, 0, 0, 0, 0 }, // Camerica/Codemasters "Quattro" cartridges [UNLICENSED]
{ 233, 6, 6, 0, 0, 0, 0 }, // BMC 22-IN-1/20-IN-1 (42-IN-1) [UNLICENSED]
{ 235, 6, 8, 0, 0, 0, 0 }, // "Golden Game" multicarts [UNLICENSED]
{ 236, 0, 6, 0, 5, 0, 0 }, // Realtec 8031, 8099, 8106, 8155 [UNLICENSED]
{ 237, 6, 6, 0, 0, 0, 0 }, // teletubbies 420-in-1/42-in-1 Y2K [UNLICENSED]
{ 240, 1, 5, 1, 5, 0, 3 }, // C&E Bootleg Board (Sheng Huo Lie Zhuan, Jing Ke Xin Zhuan) [UNLICENSED]
{ 241, 3, 5, 0, 0, 0, 0 }, // BxROM with WRAM [UNLICENSED]
{ 242, 5, 5, 0, 0, 0, 0 }, // ET-113 [UNLICENSED]
{ 246, 5, 5, 7, 7, 0, 0 }, // C&E Feng Shen Bang [UNLICENSED]
{ 248, 0, 5, 0, 7, 0, 0 }, // 卡聖 SFC-02B/-03/-004 (duplicate of 115)
// 259 - T9552 [TODO]
{ 255, 4, 7, 5, 8, 0, 0 }, // 110-in-1 multicart (same as 225) [UNLICENSED]
// 264 - Yoko [TODO]
{ 268, 0, 11, 0, 8, 0, 0 }, // 268.0 MindKids/CoolGirl [UNLICENSED]
{ 289, 5, 7, 0, 0, 0, 0 }, // bmc-60311c 17-in-1/76-in-1 [UNLICENSED]
{ 315, 0, 5, 0, 7, 0, 0 }, // BMC-830134C [UNLICENSED]
{ 319, 3, 3, 4, 4, 0, 0 }, // hp-898f [UNLICENSED]
{ 329, 1, 7, 0, 0, 0, 3 }, // UNL-EDU2000, same as 177 [UNLICENSED]
{ 332, 4, 4, 5, 5, 0, 0 }, // bmc-ws [UNLICENSED]
{ 366, 0, 6, 0, 8, 0, 0 }, // GN-45 [UNLICENSED]
{ 446, 0, 8, 0, 0, 0, 0 }, // Mindkids SMD172B_FGPA submapper 0 & 1 [UNLICENSED]
{ 470, 0, 11, 0, 0, 0, 0 }, // INX_007T_V01 [UNLICENSED]
{ 532, 4, 4, 6, 6, 0, 0 }, // CHINA_ER_SAN2
{ 552, 0, 5, 0, 6, 0, 0 } // Taito P3-044 (X1-017, actual bank order)
};
const char _file_name_no_number_fmt[] PROGMEM = "%s.%s";
const char _file_name_with_number_fmt[] PROGMEM = "%s.%02d.%s";
/******************************************
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)
#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
uint8_t mapcount = (sizeof(mapsize) / sizeof(mapsize[0]));
uint16_t mapselect;
const uint16_t PRG[] PROGMEM = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 };
uint8_t prglo = 0; // Lowest Entry
uint8_t prghi = 11; // Highest Entry
const uint16_t CHR[] PROGMEM = { 0, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096 };
uint8_t chrlo = 0; // Lowest Entry
uint8_t chrhi = 10; // Highest Entry
const uint8_t RAM[] PROGMEM = { 0, 8, 16, 32 };
uint8_t ramlo = 0; // Lowest Entry
uint8_t ramhi = 3; // Highest Entry
uint16_t prg;
uint16_t chr;
uint8_t ram;
bool mmc6 = false;
bool flashfound = false; // NESmaker 39SF040 Flash Cart
// Cartridge Config
uint16_t mapper;
uint8_t prgsize;
uint8_t chrsize;
uint8_t ramsize;
/******************************************
Menus
*****************************************/
// NES start menu
static const char nesMenuItem1[] PROGMEM = "Read iNES Rom";
static const char nesMenuItem2[] PROGMEM = "Read PRG/CHR";
static const char nesMenuItem5[] PROGMEM = "Change Mapper";
static const char nesMenuItem6[] PROGMEM = "Flash Repro";
static const char* const menuOptionsNES[] PROGMEM = { nesMenuItem1, nesMenuItem2, FSTRING_READ_SAVE, FSTRING_WRITE_SAVE, nesMenuItem5, nesMenuItem6, FSTRING_RESET };
// NES chips menu
static const char nesChipsMenuItem1[] PROGMEM = "Combined PRG+CHR";
static const char nesChipsMenuItem2[] PROGMEM = "Read only PRG";
static const char nesChipsMenuItem3[] PROGMEM = "Read only CHR";
static const char nesChipsMenuItem4[] PROGMEM = "Back";
static const char* const menuOptionsNESChips[] PROGMEM = { nesChipsMenuItem1, nesChipsMenuItem2, nesChipsMenuItem3, nesChipsMenuItem4 };
#if defined(ENABLE_FLASH)
// Repro Writer Menu
static const char nesFlashMenuItem1[] PROGMEM = "Flash NesMaker";
static const char nesFlashMenuItem2[] PROGMEM = "Flash A29040B-MAPPER0";
static const char nesFlashMenuItem3[] PROGMEM = "Back";
static const char* const menuOptionsNESFlash[] PROGMEM = { nesFlashMenuItem1, nesFlashMenuItem2, nesFlashMenuItem3 };
#endif
// NES start menu
void nesMenu() {
unsigned char answer;
// create menu with title "NES CART READER" and 7 options to choose from
convertPgm(menuOptionsNES, 7);
answer = question_box(F("NES CART READER"), menuOptions, 7, 0);
// wait for user choice to come back from the question box menu
switch (answer) {
// Read Rom
case 0:
display_Clear();
// Change working dir to root
sd.chdir("/");
readRom_NES();
println_Msg(FS(FSTRING_EMPTY));
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
#ifdef ENABLE_GLOBAL_LOG
save_log();
#endif
display_Update();
wait();
break;
// Read single chip
case 1:
nesChipMenu();
break;
// Read RAM
case 2:
sd.chdir();
sprintf(folder, "NES/SAVE");
sd.mkdir(folder, true);
sd.chdir(folder);
readRAM();
resetROM();
println_Msg(FS(FSTRING_EMPTY));
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
display_Update();
wait();
break;
// Write RAM
case 3:
writeRAM();
resetROM();
println_Msg(FS(FSTRING_EMPTY));
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
display_Update();
wait();
break;
// Change Mapper
case 4:
setDefaultRomName();
setMapper();
checkMapperSize();
setPRGSize();
setCHRSize();
setRAMSize();
checkStatus_NES();
break;
#if defined(ENABLE_FLASH)
// Write FLASH
case 5:
nesFlashMenu();
break;
#endif
// Reset
case 6:
resetArduino();
break;
default:
print_MissingModule(); // does not return
}
}
void nesChipMenu() {
// create menu with title "Select NES Chip" and 4 options to choose from
convertPgm(menuOptionsNESChips, 4);
unsigned char answer = question_box(F("Select NES Chip"), menuOptions, 4, 0);
// wait for user choice to come back from the question box menu
switch (answer) {
// Read combined PRG/CHR
case 0:
display_Clear();
// Change working dir to root
sd.chdir("/");
readRaw_NES();
println_Msg(FS(FSTRING_EMPTY));
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
#ifdef ENABLE_GLOBAL_LOG
save_log();
#endif
display_Update();
wait();
break;
// Read PRG
case 1:
CreateROMFolderInSD();
readPRG(false);
resetROM();
println_Msg(FS(FSTRING_EMPTY));
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
display_Update();
wait();
break;
// Read CHR
case 2:
CreateROMFolderInSD();
readCHR(false);
resetROM();
println_Msg(FS(FSTRING_EMPTY));
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
display_Update();
wait();
break;
// Return to Main Menu
case 3:
nesMenu();
wait();
break;
}
}
#if defined(ENABLE_FLASH)
void nesFlashMenu() {
// create menu with title "Select NES Flash Repro" and 3 options to choose from
convertPgm(menuOptionsNESFlash, 3);
unsigned char answer = question_box(F("Select Flash Writer"), menuOptions, 3, 0);
switch (answer) {
case 0:
if (mapper == 30) {
writeFLASH();
resetROM();
} else {
display_Clear();
println_Msg(FS(string_error5));
println_Msg(F("Can't write to this cartridge"));
println_Msg(FS(FSTRING_EMPTY));
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
display_Update();
}
wait();
break;
case 1:
if (mapper == 0) {
display_Clear();
A29040B_writeFLASH();
display_Update();
wait();
} else {
display_Clear();
println_Msg(FS(string_error5));
println_Msg(F("Can't write to this cartridge"));
println_Msg(mapper);
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
display_Update();
wait();
}
break;
// Return to Main Menu
case 2:
nesMenu();
wait();
break;
}
}
#endif
/******************************************
Setup
*****************************************/
void setup_NES() {
// Request 5V
setVoltage(VOLTS_SET_5V);
// 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);
rgbLed(black_color);
}
/******************************************
Get Mapping from SD database
*****************************************/
uint32_t uppow2(uint32_t n) {
for (int8_t x = 31; x >= 0; x--)
if (n & (1u << x)) {
if ((1u << x) != n)
return (1u << (x + 1));
break;
}
return n;
}
struct database_entry {
char filename[128];
char crc_str[8 + 1 + 8 + 1 + 32 + 1];
uint32_t crc;
char* crc512_str;
uint32_t crc512;
char* iNES_str;
};
void printPRG(unsigned long myOffset) {
display_Clear();
print_Msg(F("Printing PRG at "));
println_Msg(myOffset);
char myBuffer[3];
for (size_t currLine = 0; currLine < 512; currLine += 16) {
for (uint8_t currByte = 0; currByte < 16; currByte++) {
itoa(read_prg_byte(myOffset + currLine + currByte), myBuffer, 16);
for (size_t i = 0; i < 2 - strlen(myBuffer); i++) {
print_Msg(F("0"));
}
// Now print the significant bits
print_Msg(myBuffer);
print_Msg(" ");
}
println_Msg("");
}
display_Update();
}
void setDefaultRomName() {
romName[0] = 'C';
romName[1] = 'A';
romName[2] = 'R';
romName[3] = 'T';
romName[4] = '\0';
}
void setRomnameFromString(const char* input) {
uint8_t myLength = 0;
for (uint8_t i = 0; i < 20 && myLength < 15; i++) {
// Stop at first "(" to remove "(Country)"
if (input[i] == '(') {
break;
}
if (
(input[i] >= '0' && input[i] <= '9') || (input[i] >= 'A' && input[i] <= 'Z') || (input[i] >= 'a' && input[i] <= 'z')) {
romName[myLength++] = input[i];
}
}
// If name consists out of all japanese characters use CART as name
if (myLength == 0) {
setDefaultRomName();
}
}
void printDataLine_NES(void* entry) {
struct database_entry* castEntry = (struct database_entry*)entry;
uint8_t iNES[16];
uint8_t* output;
char* input;
input = castEntry->iNES_str;
output = iNES;
for (uint8_t i = 0; i < sizeof(iNES); i++) {
unsigned int buf;
sscanf(input, "%2X", &buf);
*(output++) = buf;
input += 2;
}
mapper = (iNES[6] >> 4) | (iNES[7] & 0xF0) | ((iNES[8] & 0x0F) << 8);
if ((iNES[9] & 0x0F) != 0x0F) {
// simple notation
prgsize = (iNES[4] | ((iNES[9] & 0x0F) << 8)); //*16
} else {
// exponent-multiplier notation
prgsize = (((1 << (iNES[4] >> 2)) * ((iNES[4] & 0b11) * 2 + 1)) >> 14); //*16
}
if (prgsize != 0)
prgsize = (int(log(prgsize) / log(2)));
if ((iNES[9] & 0xF0) != 0xF0) {
// simple notation
chrsize = (uppow2(iNES[5] | ((iNES[9] & 0xF0) << 4))) * 2; //*4
} else {
// exponent-multiplier notation
chrsize = (((1 << (iNES[5] >> 2)) * ((iNES[5] & 0b11) * 2 + 1)) >> 13) * 2; //*4
}
if (chrsize != 0)
chrsize = (int(log(chrsize) / log(2)));
ramsize = ((iNES[10] & 0xF0) ? (64 << ((iNES[10] & 0xF0) >> 4)) : 0) / 4096; //*4
if (ramsize != 0)
ramsize = (int(log(ramsize) / log(2)));
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 if (mapper == 82)
ram = 5; // 5K
else
ram = (int_pow(2, ramsize)) * 4;
// Mapper Variants
// Identify variant for use across multiple functions
if (mapper == 4) { // Check for MMC6/MMC3
checkMMC6();
if (mmc6) {
ram = 1; // 1K
ramsize = 1; // Must be a non-zero value
}
}
printNESSettings();
}
uint32_t oldcrc32 = 0xFFFFFFFF;
uint32_t oldcrc32MMC3 = 0xFFFFFFFF;
void getMapping() {
FsFile database;
char crcStr[9];
display_Clear();
sd.chdir();
if (!database.open("nes.txt", O_READ)) {
print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND));
// never reached
}
// Read first 512 bytes of first and last block of PRG ROM and compute CRC32
// Some mappers (like MMC3) map the last 8KB block of PRG ROM to 0xE000 while 0x8000 can contain random data after bootup
for (size_t c = 0; c < 512; c++) {
UPDATE_CRC(oldcrc32, read_prg_byte(0x8000 + c));
UPDATE_CRC(oldcrc32MMC3, read_prg_byte(0xE000 + c));
}
oldcrc32 = ~oldcrc32;
oldcrc32MMC3 = ~oldcrc32MMC3;
bool browseDatabase;
// Filter out all 0xFF checksums at 0x8000 and 0xE000
if (oldcrc32 == 0xBD7BC39F && oldcrc32MMC3 == 0xBD7BC39F) {
println_Msg(F("No data found."));
println_Msg(F("Using manual selection"));
display_Update();
delay(500);
setDefaultRomName();
browseDatabase = selectMapping(database);
} else {
println_Msg(F("Searching database"));
print_Msg(F("for "));
sprintf(crcStr, "%08lX", oldcrc32);
print_Msg(crcStr);
if (oldcrc32 != oldcrc32MMC3) {
print_Msg(F(" or "));
sprintf(crcStr, "%08lX", oldcrc32MMC3);
print_Msg(crcStr);
}
println_Msg(F("..."));
display_Update();
while (database.available()) {
struct database_entry entry;
readDatabaseEntry(database, &entry);
//if checksum search was successful set mapper and end search, also filter out 0xFF checksum
if (
entry.crc512 != 0xBD7BC39F && (entry.crc512 == oldcrc32 || entry.crc512 == oldcrc32MMC3)) {
// Rewind to start of entry
rewind_line(database, 3);
break;
}
}
if (database.available()) {
browseDatabase = true;
} else {
// File searched until end but nothing found
println_Msg(FS(FSTRING_EMPTY));
println_Msg(F("CRC not found in database"));
println_Msg(F("Using manual selection"));
display_Update();
delay(500);
// Print content of PRG for debugging
//printPRG(0x8000);
//printPRG(0xE000);
// Change ROM name to CART
setDefaultRomName();
browseDatabase = selectMapping(database);
}
}
if (browseDatabase) {
struct database_entry entry;
if (checkCartSelection(database, &readDataLine_NES, &entry, &printDataLine_NES, &setRomnameFromString)) {
// anything else: select current record
// Save Mapper
EEPROM_writeAnything(7, mapper);
EEPROM_writeAnything(9, prgsize);
EEPROM_writeAnything(10, chrsize);
EEPROM_writeAnything(11, ramsize);
}
}
database.close();
}
static void readDatabaseEntry(FsFile& database, struct database_entry* entry) {
get_line(entry->filename, &database, sizeof(entry->filename));
readDataLine_NES(database, entry);
skip_line(&database);
}
void readDataLine_NES(FsFile& database, void* e) {
struct database_entry* entry = (database_entry*)e;
get_line(entry->crc_str, &database, sizeof(entry->crc_str));
entry->crc_str[8] = 0;
entry->crc512_str = &entry->crc_str[8 + 1];
entry->crc512_str[8] = 0;
entry->iNES_str = &entry->crc_str[8 + 1 + 8 + 1];
// Convert "4E4553" to (0x4E, 0x45, 0x53)
unsigned int iNES_BUF;
for (uint8_t j = 0; j < 16; j++) {
sscanf(entry->iNES_str + j * 2, "%2X", &iNES_BUF);
iNES_HEADER[j] = iNES_BUF;
}
entry->crc = strtoul(entry->crc_str, NULL, 16);
entry->crc512 = strtoul(entry->crc512_str, NULL, 16);
}
bool selectMapping(FsFile& database) {
// Select starting letter
byte myLetter = starting_letter();
if (myLetter == 27) {
// Change Mapper
setMapper();
checkMapperSize();
setPRGSize();
setCHRSize();
setRAMSize();
return 0;
} else {
seek_first_letter_in_database(database, myLetter);
}
return 1;
}
void read_NES(const char* fileSuffix, const byte* header, const uint8_t headersize, const boolean renamerom) {
// Get name, add extension and convert to char array for sd lib
createFolderAndOpenFile("NES", "ROM", romName, fileSuffix);
//Initialize progress bar
uint32_t processedProgressBar = 0;
uint32_t totalProgressBar = (uint32_t)(headersize + prgsize * 16 * 1024 + chrsize * 4 * 1024);
draw_progressbar(0, totalProgressBar);
//Write header
if (headersize > 0) {
myFile.write(header, headersize);
// update progress bar
processedProgressBar += headersize;
draw_progressbar(processedProgressBar, totalProgressBar);
}
//Write PRG
readPRG(true);
// update progress bar
processedProgressBar += prgsize * 16 * 1024;
draw_progressbar(processedProgressBar, totalProgressBar);
//Write CHR
readCHR(true);
// update progress bar
processedProgressBar += chrsize * 4 * 1024;
draw_progressbar(processedProgressBar, totalProgressBar);
// Close the file:
myFile.close();
// Compare CRC32 with database
compareCRC("nes.txt", 0, renamerom, headersize);
}
void readRom_NES() {
read_NES("nes", iNES_HEADER, 16, true);
}
void readRaw_NES() {
read_NES("bin", NULL, 0, false);
}
/******************************************
Low Level Functions
*****************************************/
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;
ROMSEL_HI;
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);
}
#if defined(ENABLE_FLASH)
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(1);
CHR_WRITE_LOW;
_delay_us(1); // WRITING
CHR_WRITE_HI;
_delay_us(1);
MODE_READ;
set_address(0);
PHI2_HI;
//_delay_us(1);
}
#endif
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 (uint8_t i = 0; i < 5; i++) {
write_reg_byte(address, data >> i); // shift 1 bit into temp register [WRITE RAM SAFE]
}
} else {
for (uint8_t 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;
}
// Pirate Mapper 59
static void write_reg_m59(unsigned int address) {
ROMSEL_HI;
MODE_WRITE;
PRG_WRITE;
set_address(address);
set_romsel(address);
_delay_us(1); // WRITING
ROMSEL_HI;
PRG_READ;
MODE_READ;
set_address(0);
}
// Multicart Mappers 226/289/332
static void write_prg_pulsem2(unsigned int address, uint8_t data) {
PHI2_LOW;
ROMSEL_HI;
PHI2_HI;
MODE_WRITE;
PHI2_LOW;
PRG_WRITE;
PHI2_HI;
PORTK = data;
PHI2_LOW;
set_address(address); // PHI2 low, ROMSEL always HIGH
PHI2_HI;
set_romsel(address); // ROMSEL is low if need, PHI2 high
for (unsigned int i = 0; i < 8; i++) {
PHI2_LOW;
PHI2_HI;
}
PHI2_LOW;
for (unsigned int i = 0; i < 8; i++) {
PHI2_LOW;
PHI2_HI;
}
ROMSEL_HI;
PHI2_LOW;
PRG_READ;
PHI2_HI;
MODE_READ;
PHI2_LOW;
set_address(0);
PHI2_HI;
}
static unsigned char read_prg_pulsem2(unsigned int address) {
PHI2_LOW;
MODE_READ;
PHI2_HI;
PRG_READ;
PHI2_LOW;
set_address(address);
PHI2_HI;
set_romsel(address);
PHI2_LOW;
for (unsigned int i = 0; i < 8; i++) {
PHI2_LOW;
PHI2_HI;
}
return PINK;
}
void dumpPRG_pulsem2(word base, word address) {
for (size_t x = 0; x < 512; x++) {
sdBuffer[x] = read_prg_pulsem2(base + address + x);
}
myFile.write(sdBuffer, 512);
}
// Multicart Mapper 332
static unsigned char read_chr_pulsem2(unsigned int address) {
PHI2_LOW;
MODE_READ;
PHI2_HI;
ROMSEL_HI;
PHI2_LOW;
set_address(address);
PHI2_HI;
CHR_READ_LOW;
PHI2_LOW;
for (unsigned int i = 0; i < 8; i++) {
PHI2_LOW;
PHI2_HI;
}
uint8_t result = PINK;
PHI2_LOW;
CHR_READ_HI;
PHI2_HI;
return result;
}
void dumpCHR_pulsem2(word address) {
for (int x = 0; x < 512; x++) {
sdBuffer[x] = read_chr_pulsem2(address + x);
}
myFile.write(sdBuffer, 512);
}
/******************************************
File Functions
*****************************************/
void CreateROMFolderInSD() {
sd.chdir();
sprintf(folder, "NES/ROM");
sd.mkdir(folder, true);
sd.chdir(folder);
}
FsFile createNewFile(const char* prefix, const char* extension) {
char filename[FILENAME_LENGTH];
snprintf_P(filename, sizeof(filename), _file_name_no_number_fmt, prefix, extension);
for (uint8_t i = 0; i < 100; i++) {
if (!sd.exists(filename)) {
return sd.open(fileName, O_RDWR | O_CREAT);
}
snprintf_P(filename, sizeof(filename), _file_name_with_number_fmt, prefix, i, extension);
}
// Could not find an available name, recompose the original name and error out.
snprintf_P(filename, sizeof(filename), _file_name_no_number_fmt, prefix, extension);
rgbLed(red_color);
display_Clear();
print_Msg(filename);
println_Msg(F(": no available name"));
display_Update();
print_FatalError(sd_error_STR);
rgbLed(black_color);
}
void CreatePRGFileInSD() {
myFile = createNewFile("PRG", "bin");
}
void CreateCHRFileInSD() {
myFile = createNewFile("CHR", "bin");
}
//createNewFile fails to dump RAM if ROM isn't dumped first
//void CreateRAMFileInSD() {
//myFile = createNewFile("RAM", "bin");
//}
//Temporary fix
void CreateRAMFileInSD() {
char fileCount[3];
strcpy(fileName, "RAM");
strcat(fileName, ".sav");
for (uint8_t i = 0; i < 100; i++) {
if (!sd.exists(fileName)) {
myFile = sd.open(fileName, O_RDWR | O_CREAT);
break;
}
sprintf(fileCount, "%02d", i);
strcpy(fileName, "RAM.");
strcat(fileName, fileCount);
strcat(fileName, ".sav");
}
if (!myFile) {
rgbLed(red_color);
display_Clear();
println_Msg(F("RAM FILE FAILED!"));
display_Update();
//print_Error(F("SD Error"), true);
rgbLed(black_color);
}
}
/******************************************
Config Functions
*****************************************/
#if defined(ENABLE_LCD)
void printMapperSelection_NES(int index) {
display_Clear();
mapselect = pgm_read_word(mapsize + index);
print_Msg(FS(FSTRING_MAPPER));
println_Msg(mapselect);
}
#endif
void setMapper() {
uint16_t newmapper;
#ifdef ENABLE_GLOBAL_LOG
// Disable log to prevent unnecessary logging
println_Log(F("Set Mapper manually"));
dont_log = true;
#endif
// OLED
#if defined(ENABLE_OLED)
chooseMapper:
// Read stored mapper
EEPROM_readAnything(7, newmapper);
if (newmapper > 220)
newmapper = 0;
// Split into digits
uint8_t hundreds = newmapper / 100;
uint8_t tens = newmapper / 10 - hundreds * 10;
uint8_t units = newmapper - hundreds * 100 - tens * 10;
// Cycle through all 3 digits
uint8_t digit = 0;
while (digit < 3) {
display_Clear();
println_Msg(F("Select Mapper:"));
display.setCursor(23, 20);
println_Msg(hundreds);
display.setCursor(43, 20);
println_Msg(tens);
display.setCursor(63, 20);
println_Msg(units);
println_Msg("");
println_Msg("");
println_Msg("");
print_STR(press_to_change_STR, 1);
println_Msg(F("Press right to select"));
if (digit == 0) {
display.setDrawColor(1);
display.drawLine(20, 30, 30, 30);
display.setDrawColor(0);
display.drawLine(40, 30, 50, 30);
display.drawLine(60, 30, 70, 30);
display.setDrawColor(1);
} else if (digit == 1) {
display.setDrawColor(0);
display.drawLine(20, 30, 30, 30);
display.setDrawColor(1);
display.drawLine(40, 30, 50, 30);
display.setDrawColor(0);
display.drawLine(60, 30, 70, 30);
display.setDrawColor(1);
} else if (digit == 2) {
display.setDrawColor(0);
display.drawLine(20, 30, 30, 30);
display.drawLine(40, 30, 50, 30);
display.setDrawColor(1);
display.drawLine(60, 30, 70, 30);
}
display.updateDisplay();
while (1) {
/* Check Button
1 click
2 doubleClick
3 hold
4 longHold */
uint8_t 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;
}
break;
} 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;
}
break;
} else if (b == 3) {
digit++;
break;
}
}
}
display_Clear();
newmapper = hundreds * 100 + tens * 10 + units;
// Check if valid
bool validMapper = 0;
for (uint8_t currMaplist = 0; currMaplist < mapcount; currMaplist++) {
if (pgm_read_word(mapsize + currMaplist) == newmapper)
validMapper = 1;
}
if (!validMapper) {
errorLvl = 1;
display.println(F("Mapper not supported"));
display.updateDisplay();
wait();
goto chooseMapper;
}
// LCD
#elif defined(ENABLE_LCD)
navigateMenu(0, mapcount - 1, &printMapperSelection_NES);
newmapper = mapselect;
display.setCursor(0, 56 + 8);
print_Msg(F("MAPPER "));
print_Msg(newmapper);
println_Msg(F(" SELECTED"));
display_Update();
delay(500);
// Serial Monitor
#elif defined(ENABLE_SERIAL)
setmapper:
String newmap;
bool mapfound = false;
Serial.println(F("SUPPORTED MAPPERS:"));
for (int i = 0; i < mapcount; i++) {
mapselect = pgm_read_word(mapsize + i);
Serial.print("[");
Serial.print(mapselect);
Serial.print("]");
if (i < mapcount - 1) {
if ((i != 0) && ((i + 1) % 10 == 0))
Serial.println(FS(FSTRING_EMPTY));
else
Serial.print(F("\t"));
} else
Serial.println(FS(FSTRING_EMPTY));
}
Serial.print(F("Enter Mapper: "));
while (Serial.available() == 0) {}
newmap = Serial.readStringUntil('\n');
Serial.println(newmap);
newmapper = newmap.toInt();
for (uint8_t i = 0; i < mapcount; i++) {
mapselect = pgm_read_word(mapsize + i);
if (newmapper == mapselect)
mapfound = true;
}
if (mapfound == false) {
Serial.println(F("MAPPER NOT SUPPORTED!"));
Serial.println(FS(FSTRING_EMPTY));
newmapper = 0;
goto setmapper;
}
#endif
EEPROM_writeAnything(7, newmapper);
mapper = newmapper;
#ifdef ENABLE_GLOBAL_LOG
// Enable log again
dont_log = false;
#endif
}
void checkMapperSize() {
mapper_NES v;
for (uint8_t i = 0; i < mapcount; i++) {
memcpy_P(&v, mapsize + i, sizeof(v));
if (mapper == v.mapper) {
prglo = v.prglo;
prghi = v.prghi;
chrlo = v.chrlo;
chrhi = v.chrhi;
ramlo = v.ramlo;
ramhi = v.ramhi;
break;
}
}
}
#if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
void printPrgSize_NES(int index) {
display_Clear();
print_Msg(F("PRG Size: "));
println_Msg(pgm_read_word(&(PRG[index])));
}
#endif
void setPRGSize() {
uint8_t newprgsize;
#ifdef ENABLE_GLOBAL_LOG
// Disable log to prevent unnecessary logging
println_Log(F("Set PRG Size"));
dont_log = true;
#endif
#if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
display_Clear();
if (prglo == prghi)
newprgsize = prglo;
else {
newprgsize = navigateMenu(prglo, prghi, &printPrgSize_NES);
display.setCursor(0, 56); // Display selection at bottom
}
print_Msg(F("PRG SIZE "));
print_Msg(pgm_read_word(&(PRG[newprgsize])));
println_Msg(F("K"));
display_Update();
delay(500);
#elif defined(ENABLE_SERIAL)
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(pgm_read_word(&(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(FS(FSTRING_EMPTY));
goto setprg;
}
}
Serial.print(F("PRG Size = "));
Serial.print(pgm_read_word(&(PRG[newprgsize])));
Serial.println(F("K"));
#endif
EEPROM_writeAnything(9, newprgsize);
prgsize = newprgsize;
#ifdef ENABLE_GLOBAL_LOG
// Enable log again
dont_log = false;
#endif
}
#if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
void printChrSize_NES(int index) {
display_Clear();
print_Msg(F("CHR Size: "));
println_Msg(pgm_read_word(&(CHR[index])));
}
#endif
void setCHRSize() {
uint8_t newchrsize;
#ifdef ENABLE_GLOBAL_LOG
// Disable log to prevent unnecessary logging
println_Log(F("Set CHR Size"));
dont_log = true;
#endif
#if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
display_Clear();
if (chrlo == chrhi)
newchrsize = chrlo;
else {
newchrsize = navigateMenu(chrlo, chrhi, &printChrSize_NES);
display.setCursor(0, 56); // Display selection at bottom
}
print_Msg(F("CHR SIZE "));
print_Msg(pgm_read_word(&(CHR[newchrsize])));
println_Msg(F("K"));
display_Update();
delay(500);
#elif defined(ENABLE_SERIAL)
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(pgm_read_word(&(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(FS(FSTRING_EMPTY));
goto setchr;
}
}
Serial.print(F("CHR Size = "));
Serial.print(pgm_read_word(&(CHR[newchrsize])));
Serial.println(F("K"));
#endif
EEPROM_writeAnything(10, newchrsize);
chrsize = newchrsize;
#ifdef ENABLE_GLOBAL_LOG
// Enable log again
dont_log = false;
#endif
}
#if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
void printRamSize_NES(int index) {
display_Clear();
print_Msg(F("RAM Size: "));
if (mapper == 0)
println_Msg(pgm_read_byte(&(RAM[index])) / 4);
else if (mapper == 16)
println_Msg(pgm_read_byte(&(RAM[index])) * 32);
else if (mapper == 19) {
if (index == 2)
println_Msg(F("128"));
else
println_Msg(pgm_read_byte(&(RAM[index])));
} else if ((mapper == 159) || (mapper == 80))
println_Msg(pgm_read_byte(&(RAM[index])) * 16);
else if (mapper == 82)
println_Msg(index * 5);
else
println_Msg(pgm_read_byte(&(RAM[index])));
}
#endif
void setRAMSize() {
uint8_t newramsize;
#ifdef ENABLE_GLOBAL_LOG
// Disable log to prevent unnecessary logging
println_Log(F("Set RAM Size"));
dont_log = true;
#endif
#if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
display_Clear();
if (ramlo == ramhi)
newramsize = ramlo;
else {
newramsize = navigateMenu(0, ramhi, &printRamSize_NES);
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 = pgm_read_byte(&(RAM[newramsize])) * 32;
else
sizeEEP = pgm_read_byte(&(RAM[newramsize])) * 16;
print_Msg(sizeEEP);
println_Msg(F("B"));
} else if (mapper == 19) {
print_Msg(F("RAM SIZE "));
if (newramsize == 2)
println_Msg(F("128B"));
else {
print_Msg(pgm_read_byte(&(RAM[newramsize])));
println_Msg(F("K"));
}
} else if (mapper == 80) {
print_Msg(F("RAM SIZE "));
print_Msg(pgm_read_byte(&(RAM[newramsize])) * 16);
println_Msg(F("B"));
} else {
print_Msg(F("RAM SIZE "));
if (mapper == 0)
print_Msg(newramsize * 2);
else if (mapper == 82)
print_Msg(newramsize * 5);
else
print_Msg(pgm_read_byte(&(RAM[newramsize])));
println_Msg(F("K"));
}
display_Update();
delay(500);
#elif defined(ENABLE_SERIAL)
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 == 0) {
Serial.print(pgm_read_byte(&(RAM[i])) / 4);
Serial.println(F("K"));
} else if ((mapper == 16) || (mapper == 159)) {
if (mapper == 16)
Serial.print(pgm_read_byte(&(RAM[i + ramlo])) * 32);
else
Serial.print(pgm_read_byte(&(RAM[i + ramlo])) * 16);
Serial.println(F("B"));
} else if (mapper == 19) {
if (i == 2)
Serial.println(F("128B"));
else {
Serial.print(pgm_read_byte(&(RAM[i + ramlo])));
Serial.println(F("K"));
}
} else {
Serial.print(pgm_read_byte(&(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(FS(FSTRING_EMPTY));
goto setram;
}
}
if ((mapper == 16) || (mapper == 159)) {
int sizeEEP = 0;
Serial.print(F("EEPROM Size = "));
if (mapper == 16)
sizeEEP = pgm_read_byte(&(RAM[newramsize])) * 32;
else
sizeEEP = pgm_read_byte(&(RAM[newramsize])) * 16;
Serial.print(sizeEEP);
Serial.println(F("B"));
Serial.println(FS(FSTRING_EMPTY));
} else if (mapper == 19) {
Serial.print(F("RAM Size = "));
if (newramsize == 2)
Serial.println(F("128B"));
else {
Serial.print(pgm_read_byte(&(RAM[newramsize])));
Serial.println(F("K"));
}
Serial.println(FS(FSTRING_EMPTY));
} else if (mapper == 80) {
Serial.print(F("RAM Size = "));
Serial.print(pgm_read_byte(&(RAM[newramsize])) * 16);
Serial.println(F("B"));
Serial.println(FS(FSTRING_EMPTY));
} else {
Serial.print(F("RAM Size = "));
if (mapper == 0)
Serial.print(newramsize * 2);
else if (mapper == 82)
Serial.print(newramsize * 5);
else
Serial.print(pgm_read_byte(&(RAM[newramsize])));
Serial.println(F("K"));
Serial.println(FS(FSTRING_EMPTY));
}
#endif
EEPROM_writeAnything(11, newramsize);
ramsize = newramsize;
#ifdef ENABLE_GLOBAL_LOG
// Enable log again
dont_log = false;
#endif
}
// MMC6 Detection
// Mapper 4 includes both MMC3 AND MMC6
// RAM is mapped differently between MMC3 and MMC6
void checkMMC6() { // Detect MMC6 Carts - read PRG 0x3E00A ("STARTROPICS")
write_prg_byte(0x8000, 6); // PRG Bank 0 ($8000-$9FFF)
write_prg_byte(0x8001, 0x1F); // 0x3E000
uint8_t prgchk0 = read_prg_byte(0x800A);
uint8_t prgchk1 = read_prg_byte(0x800B);
uint8_t prgchk2 = read_prg_byte(0x800C);
uint8_t prgchk3 = read_prg_byte(0x800D);
if ((prgchk0 == 0x53) && (prgchk1 == 0x54) && (prgchk2 == 0x41) && (prgchk3 == 0x52))
mmc6 = true; // MMC6 Cart
}
void checkStatus_NES() {
EEPROM_readAnything(7, mapper);
EEPROM_readAnything(9, prgsize);
EEPROM_readAnything(10, chrsize);
EEPROM_readAnything(11, 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 if (mapper == 82)
ram = 5; // 5K
else
ram = (int_pow(2, ramsize)) * 4;
// Mapper Variants
// Identify variant for use across multiple functions
if (mapper == 4) { // Check for MMC6/MMC3
checkMMC6();
if (mmc6) {
ram = 1; // 1K
ramsize = 1; // Must be a non-zero value
}
} else if (mapper == 30) { // Check for Flashable/Non-Flashable
#if defined(ENABLE_FLASH)
NESmaker_ID(); // Flash ID
#endif
}
display_Clear();
println_Msg(F("NES CART READER"));
println_Msg(FS(FSTRING_EMPTY));
println_Msg(FS(FSTRING_CURRENT_SETTINGS));
println_Msg(FS(FSTRING_EMPTY));
printNESSettings();
println_Msg(FS(FSTRING_EMPTY));
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
display_Update();
wait();
}
static void printNESSettings(void) {
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 == 0) {
print_Msg(ram / 4);
println_Msg(F("K"));
} else if ((mapper == 16) || (mapper == 80) || (mapper == 159)) {
if (mapper == 16)
print_Msg(ram * 32);
else
print_Msg(ram * 16);
println_Msg(F("B"));
} else if (mapper == 19) {
if (ramsize == 2)
println_Msg(F("128B"));
else {
print_Msg(ram);
println_Msg(F("K"));
}
} else {
print_Msg(ram);
println_Msg(F("K"));
}
}
/******************************************
ROM Functions
*****************************************/
void dumpPRG(word base, word address) {
for (size_t x = 0; x < 512; x++) {
sdBuffer[x] = read_prg_byte(base + address + x);
}
myFile.write(sdBuffer, 512);
}
void dumpCHR(word address) {
for (size_t x = 0; x < 512; x++) {
sdBuffer[x] = read_chr_byte(address + x);
}
myFile.write(sdBuffer, 512);
}
void dumpCHR_M2(word address) { // MAPPER 45 - PULSE M2 LO/HI
for (size_t x = 0; x < 512; x++) {
PHI2_LOW;
sdBuffer[x] = read_chr_byte(address + x);
}
myFile.write(sdBuffer, 512);
}
void dumpMMC5RAM(word base, word address) { // MMC5 SRAM DUMP - PULSE M2 LO/HI
for (size_t x = 0; x < 512; x++) {
PHI2_LOW;
sdBuffer[x] = read_prg_byte(base + address + x);
}
myFile.write(sdBuffer, 512);
}
void writeMMC5RAM(word base, word address) { // MMC5 SRAM WRITE
uint8_t bytecheck;
myFile.read(sdBuffer, 512);
for (size_t 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 dumpBankPRG(const size_t from, const size_t to, const size_t base) {
for (size_t address = from; address < to; address += 512) {
dumpPRG(base, address);
}
}
void dumpBankCHR(const size_t from, const size_t to) {
for (size_t address = from; address < to; address += 512) {
dumpCHR(address);
}
}
void readPRG(bool readrom) {
if (!readrom) {
display_Clear();
display_Update();
rgbLed(blue_color);
set_address(0);
_delay_us(1);
CreatePRGFileInSD();
} else {
set_address(0);
_delay_us(1);
}
word base = 0x8000;
bool busConflict = false;
uint16_t banks;
if (myFile) {
switch (mapper) {
case 0:
case 3:
case 13:
case 87: // 16K/32K
case 122:
case 184: // 32K
case 185: // 16K/32K
dumpBankPRG(0, (((word)prgsize) * 0x4000) + 0x4000, base); // 16K or 32K
break;
case 1:
case 155: // 32K/64K/128K/256K/512K
if (prgsize == 1) {
write_prg_byte(0x8000, 0x80);
dumpBankPRG(0x0, 0x8000, base);
} else {
banks = int_pow(2, prgsize);
for (size_t 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, 0); // 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);
dumpBankPRG(0x0, 0x4000, base);
}
}
break;
case 2: // bus conflicts - fixed last bank
case 30: // bus conflicts in non-flashable configuration
banks = int_pow(2, prgsize);
busConflict = true;
for (size_t i = 0; i < banks; i++) {
for (size_t x = 0; x < 0x8000; x++) {
if (read_prg_byte(0xC000 + x) == i) {
write_prg_byte(0xC000 + x, i);
busConflict = false;
break;
}
}
if (busConflict) {
write_prg_byte(0xC000 + i, i);
}
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 4:
case 12:
case 37:
case 47:
case 49:
case 52:
case 64:
case 76:
case 88:
case 95:
case 115:
case 118:
case 119:
case 126:
case 134:
case 154: // 128K
case 158:
case 176:
case 206: // 32/64/128K
case 248:
case 268: // submapper 0
case 315:
case 366:
if ((mapper == 206) && (prgsize == 1)) {
dumpBankPRG(0x0, 0x8000, base);
} else {
banks = int_pow(2, prgsize) * 2;
write_prg_byte(0xA001, 0x80); // Block Register - PRG RAM Chip Enable, Writable
if (mapper == 126) {
write_prg_byte(0x6003, 0); // set MMC3 banking mode
}
if ((mapper == 115) || (mapper == 134) || (mapper == 248)) {
write_prg_byte(0x6000, 0); // set MMC3 banking mode
}
if (mapper == 176) {
write_prg_byte(0x5FF3, 0); // extended MMC3 mode: disabled
write_prg_byte(0x5FF0, 1); // 256K outer bank mode
}
for (size_t i = 0; i < banks; i += 1) {
if (mapper == 37) {
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)
}
}
if (mapper == 47) {
write_prg_byte(0x6000 + (i >> 4), 0);
}
if (mapper == 49) {
write_prg_byte(0x6000, ((i & 0x30) << 2) | 1);
}
if (mapper == 52) {
write_prg_byte(0x6000, (i & 0x70) >> 4);
}
if ((mapper == 115) || (mapper == 248)) {
write_prg_byte(0x6000, (i & 0x10) << 2); // A18
}
if (mapper == 126) {
write_prg_byte(0x6000, (i & 0x180) >> 3 | (i & 0x70) >> 4);
}
if (mapper == 134) {
write_prg_byte(0x6001, (i & 0x30) >> 4);
}
if (mapper == 176) {
write_prg_byte(0x5FF1, (i & 0xE0) >> 1);
}
if (mapper == 268) {
write_prg_byte(0x5000, ((i & 0x70) >> 4) | ((i & 0xC00) >> 6));
write_prg_byte(0x5001, ((i & 0x80) >> 3) | ((i & 0x300) >> 6) | 0x60);
write_prg_byte(0x5002, 0);
write_prg_byte(0x5003, 0);
write_prg_byte(0x6000, ((i & 0x70) >> 4) | ((i & 0xC00) >> 6));
write_prg_byte(0x6001, ((i & 0x80) >> 3) | ((i & 0x300) >> 6) | 0x60);
write_prg_byte(0x6002, 0);
write_prg_byte(0x6003, 0);
}
if (mapper == 315) {
write_prg_byte(0x6800, (i & 30) >> 3);
}
if (mapper == 366) {
write_prg_byte(0x6800 + (i & 0x70), i);
}
write_prg_byte(0x8000, 0x06); // PRG Bank 0 ($8000-$9FFF)
write_prg_byte(0x8001, i);
dumpBankPRG(0x0, 0x2000, base);
}
}
break;
case 5: // 128K/256K/512K
banks = int_pow(2, prgsize) * 2;
write_prg_byte(0x5100, 3); // 8K PRG Banks
for (size_t i = 0; i < banks; i += 2) { // 128K/256K/512K
write_prg_byte(0x5114, i | 0x80);
write_prg_byte(0x5115, (i + 1) | 0x80);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 7: // 128K/256K
case 34: // BxROM/NINA
case 39:
case 77:
case 96: // 128K
case 177: // up to 1024K
case 241:
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) { // 32K Banks
if (mapper == 34) {
write_prg_byte(0x7FFD, i); // NINA Bank select
delay(200); // NINA seems slow to switch banks
}
write_prg_byte(0x8000, i);
dumpBankPRG(0x0, 0x8000, base); // 32K Banks ($8000-$FFFF)
}
break;
case 9:
banks = int_pow(2, prgsize) * 2; // 8K banks
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xA000, i); // Switch bank at $8000
dumpBankPRG(0x0, 0x2000, base); //
}
break;
case 10:
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xA000, i);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 11:
case 144:
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xFFB0 + i, i);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 15:
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i += 2) {
write_prg_byte(0x8000, i);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 16:
case 159: // 128K/256K
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x6008, i); // Submapper 4
write_prg_byte(0x8008, i); // Submapper 5
dumpBankPRG(0x0, 0x4000, base); // 16K Banks ($8000-$BFFF)
}
break;
case 18: // 128K/256K
banks = int_pow(2, prgsize) * 2;
for (size_t 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);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 19: // 128K/256K
case 532:
for (size_t j = 0; j < 64; j++) { // Init Register
write_ram_byte(0xE000, 0); // PRG Bank 0 ($8000-$9FFF)
}
banks = int_pow(2, prgsize) * 2;
for (size_t i = 0; i < banks; i++) {
write_ram_byte(0xE000, i); // PRG Bank 0 ($8000-$9FFF)
dumpBankPRG(0x0, 0x2000, base);
}
break;
case 21: // 256K
banks = int_pow(2, prgsize) * 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xA000, i);
dumpBankPRG(0x2000, 0x4000, base);
}
break;
case 22:
case 25:
case 65:
case 75: // 128K/256K
banks = int_pow(2, prgsize) * 2;
// set vrc4 swap setting for TMNT2
if (mapper == 25)
write_prg_byte(0x9005, 0);
for (size_t i = 0; i < banks; i += 2) {
write_prg_byte(0x8000, i);
write_prg_byte(0xA000, i + 1);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 23:
banks = int_pow(2, prgsize) * 2;
write_prg_byte(0x9002, 0);
write_prg_byte(0x9008, 0);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000, i);
dumpBankPRG(0x0, 0x2000, base);
}
break;
case 24:
case 26: // 256K
case 29:
case 78: // 128K
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) { // 128K
write_prg_byte(0x8000, i);
dumpBankPRG(0x0, 0x4000, base); // 16K Banks ($8000-$BFFF)
}
break;
case 28: // using 32k mode for inner and outer banks, switching only with outer
banks = int_pow(2, prgsize) / 2;
write_prg_byte(0x5000, 0x81);
write_prg_byte(0x8000, 0);
write_prg_byte(0x5000, 0x80);
write_prg_byte(0x8000, 0);
write_prg_byte(0x5000, 0x01);
write_prg_byte(0x8000, 0);
write_prg_byte(0x5000, 0);
write_prg_byte(0x8000, 0);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x5000, 0x81);
write_prg_byte(0x8000, i);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 31:
banks = int_pow(2, prgsize) * 4;
for (size_t i = 0; i < banks; i += 8) {
write_prg_byte(0x5FF8, i);
write_prg_byte(0x5FF9, i + 1);
write_prg_byte(0x5FFA, i + 2);
write_prg_byte(0x5FFB, i + 3);
write_prg_byte(0x5FFC, i + 4);
write_prg_byte(0x5FFD, i + 5);
write_prg_byte(0x5FFE, i + 6);
write_prg_byte(0x5FFF, i + 7);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 32: // 128K/256K
banks = int_pow(2, prgsize) * 2;
for (size_t 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
dumpBankPRG(0x2000, 0x4000, base); // 8K Banks ($A000-$BFFF)
}
break;
case 33:
case 48: // 128K/256K
banks = int_pow(2, prgsize) * 2;
for (size_t 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)
dumpBankPRG(0x0, 0x4000, base); // 8K Banks ($A000-$BFFF)
}
break;
case 35:
case 90:
case 209:
case 211:
banks = int_pow(2, prgsize) * 2;
write_prg_byte(0xD000, 0x02);
for (uint8_t i = 0; i < banks; i++) {
write_prg_byte(0xD003, ((i >> 5) & 0x06) | 0x20);
write_prg_byte(0x8000, (i & 0x3f));
dumpBankPRG(0x0, 0x2000, base);
}
break;
case 36:
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xFFA0 + i, (i << 4));
write_prg_byte(0x4101, 0);
write_prg_byte(0x4102, (i << 4));
write_prg_byte(0x4103, 0);
write_prg_byte(0x4100, 0);
write_prg_byte(0x4103, 0xFF);
write_prg_byte(0xFFFF, 0xFF);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 38:
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x7000, i);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 41:
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x6000 + i, 0);
write_prg_byte(0x6000 + i, 0);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 42:
banks = int_pow(2, prgsize) * 2;
base = 0x6000; // 8k switchable PRG ROM bank at $6000-$7FFF
for (size_t i = 0; i < banks - 4; i++) {
write_prg_byte(0xE000, i & 0x0F);
dumpBankPRG(0x0, 0x2000, base);
}
base = 0x8000; // last 32k fixed to $8000-$FFFF
dumpBankPRG(0x0, 0x8000, base);
break;
case 43:
base = 0xC000;
for (size_t i = 0; i < 8; i++) {
write_prg_byte(0x4022, i);
dumpBankPRG(0x0, 0x2000, base);
}
base = 0x5000;
for (size_t i = 0; i < 4; i++) {
dumpBankPRG(0x0, 0x1000, base);
}
base = 0xE000;
dumpBankPRG(0x0, 0x1000, base);
break;
case 45: // MMC3 Clone with Outer Registers
banks = ((int_pow(2, prgsize) * 2)) - 2; // Set Number of Banks
for (size_t i = 0; i < banks; i += 2) { // 128K/256K/512K/1024K
for (word address = 0x0; address < 0x2000; address += 512) {
// set outer bank registers
write_prg_byte(0x6000, 0x00); // CHR-OR
write_prg_byte(0x6000, (i & 0xC0)); // PRG-OR
write_prg_byte(0x6000, ((i >> 2) & 0xC0)); // CHR-AND,CHR-OR/PRG-OR
write_prg_byte(0x6000, 0x80); // PRG-AND
// set inner bank registers
write_prg_byte(0x8000, 6); // PRG Bank 0 ($8000-$9FFF)
write_prg_byte(0x8001, i);
dumpPRG(base, address);
}
for (word address = 0x2000; address < 0x4000; address += 512) {
// set outer bank registers
write_prg_byte(0x6000, 0x00); // CHR-OR
write_prg_byte(0x6000, ((i + 1) & 0xC0)); // PRG-OR
write_prg_byte(0x6000, (((i + 1) >> 2) & 0xC0)); // CHR-AND,CHR-OR/PRG-OR
write_prg_byte(0x6000, 0x80); // PRG-AND
// set inner bank registers
write_prg_byte(0x8000, 7); // PRG Bank 1 ($A000-$BFFF)
write_prg_byte(0x8001, i + 1);
dumpPRG(base, address);
}
}
for (word address = 0x4000; address < 0x8000; address += 512) { // Final 2 Banks ($C000-$FFFF)
// set outer bank registers
write_prg_byte(0x6000, 0x00); // CHR-OR
write_prg_byte(0x6000, 0xC0); // PRG-OR
write_prg_byte(0x6000, 0xC0); // CHR-AND,CHR-OR/PRG-OR
write_prg_byte(0x6000, 0x80); // PRG-AND
dumpPRG(base, address);
}
break;
case 46:
banks = int_pow(2, prgsize) / 2; // 32k banks
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x6000, (i & 0x1E) >> 1); // high bits
write_prg_byte(0x8000, i & 0x01); // low bit
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 50:
banks = int_pow(2, prgsize) * 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x4122, (i & 0x08) | ((i & 0x03) << 1) | ((i & 0x04) >> 2));
dumpBankPRG(0xC000, 0xE000, base);
}
break;
case 55:
dumpBankPRG(0x0, 0x8000, base);
base = 0x6000;
for (size_t i = 0; i < 8; i++) {
dumpBankPRG(0x0, 0x0800, base);
}
break;
case 56:
banks = int_pow(2, prgsize) * 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xE000, 1);
write_prg_byte(0xF000, i);
dumpBankPRG(0x0, 0x2000, base);
}
break;
case 57:
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8800, (i & 0x07) << 5);
write_prg_byte(0x8000, 0);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 58:
case 213:
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i += 2) {
write_prg_byte(0x8000 + (i & 0x07), 0);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 59:
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i += 2) {
write_reg_m59(0x8000 + ((i & 0x07) << 4));
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 60:
dumpBankPRG(0x0, 0x4000, base);
for (size_t i = 0; i < 3; i++) {
write_prg_byte(0x8D8D, i);
delay(500);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 61:
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8010 | ((i & 0x01) << 5) | ((i >> 1) & 0x0F), 0);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 62:
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 + (i * 512) + ((i & 32) << 1), 0);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 63: // 3072K total
banks = int_pow(2, prgsize);
for (size_t i = 0; i < 192; i++) {
write_prg_byte(0x8000 + (i << 2), 0);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 66: // 64K/128K
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) { // 64K/128K
write_prg_byte(0x8000, i << 4); // bits 4-5
dumpBankPRG(0x0, 0x8000, base); // 32K Banks ($8000-$FFFF)
}
break;
case 67: // 128K
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) { // 128K
write_reg_byte(0xF800, i); // [WRITE RAM SAFE]
dumpBankPRG(0x0, 0x4000, base); // 16K Banks ($8000-$BFFF)
}
break;
case 68:
case 73: // 128K
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) { // 128K
write_prg_byte(0xF000, i);
dumpBankPRG(0x0, 0x4000, base); // 16K Banks ($8000-$BFFF)
}
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 (size_t 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)
dumpBankPRG(0x0, 0x2000, base); // 8K Banks ($8000-$9FFF)
}
break;
case 70:
case 89:
case 152: // 64K/128K
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) { // 128K
write_prg_byte(0x8000, i << 4);
dumpBankPRG(0x0, 0x4000, base); // 16K Banks ($8000-$BFFF)
}
break;
case 71: // 64K/128K/256K
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xC000, i);
dumpBankPRG(0x0, 0x4000, base); // 16K Banks ($8000-$BFFF)
}
break;
case 72: // 128K
banks = int_pow(2, prgsize);
write_prg_byte(0x8000, 0); // Reset Register
for (size_t i = 0; i < banks; i++) { // 128K
write_prg_byte(0x8000, i | 0x80); // PRG Command + Bank
write_prg_byte(0x8000, i); // PRG Bank
dumpBankPRG(0x0, 0x4000, base); // 16K Banks ($8000-$BFFF)
}
break;
case 79:
case 146:
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x4100, i << 3);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 80: // 128K
case 207: // 256K [CART SOMETIMES NEEDS POWERCYCLE]
banks = int_pow(2, prgsize) * 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x7EFA, i); // PRG Bank 0 ($8000-$9FFF)
dumpBankPRG(0x0, 0x2000, base);
}
break;
case 82: // 128K
banks = int_pow(2, prgsize) * 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x7EFA, i << 2); // PRG Bank 0 ($8000-$9FFF)
dumpBankPRG(0x0, 0x2000, base); // 8K Banks ($8000-$BFFF)
}
break;
case 85: // 128K/512K
case 117:
banks = int_pow(2, prgsize) * 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000, i); // PRG Bank 0 ($8000-$9FFF)
dumpBankPRG(0x0, 0x2000, base); // 8K Banks ($8000-$9FFF)
}
break;
case 86:
case 140: // 128K
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) { // 128K
write_prg_byte(0x6000, i << 4); // bits 4-5
dumpBankPRG(0x0, 0x8000, base); // 32K Banks ($8000-$FFFF)
}
break;
case 91:
banks = int_pow(2, prgsize) * 2;
for (size_t i = 0; i < banks; i += 1) {
write_prg_byte(0x8000 + ((i & 0x30) >> 3), i); // PRG A18-A17 (submapper 0 only)
write_prg_byte(0x7000, i); // PRG -A13
dumpBankPRG(0x0, 0x2000, base);
}
break;
case 92: // 256K
banks = int_pow(2, prgsize);
write_prg_byte(0x8000, 0); // Reset Register
for (size_t i = 0; i < banks; i++) { // 256K
write_prg_byte(0x8000, i | 0x80); // PRG Command + Bank
write_prg_byte(0x8000, i); // PRG Bank
dumpBankPRG(0x4000, 0x8000, base); // 16K Banks ($C000-$FFFF)
}
break;
case 93:
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x6000, i);
write_prg_byte(0x8000, i << 4 | 0x01);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 94: // bus conflicts - fixed last bank
banks = int_pow(2, prgsize);
busConflict = true;
for (size_t i = 0; i < banks; i++) {
for (size_t x = 0; x < 0x4000; x++) {
if (read_prg_byte(0xC000 + x) == (i << 2)) {
write_prg_byte(0xC000 + x, i << 2);
busConflict = false;
break;
}
}
if (busConflict) {
write_prg_byte(0x8000 + i, i << 2);
}
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 97: // fixed first bank
case 180: // bus conflicts - fixed fist bank
banks = int_pow(2, prgsize);
dumpBankPRG(0x0, 0x4000, base);
for (size_t i = 1; i < banks; i++) {
write_prg_byte(0x8000, i);
dumpBankPRG(0x4000, 0x8000, base);
}
break;
case 105: // 256K
write_mmc1_byte(0xA000, 0); // Clear PRG Init/IRQ (Bit 4)
write_mmc1_byte(0xA000, 0x10); // Set PRG Init/IRQ (Bit 4) to enable bank swapping
for (size_t i = 0; i < 4; i++) { // PRG CHIP 1 128K
write_mmc1_byte(0xA000, i << 1);
dumpBankPRG(0x0, 0x8000, base); // 32K Banks ($8000-$FFFF)
}
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 (size_t j = 0; j < 8; j++) { // PRG CHIP 2 128K
write_mmc1_byte(0xE000, j);
dumpBankPRG(0x0, 0x4000, base); // 16K Banks ($8000-$BFFF)
}
break;
case 111:
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x5000, i);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 113:
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x4100, (i & 0x07) << 3);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 114: // Submapper 0
case 182:
banks = int_pow(2, prgsize) * 2;
write_prg_byte(0x6000, 0);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xA000, 4);
write_prg_byte(0xC000, i);
dumpBankPRG(0x0, 0x2000, base);
}
break;
case 120:
base = 0x6000;
for (size_t i = 0; i < 8; i += 1) {
write_prg_byte(0x41FF, i);
dumpBankPRG(0x0, 0x2000, base);
}
base = 0x8000;
dumpBankPRG(0x0, 0x8000, base);
break;
case 125:
base = 0x6000;
for (size_t i = 0; i < 16; i += 1) {
write_prg_byte(0x6000, i);
dumpBankPRG(0x0, 0x2000, base);
}
break;
case 142:
banks = int_pow(2, prgsize) * 2;
base = 0x6000; // 4x 8k switchable PRG ROM banks at $6000-$DFFF
for (size_t i = 0; i < banks; i += 4) {
write_prg_byte(0xE000, 4); // Select 8 KB PRG bank at CPU $6000-$7FFF
write_prg_byte(0xF000, i);
write_prg_byte(0xE000, 1); // Select 8 KB PRG bank at CPU $8000-$9FFF
write_prg_byte(0xF000, i + 1);
write_prg_byte(0xE000, 2); // Select 8 KB PRG bank at CPU $A000-$BFFF
write_prg_byte(0xF000, i + 2);
write_prg_byte(0xE000, 3); // Select 8 KB PRG bank at CPU $C000-$DFFF
write_prg_byte(0xF000, i + 3);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 148: // Sachen SA-008-A and Tengen 800008 -- Bus conflicts
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000, i << 3);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 153: // 512K
banks = int_pow(2, prgsize);
for (size_t 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
dumpBankPRG(0x0, 0x4000, base); // 16K Banks ($8000-$BFFF)
}
break;
case 157:
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8008, i);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 162:
banks = int_pow(2, prgsize) / 2;
write_prg_byte(0x5300, 0x07); // A16-A15 controlled by $5000
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x5200, (i & 0x30) >> 4); // A20-A19
write_prg_byte(0x5000, i & 0x0F); // A18-A15
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 163:
banks = int_pow(2, prgsize) / 2;
write_prg_byte(0x5300, 0x04); // disable bit swap on writes to $5000-$5200
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x5200, (i & 0x30) >> 4); // A20-A19
write_prg_byte(0x5000, i & 0x0F); // A18-A15
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 174: // 128k
for (size_t i = 0; i < 8; i++) {
write_prg_byte(0xFF00 + (i << 4), 0);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 178:
banks = int_pow(2, prgsize);
write_prg_byte(0x4800, 0); // NROM-256 mode
write_prg_byte(0x4803, 0); // set PRG-RAM
for (size_t i = 0; i < banks; i += 2) {
write_prg_byte(0x4802, i >> 3); // high PRG (up to 8 bits?!)
write_prg_byte(0x4801, i & 0x07); // low PRG (3 bits)
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 200:
case 212:
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 + (i & 0x07), 0);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 201:
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 + (i & 0xFF), 0);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 202:
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 | (i << 1), 0);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 203:
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000, (i & 0x1F) << 2);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 210: // 128K/256K
banks = int_pow(2, prgsize) * 2;
for (size_t 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]
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 214:
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 | (i << 2), 0);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 225:
case 255:
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i += 2) {
write_prg_byte(0x8000 + (((i & 0x40) << 8) | ((i & 0x3F) << 6)), 0);
dumpBankPRG(0x0, 0x8000, base);
}
break;
// MAPPER 226 - BMC SUPER 42-IN-1, BMC 76-IN-1 1024K/2048K
// CART REQUIRES PULSING M2 TO ENABLE BANKSWITCH
// WRITING DATA TO THE SD CARD BREAKS THE M2 PULSING SEQUENCE
// SET THE BANK USING THE PULSING M2 CODE FOR EACH 512 BYTE BLOCK
case 226: // 1024K/2048K
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i += 2) {
for (word address = 0x0; address < 0x8000; address += 512) {
write_prg_pulsem2(0x8001, (i & 0x40) >> 6);
write_prg_pulsem2(0x8000, ((i & 0x20) << 2) | (i & 0x1F));
dumpPRG_pulsem2(base, address);
}
}
break;
case 227:
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8083 + ((i & 0xF) << 3), 0);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 228:
banks = int_pow(2, prgsize);
write_prg_byte(0x8000, 0);
for (size_t i = 0; i < banks; i += 2) { // up to 1024k PRG
write_prg_byte(0x8000 + ((i & 0x3F) << 6), 0);
dumpBankPRG(0x0, 0x8000, base);
}
if (prgsize > 5) { // reading the 3rd 512k PRG chip (Action 52)
for (size_t i = 0; i < 32; i += 2) {
write_prg_byte(0x9800 + ((i & 0x1F) << 6), 0);
dumpBankPRG(0x0, 0x8000, base);
}
}
break;
case 229:
write_prg_byte(0x8000, 0);
dumpBankPRG(0x0, 0x8000, base);
for (size_t i = 2; i < 32; i++) {
write_prg_byte(0x8000 + i, i);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 232:
if (oldcrc32 == 0x2B50A29C || oldcrc32 == 0x9DF7D376 || oldcrc32 == 0x596EC6E6 || oldcrc32 == 0xD003AFCA || oldcrc32 == 0x5004C6CF || oldcrc32 == 0x59D7D89D || oldcrc32 == 0x017D903E || oldcrc32 == 0xC1E6A786 || oldcrc32 == 0xD43AC4BE || oldcrc32 == 0x3E54E71D || oldcrc32 == 0x3D8497EA || oldcrc32MMC3 == 0x2B50A29C || oldcrc32MMC3 == 0x9DF7D376 || oldcrc32MMC3 == 0x596EC6E6 || oldcrc32MMC3 == 0xD003AFCA || oldcrc32MMC3 == 0x5004C6CF || oldcrc32MMC3 == 0x59D7D89D || oldcrc32MMC3 == 0x017D903E || oldcrc32MMC3 == 0xC1E6A786 || oldcrc32MMC3 == 0xD43AC4BE || oldcrc32MMC3 == 0x3E54E71D || oldcrc32MMC3 == 0x3D8497EA) {
println_Msg(F("DUMPING QUATTRO CART"));
display_Update();
for (size_t i = 0; i < 16; i++) {
write_prg_byte(0x8000, ((i & 0x04) << 2) | (i & 0x08));
write_prg_byte(0xC000, i & 0x03);
dumpBankPRG(0x0, 0x4000, base);
}
} else {
banks = int_pow(2, prgsize) / 4;
for (size_t outerbank = 0; outerbank < 4; outerbank++) {
write_prg_byte(0x8000, outerbank << 3);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xC000, i);
dumpBankPRG(0x0, 0x4000, base);
}
}
}
break;
// MAPPER 233 BMC 22-IN-1/20-IN-1 (42-IN-1) 1024K
// ROM SPLIT INTO 512K + 512K CHIPS THAT COMBINE USING /CE + CE PINS
// ENABLING PRG CHIP 1 DISABLES PRG CHIP 2 AND VICE VERSA
// CART USES DIODE AND CAP TIED TO TC4520BP THAT CONTROLS THE ENABLE LINE TO THE PRG CHIPS
// M2 IS NOT CONNECTED ON THIS CART UNLIKE OTHER RESET-BASED MULTICARTS
// SWITCHING PRG CHIPS REQUIRES A POWER CYCLE OF THE CART READER
// COMBINE THE LOWER AND UPPER DUMPS - BE SURE TO REMOVE ANY INES HEADER ON THE UPPER DUMP
case 233: // 1024K
// BMC 22-in-1/20-in-1 (42-in-1)
// POWER CYCLE TO SWITCH BETWEEN CHIPS
banks = int_pow(2, prgsize) / 2; // 512K/512K
// Check PRG Chip
write_prg_byte(0x8000, 0);
eeptemp = read_prg_byte(0x8001); // Use eeptemp to hold byte
if (eeptemp == 0x00) { // PRG CHIP 1
println_Msg(F("READING LOWER CHIP"));
display_Update();
} else { // PRG CHIP 2 - Should be 0xEF
println_Msg(F("READING UPPER CHIP"));
display_Update();
}
for (size_t i = 0; i < banks; i += 2) {
write_prg_byte(0x8000, i & 0x1F);
dumpBankPRG(0x0, 0x8000, base);
}
println_Msg(F("POWER CYCLE TO SWITCH"));
display_Update();
break;
case 235:
for (size_t i = 0; i < 32; i++) {
write_prg_byte(0x8000 + i, 0);
dumpBankPRG(0x0, 0x8000, base);
}
if (prgsize > 6) {
for (size_t i = 32; i < 64; i++) {
write_prg_byte(0x80E0 + i, 0);
dumpBankPRG(0x0, 0x8000, base);
}
if (prgsize > 7) {
for (size_t i = 64; i < 96; i++) {
write_prg_byte(0x81E0 + i, 0);
dumpBankPRG(0x0, 0x8000, base);
}
for (size_t i = 96; i < 128; i++) {
write_prg_byte(0x82E0 + i, 0);
dumpBankPRG(0x0, 0x8000, base);
}
}
}
break;
case 236:
banks = int_pow(2, prgsize);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 | ((i & 0x38) >> 3), 0); // A19-A17
write_prg_byte(0xC030 | (i & 0x0F), 0); // A17-A14
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 237: // 1024K
banks = int_pow(2, prgsize) / 2;
for (int i = 0; i < banks; i++) {
write_prg_pulsem2(0x8000, i | 0xC0); // 32K NROM Mode
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 240:
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x5FFF, (i & 0xF) << 4);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 242: // total size is 640k THIS IS NORMAL
for (size_t i = 0; i < 32; i++) { // dump 1st chip of 512k
write_prg_byte(0x8400 + (i * 4), 0);
dumpBankPRG(0x0, 0x4000, base);
}
for (size_t i = 0; i < 8; i++) { // dump 2nd chip of 128k
write_prg_byte(0x8000 + (i * 4), 0);
dumpBankPRG(0x0, 0x4000, base);
}
break;
case 246:
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i += 4) {
write_prg_byte(0x6000, (i | 0));
write_prg_byte(0x6001, (i | 1));
write_prg_byte(0x6002, (i | 2));
write_prg_byte(0x6003, (i | 3));
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 289: // 512K/1024K/2048K
banks = int_pow(2, prgsize);
for (int i = 0; i < banks; i++) {
for (word address = 0; address < 0x4000; address += 512) { // 16K
write_prg_pulsem2(0x6000, 0); // NROM-128 Mode
write_prg_pulsem2(0x6001, i); // Set Bank
dumpPRG_pulsem2(base, address);
}
}
break;
case 319: // 128K
banks = int_pow(2, prgsize) / 2;
for (int i = 0; i < banks; i++) {
write_prg_byte(0x6004, (i << 3) | 0x40); // prg a14 = cpu a14 (NROM-256)
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 332: // 256K
banks = int_pow(2, prgsize);
for (int i = 0; i < banks; i++) {
for (word address = 0x0; address < 0x4000; address += 512) {
write_prg_pulsem2(0x6000, 0x08 | (i & 0x07) | ((i & 0x08) << 3));
dumpPRG_pulsem2(base, address);
}
}
break;
case 446:
banks = int_pow(2, prgsize) * 2;
write_prg_byte(0x5003, 0);
write_prg_byte(0x5005, 0);
for (uint8_t i = 0; i < banks; i++) { // 8192 for 64MiB
write_prg_byte(0x5002, i >> 8); // outer bank LSB
write_prg_byte(0x5001, i); // outer bank MSB
write_prg_byte(0x8000, 0);
dumpBankPRG(0x0, 0x2000, base);
}
break;
case 470:
banks = int_pow(2, prgsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x5000, i >> 3);
write_prg_byte(0x8000, i & 0x07);
dumpBankPRG(0x0, 0x8000, base);
}
break;
case 552:
banks = int_pow(2, prgsize) * 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x7EFA, ((i & 0x01) << 5) | ((i & 0x02) << 3) | ((i & 0x04) << 1) | ((i & 0x08) >> 1) | ((i & 0x10) >> 3) | ((i & 0x20) >> 5)); // PRG Bank 0 ($8000-$9FFF)
dumpBankPRG(0x0, 0x2000, base); // 8K Banks ($8000-$BFFF)
}
break;
}
if (!readrom) {
myFile.flush();
myFile.close();
println_Msg(F("PRG FILE DUMPED!"));
println_Msg(FS(FSTRING_EMPTY));
display_Update();
}
}
set_address(0);
PHI2_HI;
ROMSEL_HI;
rgbLed(black_color);
}
void readCHR(bool readrom) {
if (!readrom) {
display_Clear();
display_Update();
}
uint16_t banks;
bool busConflict = false;
rgbLed(green_color);
set_address(0);
_delay_us(1);
if (chrsize == 0) {
println_Msg(F("CHR SIZE 0K"));
display_Update();
} else {
if (!readrom) {
CreateCHRFileInSD();
}
if (myFile) {
switch (mapper) {
case 0: // 8K
case 43:
case 55:
dumpBankCHR(0x0, 0x2000);
break;
case 1:
case 155:
banks = int_pow(2, chrsize);
for (size_t 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);
dumpBankCHR(0x0, 0x2000);
}
break;
case 3: // 8K/16K/32K - bus conflicts
case 29:
case 66: // 16K/32K
case 70:
case 148: // Sachen SA-008-A and Tengen 800008 - Bus conflicts
case 152: // 128K
banks = int_pow(2, chrsize) / 2;
busConflict = true;
for (size_t i = 0; i < banks; i++) {
for (size_t x = 0; x < 0x8000; x++) {
if (read_prg_byte(0x8000 + x) == i) {
write_prg_byte(0x8000 + x, i);
busConflict = false;
break;
}
}
if (busConflict) {
write_prg_byte(0x8000 + i, i);
}
dumpBankCHR(0x0, 0x2000);
}
break;
case 4:
case 12:
case 37:
case 47:
case 49:
case 52:
case 64:
case 76:
case 95: // 32K
case 115:
case 118:
case 119:
case 126:
case 134:
case 154: // 128K
case 158:
case 176:
case 206: // 16K/32K/64K
case 248:
case 315:
case 366:
banks = int_pow(2, chrsize) * 4;
write_prg_byte(0xA001, 0x80);
if (mapper == 126) {
write_prg_byte(0x6003, 0); // set MMC3 banking mode
}
if ((mapper == 115) || (mapper == 134) || (mapper == 248)) {
write_prg_byte(0x6000, 0); // set MMC3 banking mode
}
if (mapper == 176) {
write_prg_byte(0x5FF3, 0); // extended MMC3 mode: disabled
write_prg_byte(0x5FF0, 1); // 256K outer bank mode
}
for (size_t i = 0; i < banks; i++) {
if (mapper == 12) {
write_prg_byte(0x4132, (i & 0x100) >> 8 | (i & 0x100) >> 4);
}
if (mapper == 37) {
if (i == 0) {
write_prg_byte(0x6000, 0);
} else if (i == 64) {
write_prg_byte(0x6000, 3);
} else if (i == 128) {
write_prg_byte(0x6000, 4);
}
}
if (mapper == 47) {
write_prg_byte(0x6800 + ((i & 0x180) >> 7), 0);
}
if (mapper == 49) {
write_prg_byte(0x6800, ((i & 0x180) >> 1) | 1);
}
if (mapper == 52) {
write_prg_byte(0x6000, (i & 0x180) >> 3 | (i & 0x200) >> 7);
}
if ((mapper == 115) || (mapper == 248)) {
write_prg_byte(0x6000, (i & 0x100) >> 8); // A18
}
if (mapper == 126) {
write_prg_byte(0x6000, (i & 0x200) >> 5 | (i & 0x100) >> 3); // select outer bank
}
if (mapper == 134) {
write_prg_byte(0x6001, (i & 0x180) >> 3); // select outer bank
}
if (mapper == 176) {
write_prg_byte(0x5FF2, (i & 0x700) >> 3); // outer 256k bank
}
if (mapper == 268) {
write_prg_byte(0x5000, ((i & 0x380) >> 4) | ((i & 0xC00) >> 9));
write_prg_byte(0x6000, ((i & 0x380) >> 4) | ((i & 0xC00) >> 9));
}
if (mapper == 315) {
write_prg_byte(0x6800, ((i & 0x100) >> 8) | ((i & 0x80) >> 6) | ((i & 0x40) >> 3));
}
if (mapper == 366) {
write_prg_byte(0x6800 + ((i & 0x380) >> 3), i);
}
write_prg_byte(0x8000, 0x02);
write_prg_byte(0x8001, i);
dumpBankCHR(0x1000, 0x1400);
}
break;
case 5: // 128K/256K/512K
banks = int_pow(2, chrsize) / 2;
write_prg_byte(0x5101, 0); // 8K CHR Banks
for (size_t 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);
dumpBankCHR(0x0, 0x2000);
}
break;
case 9:
case 10:
banks = int_pow(2, chrsize);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xB000, i);
write_prg_byte(0xC000, i);
dumpBankCHR(0x0, 0x1000);
}
break;
case 11:
case 144:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xFFB0 + i, i << 4);
dumpBankCHR(0x0, 0x2000);
}
break;
case 16:
case 159: // 128K/256K
banks = int_pow(2, chrsize) * 4;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x6000, i); // Submapper 4
write_prg_byte(0x8000, i); // Submapper 5
dumpBankCHR(0x0, 0x400);
}
break;
case 18: // 128K/256K
banks = int_pow(2, chrsize) * 4;
for (size_t 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
dumpBankCHR(0x0, 0x400);
}
break;
case 19: // 128K/256K
case 532:
for (size_t 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 (size_t 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
dumpBankCHR(0x0, 0x2000);
}
break;
case 21: // 128K/256K
banks = int_pow(2, chrsize) * 4;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xB000, i & 0xF); // CHR Bank Lower 4 bits
if (chrsize == 5) // Check CHR Size to determine VRC4a (128K) or VRC4c (256K)
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)
dumpBankCHR(0x0, 0x400);
}
break;
case 22: // 128K
banks = int_pow(2, chrsize) * 4;
for (size_t 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
dumpBankCHR(0x0, 0x400);
}
break;
case 23:
{ // 128K
banks = int_pow(2, chrsize) * 4;
// Detect VRC4e Carts - read PRG 0x1FFF6 (DATE)
// Boku Dracula-kun = 890810, Tiny Toon = 910809
// Crisis Force = 910701, Parodius Da! = 900916
write_prg_byte(0x8000, 15);
uint8_t prgchk0 = read_prg_byte(0x9FF6);
if (prgchk0 == 0x30) { // Check for "0" in middle of date. If true, assume VRC4e Cart
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xB000, i & 0xF); // CHR Bank Lower 4 bits
write_prg_byte(0xB004, (i >> 4) & 0xF); // CHR Bank Upper 4 bits VRC4e
dumpBankCHR(0x0, 0x400);
}
break;
}
// VRC2b/VRC4f - See https://www.nesdev.org/wiki/VRC2_and_VRC4
for (size_t i = 0; i < banks; i += 8) {
write_prg_byte(0xB000, i & 0xF); // CHR Bank 0: Lower 4 bits
write_prg_byte(0xB001, (i >> 4) & 0xF); // CHR Bank 0: Upper 4 bits
write_prg_byte(0xB002, (i + 1) & 0xF); // CHR Bank 1: Lower 4 bits
write_prg_byte(0xB003, ((i + 1) >> 4) & 0xF); // CHR Bank 1: Upper 4 bits
write_prg_byte(0xC000, (i + 2) & 0xF); // CHR Bank 2: Lower 4 bits
write_prg_byte(0xC001, ((i + 2) >> 4) & 0xF); // CHR Bank 2: Upper 4 bits
write_prg_byte(0xC002, (i + 3) & 0xF); // CHR Bank 3: Lower 4 bits
write_prg_byte(0xC003, ((i + 3) >> 4) & 0xF); // CHR Bank 3: Upper 4 bits
write_prg_byte(0xD000, (i + 4) & 0xF); // CHR Bank 4: Lower 4 bits
write_prg_byte(0xD001, ((i + 4) >> 4) & 0xF); // CHR Bank 4: Upper 4 bits
write_prg_byte(0xD002, (i + 5) & 0xF); // CHR Bank 5: Lower 4 bits
write_prg_byte(0xD003, ((i + 5) >> 4) & 0xF); // CHR Bank 5: Upper 4 bits
write_prg_byte(0xE000, (i + 6) & 0xF); // CHR Bank 6: Lower 4 bits
write_prg_byte(0xE001, ((i + 6) >> 4) & 0xF); // CHR Bank 6: Upper 4 bits
write_prg_byte(0xE002, (i + 7) & 0xF); // CHR Bank 7: Lower 4 bits
write_prg_byte(0xE003, ((i + 7) >> 4) & 0xF); // CHR Bank 7: Upper 4 bits
dumpBankCHR(0x0, 0x2000); // 8 Banks for a total of 8 KiB
}
break;
}
case 24: // 128K
banks = int_pow(2, chrsize) * 4;
write_prg_byte(0xB003, 0); // PPU Banking Mode 0
for (size_t 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]
dumpBankCHR(0x0, 0x2000); // 1K Banks
}
break;
case 25: // 128K/256K
banks = int_pow(2, chrsize) * 4;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xB000, i & 0xF); // CHR Bank Lower 4 bits
write_prg_byte(0xB00A, (i >> 4) & 0xF); // Combine VRC2c and VRC4b, VRC4d reg
dumpBankCHR(0x0, 0x400);
}
break;
case 26: // 128K/256K
banks = int_pow(2, chrsize) * 4;
write_prg_byte(0xB003, 0);
for (size_t i = 0; i < banks; i += 4) {
write_prg_byte(0xD000, i + 0); // 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
dumpBankCHR(0x0, 0x1000); // 1K Banks
}
break;
case 32: // 128K
case 65: // 128K/256K
banks = int_pow(2, chrsize) * 4;
for (size_t 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
dumpBankCHR(0x0, 0x2000);
}
break;
case 33: // 128K/256K
case 48: // 256K
banks = int_pow(2, chrsize) * 2;
for (size_t 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
dumpBankCHR(0x0, 0x1000);
}
break;
case 34: // NINA
banks = int_pow(2, chrsize);
for (size_t i = 0; i < banks; i += 1) {
write_prg_byte(0x7FFE, i); // Select 4 KB CHR bank at $0000
delay(200); // NINA seems slow to switch banks
dumpBankCHR(0x0, 0x1000);
}
break;
case 35:
case 90:
case 209:
case 211:
banks = int_pow(2, chrsize) / 2;
write_prg_byte(0xD000, 0x02);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xD003, ((i >> 3) & 0x18) | 0x20);
write_prg_byte(0x9000, (i & 0x3f));
dumpBankCHR(0x0, 0x2000);
}
break;
case 36:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x4200, i);
dumpBankCHR(0x0, 0x2000);
}
break;
case 38:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x7000, i << 2);
dumpBankCHR(0x0, 0x2000);
}
break;
case 41:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x6004 + ((i & 0x0C) << 1), 0);
write_prg_byte(0x6004 + ((i & 0x0C) << 1), 0);
write_prg_byte(0xFFF0 + (i & 0x03), i & 0x03);
write_prg_byte(0xFFF0 + (i & 0x03), i & 0x03);
dumpBankCHR(0x0, 0x2000);
}
break;
case 42:
banks = int_pow(2, chrsize);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000, i & 0x0F);
dumpBankCHR(0x0, 0x1000);
}
break;
case 45: // 128K/256K/512K/1024K
banks = int_pow(2, chrsize) * 4;
write_prg_byte(0xA001, 0x80); // Unlock Write Protection - not used by some carts
for (size_t i = 0; i < banks; i++) {
// set outer bank registers
write_prg_byte(0x6000, 0); // CHR-OR
write_prg_byte(0x6000, 0); // PRG-OR
write_prg_byte(0x6000, (((i / 256) << 4) | 0x0F)); // CHR-AND,CHR-OR/PRG-OR
write_prg_byte(0x6000, 0x80); // PRG-AND
// set inner bank registers
write_prg_byte(0x8000, 0x2); // CHR Bank 2 ($1000-$13FF)
write_prg_byte(0x8001, i);
for (size_t address = 0x1000; address < 0x1200; address += 512) {
dumpCHR_M2(address); // Read CHR with M2 Pulse
}
// set outer bank registers
write_prg_byte(0x6000, 0); // CHR-OR
write_prg_byte(0x6000, 0); // PRG-OR
write_prg_byte(0x6000, (((i / 256) << 4) | 0x0F)); // CHR-AND,CHR-OR/PRG-OR
write_prg_byte(0x6000, 0x80); // PRG-AND
// set inner bank registers
write_prg_byte(0x8000, 0x2); // CHR Bank 2 ($1000-$13FF)
write_prg_byte(0x8001, i);
for (size_t address = 0x1200; address < 0x1400; address += 512) {
dumpCHR_M2(address); // Read CHR with M2 Pulse
}
}
break;
case 46:
banks = int_pow(2, chrsize); // 8k banks
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x6000, (i & 0x78) << 1); // high bits
write_prg_byte(0x8000, (i & 0x07) << 4); // low bits
dumpBankCHR(0x0, 0x2000);
}
break;
case 56:
banks = int_pow(2, chrsize) * 4;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xFC00, i);
dumpBankCHR(0x0, 0x400);
}
break;
case 57:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8800, i & 0x07); // A15-A13
write_prg_byte(0x8000, 0x80 | ((i & 0x08) << 3)); // A16
dumpBankCHR(0x0, 0x2000);
}
break;
case 58:
case 213:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 + ((i & 0x07) << 3), 0);
dumpBankCHR(0x0, 0x2000);
}
break;
case 59:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_reg_m59(0x8000 + (i & 0x07));
dumpBankCHR(0x0, 0x2000);
}
break;
case 60:
for (size_t i = 0; i < 4; i++) {
write_prg_byte(0x8D8D, i);
delay(500);
dumpBankCHR(0x0, 0x2000);
}
break;
case 61:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 | (i << 8), 0);
dumpBankCHR(0x0, 0x2000);
}
break;
case 62:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 + (i / 4), i & 3);
dumpBankCHR(0x0, 0x2000);
}
break;
case 67: // 128K
banks = int_pow(2, chrsize) * 2;
for (size_t 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
dumpBankCHR(0x0, 0x2000);
}
break;
case 68: // 128K/256K
banks = int_pow(2, chrsize) * 2;
for (size_t 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
dumpBankCHR(0x0, 0x2000);
}
break;
case 69: // 128K/256K
banks = int_pow(2, chrsize) * 4;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000, 0); // Command Register - CHR Bank 0
write_prg_byte(0xA000, i); // Parameter Register - ($0000-$03FF)
dumpBankCHR(0x0, 0x400); // 1K Banks
}
break;
case 72: // 128K
banks = int_pow(2, chrsize) / 2;
write_prg_byte(0x8000, 0); // Reset Register
for (size_t i = 0; i < banks; i++) { // 8K Banks
write_prg_byte(0x8000, i | 0x40); // CHR Command + Bank
write_prg_byte(0x8000, i); // CHR Bank
dumpBankCHR(0x0, 0x2000);
}
break;
case 75: // 128K
banks = int_pow(2, chrsize);
for (size_t 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
dumpBankCHR(0x0, 0x1000);
}
break;
case 77: // 32K
banks = int_pow(2, chrsize) * 2;
for (size_t i = 0; i < banks; i++) { // 2K Banks
write_prg_byte(0x8000, i << 4); // CHR Bank 0
dumpBankCHR(0x0, 0x800);
}
break;
case 78: // 128K
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) { // 8K Banks
write_prg_byte(0x8000, i << 4); // CHR Bank 0
dumpBankCHR(0x0, 0x2000); // 8K Banks ($0000-$1FFF)
}
break;
case 79:
case 146:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x4100, i);
dumpBankCHR(0x0, 0x2000);
}
break;
case 80: // 128K/256K
case 82: // 128K/256K
case 207: // 128K [CART SOMETIMES NEEDS POWERCYCLE]
case 552:
banks = int_pow(2, chrsize) * 2;
write_prg_byte(0x7EF6, 0); // CHR mode [2x 2KiB banks at $0000-$0FFF]
for (size_t i = 0; i < banks; i += 2) {
write_prg_byte(0x7EF0, i << 1);
write_prg_byte(0x7EF1, (i + 1) << 1);
dumpBankCHR(0x0, 0x1000);
}
break;
case 85: // 128K
banks = int_pow(2, chrsize) * 4;
for (size_t 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
dumpBankCHR(0x0, 0x2000);
}
break;
case 86: // 64K
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) { // 8K Banks
if (i < 4)
write_prg_byte(0x6000, i & 0x3);
else
write_prg_byte(0x6000, (i | 0x40) & 0x43);
dumpBankCHR(0x0, 0x2000);
}
break;
case 87: // 16K/32K
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) { // 16K/32K
write_prg_byte(0x6000, ((i & 0x1) << 1) | ((i & 0x2) >> 1));
dumpBankCHR(0x0, 0x2000);
}
break;
case 88:
banks = int_pow(2, chrsize) * 4;
write_prg_byte(0xA001, 0x80);
for (size_t i = 0; i < banks; i += 2) {
if (i < 64) {
write_prg_byte(0x8000, 0);
write_prg_byte(0x8001, i);
dumpBankCHR(0x0, 0x0800);
} else {
write_prg_byte(0x8000, 2);
write_prg_byte(0x8001, i);
write_prg_byte(0x8000, 3);
write_prg_byte(0x8001, i + 1);
dumpBankCHR(0x1000, 0x1800);
}
}
break;
case 89: // 128K
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) { // 8K Banks
if (i < 8)
write_prg_byte(0x8000, i & 0x7);
else
write_prg_byte(0x8000, (i | 0x80) & 0x87);
dumpBankCHR(0x0, 0x2000);
}
break;
case 91:
banks = int_pow(2, chrsize) * 2;
for (size_t i = 0; i < banks; i += 1) {
write_prg_byte(0x8000 + ((i & 0x100) >> 8), i); // CHR A19 (submapper 0 only)
write_prg_byte(0x6000, i); // CHR A18-A11
dumpBankCHR(0x0, 0x0800);
}
break;
case 92: // 128K
banks = int_pow(2, chrsize) / 2;
write_prg_byte(0x8000, 0); // Reset Register
for (size_t i = 0; i < banks; i++) { // 8K Banks
write_prg_byte(0x8000, i | 0x40); // CHR Command + Bank
write_prg_byte(0x8000, i); // CHR Bank
dumpBankCHR(0x0, 0x2000);
}
break;
case 113:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x4100, (i & 0x08) << 3 | (i & 0x07));
dumpBankCHR(0x0, 0x2000);
}
break;
case 114: // Submapper 0
case 182:
banks = int_pow(2, chrsize) * 4;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x6000, (i & 0x80) >> 7);
write_prg_byte(0xA000, 6);
write_prg_byte(0xC000, i);
dumpBankCHR(0x1000, 0x1400);
}
break;
case 117:
banks = int_pow(2, chrsize) * 4;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0xA000, i);
dumpBankCHR(0x0, 0x0400);
}
break;
case 140: // 32K/128K
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) { // 8K Banks
write_prg_byte(0x6000, i);
dumpBankCHR(0x0, 0x2000);
}
break;
case 174: // 64k
for (size_t i = 0; i < 8; i++) {
write_prg_byte(0xFF00 + (i << 1), 0);
dumpBankCHR(0x0, 0x2000);
}
break;
case 122:
case 184: // 16K/32K
banks = int_pow(2, chrsize);
for (size_t i = 0; i < banks; i++) { // 4K Banks
write_prg_byte(0x6000, i); // CHR LOW (Bits 0-2) ($0000-$0FFF)
dumpBankCHR(0x0, 0x1000); // 4K Banks ($0000-$0FFF)
}
break;
case 185: // 8K [READ 32K TO OVERRIDE LOCKOUT]
for (size_t i = 0; i < 4; i++) { // Read 32K to locate valid 8K
write_prg_byte(0x8000, i);
uint8_t chrcheck = read_chr_byte(0);
for (size_t address = 0x0; address < 0x2000; address += 512) {
for (size_t x = 0; x < 512; x++) {
sdBuffer[x] = read_chr_byte(address + x);
}
if (chrcheck != 0xFF)
myFile.write(sdBuffer, 512);
}
}
break;
case 200:
case 212:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 + (i & 0x07), 0);
dumpBankCHR(0x0, 0x2000);
}
break;
case 201:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 + (i & 0xFF), 0);
dumpBankCHR(0x0, 0x2000);
}
break;
case 202:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 | (i << 1), 0);
dumpBankCHR(0x0, 0x2000);
}
break;
case 203:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000, (i & 0x03));
dumpBankCHR(0x0, 0x2000);
}
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 (size_t 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
dumpBankCHR(0x0, 0x2000);
}
break;
case 214:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 | (i << 2), 0);
dumpBankCHR(0x0, 0x2000);
}
break;
case 225:
case 255:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 + (((i & 0x40) << 8) | (i & 0x3F)), 0);
dumpBankCHR(0x0, 0x2000);
}
break;
case 228:
banks = int_pow(2, chrsize) / 2;
write_prg_byte(0x8000, 0);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 + ((i & 0x3C) >> 2), i & 0x03);
dumpBankCHR(0x0, 0x2000);
}
break;
case 229:
for (size_t i = 0; i < 32; i++) {
write_prg_byte(0x8000 + i, i);
dumpBankCHR(0x0, 0x2000);
}
break;
case 236:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x8000 | (i & 0x0F), 0); // A16-A13
dumpBankCHR(0x0, 0x2000);
}
break;
case 240:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x5FFF, (i & 0xF));
dumpBankCHR(0x0, 0x2000);
}
break;
case 246:
banks = int_pow(2, chrsize) / 2;
for (size_t i = 0; i < banks; i += 4) {
write_prg_byte(0x6004, (i | 0));
write_prg_byte(0x6005, (i | 1));
write_prg_byte(0x6006, (i | 2));
write_prg_byte(0x6007, (i | 3));
dumpBankCHR(0x0, 0x2000);
}
break;
case 268: // mapper 268.0
banks = int_pow(2, chrsize) * 4;
write_prg_byte(0xA001, 0x80);
for (size_t i = 0; i < banks; i++) {
write_prg_byte(0x6000, ((i & 0x380) >> 4) | ((i & 0xC00) >> 9));
write_prg_byte(0x8000, 0x02);
write_prg_byte(0x8001, i);
dumpBankCHR(0x1000, 0x1400);
}
break;
case 319: // 64K
banks = int_pow(2, chrsize) / 2;
for (int i = 0; i < banks; i++) {
write_prg_byte(0x6000, i << 4);
dumpBankCHR(0x0, 0x2000);
}
break;
case 332: // 128K
banks = int_pow(2, chrsize) / 2;
for (int i = 0; i < banks; i++) {
for (word address = 0x0; address < 0x2000; address += 512) {
write_prg_pulsem2(0x6000, (i & 0x08) << 3);
write_prg_pulsem2(0x6001, 0x30 | (i & 0x07));
dumpCHR_pulsem2(address);
}
}
break;
}
if (!readrom) {
myFile.flush();
myFile.close();
println_Msg(F("CHR FILE DUMPED!"));
println_Msg(FS(FSTRING_EMPTY));
display_Update();
}
}
}
set_address(0);
PHI2_HI;
ROMSEL_HI;
rgbLed(black_color);
}
/******************************************
RAM Functions
*****************************************/
void readRAM() {
display_Clear();
display_Update();
uint16_t banks;
rgbLed(turquoise_color);
set_address(0);
_delay_us(1);
if (ramsize == 0) {
println_Msg(F("RAM SIZE 0K"));
display_Update();
} else {
CreateRAMFileInSD();
word base = 0x6000;
if (myFile) {
switch (mapper) {
case 0: // 2K/4K
dumpBankPRG(0x0, (0x800 * ramsize), base); // 2K/4K
break; // SWITCH MUST BE IN OFF POSITION
case 1:
case 155: // 8K/16K/32K
banks = int_pow(2, ramsize) / 2; // banks = 1,2,4
for (size_t 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);
dumpBankPRG(0x0, 0x2000, base); // 8K
}
break;
case 4: // 1K/8K (MMC6/MMC3)
if (mmc6) { // MMC6 1K
write_prg_byte(0x8000, 0x20); // PRG RAM ENABLE
write_prg_byte(0xA001, 0x20); // PRG RAM PROTECT - Enable reading RAM at $7000-$71FF
for (size_t address = 0x1000; address < 0x1200; address += 512) { // 512B
dumpMMC5RAM(base, address);
}
write_prg_byte(0x8000, 0x20); // PRG RAM ENABLE
write_prg_byte(0xA001, 0x80); // PRG RAM PROTECT - Enable reading RAM at $7200-$73FF
for (size_t address = 0x1200; address < 0x1400; address += 512) { // 512B
dumpMMC5RAM(base, address);
}
write_prg_byte(0x8000, 6); // PRG RAM DISABLE
} else { // MMC3 8K
write_prg_byte(0xA001, 0xC0); // PRG RAM CHIP ENABLE - Chip Enable, Write Protect
dumpBankPRG(0x0, 0x2000, base); // 8K
}
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 (size_t i = 0; i < (banks / 2); i++) { // Chip 1
write_prg_byte(0x5113, i);
for (size_t address = 0; address < 0x2000; address += 512) { // 8K
dumpMMC5RAM(base, address);
}
}
for (size_t j = 4; j < (banks / 2) + 4; j++) { // Chip 2
write_prg_byte(0x5113, j);
for (size_t address = 0; address < 0x2000; address += 512) { // 8K
dumpMMC5RAM(base, address);
}
}
} else { // 8K/32K Single SRAM Chip
for (size_t i = 0; i < banks; i++) { // banks = 1 or 4
write_prg_byte(0x5113, i);
for (size_t 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]
size_t eepsize;
if (mapper == 159)
eepsize = 128;
else
eepsize = 256;
for (size_t address = 0; address < eepsize; address++) {
EepromREAD(address);
}
myFile.write(sdBuffer, eepsize);
// display_Clear(); // TEST PURPOSES - DISPLAY EEPROM DATA
break;
}
case 19:
if (ramsize == 2) { // PRG RAM 128B
for (size_t x = 0; x < 128; x++) {
write_ram_byte(0xF800, x); // PRG RAM ENABLE
sdBuffer[x] = read_prg_byte(0x4800); // DATA PORT
}
myFile.write(sdBuffer, 128);
} else { // SRAM 8K
for (size_t i = 0; i < 64; i++) { // Init Register
write_ram_byte(0xE000, 0);
}
dumpBankPRG(0x0, 0x2000, base); // 8K
}
break;
case 80: // 1K
write_prg_byte(0x7EF8, 0xA3); // PRG RAM ENABLE 0
write_prg_byte(0x7EF9, 0xA3); // PRG RAM ENABLE 1
for (size_t x = 0; x < 128; x++) { // PRG RAM 1K ($7F00-$7FFF) MIRRORED ONCE
sdBuffer[x] = read_prg_byte(0x7F00 + x);
}
myFile.write(sdBuffer, 128);
write_prg_byte(0x7EF8, 0xFF); // PRG RAM DISABLE 0
write_prg_byte(0x7EF9, 0xFF); // PRG RAM DISABLE 1
break;
case 82: // 5K
write_prg_byte(0x7EF7, 0xCA); // PRG RAM ENABLE 0 ($6000-$67FF)
write_prg_byte(0x7EF8, 0x69); // PRG RAM ENABLE 1 ($6800-$6FFF)
write_prg_byte(0x7EF9, 0x84); // PRG RAM ENABLE 2 ($7000-$73FF)
for (size_t address = 0x0; address < 0x1400; address += 512) { // PRG RAM 5K ($6000-$73FF)
dumpMMC5RAM(base, address);
}
write_prg_byte(0x7EF7, 0xFF); // PRG RAM DISABLE 0 ($6000-$67FF)
write_prg_byte(0x7EF8, 0xFF); // PRG RAM DISABLE 1 ($6800-$6FFF)
write_prg_byte(0x7EF9, 0xFF); // PRG RAM DISABLE 2 ($7000-$73FF)
break;
default:
if (mapper == 118) // 8K
write_prg_byte(0xA001, 0xC0); // PRG RAM CHIP ENABLE - Chip Enable, Write Protect
else if (mapper == 19) {
for (size_t 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
dumpBankPRG(0x0, 0x2000, base); // 8K
if (mapper == 85) // 8K
write_reg_byte(0xE000, 0); // PRG RAM DISABLE [WRITE RAM SAFE]
break;
}
myFile.flush();
myFile.close();
println_Msg(F("RAM FILE DUMPED!"));
println_Msg(FS(FSTRING_EMPTY));
display_Update();
if ((mapper == 16) || (mapper == 159))
printCRC(fileName, NULL, 0);
else
printCRC(fileName, NULL, 0);
}
}
set_address(0);
PHI2_HI;
ROMSEL_HI;
rgbLed(black_color);
}
void writeBankPRG(const size_t from, const size_t to, const size_t base) {
for (size_t address = from; address < to; address += 512) {
myFile.read(sdBuffer, 512);
for (size_t x = 0; x < 512; x++) {
write_prg_byte(base + address + x, sdBuffer[x]);
}
}
}
void writeBankWRAM(const size_t from, const size_t to, const size_t base) {
for (size_t address = from; address < to; address += 512) {
myFile.read(sdBuffer, 512);
for (size_t x = 0; x < 512; x++) {
write_wram_byte(base + address + x, sdBuffer[x]);
}
}
}
void writeRAM() {
display_Clear();
if (ramsize == 0) {
print_Error(F("RAM SIZE 0K"));
} else {
fileBrowser(F("Select RAM File"));
word base = 0x6000;
uint16_t banks;
sd.chdir();
sprintf(filePath, "%s/%s", filePath, fileName);
display_Clear();
println_Msg(F("Writing File: "));
println_Msg(filePath);
println_Msg(fileName);
display_Update();
//open file on sd card
if (myFile.open(filePath, O_READ)) {
switch (mapper) {
case 0: // 2K/4K
writeBankPRG(0x0, (0x800 * ramsize), base); // 2K/4K
break; // SWITCH MUST BE IN OFF POSITION
case 1:
case 155:
banks = int_pow(2, ramsize) / 2; // banks = 1,2,4
for (size_t 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);
writeBankPRG(0x0, 0x2000, base); // 8K
}
break;
case 4: // 1K/8K (MMC6/MMC3)
if (mmc6) { // MMC6 1K
write_prg_byte(0x8000, 0x20); // PRG RAM ENABLE
write_prg_byte(0xA001, 0x30); // PRG RAM PROTECT - Enable reading/writing to RAM at $7000-$71FF
writeBankWRAM(0x1000, 0x1200, base); // 512B
write_prg_byte(0x8000, 0x20); // PRG RAM ENABLE
write_prg_byte(0xA001, 0xC0); // PRG RAM PROTECT - Enable reading/writing to RAM at $7200-$73FF
writeBankWRAM(0x1200, 0x1400, base); // 512B
write_prg_byte(0x8000, 0x6); // PRG RAM DISABLE
} else { // MMC3 8K
write_prg_byte(0xA001, 0x80); // PRG RAM CHIP ENABLE - Chip Enable, Allow Writes
writeBankPRG(0x0, 0x2000, base); // 8K
write_prg_byte(0xA001, 0xC0); // PRG RAM CHIP ENABLE - Chip Enable, Write Protect
}
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 (size_t i = 0; i < (banks / 2); i++) { // Chip 1
write_prg_byte(0x5113, i);
for (size_t address = 0; address < 0x2000; address += 512) { // 8K
writeMMC5RAM(base, address);
}
}
for (size_t j = 4; j < (banks / 2) + 4; j++) { // Chip 2
write_prg_byte(0x5113, j);
for (size_t 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 (size_t i = 0; i < banks; i++) { // banks = 1 or 4
write_prg_byte(0x5113, i);
for (size_t 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]
size_t eepsize;
if (mapper == 159)
eepsize = 128;
else
eepsize = 256;
myFile.read(sdBuffer, eepsize);
for (size_t address = 0; address < eepsize; address++) {
EepromWRITE(address);
if ((address % 128) == 0)
display_Clear();
print_Msg(F("."));
display_Update();
}
break;
}
case 19:
if (ramsize == 2) { // PRG RAM 128B
myFile.read(sdBuffer, 128);
for (size_t x = 0; x < 128; x++) {
write_ram_byte(0xF800, x); // PRG RAM ENABLE
write_prg_byte(0x4800, sdBuffer[x]); // DATA PORT
}
} else { // SRAM 8K
for (size_t 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
writeBankPRG(0x0, 0x2000, base); // 8K
write_ram_byte(0xF800, 0x0F); // PRG RAM WRITE PROTECT
}
break;
case 80: // 1K
write_prg_byte(0x7EF8, 0xA3); // PRG RAM ENABLE 0
write_prg_byte(0x7EF9, 0xA3); // PRG RAM ENABLE 1
for (size_t address = 0x1F00; address < 0x2000; address += 512) { // PRG RAM 1K ($7F00-$7FFF)
myFile.read(sdBuffer, 128);
for (size_t x = 0; x < 128; x++) {
write_prg_byte(base + address + x, sdBuffer[x]);
}
}
write_prg_byte(0x7EF8, 0xFF); // PRG RAM DISABLE 0
write_prg_byte(0x7EF9, 0xFF); // PRG RAM DISABLE 1
break;
case 82: // 5K
write_prg_byte(0x7EF7, 0xCA); // PRG RAM ENABLE 0 ($6000-$67FF)
write_prg_byte(0x7EF8, 0x69); // PRG RAM ENABLE 1 ($6800-$6FFF)
write_prg_byte(0x7EF9, 0x84); // PRG RAM ENABLE 2 ($7000-$73FF)
for (size_t address = 0x0; address < 0x1400; address += 1024) { // PRG RAM 5K ($6000-$73FF)
myFile.read(sdBuffer, 512);
uint8_t firstbyte = sdBuffer[0];
for (size_t x = 0; x < 512; x++)
write_prg_byte(base + address + x, sdBuffer[x]);
myFile.read(sdBuffer, 512);
for (size_t x = 0; x < 512; x++)
write_prg_byte(base + address + x + 512, sdBuffer[x]);
write_prg_byte(base + address, firstbyte); // REWRITE 1ST BYTE
}
write_prg_byte(0x7EF7, 0xFF); // PRG RAM DISABLE 0 ($6000-$67FF)
write_prg_byte(0x7EF8, 0xFF); // PRG RAM DISABLE 1 ($6800-$6FFF)
write_prg_byte(0x7EF9, 0xFF); // PRG RAM DISABLE 2 ($7000-$73FF)
break;
default:
if (mapper == 118) // 8K
write_prg_byte(0xA001, 0x80); // PRG RAM CHIP ENABLE - Chip Enable, Allow Writes
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
writeBankPRG(0x0, 0x2000, base);
if (mapper == 118) // 8K
write_prg_byte(0xA001, 0xC0); // PRG RAM CHIP ENABLE - Chip Enable, 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;
}
myFile.close();
rgbLed(green_color);
println_Msg(FS(FSTRING_EMPTY));
println_Msg(F("RAM FILE WRITTEN!"));
display_Update();
} else {
print_FatalError(sd_error_STR);
}
}
rgbLed(black_color);
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(9, 0); // PRG SIZE
EEPROM_writeAnything(10, 0); // CHR SIZE
EEPROM_writeAnything(11, 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
uint8_t 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 (uint8_t 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(uint8_t address) { // 24C01 [Little Endian]
for (uint8_t 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(uint8_t address) { // 24C02
for (uint8_t 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 (uint8_t 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 (uint8_t 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(uint8_t 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(uint8_t 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
}
#if defined(ENABLE_FLASH)
/******************************************
NESmaker Flash Cart [SST 39SF40]
*****************************************/
void NESmaker_Cmd(byte cmd) {
write_prg_byte(0xC000, 0x01);
write_prg_byte(0x9555, 0xAA);
write_prg_byte(0xC000, 0x00);
write_prg_byte(0xAAAA, 0x55);
write_prg_byte(0xC000, 0x01);
write_prg_byte(0x9555, cmd);
}
// SST 39SF040 Software ID
void NESmaker_ID() { // Read Flash ID
NESmaker_Cmd(0xFF); // Reset
NESmaker_Cmd(0x90); // Software ID Entry
flashid = read_prg_byte(0x8000) << 8;
flashid |= read_prg_byte(0x8001);
sprintf(flashid_str, "%04X", flashid);
NESmaker_Cmd(0xF0); // Software ID Exit
if (flashid == 0xBFB7) // SST 39SF040
flashfound = 1;
}
void NESmaker_SectorErase(uint8_t bank, word address) {
NESmaker_Cmd(0x80);
write_prg_byte(0xC000, 0x01);
write_prg_byte(0x9555, 0xAA);
write_prg_byte(0xC000, 0x00);
write_prg_byte(0xAAAA, 0x55);
write_prg_byte(0xC000, bank); // $00-$1F
write_prg_byte(address, 0x30); // Sector Erase ($8000/$9000/$A000/$B000)
}
void NESmaker_ByteProgram(uint8_t bank, word address, uint8_t data) {
NESmaker_Cmd(0xA0);
write_prg_byte(0xC000, bank); // $00-$1F
write_prg_byte(address, data); // $8000-$BFFF
}
// SST 39SF040 Chip Erase [NOT IMPLEMENTED]
void NESmaker_ChipErase() { // Typical 70ms
NESmaker_Cmd(0x80);
NESmaker_Cmd(0x10); // Chip Erase
}
void writeFLASH() {
display_Clear();
if (!flashfound) {
rgbLed(red_color);
println_Msg(F("FLASH NOT DETECTED"));
display_Update();
} else {
print_Msg(F("Flash ID: "));
println_Msg(flashid_str);
println_Msg(FS(FSTRING_EMPTY));
println_Msg(F("NESmaker Flash Found"));
println_Msg(FS(FSTRING_EMPTY));
display_Update();
delay(100);
fileBrowser(F("Select FLASH File"));
word base = 0x8000;
sd.chdir();
sprintf(filePath, "%s/%s", filePath, fileName);
rgbLed(red_color);
display_Clear();
println_Msg(F("Writing File: "));
println_Msg(filePath);
println_Msg(fileName);
display_Update();
uint8_t bytecheck;
uint16_t banks;
//open file on sd card
if (myFile.open(filePath, O_READ)) {
banks = int_pow(2, prgsize); // 256K/512K
for (size_t i = 0; i < banks; i++) { // 16K Banks
for (size_t sector = 0; sector < 0x4000; sector += 0x1000) { // 4K Sectors ($8000/$9000/$A000/$B000)
// Sector Erase
NESmaker_SectorErase(i, base + sector);
delay(18); // Typical 18ms
for (uint8_t j = 0; j < 2; j++) { // Confirm erase twice
do {
bytecheck = read_prg_byte(base + sector);
delay(18);
} while (bytecheck != 0xFF);
}
// Program Byte
for (size_t addr = 0x0; addr < 0x1000; addr += 512) {
myFile.read(sdBuffer, 512);
for (size_t x = 0; x < 512; x++) {
word location = base + sector + addr + x;
NESmaker_ByteProgram(i, location, sdBuffer[x]);
delayMicroseconds(14); // Typical 14us
for (uint8_t k = 0; k < 2; k++) { // Confirm write twice
do {
bytecheck = read_prg_byte(location);
delayMicroseconds(14);
} while (bytecheck != sdBuffer[x]);
}
}
}
}
#if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
display.print(F("*"));
display.updateDisplay();
#else
Serial.print(F("*"));
if ((i != 0) && ((i + 1) % 16 == 0))
Serial.println(FS(FSTRING_EMPTY));
#endif
}
myFile.close();
rgbLed(green_color);
println_Msg(FS(FSTRING_EMPTY));
println_Msg(F("FLASH FILE WRITTEN!"));
display_Update();
} else {
rgbLed(red_color);
println_Msg(F("SD ERROR"));
display_Update();
}
}
display_Clear();
rgbLed(black_color);
sd.chdir(); // root
filePath[0] = '\0'; // Reset filePath
}
/******************************************
A29040B Flash Cart [A29040B]
*****************************************/
// A29040B Software ID
void A29040B_ID() { // Read Flash ID
write_prg_byte(0x9555, 0xAA);
write_prg_byte(0xAAAA, 0x55);
write_prg_byte(0x9555, 0x90);
flashid = read_prg_byte(0x8000) << 8;
flashid |= read_prg_byte(0x8001);
sprintf(flashid_str, "%04X", flashid);
if (flashid == 0x3786) // A29040B
flashfound = 1;
A29040B_PRG_ResetFlash();
}
void A29040B_PRG_ResetFlash() { // Reset Flash
write_prg_byte(0x9555, 0xAA);
write_prg_byte(0xAAAA, 0x55);
write_prg_byte(0x9555, 0xF0); // Reset
delayMicroseconds(14); // Typical 14us
}
void A29040B_PRG_Write(uint16_t address, uint8_t data) {
write_prg_byte(0x9555, 0xAA);
write_prg_byte(0xAAAA, 0x55);
write_prg_byte(0x9555, 0xA0);
write_prg_byte(address, data); // $8000-$BFFF
delayMicroseconds(20); // Typical 14us
}
void A29040B_PRG_SectorErase(uint16_t sec) {
if (flashfound) {
write_prg_byte(0x9555, 0xAA);
write_prg_byte(0xAAAA, 0x55);
write_prg_byte(0x9555, 0x80); //->setup
write_prg_byte(0x9555, 0xAA);
write_prg_byte(0xAAAA, 0x55);
write_prg_byte(sec, 0x30); //->erase
delay(1000); // WAIT MORE
} else {
println_Msg(F("FLASH NOT DETECTED OR SECTOR PROTECTED"));
}
}
void A29040B_PRG_ChipErase() {
if (flashfound) {
write_prg_byte(0x9555, 0xAA);
write_prg_byte(0xAAAA, 0x55);
write_prg_byte(0x9555, 0x80); //->setup
write_prg_byte(0x9555, 0xAA);
write_prg_byte(0xAAAA, 0x55);
write_prg_byte(0x9555, 0x10); //->erase
delay(8000); // WAIT MORE
} else {
println_Msg(F("FLASH NOT DETECTED OR SECTOR PROTECTED"));
}
}
// CHR ================================================
void A29040B_CHR_ResetFlash() { // Reset Flash
write_chr_byte(0x0555, 0xAA); // Original address for CHR
write_chr_byte(0x02AA, 0x55); // Original address for CHR
write_chr_byte(0x0555, 0xF0); // Reset command with original address
delayMicroseconds(14); // Typical 14us
}
void A29040B_CHR_Write(uint16_t address, uint8_t data) {
write_chr_byte(0x0555, 0xAA); // Original address for CHR
write_chr_byte(0x02AA, 0x55); // Original address for CHR
write_chr_byte(0x0555, 0xA0); // Program command with original address
write_chr_byte(address, data); // CHR address range (0x0000 - 0x1FFF)
delayMicroseconds(20); // Typical 14us
}
void A29040B_CHR_SectorErase(uint16_t sec) {
if (flashfound) {
write_chr_byte(0x0555, 0xAA); // Original address for CHR
write_chr_byte(0x02AA, 0x55); // Original address for CHR
write_chr_byte(0x0555, 0x80); // Erase Setup with original address
write_chr_byte(0x0555, 0xAA); // Original address for CHR
write_chr_byte(0x02AA, 0x55); // Original address for CHR
write_chr_byte(sec, 0x30); // Sector Erase Command with sector address
delay(1000); // WAIT MORE
} else {
println_Msg(F("FLASH NOT DETECTED OR SECTOR PROTECTED"));
}
}
void A29040B_CHR_ChipErase() {
if (flashfound) {
write_chr_byte(0x0555, 0xAA); // Original address for CHR
write_chr_byte(0x02AA, 0x55); // Original address for CHR
write_chr_byte(0x0555, 0x80); // Erase Setup with original address
write_chr_byte(0x0555, 0xAA); // Original address for CHR
write_chr_byte(0x02AA, 0x55); // Original address for CHR
write_chr_byte(0x0555, 0x10); // Chip Erase Command with original address
delay(8000); // WAIT MORE
} else {
println_Msg(F("FLASH NOT DETECTED OR SECTOR PROTECTED"));
}
}
#define A29040B_TITLE "FLASH A29040B MAPPER 0"
void A29040B_writeFLASH() {
display_Clear();
A29040B_ID();
char data_str[10];
uint32_t prgSize = 0;
uint32_t chrSize = 0;
if (!flashfound) {
rgbLed(red_color);
println_Msg(F(A29040B_TITLE));
println_Msg(FS(FSTRING_EMPTY));
print_Msg(F("Flash ID: "));
println_Msg(flashid_str);
println_Msg(FS(FSTRING_EMPTY));
println_Msg(F("FLASH NOT FOUND"));
display_Update();
wait();
} else {
println_Msg(F(A29040B_TITLE));
println_Msg(FS(FSTRING_EMPTY));
print_Msg(F("Flash ID: "));
println_Msg(flashid_str);
println_Msg(FS(FSTRING_EMPTY));
println_Msg(F("Flash Found"));
println_Msg(FS(FSTRING_EMPTY));
display_Update();
delay(3000);
fileBrowser(F("Select FLASH File"));
sd.chdir();
sprintf(filePath, "%s/%s", filePath, fileName);
if (myFile.open(filePath, O_READ)) {
// Step 1: Read the header and extract PRG and CHR sizes
uint8_t header[16];
myFile.read(header, 16); // Read the 16-byte header
uint32_t prgAddress = 0x8000;
prgSize = (uint32_t)header[4] * 16384; // PRG size in bytes (header[4] gives size in 16 KB units)
chrSize = (uint32_t)header[5] * 8192; // CHR size in bytes (header[5] gives size in 8 KB units)
// Output the sizes for verification
display_Clear();
println_Msg(F(A29040B_TITLE));
println_Msg(FS(FSTRING_EMPTY));
println_Msg(F("PRG Size:"));
sprintf(data_str, "%lu", prgSize);
println_Msg(data_str);
println_Msg(F("CHR Size:"));
sprintf(data_str, "%lu", chrSize);
println_Msg(data_str);
display_Update();
delay(3000);
// Step 2: Erase the entire PRG space
rgbLed(red_color);
display_Clear();
println_Msg(F(A29040B_TITLE));
println_Msg(FS(FSTRING_EMPTY));
A29040B_PRG_ResetFlash();
println_Msg(F("ERASING PRG..."));
display_Update();
A29040B_PRG_ChipErase();
uint8_t readByte = read_prg_byte(prgAddress);
if (readByte != 0xFF) {
println_Msg(F("Erase Error!"));
} else {
println_Msg(F("Erase OK!"));
}
display_Update();
// Verify that the first byte has been erased
uint8_t erase_check = read_prg_byte(0x8000);
if (erase_check != 0xFF) {
println_Msg(F("SECTOR NOT ERASED"));
sprintf(data_str, "%02X", erase_check);
println_Msg(data_str);
return;
}
delay(18); // Adjust delay as needed
rgbLed(red_color);
println_Msg(FS(FSTRING_EMPTY));
println_Msg(F("Writing PRG Data..."));
display_Update();
A29040B_PRG_ResetFlash();
// Step 3: Write PRG data
uint32_t bytesProcessed = 0;
uint8_t buffer[512];
myFile.seek(16); // Skip header to start of PRG data
while (bytesProcessed < prgSize) {
int bytesRead = myFile.read(buffer, sizeof(buffer));
if (bytesRead <= 0) break;
for (int i = 0; i < bytesRead; i++) {
A29040B_PRG_Write(prgAddress++, buffer[i]);
delayMicroseconds(14); // Typical 14us
uint8_t readByte = read_prg_byte(prgAddress - 1);
delayMicroseconds(14); // Typical 14us
if (readByte != buffer[i]) {
println_Msg(F("Write Error!"));
sprintf(data_str, "%02X", readByte);
println_Msg(data_str);
myFile.close();
break;
}
}
bytesProcessed += bytesRead;
}
// Step 4: Erase and Write CHR data
A29040B_CHR_ResetFlash();
display_Clear();
println_Msg(F(A29040B_TITLE));
println_Msg(FS(FSTRING_EMPTY));
println_Msg(F("ERASING CHR..."));
display_Update();
A29040B_CHR_ChipErase();
delay(20);
display_Clear();
uint32_t chrAddress = 0x0000;
bytesProcessed = 0;
myFile.seek(16 + prgSize); // Seek to the start of CHR data
readByte = read_chr_byte(chrAddress);
if (readByte != 0xFF) {
println_Msg(F("Erase Error!"));
} else {
println_Msg(F("Erase OK!"));
}
display_Update();
println_Msg(F("Writing CHR Data..."));
display_Update();
while (bytesProcessed < chrSize) {
int bytesRead = myFile.read(buffer, sizeof(buffer));
if (bytesRead <= 0) break;
for (int i = 0; i < bytesRead; i++) {
A29040B_CHR_Write(chrAddress++, buffer[i]);
delayMicroseconds(14); // Typical 14us
uint8_t readByte = read_chr_byte(chrAddress - 1);
delayMicroseconds(14); // Typical 14us
if (readByte != buffer[i]) {
println_Msg(F("Write Error!"));
sprintf(data_str, "%02X", readByte);
println_Msg(data_str);
myFile.close();
break;
}
}
bytesProcessed += bytesRead;
}
delay(3000);
myFile.close();
rgbLed(green_color);
display_Clear();
println_Msg(F(A29040B_TITLE));
println_Msg(FS(FSTRING_EMPTY));
println_Msg(F("FLASH FILE WRITTEN!"));
display_Update();
} else {
rgbLed(red_color);
println_Msg(F("SD ERROR"));
display_Update();
}
display_Update();
}
}
#endif
// avoid warnings
#undef MODE_READ
#undef MODE_WRITE
#endif
//******************************************
// End of File
//******************************************