From d075c2b46aa4b43bcf216ed8e01c63ba83e95ec8 Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Mon, 5 Dec 2022 13:54:50 +0400 Subject: [PATCH] COOLBOY fixes and self-writable feature --- src/boards/coolboy.cpp | 261 +++++++++++++++++++++++++++++++++++------ 1 file changed, 228 insertions(+), 33 deletions(-) diff --git a/src/boards/coolboy.cpp b/src/boards/coolboy.cpp index e220ae80..6b5e7b64 100644 --- a/src/boards/coolboy.cpp +++ b/src/boards/coolboy.cpp @@ -21,7 +21,7 @@ * * COOLBOY cartridges use registers at address $6xxx * MINDKIDS cartridges use a solder pad labelled "5/6K" to select between $5000 and $6000 - * + * * $xxx0 * 7 bit 0 * ---- ---- @@ -76,6 +76,44 @@ #include "mapinc.h" #include "mmc3.h" +const int ROM_CHIP = 0x00; +const int WRAM_CHIP = 0x10; +const int CFI_CHIP = 0x11; +const int FLASH_CHIP = 0x12; + +const int FLASH_SECTOR_SIZE = 128 * 1024; + +extern uint8* WRAM; +static uint8* CFI = NULL; +static uint8* Flash = NULL; + +static uint8 flash_save = 0; +static uint8 flash_state = 0; +static uint16 flash_buffer_a[10]; +static uint8 flash_buffer_v[10]; +static uint8 cfi_mode = 0; + +// Macronix 256-mbit memory CFI data +const uint8 cfi_data[] = +{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x51, 0x52, 0x59, 0x02, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x27, 0x36, 0x00, 0x00, 0x03, + 0x06, 0x09, 0x13, 0x03, 0x05, 0x03, 0x02, 0x19, + 0x02, 0x00, 0x06, 0x00, 0x01, 0xFF, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0x50, 0x52, 0x49, 0x31, 0x33, 0x14, 0x02, 0x01, + 0x00, 0x08, 0x00, 0x00, 0x02, 0x95, 0xA5, 0x05, + 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + static void COOLBOYCW(uint32 A, uint8 V) { uint32 mask = 0xFF ^ (EXPREGS[0] & 0x80); if (EXPREGS[3] & 0x10) { @@ -92,7 +130,8 @@ static void COOLBOYCW(uint32 A, uint8 V) { | ((EXPREGS[2] & 0x0F) << 3) // 6-3 bits | ((A >> 10) & 7) // 2-0 bits ); - } else { + } + else { if (EXPREGS[3] & 0x40) { // Weird mode, again int cbase = (MMC3_cmd & 0x80) << 5; switch (cbase ^ A) { // Don't even try do understand @@ -112,6 +151,13 @@ static void COOLBOYPW(uint32 A, uint8 V) { uint32 mask = ((0x3F | (EXPREGS[1] & 0x40) | ((EXPREGS[1] & 0x20) << 2)) ^ ((EXPREGS[0] & 0x40) >> 2)) ^ ((EXPREGS[1] & 0x80) >> 2); uint32 base = ((EXPREGS[0] & 0x07) >> 0) | ((EXPREGS[1] & 0x10) >> 1) | ((EXPREGS[1] & 0x0C) << 2) | ((EXPREGS[0] & 0x30) << 2); + if (cfi_mode) + { + setprg32r(CFI_CHIP, 0x8000, 0); + } + + int chip = !flash_save ? ROM_CHIP : FLASH_CHIP; + // Very weird mode // Last banks are first in this mode, ignored when MMC3_cmd&0x40 if ((EXPREGS[3] & 0x40) && (V >= 0xFE) && !((MMC3_cmd & 0x40) != 0)) { @@ -123,26 +169,28 @@ static void COOLBOYPW(uint32 A, uint8 V) { } } - // Regular MMC3 mode, internal ROM size can be up to 2048kb! - if (!(EXPREGS[3] & 0x10)) - setprg8(A, (((base << 4) & ~mask)) | (V & mask)); - else { // NROM mode + if (!(EXPREGS[3] & 0x10)) { + // Regular MMC3 mode but can be extended to 2MByte + setprg8r(chip, A, (((base << 4) & ~mask)) | (V & mask)); + } + else { + // NROM mode mask &= 0xF0; uint8 emask; if ((((EXPREGS[1] & 2) != 0))) // 32kb mode emask = (EXPREGS[3] & 0x0C) | ((A & 0x4000) >> 13); else // 16kb mode emask = EXPREGS[3] & 0x0E; - setprg8(A, ((base << 4) & ~mask) // 7-4 bits are from base (see below) - | (V & mask) // ... or from MM3 internal regs, depends on mask - | emask // 3-1 (or 3-2 when (EXPREGS[3]&0x0C is set) from EXPREGS[3] - | ((A & 0x2000) >> 13)); // 0th just as is + setprg8r(chip, A, ((base << 4) & ~mask) // 7-4 bits are from base (see below) + | (V & mask) // ... or from MM3 internal regs, depends on mask + | emask // 3-1 (or 3-2 when (EXPREGS[3]&0x0C is set) from EXPREGS[3] + | ((A & 0x2000) >> 13)); // 0th just as is } } static DECLFW(COOLBOYWrite) { - if(A001B & 0x80) - CartBW(A,V); + if (A001B & 0x80) + CartBW(A, V); // Deny any further writes when 7th bit is 1 AND 4th is 0 if ((EXPREGS[3] & 0x90) != 0x80) { @@ -152,9 +200,107 @@ static DECLFW(COOLBOYWrite) { } } -static void COOLBOYReset(void) { +static DECLFW(MINDKIDSWrite) { + if (A >= 0x6000) { + if (A001B & 0x80) + CartBW(A, V); + return; + } + + // Deny any further writes when 7th bit is 1 AND 4th is 0 + if ((EXPREGS[3] & 0x90) != 0x80) { + EXPREGS[A & 3] = V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } +} + +static DECLFR(COOLBOYFlashRead) { + return CartBR(A); +} + +static DECLFW(COOLBOYFlashWrite) { + if (A < 0xC000) + MMC3_CMDWrite(A, V); + else + MMC3_IRQWrite(A, V); + + if (flash_save) { + if (flash_state < sizeof(flash_buffer_a) / sizeof(flash_buffer_a[0])) { + flash_buffer_a[flash_state] = A & 0xFFF; + flash_buffer_v[flash_state] = V; + flash_state++; + + // enter CFI mode + if ((flash_state == 1) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0x98)) { + cfi_mode = 1; + flash_state = 0; + FixMMC3PRG(MMC3_cmd); + } + + // erase sector + if ((flash_state == 6) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x0555) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x0AAA) && (flash_buffer_v[2] == 0x80) && + (flash_buffer_a[3] == 0x0AAA) && (flash_buffer_v[3] == 0xAA) && + (flash_buffer_a[4] == 0x0555) && (flash_buffer_v[4] == 0x55) && + (flash_buffer_v[5] == 0x30)) { + int offset = &Page[A >> 11][A] - Flash; + int sector = offset / FLASH_SECTOR_SIZE; + for (uint32 i = sector * FLASH_SECTOR_SIZE; i < (sector + 1) * FLASH_SECTOR_SIZE; i++) + Flash[i % PRGsize[ROM_CHIP]] = 0xFF; + FCEU_printf("Flash sector #%d is erased (0x%08x - 0x%08x)\n", sector, offset, offset + FLASH_SECTOR_SIZE); + } + + // erase chip, lol + if ((flash_state == 6) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x0555) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x0AAA) && (flash_buffer_v[2] == 0x80) && + (flash_buffer_a[3] == 0x0AAA) && (flash_buffer_v[3] == 0xAA) && + (flash_buffer_a[4] == 0x0555) && (flash_buffer_v[4] == 0x55) && + (flash_buffer_v[5] == 0x10)) { + memset(Flash, 0xFF, PRGsize[ROM_CHIP]); + FCEU_printf("Flash chip erased.\n"); + } + + // write byte + if ((flash_state == 4) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x0555) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x0AAA) && (flash_buffer_v[2] == 0xA0)) { + int offset = &Page[A >> 11][A] - Flash; + if (CartBR(A) != 0xFF) { + FCEU_PrintError("Error: can't write to 0x%08x, flash sector is not erased\n", offset); + } + else { + CartBW(A, V); + } + flash_state = 0; + } + + // not a command + if (((A & 0xFFF) != 0x0AAA) && ((A & 0xFFF) != 0x0555)) { + flash_state = 0; + } + + // reset + if (V == 0xF0) { + flash_state = 0; + cfi_mode = 0; + FixMMC3PRG(MMC3_cmd); + } + } + } +} + +static void CommonReset(void) { MMC3RegReset(); EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; + flash_state = 0; + cfi_mode = 0; FixMMC3PRG(MMC3_cmd); FixMMC3CHR(MMC3_cmd); } @@ -166,6 +312,8 @@ static void COOLBOYPower(void) { FixMMC3CHR(MMC3_cmd); SetWriteHandler(0x5000, 0x5fff, CartBW); // some games access random unmapped areas and crashes because of KT-008 PCB hack in MMC3 source lol SetWriteHandler(0x6000, 0x6fff, COOLBOYWrite); + SetWriteHandler(0x8000, 0xFFFF, COOLBOYFlashWrite); + SetReadHandler(0x8000, 0xFFFF, COOLBOYFlashRead); } static void MINDKIDSPower(void) { @@ -173,41 +321,88 @@ static void MINDKIDSPower(void) { EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; FixMMC3PRG(MMC3_cmd); FixMMC3CHR(MMC3_cmd); - SetWriteHandler(0x5000, 0x5fff, COOLBOYWrite); + SetWriteHandler(0x5000, 0x7fff, MINDKIDSWrite); + SetWriteHandler(0x8000, 0xFFFF, COOLBOYFlashWrite); + SetReadHandler(0x8000, 0xFFFF, COOLBOYFlashRead); +} + +static void CommonClose(void) { + if (WRAM) + FCEU_gfree(WRAM); + if (Flash) + FCEU_gfree(Flash); + if (CFI) + FCEU_gfree(CFI); + WRAM = Flash = CFI = NULL; +} + +void CommonInit(CartInfo* info, int submapper) +{ + GenMMC3_Init(info, 2048, info->vram_size / 1024, !info->ines2 ? 8 : (info->wram_size + info->battery_wram_size) / 1024, info->battery); + pwrap = COOLBOYPW; + cwrap = COOLBOYCW; + + switch (submapper) + { + case 1: + info->Power = MINDKIDSPower; + default: + info->Power = COOLBOYPower; + break; + } + info->Reset = CommonReset; + info->Close = CommonClose; + + flash_save = info->battery; + + if (flash_save) { + CFI = (uint8*)FCEU_gmalloc(sizeof(cfi_data) * 2); + for (int i = 0; i < sizeof(cfi_data); i++) { + CFI[i * 2] = CFI[i * 2 + 1] = cfi_data[i]; + } + SetupCartPRGMapping(CFI_CHIP, CFI, sizeof(cfi_data) * 2, 0); + + Flash = (uint8*)FCEU_gmalloc(PRGsize[ROM_CHIP]); + for (int i = 0; i < PRGsize[ROM_CHIP]; i++) { + Flash[i] = PRGptr[ROM_CHIP][i % PRGsize[ROM_CHIP]]; + } + SetupCartPRGMapping(FLASH_CHIP, Flash, PRGsize[ROM_CHIP], 1); + info->SaveGame[1] = Flash; + info->SaveGameLen[1] = PRGsize[ROM_CHIP]; + } + + AddExState(EXPREGS, 4, 0, "EXPR"); + if (flash_save) + { + AddExState(&flash_state, sizeof(flash_state), 0, "FLST"); + AddExState(flash_buffer_a, sizeof(flash_buffer_a), 0, "FLBA"); + AddExState(flash_buffer_v, sizeof(flash_buffer_v), 0, "FLBV"); + AddExState(&cfi_mode, sizeof(cfi_mode), 0, "CFIM"); + AddExState(Flash, PRGsize[ROM_CHIP], 0, "FLAS"); + } } // Registers at $6xxx -void COOLBOY_Init(CartInfo *info) { - GenMMC3_Init(info, 2048, 256, 8, 1); - pwrap = COOLBOYPW; - cwrap = COOLBOYCW; - info->Power = COOLBOYPower; - info->Reset = COOLBOYReset; - AddExState(EXPREGS, 4, 0, "EXPR"); +void COOLBOY_Init(CartInfo* info) { + CommonInit(info, 0); } // Registers at $5xxx -void MINDKIDS_Init(CartInfo *info) { - GenMMC3_Init(info, 2048, 256, 8, 1); - pwrap = COOLBOYPW; - cwrap = COOLBOYCW; - info->Power = MINDKIDSPower; - info->Reset = COOLBOYReset; - AddExState(EXPREGS, 4, 0, "EXPR"); +void MINDKIDS_Init(CartInfo* info) { + CommonInit(info, 1); } // For NES 2.0 loader -void SMD132_SMD133_Init(CartInfo *info) { +void SMD132_SMD133_Init(CartInfo* info) { + switch (info->submapper) { case 0: - COOLBOY_Init(info); - break; case 1: - MINDKIDS_Init(info); + CommonInit(info, info->submapper); break; default: - FCEU_PrintError("Unknown submapper: #%d.", info->submapper); + FCEU_PrintError("Submapper #%d is not supported", info->submapper); break; } }