merge mapper code from FCEUX
[fceu.git] / boards / 116.c
diff --git a/boards/116.c b/boards/116.c
new file mode 100644 (file)
index 0000000..3a21668
--- /dev/null
@@ -0,0 +1,359 @@
+/* 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