--- /dev/null
+/* FCE Ultra - NES/Famicom Emulator\r
+ *\r
+ * Copyright notice for this file:\r
+ * Copyright (C) 2011 CaH4e3\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\r
+ * \r
+ * SL12 Protected 3-in-1 mapper hardware (VRC2, MMC3, MMC1)\r
+ * the same as 603-5052 board (TODO: add reading registers, merge)\r
+ * SL1632 2-in-1 protected board, similar to SL12 (TODO: find difference)\r
+ *\r
+ * Known PCB:\r
+ *\r
+ * Garou Densetsu Special (G0904.PCB, Huang-1, GAL dip: W conf.)\r
+ * Kart Fighter (008, Huang-1, GAL dip: W conf.)\r
+ * Somari (008, C5052-13, GAL dip: P conf., GK2-P/GK2-V maskroms)\r
+ * Somari (008, Huang-1, GAL dip: W conf., GK1-P/GK1-V maskroms)\r
+ * AV Mei Shao Nv Zhan Shi ()\r
+ * Samurai Spirits (Full version) (Huang-1, GAL dip: unk conf. GS-2A/GS-4A maskroms)\r
+ * Contra Fighter (603-5052 board, C5052-3, GAL dip: unk conf. SC603-A/SCB603-B maskroms)\r
+ *\r
+ */\r
+\r
+#include "mapinc.h"\r
+#include "mmc3.h"\r
+\r
+static uint8 mode;\r
+static uint8 vrc2_chr[8], vrc2_prg[2], vrc2_mirr;\r
+static uint8 mmc3_regs[10], mmc3_ctrl, mmc3_mirr;\r
+extern uint8 IRQCount,IRQLatch,IRQa;\r
+extern uint8 IRQReload;\r
+static uint8 mmc1_regs[4], mmc1_buffer, mmc1_shift;\r
+\r
+static SFORMAT StateRegs[]=\r
+{\r
+ {&mode, 1, "MODE"},\r
+ {vrc2_chr, 8, "vrch"},\r
+ {vrc2_prg, 2, "vrpr"},\r
+ {&vrc2_mirr, 1, "vrmi"},\r
+ {mmc3_regs, 10, "m3re"},\r
+ {&mmc3_ctrl, 1, "m3ct"},\r
+ {&mmc3_mirr, 1, "m3mi"},\r
+ {&IRQReload, 1, "IRQR"},\r
+ {&IRQCount, 1, "IRQC"},\r
+ {&IRQLatch, 1, "IRQL"},\r
+ {&IRQa, 1, "IRQA"},\r
+ {mmc1_regs, 4, "m1r"},\r
+ {&mmc1_buffer, 1, "m1bf"},\r
+ {&mmc1_shift, 1, "m1mi"},\r
+ {0}\r
+};\r
+\r
+static void SyncPRG(void)\r
+{\r
+ switch(mode & 3) {\r
+ case 0:\r
+ setprg8(0x8000, vrc2_prg[0]);\r
+ setprg8(0xA000, vrc2_prg[1]);\r
+ setprg8(0xC000, ~1);\r
+ setprg8(0xE000, ~0);\r
+ break;\r
+ case 1: {\r
+ uint32 swap = (mmc3_ctrl >> 5) & 2;\r
+ setprg8(0x8000, mmc3_regs[6 + swap]);\r
+ setprg8(0xA000, mmc3_regs[7]);\r
+ setprg8(0xC000, mmc3_regs[6 + (swap ^ 2)]);\r
+ setprg8(0xE000, mmc3_regs[9]);\r
+ break;\r
+ }\r
+ case 2:\r
+ case 3: {\r
+ uint8 bank = mmc1_regs[3] & 0xF;\r
+ if(mmc1_regs[0] & 8)\r
+ {\r
+ if(mmc1_regs[0] & 4)\r
+ {\r
+ setprg16(0x8000, bank);\r
+ setprg16(0xC000, 0x0F);\r
+ }\r
+ else\r
+ {\r
+ setprg16(0x8000, 0);\r
+ setprg16(0xC000, bank);\r
+ }\r
+ }\r
+ else\r
+ setprg32(0x8000, bank >> 1);\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+static void SyncCHR(void)\r
+{\r
+ uint32 base = (mode & 4) << 6;\r
+ switch(mode & 3) {\r
+ case 0:\r
+ setchr1(0x0000, base|vrc2_chr[0]);\r
+ setchr1(0x0400, base|vrc2_chr[1]);\r
+ setchr1(0x0800, base|vrc2_chr[2]);\r
+ setchr1(0x0c00, base|vrc2_chr[3]);\r
+ setchr1(0x1000, base|vrc2_chr[4]);\r
+ setchr1(0x1400, base|vrc2_chr[5]);\r
+ setchr1(0x1800, base|vrc2_chr[6]);\r
+ setchr1(0x1c00, base|vrc2_chr[7]);\r
+ break;\r
+ case 1: {\r
+ uint32 swap = (mmc3_ctrl & 0x80) << 5;\r
+ setchr1(0x0000 ^ swap, base|((mmc3_regs[0])&0xFE));\r
+ setchr1(0x0400 ^ swap, base|(mmc3_regs[0]|1));\r
+ setchr1(0x0800 ^ swap, base|((mmc3_regs[1])&0xFE));\r
+ setchr1(0x0c00 ^ swap, base|(mmc3_regs[1]|1));\r
+ setchr1(0x1000 ^ swap, base|mmc3_regs[2]);\r
+ setchr1(0x1400 ^ swap, base|mmc3_regs[3]);\r
+ setchr1(0x1800 ^ swap, base|mmc3_regs[4]);\r
+ setchr1(0x1c00 ^ swap, base|mmc3_regs[5]);\r
+ break;\r
+ }\r
+ case 2:\r
+ case 3:\r
+ if(mmc1_regs[0]&0x10)\r
+ {\r
+ setchr4(0x0000, mmc1_regs[1]);\r
+ setchr4(0x1000, mmc1_regs[2]);\r
+ }\r
+ else\r
+ setchr8(mmc1_regs[1] >> 1);\r
+ break;\r
+ }\r
+}\r
+\r
+static void SyncMIR(void)\r
+{\r
+ switch(mode & 3) {\r
+ case 0: {\r
+ setmirror((vrc2_mirr&1)^1);\r
+ break;\r
+ }\r
+ case 1: {\r
+ setmirror((mmc3_mirr&1)^1);\r
+ break;\r
+ }\r
+ case 2:\r
+ case 3: {\r
+ switch(mmc1_regs[0]&3) {\r
+ case 0: setmirror(MI_0); break;\r
+ case 1: setmirror(MI_1); break;\r
+ case 2: setmirror(MI_V); break;\r
+ case 3: setmirror(MI_H); break;\r
+ }\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+static void Sync(void)\r
+{\r
+ SyncPRG();\r
+ SyncCHR();\r
+ SyncMIR();\r
+}\r
+\r
+static DECLFW(UNLSL12ModeWrite)\r
+{\r
+ printf("%04X:%02X\n",A,V);\r
+ if((A & 0x4100) == 0x4100) {\r
+ mode = V;\r
+ if(A&1) { // hacky hacky, there are two configuration modes on SOMARI HUANG-1 PCBs\r
+ // Solder pads with P1/P2 shorted called SOMARI P,\r
+ // Solder pads with W1/W2 shorted called SOMARI W\r
+ // Both identical 3-in-1 but W wanted MMC1 registers\r
+ // to be reset when switch to MMC1 mode P one - doesn't\r
+ // There is issue with W version of Somari at starting copyrights\r
+ mmc1_regs[0] = 0xc;\r
+ mmc1_regs[3] = 0;\r
+ mmc1_buffer = 0;\r
+ mmc1_shift = 0;\r
+ }\r
+ Sync();\r
+ }\r
+}\r
+\r
+static DECLFW(UNLSL12Write)\r
+{\r
+ printf("%04X:%02X\n",A,V);\r
+ if(A==0xA123)\r
+ {\r
+ int zzz=9;\r
+ }\r
+ switch(mode & 3) {\r
+ case 0: {\r
+ if((A>=0xB000)&&(A<=0xE003))\r
+ {\r
+ int32 ind=((((A&2)|(A>>10))>>1)+2)&7;\r
+ int32 sar=((A&1)<<2);\r
+ vrc2_chr[ind]=(vrc2_chr[ind]&(0xF0>>sar))|((V&0x0F)<<sar);\r
+ SyncCHR();\r
+ }\r
+ else\r
+ switch(A&0xF000) {\r
+ case 0x8000: vrc2_prg[0] = V; SyncPRG(); break;\r
+ case 0xA000: vrc2_prg[1] = V; SyncPRG(); break;\r
+ case 0x9000: vrc2_mirr = V; SyncMIR(); break;\r
+ }\r
+ break;\r
+ }\r
+ case 1: {\r
+ switch(A & 0xE001) {\r
+ case 0x8000: {\r
+ uint8 old_ctrl = mmc3_ctrl;\r
+ mmc3_ctrl = V;\r
+ if((old_ctrl&0x40) != (mmc3_ctrl&0x40))\r
+ SyncPRG();\r
+ if((old_ctrl&0x80) != (mmc3_ctrl&0x80))\r
+ SyncCHR();\r
+ break;\r
+ }\r
+ case 0x8001:\r
+ mmc3_regs[mmc3_ctrl & 7] = V;\r
+ if((mmc3_ctrl & 7) < 6)\r
+ SyncCHR();\r
+ else\r
+ SyncPRG();\r
+ break;\r
+ case 0xA000:\r
+ mmc3_mirr = V;\r
+ SyncMIR();\r
+ break;\r
+ case 0xC000:\r
+ IRQLatch = V;\r
+ break;\r
+ case 0xC001:\r
+ IRQReload = 1;\r
+ break;\r
+ case 0xE000:\r
+ X6502_IRQEnd(FCEU_IQEXT);\r
+ IRQa=0;\r
+ break;\r
+ case 0xE001:\r
+ IRQa=1;\r
+ break;\r
+ }\r
+ break;\r
+ }\r
+ case 2:\r
+ case 3: {\r
+ if(V & 0x80)\r
+ {\r
+ mmc1_regs[0] |= 0xc;\r
+ mmc1_buffer = mmc1_shift = 0;\r
+ SyncPRG();\r
+ }\r
+ else\r
+ {\r
+ uint8 n = (A >> 13) - 4;\r
+ mmc1_buffer |= (V & 1) << (mmc1_shift++);\r
+ if(mmc1_shift == 5)\r
+ {\r
+ mmc1_regs[n] = mmc1_buffer;\r
+ mmc1_buffer = mmc1_shift = 0;\r
+ switch(n) {\r
+ case 0: SyncMIR();\r
+ case 2: SyncCHR(); \r
+ case 3:\r
+ case 1: SyncPRG();\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+static void UNLSL12HBIRQ(void)\r
+{\r
+ if((mode & 3) == 1)\r
+ {\r
+ int32 count = IRQCount;\r
+ if(!count || IRQReload)\r
+ {\r
+ IRQCount = IRQLatch;\r
+ IRQReload = 0;\r
+ }\r
+ else\r
+ IRQCount--;\r
+ if(!IRQCount)\r
+ {\r
+ if(IRQa)\r
+ X6502_IRQBegin(FCEU_IQEXT);\r
+ }\r
+ }\r
+}\r
+\r
+static void StateRestore(int version)\r
+{\r
+ Sync();\r
+}\r
+\r
+static void UNLSL12Power(void)\r
+{\r
+ mode = 0;\r
+ vrc2_chr[0] = ~0;\r
+ vrc2_chr[1] = ~0;\r
+ vrc2_chr[2] = ~0;\r
+ 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\r
+ vrc2_chr[4] = 4;\r
+ vrc2_chr[5] = 5;\r
+ vrc2_chr[6] = 6;\r
+ vrc2_chr[7] = 7;\r
+ vrc2_prg[0] = 0;\r
+ vrc2_prg[1] = 1;\r
+ vrc2_mirr = 0;\r
+ mmc3_regs[0] = 0;\r
+ mmc3_regs[1] = 2;\r
+ mmc3_regs[2] = 4;\r
+ mmc3_regs[3] = 5;\r
+ mmc3_regs[4] = 6;\r
+ mmc3_regs[5] = 7;\r
+ mmc3_regs[6] = ~3;\r
+ mmc3_regs[7] = ~2;\r
+ mmc3_regs[8] = ~1;\r
+ mmc3_regs[9] = ~0;\r
+ mmc3_ctrl = mmc3_mirr = IRQCount = IRQLatch = IRQa = 0;\r
+ mmc1_regs[0] = 0xc;\r
+ mmc1_regs[1] = 0;\r
+ mmc1_regs[2] = 0;\r
+ mmc1_regs[3] = 0;\r
+ mmc1_buffer = 0;\r
+ mmc1_shift = 0;\r
+ Sync();\r
+ SetReadHandler(0x8000,0xFFFF,CartBR);\r
+ SetWriteHandler(0x4100,0x7FFF,UNLSL12ModeWrite);\r
+ SetWriteHandler(0x8000,0xFFFF,UNLSL12Write);\r
+}\r
+\r
+void UNLSL12_Init(CartInfo *info)\r
+{\r
+ info->Power = UNLSL12Power;\r
+ GameHBIRQHook = UNLSL12HBIRQ;\r
+ GameStateRestore = StateRestore;\r
+ AddExState(&StateRegs, ~0, 0, 0);\r
+}\r
+\r
+void Mapper116_Init(CartInfo *info)\r
+{\r
+ UNLSL12_Init(info);\r
+}\r