merge mappers from FCEU-mm
[fceu.git] / boards / 116.c
1 /* FCE Ultra - NES/Famicom Emulator\r
2  *\r
3  * Copyright notice for this file:\r
4  *  Copyright (C) 2011 CaH4e3\r
5  *\r
6  * This program is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 2 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * This program is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with this program; if not, write to the Free Software\r
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19  *\r
20  * SL12 Protected 3-in-1 mapper hardware (VRC2, MMC3, MMC1)\r
21  * the same as 603-5052 board (TODO: add reading registers, merge)\r
22  * SL1632 2-in-1 protected board, similar to SL12 (TODO: find difference)\r
23  *\r
24  * Known PCB:\r
25  *\r
26  * Garou Densetsu Special (G0904.PCB, Huang-1, GAL dip: W conf.)\r
27  * Kart Fighter (008, Huang-1, GAL dip: W conf.)\r
28  * Somari (008, C5052-13, GAL dip: P conf., GK2-P/GK2-V maskroms)\r
29  * Somari (008, Huang-1, GAL dip: W conf., GK1-P/GK1-V maskroms)\r
30  * 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)\r
31  * Samurai Spirits (Full version) (Huang-1, GAL dip: unk conf. GS-2A/GS-4A maskroms)\r
32  * Contra Fighter (603-5052 PCB, C5052-3, GAL dip: unk conf. SC603-A/SCB603-B maskroms)\r
33  *\r
34  */\r
35 \r
36 #include "mapinc.h"\r
37 \r
38 static uint8 mode;\r
39 static uint8 vrc2_chr[8], vrc2_prg[2], vrc2_mirr;\r
40 static uint8 mmc3_regs[10], mmc3_ctrl, mmc3_mirr;\r
41 static uint8 IRQCount,IRQLatch,IRQa;\r
42 static uint8 IRQReload;\r
43 static uint8 mmc1_regs[4], mmc1_buffer, mmc1_shift;\r
44 \r
45 static SFORMAT StateRegs[]=\r
46 {\r
47   {&mode, 1, "MODE"},\r
48   {vrc2_chr, 8, "VRCC"},\r
49   {vrc2_prg, 2, "VRCP"},\r
50   {&vrc2_mirr, 1, "VRCM"},\r
51   {mmc3_regs, 10, "M3RG"},\r
52   {&mmc3_ctrl, 1, "M3CT"},\r
53   {&mmc3_mirr, 1, "M3MR"},\r
54   {&IRQReload, 1, "IRQR"},\r
55   {&IRQCount, 1, "IRQC"},\r
56   {&IRQLatch, 1, "IRQL"},\r
57   {&IRQa, 1, "IRQA"},\r
58   {mmc1_regs, 4, "M1RG"},\r
59   {&mmc1_buffer, 1, "M1BF"},\r
60   {&mmc1_shift, 1, "M1MR"},\r
61   {0}\r
62 };\r
63 \r
64 static void SyncPRG(void)\r
65 {\r
66   switch(mode & 3) {\r
67    case 0:\r
68      setprg8(0x8000, vrc2_prg[0]);\r
69      setprg8(0xA000, vrc2_prg[1]);\r
70      setprg8(0xC000, ~1);\r
71      setprg8(0xE000, ~0);\r
72      break;\r
73    case 1: {\r
74      uint32 swap = (mmc3_ctrl >> 5) & 2;\r
75      setprg8(0x8000, mmc3_regs[6 + swap]);\r
76      setprg8(0xA000, mmc3_regs[7]);\r
77      setprg8(0xC000, mmc3_regs[6 + (swap ^ 2)]);\r
78      setprg8(0xE000, mmc3_regs[9]);\r
79      break;\r
80    }\r
81    case 2:\r
82    case 3: {\r
83      uint8 bank = mmc1_regs[3] & 0xF;\r
84          if(mmc1_regs[0] & 8)\r
85      {\r
86        if(mmc1_regs[0] & 4)\r
87        {\r
88          setprg16(0x8000, bank);\r
89          setprg16(0xC000, 0x0F);\r
90        }\r
91        else\r
92        {\r
93          setprg16(0x8000, 0);\r
94          setprg16(0xC000, bank);\r
95        }\r
96      }\r
97          else\r
98            setprg32(0x8000, bank >> 1);\r
99      break;\r
100    }\r
101   }\r
102 }\r
103 \r
104 static void SyncCHR(void)\r
105 {\r
106   uint32 base = (mode & 4) << 6;\r
107   switch(mode & 3) {\r
108    case 0:\r
109      setchr1(0x0000, base|vrc2_chr[0]);\r
110      setchr1(0x0400, base|vrc2_chr[1]);\r
111      setchr1(0x0800, base|vrc2_chr[2]);\r
112      setchr1(0x0c00, base|vrc2_chr[3]);\r
113      setchr1(0x1000, base|vrc2_chr[4]);\r
114      setchr1(0x1400, base|vrc2_chr[5]);\r
115      setchr1(0x1800, base|vrc2_chr[6]);\r
116      setchr1(0x1c00, base|vrc2_chr[7]);\r
117      break;\r
118    case 1: {\r
119      uint32 swap = (mmc3_ctrl & 0x80) << 5;\r
120      setchr1(0x0000 ^ swap, base|((mmc3_regs[0])&0xFE));\r
121      setchr1(0x0400 ^ swap, base|(mmc3_regs[0]|1));\r
122      setchr1(0x0800 ^ swap, base|((mmc3_regs[1])&0xFE));\r
123      setchr1(0x0c00 ^ swap, base|(mmc3_regs[1]|1));\r
124      setchr1(0x1000 ^ swap, base|mmc3_regs[2]);\r
125      setchr1(0x1400 ^ swap, base|mmc3_regs[3]);\r
126      setchr1(0x1800 ^ swap, base|mmc3_regs[4]);\r
127      setchr1(0x1c00 ^ swap, base|mmc3_regs[5]);\r
128      break;\r
129    }\r
130    case 2:\r
131    case 3:\r
132      if(mmc1_regs[0]&0x10)\r
133      {\r
134        setchr4(0x0000, mmc1_regs[1]);\r
135        setchr4(0x1000, mmc1_regs[2]);\r
136      }\r
137      else\r
138        setchr8(mmc1_regs[1] >> 1);\r
139      break;\r
140   }\r
141 }\r
142 \r
143 static void SyncMIR(void)\r
144 {\r
145   switch(mode & 3) {\r
146    case 0: {\r
147      setmirror((vrc2_mirr&1)^1);\r
148      break;\r
149    }\r
150    case 1: {\r
151      setmirror((mmc3_mirr&1)^1);\r
152      break;\r
153    }\r
154    case 2:\r
155    case 3: {\r
156      switch(mmc1_regs[0]&3) {\r
157        case 0: setmirror(MI_0); break;\r
158        case 1: setmirror(MI_1); break;\r
159        case 2: setmirror(MI_V); break;\r
160        case 3: setmirror(MI_H); break;\r
161      }\r
162      break;\r
163    }\r
164   }\r
165 }\r
166 \r
167 static void Sync(void)\r
168 {\r
169   SyncPRG();\r
170   SyncCHR();\r
171   SyncMIR();\r
172 }\r
173 \r
174 static DECLFW(UNLSL12ModeWrite)\r
175 {\r
176 //  FCEU_printf("%04X:%02X\n",A,V);\r
177   if((A & 0x4100) == 0x4100) {\r
178     mode = V;\r
179     if(A&1) { // hacky hacky, there are two configuration modes on SOMARI HUANG-1 PCBs\r
180               // Solder pads with P1/P2 shorted called SOMARI P,\r
181               // Solder pads with W1/W2 shorted called SOMARI W\r
182               // Both identical 3-in-1 but W wanted MMC1 registers\r
183               // to be reset when switch to MMC1 mode P one - doesn't\r
184               // There is issue with W version of Somari at starting copyrights\r
185       mmc1_regs[0] = 0xc;\r
186       mmc1_regs[3] = 0;\r
187       mmc1_buffer = 0;\r
188       mmc1_shift = 0;\r
189     }\r
190     Sync();\r
191   }\r
192 }\r
193 \r
194 static DECLFW(UNLSL12Write)\r
195 {\r
196 //  FCEU_printf("%04X:%02X\n",A,V);\r
197   switch(mode & 3) {\r
198    case 0: {\r
199      if((A>=0xB000)&&(A<=0xE003))\r
200      {\r
201        int32 ind=((((A&2)|(A>>10))>>1)+2)&7;\r
202        int32 sar=((A&1)<<2);\r
203        vrc2_chr[ind]=(vrc2_chr[ind]&(0xF0>>sar))|((V&0x0F)<<sar);\r
204        SyncCHR();\r
205      }\r
206      else\r
207        switch(A&0xF000) {\r
208         case 0x8000: vrc2_prg[0] = V; SyncPRG(); break;\r
209         case 0xA000: vrc2_prg[1] = V; SyncPRG(); break;\r
210         case 0x9000: vrc2_mirr = V; SyncMIR(); break;\r
211        }\r
212      break;\r
213    }\r
214    case 1: {\r
215      switch(A & 0xE001) {\r
216       case 0x8000: {\r
217         uint8 old_ctrl = mmc3_ctrl;\r
218         mmc3_ctrl = V;\r
219         if((old_ctrl&0x40) != (mmc3_ctrl&0x40))\r
220           SyncPRG();\r
221         if((old_ctrl&0x80) != (mmc3_ctrl&0x80))\r
222           SyncCHR();\r
223         break;\r
224       }\r
225       case 0x8001:\r
226         mmc3_regs[mmc3_ctrl & 7] = V;\r
227         if((mmc3_ctrl & 7) < 6)\r
228           SyncCHR();\r
229         else\r
230           SyncPRG();\r
231         break;\r
232       case 0xA000:\r
233         mmc3_mirr = V;\r
234         SyncMIR();\r
235         break;\r
236       case 0xC000:\r
237         IRQLatch = V;\r
238         break;\r
239       case 0xC001:\r
240         IRQReload = 1;\r
241         break;\r
242       case 0xE000:\r
243         X6502_IRQEnd(FCEU_IQEXT);\r
244         IRQa=0;\r
245         break;\r
246       case 0xE001:\r
247         IRQa=1;\r
248         break;\r
249      }\r
250      break;\r
251    }\r
252    case 2:\r
253    case 3: {\r
254      if(V & 0x80)\r
255      {\r
256        mmc1_regs[0] |= 0xc;\r
257        mmc1_buffer = mmc1_shift = 0;\r
258        SyncPRG();\r
259      }\r
260      else\r
261      {\r
262        uint8 n = (A >> 13) - 4;\r
263        mmc1_buffer |=  (V & 1) << (mmc1_shift++);\r
264        if(mmc1_shift == 5)\r
265        {\r
266          mmc1_regs[n] = mmc1_buffer;\r
267          mmc1_buffer = mmc1_shift = 0;\r
268          switch(n) {\r
269           case 0: SyncMIR();\r
270           case 2: SyncCHR();\r
271           case 3:\r
272           case 1: SyncPRG();\r
273          }\r
274        }\r
275      }\r
276      break;\r
277    }\r
278   }\r
279 }\r
280 \r
281 static void UNLSL12HBIRQ(void)\r
282 {\r
283   if((mode & 3) == 1)\r
284   {\r
285     int32 count = IRQCount;\r
286     if(!count || IRQReload)\r
287     {\r
288       IRQCount = IRQLatch;\r
289       IRQReload = 0;\r
290     }\r
291     else\r
292       IRQCount--;\r
293     if(!IRQCount)\r
294     {\r
295       if(IRQa)\r
296         X6502_IRQBegin(FCEU_IQEXT);\r
297     }\r
298   }\r
299 }\r
300 \r
301 static void StateRestore(int version)\r
302 {\r
303   Sync();\r
304 }\r
305 \r
306 static void UNLSL12Power(void)\r
307 {\r
308   mode = 0;\r
309   vrc2_chr[0] = ~0;\r
310   vrc2_chr[1] = ~0;\r
311   vrc2_chr[2] = ~0;\r
312   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
313   vrc2_chr[4] = 4;\r
314   vrc2_chr[5] = 5;\r
315   vrc2_chr[6] = 6;\r
316   vrc2_chr[7] = 7;\r
317   vrc2_prg[0] = 0;\r
318   vrc2_prg[1] = 1;\r
319   vrc2_mirr = 0;\r
320   mmc3_regs[0] = 0;\r
321   mmc3_regs[1] = 2;\r
322   mmc3_regs[2] = 4;\r
323   mmc3_regs[3] = 5;\r
324   mmc3_regs[4] = 6;\r
325   mmc3_regs[5] = 7;\r
326   mmc3_regs[6] = ~3;\r
327   mmc3_regs[7] = ~2;\r
328   mmc3_regs[8] = ~1;\r
329   mmc3_regs[9] = ~0;\r
330   mmc3_ctrl = mmc3_mirr = IRQCount = IRQLatch = IRQa = 0;\r
331   mmc1_regs[0] = 0xc;\r
332   mmc1_regs[1] = 0;\r
333   mmc1_regs[2] = 0;\r
334   mmc1_regs[3] = 0;\r
335   mmc1_buffer = 0;\r
336   mmc1_shift = 0;\r
337   Sync();\r
338   SetReadHandler(0x8000,0xFFFF,CartBR);\r
339   SetWriteHandler(0x4100,0x7FFF,UNLSL12ModeWrite);\r
340   SetWriteHandler(0x8000,0xFFFF,UNLSL12Write);\r
341 }\r
342 \r
343 void UNLSL12_Init(CartInfo *info)\r
344 {\r
345   info->Power = UNLSL12Power;\r
346   GameHBIRQHook = UNLSL12HBIRQ;\r
347   GameStateRestore = StateRestore;\r
348   AddExState(&StateRegs, ~0, 0, 0);\r
349 }\r