diff --git a/src/boards/116.c b/src/boards/116.c index b9c8b2c..f9052d4 100644 --- a/src/boards/116.c +++ b/src/boards/116.c @@ -1,7 +1,7 @@ /* FCE Ultra - NES/Famicom Emulator * * Copyright notice for this file: - * Copyright (C) 2011 CaH4e3 + * Copyright (C) 2023 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,353 +16,113 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * SL12 Protected 3-in-1 mapper hardware (VRC2, MMC3, MMC1) - * the same as 603-5052 board (TODO: add reading registers, merge) - * SL1632 2-in-1 protected board, similar to SL12 (TODO: find difference) - * - * Known PCB: - * - * Garou Densetsu Special (G0904.PCB, Huang-1, GAL dip: W conf.) - * Kart Fighter (008, Huang-1, GAL dip: W conf.) - * Somari (008, C5052-13, GAL dip: P conf., GK2-P/GK2-V maskroms) - * Somari (008, Huang-1, GAL dip: W conf., GK1-P/GK1-V maskroms) - * AV Mei Shao Nv Zhan Shi (aka AV Pretty Girl Fighting) (SL-12 PCB, Hunag-1, GAL dip: unk conf. SL-11A/SL-11B maskroms) - * Samurai Spirits (Full version) (Huang-1, GAL dip: unk conf. GS-2A/GS-4A maskroms) - * Contra Fighter (603-5052 PCB, C5052-3, GAL dip: unk conf. SC603-A/SCB603-B maskroms) - * + */ + +/* NES 2.0 Mapper 556 + * Used for the for the 餓狼傳說 激鬥篇 HiK 7-in-1 (JY-215) multicart. */ #include "mapinc.h" +#include "asic_mmc1.h" +#include "asic_mmc3.h" +#include "asic_vrc2and4.h" -static uint8 vrc2_chr[8] = { 0 }; -static uint8 vrc2_prg[2] = { 0 }; -static uint8 vrc2_mirr = 0; +static uint8 submapper; +static uint8 reg; +static uint8 init; -static uint8 mmc3_regs[10] = { 0 }; -static uint8 mmc3_ctrl = 0; -static uint8 mmc3_mirr = 0; - -static uint8 mmc1_regs[4] = { 0 }; -static uint8 mmc1_buffer = 0; -static uint8 mmc1_shift = 0; - -static uint8 IRQCount = 0; -static uint8 IRQLatch = 0; -static uint8 IRQa = 0; -static uint8 IRQReload = 0; -static uint8 mode = 0; - -static uint8 submapper = 0; -static uint8 game = 0; - -extern uint32 ROM_size; -extern uint32 VROM_size; - -static SFORMAT StateRegs[] = -{ - { &mode, 1, "MODE" }, - { vrc2_chr, 8, "VRCC" }, - { vrc2_prg, 2, "VRCP" }, - { &vrc2_mirr, 1, "VRCM" }, - { mmc3_regs, 10, "M3RG" }, - { &mmc3_ctrl, 1, "M3CT" }, - { &mmc3_mirr, 1, "M3MR" }, - { &IRQReload, 1, "IRQR" }, - { &IRQCount, 1, "IRQC" }, - { &IRQLatch, 1, "IRQL" }, - { &IRQa, 1, "IRQA" }, - { mmc1_regs, 4, "M1RG" }, - { &mmc1_buffer, 1, "M1BF" }, - { &mmc1_shift, 1, "M1MR" }, - { &submapper, 1, "SUBM" }, - { &game, 1, "GAME" }, - { 0 } +static SFORMAT StateRegs[] = { + { ®, 1, "MODE" }, + { &init, 1, "INIT" }, + { 0 }, }; -static void SyncPRG(void) { - uint8 mask = (submapper != 3) ? 0x3F : (game ? 0x0F : 0x1F); - uint8 outer = game ? (game + 1) * 0x10 : 0; - switch (mode & 3) { - case 0: - setprg8(0x8000, (outer & ~mask) | (vrc2_prg[0] & mask)); - setprg8(0xA000, (outer & ~mask) | (vrc2_prg[1] & mask)); - setprg8(0xC000, (outer & ~mask) | (~1 & mask)); - setprg8(0xE000, (outer & ~mask) | (~0 & mask)); - break; - case 1: - { - uint32 swap = (mmc3_ctrl >> 5) & 2; - setprg8(0x8000, (outer & ~mask) | (mmc3_regs[6 + swap] & mask)); - setprg8(0xA000, (outer & ~mask) | (mmc3_regs[7] & mask)); - setprg8(0xC000, (outer & ~mask) | (mmc3_regs[6 + (swap ^ 2)] & mask)); - setprg8(0xE000, (outer & ~mask) | (mmc3_regs[9] & mask)); - break; - } - case 2: - case 3: - { - uint8 bank = mmc1_regs[3] & mask; - if (mmc1_regs[0] & 8) { - if (submapper == 2) - bank >>= 1; - if (mmc1_regs[0] & 4) { - setprg16(0x8000, bank); - setprg16(0xC000, 0x0F); - } else { - setprg16(0x8000, 0); - setprg16(0xC000, bank); - } - } else - setprg32(0x8000, ((outer & ~mask) >> 1) | (bank >> 1)); - } - break; +static void sync (void) { + int prgAND = 0x3F; + int chrAND = 0xFF; + int prgOR = 0x00 &~prgAND; + int chrOR = reg <<6 &0x100 &~chrAND; + if (reg &0x02) { + prgAND >>= 1; + prgOR >>= 1; + chrAND >>= 2; + chrOR >>= 2; + MMC1_syncPRG(prgAND, prgOR); + MMC1_syncCHR(chrAND, chrOR); + MMC1_syncMirror(); + } else + if (reg &0x01) { + MMC3_syncPRG(prgAND, prgOR); + MMC3_syncCHR(chrAND, chrOR); + MMC3_syncMirror(); + } else { + VRC24_syncPRG(prgAND, prgOR); + VRC24_syncCHR(chrAND, chrOR); + VRC24_syncMirror(); } } -static void SyncCHR(void) { - uint32 mask = game ? 0x7F : 0xFF; - uint32 outer = game ? (game + 1) * 0x80 : 0; - uint32 base = (mode & 4) << 6; - switch (mode & 3) { - case 0: - setchr1(0x0000, ((outer | base) & ~mask) | (vrc2_chr[0] & mask)); - setchr1(0x0400, ((outer | base) & ~mask) | (vrc2_chr[1] & mask)); - setchr1(0x0800, ((outer | base) & ~mask) | (vrc2_chr[2] & mask)); - setchr1(0x0c00, ((outer | base) & ~mask) | (vrc2_chr[3] & mask)); - setchr1(0x1000, ((outer | base) & ~mask) | (vrc2_chr[4] & mask)); - setchr1(0x1400, ((outer | base) & ~mask) | (vrc2_chr[5] & mask)); - setchr1(0x1800, ((outer | base) & ~mask) | (vrc2_chr[6] & mask)); - setchr1(0x1c00, ((outer | base) & ~mask) | (vrc2_chr[7] & mask)); - break; - case 1: { - uint32 swap = (mmc3_ctrl & 0x80) << 5; - setchr1(0x0000 ^ swap, ((outer | base) & ~mask) | ((mmc3_regs[0] & 0xFE) & mask)); - setchr1(0x0400 ^ swap, ((outer | base) & ~mask) | ((mmc3_regs[0] | 1) & mask)); - setchr1(0x0800 ^ swap, ((outer | base) & ~mask) | ((mmc3_regs[1] & 0xFE) & mask)); - setchr1(0x0c00 ^ swap, ((outer | base) & ~mask) | ((mmc3_regs[1] | 1) & mask)); - setchr1(0x1000 ^ swap, ((outer | base) & ~mask) | (mmc3_regs[2] & mask)); - setchr1(0x1400 ^ swap, ((outer | base) & ~mask) | (mmc3_regs[3] & mask)); - setchr1(0x1800 ^ swap, ((outer | base) & ~mask) | (mmc3_regs[4] & mask)); - setchr1(0x1c00 ^ swap, ((outer | base) & ~mask) | (mmc3_regs[5] & mask)); - break; - } - case 2: - case 3: - if (mmc1_regs[0] & 0x10) { - setchr4(0x0000, (outer & ~mask) | (mmc1_regs[1] & mask)); - setchr4(0x1000, (outer & ~mask) | (mmc1_regs[2] & mask)); - } else - setchr8(((outer & ~mask) >> 1) | (mmc1_regs[1] & mask) >> 1); - break; - } +int Huang2_getPRGBank (uint8 bank) { + return MMC1_getPRGBank(bank) >>1; } -static void SyncMIR(void) { - switch (mode & 3) { - case 0: { - setmirror((vrc2_mirr & 1) ^ 1); - break; - } - case 1: { - setmirror((mmc3_mirr & 1) ^ 1); - break; - } - case 2: - case 3: { - switch (mmc1_regs[0] & 3) { - case 0: setmirror(MI_0); break; - case 1: setmirror(MI_1); break; - case 2: setmirror(MI_V); break; - case 3: setmirror(MI_H); break; - } - break; - } - } -} - -static void Sync(void) { - SyncPRG(); - SyncCHR(); - SyncMIR(); -} - -static DECLFW(UNLSL12ModeWrite) { - if (A & 0x100) { - mode = V; - if (A & 1) { /* hacky hacky, there are two configuration modes on SOMARI HUANG-1 PCBs - * Solder pads with P1/P2 shorted called SOMARI P, - * Solder pads with W1/W2 shorted called SOMARI W - * Both identical 3-in-1 but W wanted MMC1 registers - * to be reset when switch to MMC1 mode P one - doesn't - * There is issue with W version of Somari at starting copyrights - */ - mmc1_regs[0] = 0xc; - mmc1_regs[3] = 0; - mmc1_buffer = 0; - mmc1_shift = 0; - } - Sync(); - } -} - -static DECLFW(UNLSL12Write) { -/* FCEU_printf("%04X:%02X\n",A,V); */ - switch (mode & 3) { - case 0: { - if ((A >= 0xB000) && (A <= 0xE003)) { - int32 ind = ((((A & 2) | (A >> 10)) >> 1) + 2) & 7; - int32 sar = ((A & 1) << 2); - vrc2_chr[ind] = (vrc2_chr[ind] & (0xF0 >> sar)) | ((V & 0x0F) << sar); - SyncCHR(); - } else - switch (A & 0xF000) { - case 0x8000: vrc2_prg[0] = V; SyncPRG(); break; - case 0xA000: vrc2_prg[1] = V; SyncPRG(); break; - case 0x9000: vrc2_mirr = V; SyncMIR(); break; - } - break; - } - case 1: { - switch (A & 0xE001) { - case 0x8000: { - uint8 old_ctrl = mmc3_ctrl; - mmc3_ctrl = V; - if ((old_ctrl & 0x40) != (mmc3_ctrl & 0x40)) - SyncPRG(); - if ((old_ctrl & 0x80) != (mmc3_ctrl & 0x80)) - SyncCHR(); - break; - } - case 0x8001: - mmc3_regs[mmc3_ctrl & 7] = V; - if ((mmc3_ctrl & 7) < 6) - SyncCHR(); - else - SyncPRG(); - break; - case 0xA000: - mmc3_mirr = V; - SyncMIR(); - break; - case 0xC000: - IRQLatch = V; - break; - case 0xC001: - IRQReload = 1; - break; - case 0xE000: - X6502_IRQEnd(FCEU_IQEXT); - IRQa = 0; - break; - case 0xE001: - IRQa = 1; - break; - } - break; - } - case 2: - case 3: { - if (V & 0x80) { - mmc1_regs[0] |= 0xc; - mmc1_buffer = mmc1_shift = 0; - SyncPRG(); - } else { - uint8 n = (A >> 13) - 4; - mmc1_buffer |= (V & 1) << (mmc1_shift++); - if (mmc1_shift == 5) { - mmc1_regs[n] = mmc1_buffer; - mmc1_buffer = mmc1_shift = 0; - switch (n) { - case 0: SyncMIR(); break; - case 2: SyncCHR(); break; - case 3: - case 1: SyncPRG(); break; - } - } - } - break; - } - } -} - -static void UNLSL12HBIRQ(void) { - if ((mode & 3) == 1) { - int32 count = IRQCount; - if (!count || IRQReload) { - IRQCount = IRQLatch; - IRQReload = 0; - } else - IRQCount--; - if (!IRQCount) { - if (IRQa) - X6502_IRQBegin(FCEU_IQEXT); +static void applyMode (uint8 clear) { + if (reg &0x02) { + MMC1_activate(clear && init &1, sync, MMC1_TYPE_MMC1B, submapper == 2? Huang2_getPRGBank: NULL, NULL, NULL, NULL); + MMC1_writeReg(0x8000, 0x80); + init &= ~1; + } else + if (reg &0x01) { + MMC3_activate(clear && init &2, sync, MMC3_TYPE_AX5202P, NULL, NULL, NULL, NULL); + init &= ~2; + } else { + VRC2_activate(clear && init &4, sync, 0x01, 0x02, NULL, NULL, NULL, NULL); + if (init &4) { + VRC24_writeReg(0xB000, 0xFF); VRC24_writeReg(0xB001, 0xFF); VRC24_writeReg(0xB002, 0xFF); VRC24_writeReg(0xB003, 0xFF); + VRC24_writeReg(0xC000, 0xFF); VRC24_writeReg(0xC001, 0xFF); VRC24_writeReg(0xC002, 0xFF); VRC24_writeReg(0xC003, 0xFF); + VRC24_writeReg(0xD000, 0xFF); VRC24_writeReg(0xD001, 0xFF); VRC24_writeReg(0xD002, 0xFF); VRC24_writeReg(0xD003, 0xFF); + VRC24_writeReg(0xE000, 0xFF); VRC24_writeReg(0xE001, 0xFF); VRC24_writeReg(0xE002, 0xFF); VRC24_writeReg(0xE003, 0xFF); + init &= ~4; } } } -static void StateRestore(int version) { - Sync(); -} - -static void UNLSL12Reset(void) { - /* this is suppose to increment during power cycle */ - /* but we dont have a way to do that, so increment on reset instead. */ - if (submapper == 3) { - game = game + 1; - if (game > 4) - game = 0; +static DECLFW(writeReg) { + if (A &0x100) { + uint8 previousReg = reg; + reg = V; + if ((previousReg ^V) &3) + applyMode(1); + else + sync(); } - Sync(); } -static void UNLSL12Power(void) { - game = (submapper == 3) ? 4 : 0; - mode = 1; - vrc2_chr[0] = ~0; - vrc2_chr[1] = ~0; - vrc2_chr[2] = ~0; - vrc2_chr[3] = ~0; /* W conf. of Somari wanted CHR3 has to be set to BB bank (or similar), but doesn't do that directly */ - vrc2_chr[4] = 4; - vrc2_chr[5] = 5; - vrc2_chr[6] = 6; - vrc2_chr[7] = 7; - vrc2_prg[0] = 0; - vrc2_prg[1] = 1; - vrc2_mirr = 0; - mmc3_regs[0] = 0; - mmc3_regs[1] = 2; - mmc3_regs[2] = 4; - mmc3_regs[3] = 5; - mmc3_regs[4] = 6; - mmc3_regs[5] = 7; - mmc3_regs[6] = ~3; - mmc3_regs[7] = ~2; - mmc3_regs[8] = ~1; - mmc3_regs[9] = ~0; - mmc3_ctrl = mmc3_mirr = IRQCount = IRQLatch = IRQa = 0; - mmc1_regs[0] = 0xc; - mmc1_regs[1] = 0; - mmc1_regs[2] = 0; - mmc1_regs[3] = 0; - mmc1_buffer = 0; - mmc1_shift = 0; - Sync(); +static void reset (void) { + reg = 0; + init = 7; + applyMode(1); +} + +static void power (void) { + reg = 0; + init = 7; SetReadHandler(0x8000, 0xFFFF, CartBR); - SetWriteHandler(0x4100, 0x5FFF, UNLSL12ModeWrite); - SetWriteHandler(0x8000, 0xFFFF, UNLSL12Write); + SetWriteHandler(0x4020, 0x5FFF, writeReg); + applyMode(1); } -void UNLSL12_Init(CartInfo *info) { - info->Power = UNLSL12Power; - info->Reset = UNLSL12Reset; - GameHBIRQHook = UNLSL12HBIRQ; - GameStateRestore = StateRestore; - AddExState(&StateRegs, ~0, 0, 0); - submapper = info->submapper; - if (submapper == 0) { - /* PRG 128K and CHR 128K is Huang-2 (submapper 2) */ - if (ROM_size == 8 && VROM_size == 16) - submapper = 2; - } +static void restore (int version) { + applyMode(0); +} + +void UNLSL12_Init (CartInfo *info) { + submapper = info->submapper; + MMC1_addExState(); + MMC3_addExState(); + VRC24_addExState(); + info->Reset = reset; + info->Power = power; + GameStateRestore = restore; + AddExState(StateRegs, ~0, 0, 0); } diff --git a/src/boards/asic_mmc1.c b/src/boards/asic_mmc1.c index 91fad25..3093cbd 100644 --- a/src/boards/asic_mmc1.c +++ b/src/boards/asic_mmc1.c @@ -96,7 +96,7 @@ void FP_FASTAPASS(1) MMC1_cpuCycle(int a) { while (a--) if (MMC1_filter) MMC1_filter--; } -DECLFW(MMC1_write) { +DECLFW(MMC1_writeReg) { if (V &0x80) { MMC1_reg[0] |= 0x0C; MMC1_shift = 0; @@ -125,7 +125,7 @@ static void MMC1_setHandlers () { SetReadHandler (0x6000, 0x7FFF, MMC1_readWRAM); SetWriteHandler(0x6000, 0x7FFF, MMC1_writeWRAM); SetReadHandler (0x8000, 0xFFFF, CartBR); - SetWriteHandler(0x8000, 0xFFFF, MMC1_write); + SetWriteHandler(0x8000, 0xFFFF, MMC1_writeReg); MapIRQHook = MMC1_cpuCycle; } diff --git a/src/boards/asic_mmc1.h b/src/boards/asic_mmc1.h index 25f26a3..971d719 100644 --- a/src/boards/asic_mmc1.h +++ b/src/boards/asic_mmc1.h @@ -31,7 +31,7 @@ void MMC1_syncPRG (int, int); void MMC1_syncCHR (int, int); void MMC1_syncMirror (); void FP_FASTAPASS(1) MMC1_cpuCycle(int); -DECLFW (MMC1_write); +DECLFW (MMC1_writeReg); void MMC1_addExState (); void MMC1_restore (int); void MMC1_power ();