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 |
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 |