merge mapper code from FCEUX
[fceu.git] / boards / 116.c
CommitLineData
386f5371 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
39static uint8 mode;\r
40static uint8 vrc2_chr[8], vrc2_prg[2], vrc2_mirr;\r
41static uint8 mmc3_regs[10], mmc3_ctrl, mmc3_mirr;\r
42extern uint8 IRQCount,IRQLatch,IRQa;\r
43extern uint8 IRQReload;\r
44static uint8 mmc1_regs[4], mmc1_buffer, mmc1_shift;\r
45\r
46static 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
65static 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
105static 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
144static 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
168static void Sync(void)\r
169{\r
170 SyncPRG();\r
171 SyncCHR();\r
172 SyncMIR();\r
173}\r
174\r
175static 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
195static 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
286static 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
306static void StateRestore(int version)\r
307{\r
308 Sync();\r
309}\r
310\r
311static 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
348void 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
356void Mapper116_Init(CartInfo *info)\r
357{\r
358 UNLSL12_Init(info);\r
359}\r