//****************************************** // 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 - irrelevant (FDS emulation) { 21, 4, 4, 5, 7, 0, 1 }, // Konami VRC4a / VRC4c [sram r/w] { 22, 3, 3, 5, 7, 0, 0 }, // Konami 351618 (VRC2a) { 23, 3, 3, 5, 7, 0, 0 }, // Konami VRC2b / VRC4e / VRC4f { 24, 0, 4, 0, 6, 0, 0 }, // Konami 351951 (VRC6a) { 25, 3, 4, 5, 7, 0, 1 }, // Konami VRC2c / VRC4b / VRC4d [sram r/w] { 26, 0, 4, 0, 6, 1, 1 }, // Konami 351949A (VRC6b) [sram r/w] { 27, 0, 7, 0, 8, 0, 0 }, // CC-21 { 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 }, // Subor Study & Game 32-in-1 (duplicate of 241) { 40, 0, 3, 0, 3, 0, 0 }, // NTDEC 2722/2752 { 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 { 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 (11-in-1 Ball Games) [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 213) { 59, 0, 3, 0, 4, 0, 0 }, // BS-01/VT1512A (BMC-T3H53 + BMC-D1038) { 60, 1, 2, 1, 3, 0, 0 }, // Reset-based 4-in-1 NROM-128 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, 0, 5, 0, 6, 0, 0 }, // 43-393 / 43-406 / 860908C { 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 bank order) [prgram r/w] // 83 - Cony [TODO] // 84 - not used (PC-SMB2J) { 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 - irrelevant (GBC to NES ports) // 99 - irrelevant (Nintendo Vs. System) // 100 - irrelevant (Nesticle MMC3) // 101 - irrelevant (Jaleco / Konami CNROM with wrong bit order, use mapper 87 instead) // 102 - Quietust's Drip port (duplicate of 284) // 103 - Whirlwind Manu LH30 (Doki Doki Panic) [TODO] { 104, 7, 7, 0, 0, 0, 0 }, // Pegasus 5-in-1 [1280K] { 105, 4, 4, 0, 0, 0, 0 }, // NES-EVENT (Nintendo World Championships) // 106 - 890418 (Super Mario Bros 3 bootleg) [TODO] { 107, 0, 7, 0, 9, 0, 0 }, // Magic Dragon // 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, 0, 7, 0, 6, 0, 0 }, // NTDEC MMC3 { 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, 0, 5, 0, 7, 0, 0 }, // 哥德 [Gēdé] SOMARI-P { 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 }, // TEC9719 with swapped CHR // 127 - Double Dragon pirate [TODO] // 128 - 1994 Super HiK 4-in-1 [TODO] // 129 - not used (duplicate of 58) // 130 - not used (duplicate of 331) // 131 - not used (duplicate of 205) // { 132, 0, 2, 0, 3, 0, 0 }, // TXC Corporation 01-22003-400 / 01-22111-100 / 01-22270-000 [TODO] // 133 - 聖謙 [Sachen] 3009 / 72008 聖謙 [Dao Shuai] [TODO] { 134, 0, 6, 0, 8, 0, 0 }, // WX-KB4K / T4A54A / BS-5652 // 135 - 聖謙 [Sachen] TC-021A (duplicate of 141?) [TODO] // 136 - 聖謙 [Sachen] 3011 / SA-002 [TODO] // 137 - 聖謙 [Sachen] SA8259D [TODO] // 138 - 聖謙 [Sachen] SA8259B [TODO] // 139 - 聖謙 [Sachen] SA8259C [TODO] { 140, 3, 3, 3, 5, 0, 0 }, // Jaleco GNROM // 141 - 聖謙 [Sachen] 2M-RAM-COB [TODO] { 142, 1, 3, 0, 0, 0, 0 }, // Kaiser KS-7032 // 143 - 聖謙 [Sachen] TC-A001-72P / SA-014 [TODO] { 144, 2, 2, 4, 4, 0, 0 }, // AGCI-50282 (Death Race) // 145 - 聖謙 [Sachen] SA-72007 [TODO] { 146, 1, 2, 2, 3, 0, 0 }, // 聖謙 [Sachen] 3015 (duplicate of 79) // 147 - 聖謙 [Sachen] 3018 [TODO] { 148, 1, 2, 0, 4, 0, 0 }, // 聖謙 [Sachen] SA-008-A // 149 - 聖謙 [Sachen] SA-0036 [TODO] // 150 - 聖謙 [Sachen] SA-015 / SA-630 [TODO] // 151 - not used (duplicate of 75) { 152, 2, 3, 5, 5, 0, 0 }, // Bandai UOROM 1SM { 153, 5, 5, 0, 0, 1, 1 }, // Bandai FCG with 8 KiB PRG-RAM [sram r/w] { 154, 3, 3, 5, 5, 0, 0 }, // Namco 3453 (Devil Man) { 155, 3, 3, 3, 5, 0, 1 }, // Nintendo SxROM (MMC1A) [sram r/w] // 156 - DIS23C01 다우 [DAOU] ROM Controller [TODO] { 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] // 160 - not used (duplicate of 90) // 161 - not used (duplicate of 1) { 162, 6, 7, 0, 0, 0, 0 }, // 外星 [Wàixīng] FS304 { 163, 6, 7, 0, 0, 0, 0 }, // 南晶 [Nánjīng] FC-001 // 164 - 燕城 [Yànchéng] cy2000-3 [TODO] { 165, 5, 5, 5, 5, 0, 0 }, // 圣火徽章 [Fire Emblem] Chinese version by 外星 [Wàixīng] // 166 - not used (wrong bank order, use 167 instead) // 167 - Subor educational cartridges [TODO] { 168, 2, 2, 0, 0, 0, 0 }, // Racermate Challenge 2 // 169 - not used (duplicate of 15) // 170 - 藤屋 [Fujiya] NROM [TODO] // 171 - 步步高 [Bùbùgāo / BBK] [TODO] // 172 - Super Mega SMCYII-900 [TODO] // 173 - Idea-Tek ET.xx [TODO] { 174, 3, 3, 4, 4, 0, 0 }, // NTDEC 5-in-1 { 175, 4, 4, 5, 5, 0, 0 }, // Kaiser KS-122 (15-in-1) { 176, 4, 4, 5, 5, 0, 0 }, // YH-xxx / SFC-12B / many others... { 177, 1, 7, 0, 0, 0, 0 }, // 恒格电子 [Hénggé Diànzǐ] { 178, 5, 5, 0, 0, 0, 0 }, // 外星 [Wàixīng] FS305 / 南晶 [Nánjīng] NJ0430 / PB030703-1x1 // 179 - duplicate of 176 { 180, 3, 3, 0, 0, 0, 0 }, // Inverse UNROM (Crazy Climber) // 181 - duplicate of 185 { 182, 3, 4, 5, 6, 0, 0 }, // YH-001 (duplicate of 114) // 183 - 09035 [TODO] { 184, 1, 1, 2, 3, 0, 0 }, // Sunsoft-1 { 185, 0, 1, 1, 1, 0, 0 }, // Nintendo CNROM + Security // 186 - irrelevant (Fukutake Shoten's Family Study Box BIOS) // 187 - 卡聖 [Kǎshèng] A98402 [TODO] // 188 - Bandai Karaoke Studio [TODO] { 189, 3, 5, 5, 7, 0, 0 }, // TXC 01-22017-000 / 01-22018-400 // 190 - Zemina [TODO] // 191 - Pirate TQROM variant [TODO] // 192 - Waixing FS308 [TODO] // 193 - NTDEC 2394 [TODO] // 194 - Waixing FS30x [TODO] { 195, 0, 5, 0, 6, 0, 0 }, // 外星 [Wàixīng] FS303 { 196, 0, 5, 0, 6, 0, 0 }, // MRCM UT1374 // 197 - TLROM-512 [TODO] // 198 - TNROM-640 [TODO] // 199 - 外星 [Wàixīng] FS309 [TODO] { 200, 1, 4, 1, 4, 0, 0 }, // 36-in-1 (HN-02 / MG109, address-latch-based NROM-128 multicarts) { 201, 1, 8, 1, 9, 0, 0 }, // 21-in-1 (address-latch-based NROM-256 multicarts) { 202, 0, 3, 1, 4, 0, 0 }, // SP60 famiclone's 150-in-1 { 203, 1, 4, 1, 4, 0, 0 }, // 35-in-1 (NROM-128 multicarts) // 204 - address-latch-based NROM-128 multicarts [TODO] // 205 - JC-016-2 [TODO] { 206, 1, 3, 2, 4, 0, 0 }, // Namco N118 (DxROM) { 207, 4, 4, 5, 5, 0, 0 }, // Taito Ashura (不動明王伝 / Fudou Myouou Den, duplicate of 80) // 208 - 哥德 [Gēdé] SL-37017 (快打傳説 / Street Fighter IV) [TODO] { 209, 0, 7, 1, 8, 0, 0 }, // 晶太 [Jīngtài] YY850629C { 210, 3, 5, 5, 6, 0, 0 }, // Namco N175 / N340 { 211, 0, 7, 1, 8, 0, 0 }, // 晶太 [Jīngtài] EL860339C { 212, 0, 3, 0, 4, 0, 0 }, // CS669 (BMC Super HiK 300-in-1) { 213, 1, 6, 1, 6, 0, 0 }, // GK-192 (duplicate of 58) { 214, 0, 3, 0, 4, 0, 0 }, // Super Gun 20-in-1 // 215 - Realtec 823x(A) [TODO] // 216 - Bonza [TODO] // 217 - GI 9549 / ET-450 [TODO] // 218 - Magic Floor [TODO] // 219 - 卡聖 [Kǎshèng] A9746 [TODO] // 220 - not used (reserved for debugging) { 221, 0, 7, 0, 0, 0, 0 }, // NTDEC N625092 (400-in-1) // 222 - 810343-C [TODO] // 223 - not used (duplicate of 199) { 224, 0, 11, 0, 8, 0, 0 }, // 晶科泰 [Jncota] KT-008 { 225, 4, 7, 5, 8, 0, 0 }, // ET-4310 / K-1010 { 226, 6, 7, 0, 0, 0, 0 }, // 0380 / 910307 { 227, 1, 5, 0, 0, 0, 0 }, // 810449-C-A1 / 外星 [Wàixīng] FW01 / N120-72 { 228, 4, 7, 5, 7, 0, 0 }, // Action 52 / Cheetahmen II { 229, 5, 5, 6, 6, 0, 0 }, // SC 0892 / BMC 31-IN-1 // 230 - CTC-43A [TODO] // 231 - 20-in-1 [TODO] { 232, 4, 4, 0, 0, 0, 0 }, // BIC BF9096 (Camerica/Codemasters "Quattro" cartridges) { 233, 6, 6, 0, 0, 0, 0 }, // Reset-based Tsang Hai 4+4 Mib // 234 - Maxi 15 [undumpable from cartridge edge connectors] { 235, 6, 8, 0, 0, 0, 0 }, // Golden Game modular multicart { 236, 0, 6, 0, 5, 0, 0 }, // Realtec 8031 / 8099 / 8106 / 8155 { 237, 6, 6, 0, 0, 0, 0 }, // Teletubbies 420-in-1 / 42-in-1 Y2K // 238 - Sakano MMC3 (Contra Fighter) [TODO] // 239 - not used { 240, 1, 5, 1, 5, 0, 3 }, // 聖火列傳 [Sheng Huo Lie Zhuan] / 荊軻新傳 [Jing Ke Xin Zhuan] { 241, 3, 5, 0, 0, 0, 0 }, // BNROM with WRAM { 242, 5, 5, 0, 0, 0, 0 }, // ET-113 / 43272 // 243 - 聖謙 [Sachen] SA-020A (duplicate of 150) // 244 - C&E's Decathlon [TODO] { 245, 6, 6, 0, 0, 0, 0 }, // 外星 [Wàixīng] FS003 (勇者斗恶龙 VII) { 246, 5, 5, 7, 7, 0, 0 }, // G0151-1 (封神榜/Fēngshénbǎng) // 247 - not used { 248, 0, 5, 0, 7, 0, 0 }, // 卡聖 [Kǎshèng] SFC-02B/-03/-004 (duplicate of 115) // 249 - 外星 [Wàixīng] T9552 (duplicate of 4) // 250 - Nitra L4015 [TODO] // 251 - duplicate of 45 { 252, 4, 4, 5, 5, 0, 0 }, // 外星 [Wàixīng] (三国志/Sangokushi) { 253, 4, 4, 7, 7, 0, 0 }, // 外星 [Wàixīng] F009S (Dragon Ball Z: 強襲! サイヤ人) // 254 - Pikachu Y2K (Felix the Cat hack) { 255, 4, 7, 5, 8, 0, 0 }, // ET-4310 / K-1010 (duplicate of 225) // 256 - irrelevant (OneBus) // 257 - irrelevant (东达 [Dōngdá] Pyramid PEC-586) // 258 - Shanghai Paradise 158B [TODO] // 259 - F-15 [TODO] // 260 - HP10xx / HP20xx [TODO] { 261, 0, 4, 0, 5, 0, 0 }, // 810544-C-A1 / NTDEC 2746 // 262 - 侍魂/Street Heroes [TODO] // 263 - S.M.I. NSM-xxx [TODO] // 264 - Yoko Soft / Cony Soft [TODO] // 265 - T-262 multicarts [TODO] // 266 - City Fighter IV [TODO] // 267 - 晶太 [Jīngtài] EL861121C / JY-119 multicart [TODO] { 268, 0, 11, 0, 8, 0, 0 }, // KP6022 / AA6023 ASIC (Mindkids/Coolboy) [268.0-1] { 286, 0, 3, 0, 5, 0, 0 }, // Benshieng BS-5 multicarts [TODO] { 288, 0, 3, 0, 4, 0, 0 }, // GKCXIN1 (21-in-1) { 289, 5, 7, 0, 0, 0, 0 }, // 60311C / N76A-1 { 290, 0, 5, 0, 4, 0, 0 }, // Asder 20-in-1 // 313 - undumpable (reset-based TKROM multicarts) { 315, 0, 5, 0, 7, 0, 0 }, // 820732C / 830134C { 319, 3, 3, 4, 4, 0, 0 }, // HP-898F / KD-7/9-E { 329, 1, 7, 0, 0, 0, 3 }, // EDU2000 (duplicate of 177) { 331, 0, 5, 0, 7, 0, 0 }, // NewStar multicarts (NS03, 7-in-1, 12-in-1) { 332, 3, 4, 4, 5, 0, 0 }, // WS-1001 { 351, 0, 6, 0, 8, 0, 0 }, // Techline XB multicarts { 366, 0, 6, 0, 8, 0, 0 }, // GN-45 { 396, 0, 6, 0, 0, 0, 0 }, // 晶太 [Jīngtài] YY850437C { 422, 1, 8, 0, 8, 0, 0 }, // TEC9719 { 446, 0, 8, 0, 0, 0, 0 }, // SMD172B_FPGA { 470, 0, 11, 0, 0, 0, 0 }, // INX_007T_V01 { 532, 4, 4, 6, 6, 0, 0 }, // CHINA_ER_SAN2 (duplicate of 19) { 534, 1, 8, 0, 8, 0, 0 }, // ING003C / PJ-008 / AT-207 // 551 - 晶科泰 [Jncota] KT-xxx [TODO] { 552, 0, 5, 0, 6, 0, 0 }, // Taito P3-044 (X1-017, actual bank order) { 995, 0, 11, 0, 8, 0, 0 }, // [placeholder for 268.2-3] { 996, 0, 8, 0, 8, 0, 0 }, // [placeholder for 268.4-5] { 997, 0, 11, 0, 8, 0, 0 }, // [placeholder for 268.6-7] { 998, 0, 6, 0, 6, 0, 0 }, // [placeholder for 268.8-9] { 999, 0, 6, 0, 6, 0, 0 } // [placeholder for 268.10-11] }; 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 if (oldcrc32 == 0xE37A6AA8 || oldcrc32 == 0x90046A48 || oldcrc32 == 0xCBA2352F || oldcrc32 == 0x56DA99FA || oldcrc32MMC3 == 0xE37A6AA8 || oldcrc32MMC3 == 0x90046A48 || oldcrc32MMC3 == 0xCBA2352F || oldcrc32MMC3 == 0x56DA99FA) { println_Msg(F("DUMPING 8KiB PRG")); display_Update(); dumpBankPRG(0x0, 0x2000, base); } else { banks = int_pow(2, prgsize); dumpBankPRG(0x0, 0x4000 * banks, base); } 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 44: case 47: case 49: case 52: case 64: case 74: case 76: case 88: case 95: case 115: case 116: case 118: case 119: case 126: case 134: case 154: // 128K case 158: case 165: // 512K case 176: case 195: case 196: case 206: // 32/64/128K case 224: case 245: // 1024K case 248: case 268: // submapper 0 case 315: case 351: case 366: case 422: case 534: case 995: case 996: case 997: case 998: case 999: 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) || (mapper == 422) || (mapper == 534)) { write_prg_byte(0x6803, 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 } if (mapper == 351) { write_prg_byte(0x5000, 0); write_prg_byte(0x5001, 0); write_prg_byte(0x5002, 0); } for (size_t i = 0; i < banks; i++) { 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 == 44) { write_prg_byte(0xA001, 0x80 | ((i >> 4) & 0x07)); } 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 == 116) { write_prg_byte(0x4100, 0x01); // MMC3 mode } if ((mapper == 126) || (mapper == 422) || (mapper == 534)) { write_prg_byte(0x6800, (i & 0x300) >> 4 | (i & 0x70) >> 4); // submapper 0 // write_prg_byte(0x6800, (i & 0x80) >> 2 | (i & 0x70) >> 4); // submapper 1 } if (mapper == 134) { write_prg_byte(0x6000, (i & 0x40) >> 2); // A19 write_prg_byte(0x6001, (i & 0x30) >> 4); // A18-17 } if (mapper == 176) { write_prg_byte(0x5FF1, (i & 0xE0) >> 1); } if (mapper == 245) { // uses CHR A11 as PRG A19 if (i == 0) { write_prg_byte(0x8000, 0); write_prg_byte(0x8001, 0); } if (i == 64) { write_prg_byte(0x8000, 0); write_prg_byte(0x8001, 0xFF); } } if ((mapper == 224) || (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(0x6000, ((i & 0x70) >> 4) | ((i & 0xC00) >> 6)); write_prg_byte(0x6001, ((i & 0x80) >> 3) | ((i & 0x300) >> 6) | 0x60); } if (mapper == 315) { write_prg_byte(0x6800, (i & 30) >> 3); } if (mapper == 351) { write_prg_byte(0x5001, i << 1); } if (mapper == 366) { write_prg_byte(0x6800 + (i & 0x70), i); } if (mapper == 995) { write_prg_byte(0x5000, ((i & 0x70) >> 4) | ((i & 0xC00) >> 6)); write_prg_byte(0x5001, ((i & 0x80) >> 4) | ((i & 0x100) >> 6) | ((i & 0x200) >> 8) | 0x60); write_prg_byte(0x6000, ((i & 0x70) >> 4) | ((i & 0xC00) >> 6)); write_prg_byte(0x6001, ((i & 0x80) >> 4) | ((i & 0x100) >> 6) | ((i & 0x200) >> 8) | 0x60); } if (mapper == 996) { write_prg_byte(0x5000, ((i & 0x70) >> 4) | ((i & 0x180) >> 3)); write_prg_byte(0x5001, 0x60); write_prg_byte(0x6000, ((i & 0x70) >> 4) | ((i & 0x180) >> 3)); write_prg_byte(0x6001, 0x60); } if (mapper == 997) { if (i >= banks / 2) { write_prg_byte(0x5000, ((i & 0x70) >> 4) | ((i & 0xC00) >> 6) | 0x88); write_prg_byte(0x6000, ((i & 0x70) >> 4) | ((i & 0xC00) >> 6) | 0x88); } else { write_prg_byte(0x5000, ((i & 0x70) >> 4) | ((i & 0xC00) >> 6)); 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, 0x00); write_prg_byte(0x6003, 0x00); } if ((mapper == 998) || (mapper == 999)) { write_prg_byte(0x5000, ((i & 0x70) >> 4)); write_prg_byte(0x5001, 0x60); write_prg_byte(0x6000, ((i & 0x70) >> 4)); write_prg_byte(0x6001, 0x60); } if ((mapper == 224) || (mapper == 268) || (mapper == 995) || (mapper == 996) || (mapper == 997) || (mapper == 998) || (mapper == 999)) { write_prg_byte(0x5002, 0x00); write_prg_byte(0x5003, 0x00); write_prg_byte(0x6002, 0x00); write_prg_byte(0x6003, 0x00); } 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 23: case 25: case 65: case 75: // 128K/256K banks = int_pow(2, prgsize) * 2; if (mapper == 23) { write_prg_byte(0x9002, 0); write_prg_byte(0x9008, 0); } if (mapper == 25) { write_prg_byte(0x9005, 0); // set vrc4 swap setting for TMNT2 } 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++) { write_prg_byte(0x8000, i); dumpBankPRG(0x0, 0x4000, base); } break; case 27: 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 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 40: banks = int_pow(2, prgsize) * 2; for (size_t i = 0; i < banks; i++) { write_prg_byte(0xFFFF, i); dumpBankPRG(0x4000, 0x6000, base); } if (prgsize > 2) { write_prg_byte(0xC018, 0); dumpBankPRG(0x0, 0x8000, base); write_prg_byte(0xC058, 0); 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, 0x800, 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++) { 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 104: // 1280K for (size_t i = 1; i < 80; i++) { write_prg_byte(0x8000, (i & 0x70) >> 4); // outer bank write_prg_byte(0xC000, i & 0x0F); // inner bank dumpBankPRG(0x0, 0x4000, 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 107: banks = int_pow(2, prgsize) / 2; for (size_t i = 0; i < banks; i++) { write_prg_byte(0xC000, i << 1); dumpBankPRG(0x0, 0x8000, base); } 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 112: banks = int_pow(2, prgsize) * 2; for (size_t i = 0; i < banks; i++) { write_prg_byte(0x8000, 0); write_prg_byte(0xA000, i); dumpBankPRG(0x0, 0x2000, 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++) { 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++) { 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 168: banks = int_pow(2, prgsize); for (size_t i = 0; i < banks; i++) { write_prg_byte(0x8000, i << 6); dumpBankPRG(0x0, 0x4000, 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 175: // 256K banks = int_pow(2, prgsize); for (size_t i = 0; i < banks; i++) { write_prg_byte(0xA000, i << 2); write_prg_byte(0x8000, i); 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 189: banks = int_pow(2, prgsize) / 2; for (size_t i = 0; i < banks; i++) { write_prg_byte(0x4132, (i << 4) & 0xF0); 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 221: banks = int_pow(2, prgsize); for (size_t i = 0; i < banks; i++) { write_prg_byte(0x8000 + ((i << 2) & 0xE0) + ((i << 3) & 0x200), 0); write_prg_byte(0xC000 + (i & 7), 0); dumpBankPRG(0x0, 0x4000, base); } break; case 225: case 255: banks = int_pow(2, prgsize); for (size_t i = 0; i < banks; i++) { write_prg_byte(0x9000 + ((i & 0x40) << 8) + ((i & 0x3F) << 6), i); dumpBankPRG(0x0, 0x4000, 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 252: case 253: banks = int_pow(2, prgsize) * 2; for (size_t i = 0; i < banks; i++) { write_prg_byte(0xA010, i); dumpBankPRG(0x2000, 0x4000, base); } break; case 261: banks = int_pow(2, prgsize); for (size_t i = 0; i < banks; i++) { write_prg_byte(0xF000 + ((i & 0x0E) << 6) + ((i & 0x01) << 5), i); dumpBankPRG(0x0, 0x4000, base); } break; case 286: banks = int_pow(2, prgsize) * 2; for (size_t i = 0; i < banks; i++) { write_prg_byte(0xA0F0 + i, i); dumpBankPRG(0x0, 0x2000, base); } break; case 288: banks = int_pow(2, prgsize) / 2; for (size_t i = 0; i < banks; i++) { write_prg_byte(0x8000 + ((i << 3) & 0x18), i); 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 290: banks = int_pow(2, prgsize); for (size_t i = 0; i < banks; i++) { write_prg_byte(0x8000 | ((i << 10) & 0x7800) | ((i << 6) & 0x40), i); dumpBankPRG(0x0, 0x4000, base); } 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 331: banks = int_pow(2, prgsize); for (int i = 0; i < banks; i++) { write_prg_byte(0xE000, i >> 3); write_prg_byte(0xA000, i); write_prg_byte(0xC000, i); dumpBankPRG(0x0, 0x8000, base); } break; case 332: 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 396: banks = int_pow(2, prgsize); for (int i = 0; i < banks; i++) { write_prg_byte(0xA000, (i >> 3) & 0x07); write_prg_byte(0x8000, i & 0x07); dumpBankPRG(0x0, 0x4000, base); } 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 44: case 47: case 49: case 52: case 64: case 74: case 95: // 32K case 115: case 116: case 118: case 119: case 126: case 134: case 158: case 176: case 189: case 195: case 196: case 206: // 16K/32K/64K case 224: case 248: case 268: case 315: case 351: case 366: case 422: case 534: banks = int_pow(2, chrsize) * 4; write_prg_byte(0xA001, 0x80); if ((mapper == 126) || (mapper == 422) || (mapper == 534)) { write_prg_byte(0x6803, 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 } if (mapper == 351) { write_prg_byte(0x5000, 0); write_prg_byte(0x5001, 0); write_prg_byte(0x5002, 0); } 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 == 44) { write_prg_byte(0xA001, 0x80 | ((i >> 7) & 0x07)); } 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 == 116) { write_prg_byte(0x4100, 0x01 | ((i & 0x100) >> 6)); // A18 } if (mapper == 126) { write_prg_byte(0x6800, (i & 0x200) >> 5 | (i & 0x100) >> 3); // select outer bank } if (mapper == 134) { write_prg_byte(0x6000, (i & 0x200) >> 4); // A19 write_prg_byte(0x6001, (i & 0x180) >> 3); // A18-17 } if (mapper == 176) { write_prg_byte(0x5FF2, (i & 0x700) >> 3); // outer 256k bank } if ((mapper == 224) || (mapper == 268) || (mapper == 995) || (mapper == 996) || (mapper == 997)) { 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 == 351) { write_prg_byte(0x5000, (i >> 1) & 0xFC); } if (mapper == 366) { write_prg_byte(0x6800 + ((i & 0x380) >> 3), i); } if ((mapper == 422) || (mapper == 534)) { write_prg_byte(0x6800, (i & 0x380) >> 4); } if ((mapper == 998) || (mapper == 999)) { write_prg_byte(0x5000, (i & 0x80) >> 4); write_prg_byte(0x6000, (i & 0x80) >> 4); } 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: { 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); // Load last bank uint16_t m23reg = 0xB001; if (read_prg_byte(0x9FF6) == 0x30) { m23reg = 0xB004; } for (size_t i = 0; i < banks; i++) { write_prg_byte(0xB000, i & 0xF); // CHR Bank 0: Lower 4 bits write_prg_byte(m23reg, (i >> 4) & 0x1F); // CHR Bank 0: Upper 5 bits dumpBankCHR(0x0, 0x400); } 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++) { write_prg_byte(0xD000, i); // CHR Bank 0 dumpBankCHR(0x0, 0x400); // 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++) { write_prg_byte(0xD000, i); dumpBankCHR(0x0, 0x400); } break; case 27: banks = int_pow(2, chrsize) * 4; for (size_t i = 0; i < banks; i++) { write_prg_byte(0xB000, i & 0x0F); write_prg_byte(0xB001, i >> 4); dumpBankCHR(0x0, 0x400); } break; case 32: // 128K case 65: // 128K/256K banks = int_pow(2, chrsize) * 4; for (size_t i = 0; i < banks; i++) { write_prg_byte(0xB000, i); dumpBankCHR(0x0, 0x400); } break; case 33: // 128K/256K case 48: // 256K banks = int_pow(2, chrsize) * 2; for (size_t i = 0; i < banks; i++) { write_prg_byte(0x8002, i); dumpBankCHR(0x0, 0x800); } break; case 34: // NINA banks = int_pow(2, chrsize); for (size_t i = 0; i < banks; i++) { 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++) { // 2K Banks write_prg_byte(0x8800, i); // CHR Bank 0 dumpBankCHR(0x0, 0x800); } 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 76: banks = int_pow(2, chrsize) * 2; for (size_t i = 0; i < banks; i++) { write_prg_byte(0x8000, 2); write_prg_byte(0x8001, i); dumpBankCHR(0x0, 0x800); } 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, 0x800); } 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++) { write_prg_byte(0x8000 + ((i & 0x100) >> 8), i); // CHR A19 (submapper 0 only) write_prg_byte(0x6000, i); // CHR A18-A11 dumpBankCHR(0x0, 0x800); } 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 107: banks = int_pow(2, chrsize) / 2; for (size_t i = 0; i < banks; i++) { write_prg_byte(0xC000, i); dumpBankCHR(0x0, 0x2000); } break; case 112: banks = int_pow(2, chrsize) * 4; for (size_t i = 0; i < banks; i++) { write_prg_byte(0x8000, 6); write_prg_byte(0xA000, i); dumpBankCHR(0x1000, 0x1400); } 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(0x6001, (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, 0x400); } 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 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 154: // 128K for (size_t i = 0; i < 64; i += 2) { write_prg_byte(0x8000, 0); write_prg_byte(0x8001, i); dumpBankCHR(0x0, 0x800); } for (size_t i = 0; i < 64; i++) { write_prg_byte(0x8000, 2); write_prg_byte(0x8001, i); dumpBankCHR(0x1000, 0x1400); } break; case 165: // 128K banks = int_pow(2, chrsize); for (size_t i = 0; i < banks; i++) { write_prg_byte(0x8000, 0x02); write_prg_byte(0x8001, i); dumpBankCHR(0x1000, 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 175: // 128K banks = int_pow(2, chrsize) / 2; for (size_t i = 0; i < banks; i++) { write_prg_byte(0xA000, i << 2); write_prg_byte(0x8000, i); dumpBankCHR(0x0, 0x2000); } 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++) { write_prg_byte(0x8000, i); // CHR Bank 0 dumpBankCHR(0x0, 0x400); } 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), i); 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 252: case 253: banks = int_pow(2, chrsize) * 4; for (size_t i = 0; i < banks; i++) { write_prg_byte(0xB000, i & 0x0F); write_prg_byte(0xB004, ((i >> 4) & 0x0F) | ((i >> 8) << 4)); dumpBankCHR(0x0, 0x400); } break; case 261: banks = int_pow(2, chrsize) / 2; for (size_t i = 0; i < banks; i++) { write_prg_byte(0xF000 + (i & 0x0F), i); dumpBankCHR(0x0, 0x2000); } break; case 286: banks = int_pow(2, chrsize) * 2; for (int i = 0; i < banks; i++) { write_prg_byte(0x8000 + i, i); dumpBankCHR(0x0, 0x800); } break; case 288: banks = int_pow(2, chrsize) / 2; for (int i = 0; i < banks; i++) { write_prg_byte(0x8000 + (i & 0x07), i); dumpBankCHR(0x0, 0x2000); } break; case 290: banks = int_pow(2, chrsize) / 2; for (int i = 0; i < banks; i++) { write_prg_byte(0x8000 | ((i << 5) & 0x300) | (i & 0x07), i); dumpBankCHR(0x0, 0x2000); } 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 331: banks = int_pow(2, chrsize); for (int i = 0; i < banks; i++) { write_prg_byte(0xE000, i >> 3); write_prg_byte(0xA000, i << 3); dumpBankCHR(0x0, 0x1000); } 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(0x6001, 0x30 | (i & 0x07)); write_prg_pulsem2(0x6000, (i & 0x08) << 3); 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 //******************************************