merge mapper code from FCEUX
[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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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 ()\r
31  * Samurai Spirits (Full version) (Huang-1, GAL dip: unk conf. GS-2A/GS-4A maskroms)\r
32  * Contra Fighter (603-5052 board, C5052-3, GAL dip: unk conf. SC603-A/SCB603-B maskroms)\r
33  *\r
34  */\r
35 \r
36 #include "mapinc.h"\r
37 #include "mmc3.h"\r
38 \r
39 static uint8 mode;\r
40 static uint8 vrc2_chr[8], vrc2_prg[2], vrc2_mirr;\r
41 static uint8 mmc3_regs[10], mmc3_ctrl, mmc3_mirr;\r
42 extern uint8 IRQCount,IRQLatch,IRQa;\r
43 extern uint8 IRQReload;\r
44 static uint8 mmc1_regs[4], mmc1_buffer, mmc1_shift;\r
45 \r
46 static SFORMAT StateRegs[]=\r
47 {\r
48   {&mode, 1, "MODE"},\r
49   {vrc2_chr, 8, "vrch"},\r
50   {vrc2_prg, 2, "vrpr"},\r
51   {&vrc2_mirr, 1, "vrmi"},\r
52   {mmc3_regs, 10, "m3re"},\r
53   {&mmc3_ctrl, 1, "m3ct"},\r
54   {&mmc3_mirr, 1, "m3mi"},\r
55   {&IRQReload, 1, "IRQR"},\r
56   {&IRQCount, 1, "IRQC"},\r
57   {&IRQLatch, 1, "IRQL"},\r
58   {&IRQa, 1, "IRQA"},\r
59   {mmc1_regs, 4, "m1r"},\r
60   {&mmc1_buffer, 1, "m1bf"},\r
61   {&mmc1_shift, 1, "m1mi"},\r
62   {0}\r
63 };\r
64 \r
65 static void SyncPRG(void)\r
66 {\r
67   switch(mode & 3) {\r
68    case 0:\r
69      setprg8(0x8000, vrc2_prg[0]);\r
70      setprg8(0xA000, vrc2_prg[1]);\r
71      setprg8(0xC000, ~1);\r
72      setprg8(0xE000, ~0);\r
73      break;\r
74    case 1: {\r
75      uint32 swap = (mmc3_ctrl >> 5) & 2;\r
76      setprg8(0x8000, mmc3_regs[6 + swap]);\r
77      setprg8(0xA000, mmc3_regs[7]);\r
78      setprg8(0xC000, mmc3_regs[6 + (swap ^ 2)]);\r
79      setprg8(0xE000, mmc3_regs[9]);\r
80      break;\r
81    }\r
82    case 2:\r
83    case 3: {\r
84      uint8 bank = mmc1_regs[3] & 0xF;\r
85          if(mmc1_regs[0] & 8)\r
86      {\r
87        if(mmc1_regs[0] & 4)\r
88        {\r
89          setprg16(0x8000, bank);\r
90          setprg16(0xC000, 0x0F);\r
91        }\r
92        else\r
93        {\r
94          setprg16(0x8000, 0);\r
95          setprg16(0xC000, bank);\r
96        }\r
97      }\r
98          else\r
99            setprg32(0x8000, bank >> 1);\r
100      break;\r
101    }\r
102   }\r
103 }\r
104 \r
105 static void SyncCHR(void)\r
106 {\r
107   uint32 base = (mode & 4) << 6;\r
108   switch(mode & 3) {\r
109    case 0:\r
110      setchr1(0x0000, base|vrc2_chr[0]);\r
111      setchr1(0x0400, base|vrc2_chr[1]);\r
112      setchr1(0x0800, base|vrc2_chr[2]);\r
113      setchr1(0x0c00, base|vrc2_chr[3]);\r
114      setchr1(0x1000, base|vrc2_chr[4]);\r
115      setchr1(0x1400, base|vrc2_chr[5]);\r
116      setchr1(0x1800, base|vrc2_chr[6]);\r
117      setchr1(0x1c00, base|vrc2_chr[7]);\r
118      break;\r
119    case 1: {\r
120      uint32 swap = (mmc3_ctrl & 0x80) << 5;\r
121      setchr1(0x0000 ^ swap, base|((mmc3_regs[0])&0xFE));\r
122      setchr1(0x0400 ^ swap, base|(mmc3_regs[0]|1));\r
123      setchr1(0x0800 ^ swap, base|((mmc3_regs[1])&0xFE));\r
124      setchr1(0x0c00 ^ swap, base|(mmc3_regs[1]|1));\r
125      setchr1(0x1000 ^ swap, base|mmc3_regs[2]);\r
126      setchr1(0x1400 ^ swap, base|mmc3_regs[3]);\r
127      setchr1(0x1800 ^ swap, base|mmc3_regs[4]);\r
128      setchr1(0x1c00 ^ swap, base|mmc3_regs[5]);\r
129      break;\r
130    }\r
131    case 2:\r
132    case 3:\r
133      if(mmc1_regs[0]&0x10)\r
134      {\r
135        setchr4(0x0000, mmc1_regs[1]);\r
136        setchr4(0x1000, mmc1_regs[2]);\r
137      }\r
138      else\r
139        setchr8(mmc1_regs[1] >> 1);\r
140      break;\r
141   }\r
142 }\r
143 \r
144 static void SyncMIR(void)\r
145 {\r
146   switch(mode & 3) {\r
147    case 0: {\r
148      setmirror((vrc2_mirr&1)^1);\r
149      break;\r
150    }\r
151    case 1: {\r
152      setmirror((mmc3_mirr&1)^1);\r
153      break;\r
154    }\r
155    case 2:\r
156    case 3: {\r
157      switch(mmc1_regs[0]&3) {\r
158        case 0: setmirror(MI_0); break;\r
159        case 1: setmirror(MI_1); break;\r
160        case 2: setmirror(MI_V); break;\r
161        case 3: setmirror(MI_H); break;\r
162      }\r
163      break;\r
164    }\r
165   }\r
166 }\r
167 \r
168 static void Sync(void)\r
169 {\r
170   SyncPRG();\r
171   SyncCHR();\r
172   SyncMIR();\r
173 }\r
174 \r
175 static DECLFW(UNLSL12ModeWrite)\r
176 {\r
177   printf("%04X:%02X\n",A,V);\r
178   if((A & 0x4100) == 0x4100) {\r
179     mode = V;\r
180     if(A&1) { // hacky hacky, there are two configuration modes on SOMARI HUANG-1 PCBs\r
181               // Solder pads with P1/P2 shorted called SOMARI P,\r
182               // Solder pads with W1/W2 shorted called SOMARI W\r
183               // Both identical 3-in-1 but W wanted MMC1 registers\r
184               // to be reset when switch to MMC1 mode P one - doesn't\r
185               // There is issue with W version of Somari at starting copyrights\r
186       mmc1_regs[0] = 0xc;\r
187       mmc1_regs[3] = 0;\r
188       mmc1_buffer = 0;\r
189       mmc1_shift = 0;\r
190     }\r
191     Sync();\r
192   }\r
193 }\r
194 \r
195 static DECLFW(UNLSL12Write)\r
196 {\r
197   printf("%04X:%02X\n",A,V);\r
198         if(A==0xA123)\r
199         {\r
200                 int zzz=9;\r
201         }\r
202   switch(mode & 3) {\r
203    case 0: {\r
204      if((A>=0xB000)&&(A<=0xE003))\r
205      {\r
206        int32 ind=((((A&2)|(A>>10))>>1)+2)&7;\r
207        int32 sar=((A&1)<<2);\r
208        vrc2_chr[ind]=(vrc2_chr[ind]&(0xF0>>sar))|((V&0x0F)<<sar);\r
209        SyncCHR();\r
210      }\r
211      else\r
212        switch(A&0xF000) {\r
213         case 0x8000: vrc2_prg[0] = V; SyncPRG(); break;\r
214         case 0xA000: vrc2_prg[1] = V; SyncPRG(); break;\r
215         case 0x9000: vrc2_mirr = V; SyncMIR(); break;\r
216        }\r
217      break;\r
218    }\r
219    case 1: {\r
220      switch(A & 0xE001) {\r
221       case 0x8000: {\r
222         uint8 old_ctrl = mmc3_ctrl;\r
223         mmc3_ctrl = V;\r
224         if((old_ctrl&0x40) != (mmc3_ctrl&0x40))\r
225           SyncPRG();\r
226         if((old_ctrl&0x80) != (mmc3_ctrl&0x80))\r
227           SyncCHR();\r
228         break;\r
229       }\r
230       case 0x8001:\r
231         mmc3_regs[mmc3_ctrl & 7] = V;\r
232         if((mmc3_ctrl & 7) < 6)\r
233           SyncCHR();\r
234         else\r
235           SyncPRG();\r
236         break;\r
237       case 0xA000:\r
238         mmc3_mirr = V;\r
239         SyncMIR();\r
240         break;\r
241       case 0xC000:\r
242         IRQLatch = V;\r
243         break;\r
244       case 0xC001:\r
245         IRQReload = 1;\r
246         break;\r
247       case 0xE000:\r
248         X6502_IRQEnd(FCEU_IQEXT);\r
249         IRQa=0;\r
250         break;\r
251       case 0xE001:\r
252         IRQa=1;\r
253         break;\r
254      }\r
255      break;\r
256    }\r
257    case 2:\r
258    case 3: {\r
259      if(V & 0x80)\r
260      {\r
261        mmc1_regs[0] |= 0xc;\r
262        mmc1_buffer = mmc1_shift = 0;\r
263        SyncPRG();\r
264      }\r
265      else\r
266      {\r
267        uint8 n = (A >> 13) - 4;\r
268        mmc1_buffer |=  (V & 1) << (mmc1_shift++);\r
269        if(mmc1_shift == 5)\r
270        {\r
271          mmc1_regs[n] = mmc1_buffer;\r
272          mmc1_buffer = mmc1_shift = 0;\r
273          switch(n) {\r
274           case 0: SyncMIR();\r
275           case 2: SyncCHR(); \r
276           case 3:\r
277           case 1: SyncPRG();\r
278          }\r
279        }\r
280      }\r
281      break;\r
282    }\r
283   }\r
284 }\r
285 \r
286 static void UNLSL12HBIRQ(void)\r
287 {\r
288   if((mode & 3) == 1)\r
289   {\r
290     int32 count = IRQCount;\r
291     if(!count || IRQReload)\r
292     {\r
293       IRQCount = IRQLatch;\r
294       IRQReload = 0;\r
295     }\r
296     else\r
297       IRQCount--;\r
298     if(!IRQCount)\r
299     {\r
300       if(IRQa)\r
301         X6502_IRQBegin(FCEU_IQEXT);\r
302     }\r
303   }\r
304 }\r
305 \r
306 static void StateRestore(int version)\r
307 {\r
308   Sync();\r
309 }\r
310 \r
311 static void UNLSL12Power(void)\r
312 {\r
313   mode = 0;\r
314   vrc2_chr[0] = ~0;\r
315   vrc2_chr[1] = ~0;\r
316   vrc2_chr[2] = ~0;\r
317   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
318   vrc2_chr[4] = 4;\r
319   vrc2_chr[5] = 5;\r
320   vrc2_chr[6] = 6;\r
321   vrc2_chr[7] = 7;\r
322   vrc2_prg[0] = 0;\r
323   vrc2_prg[1] = 1;\r
324   vrc2_mirr = 0;\r
325   mmc3_regs[0] = 0;\r
326   mmc3_regs[1] = 2;\r
327   mmc3_regs[2] = 4;\r
328   mmc3_regs[3] = 5;\r
329   mmc3_regs[4] = 6;\r
330   mmc3_regs[5] = 7;\r
331   mmc3_regs[6] = ~3;\r
332   mmc3_regs[7] = ~2;\r
333   mmc3_regs[8] = ~1;\r
334   mmc3_regs[9] = ~0;\r
335   mmc3_ctrl = mmc3_mirr = IRQCount = IRQLatch = IRQa = 0;\r
336   mmc1_regs[0] = 0xc;\r
337   mmc1_regs[1] = 0;\r
338   mmc1_regs[2] = 0;\r
339   mmc1_regs[3] = 0;\r
340   mmc1_buffer = 0;\r
341   mmc1_shift = 0;\r
342   Sync();\r
343   SetReadHandler(0x8000,0xFFFF,CartBR);\r
344   SetWriteHandler(0x4100,0x7FFF,UNLSL12ModeWrite);\r
345   SetWriteHandler(0x8000,0xFFFF,UNLSL12Write);\r
346 }\r
347 \r
348 void UNLSL12_Init(CartInfo *info)\r
349 {\r
350   info->Power = UNLSL12Power;\r
351   GameHBIRQHook = UNLSL12HBIRQ;\r
352   GameStateRestore = StateRestore;\r
353   AddExState(&StateRegs, ~0, 0, 0);\r
354 }\r
355 \r
356 void Mapper116_Init(CartInfo *info)\r
357 {\r
358         UNLSL12_Init(info);\r
359 }\r