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 |
43725da7 |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r |
19 | *\r |
386f5371 |
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 |
43725da7 |
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 |
386f5371 |
31 | * Samurai Spirits (Full version) (Huang-1, GAL dip: unk conf. GS-2A/GS-4A maskroms)\r |
43725da7 |
32 | * Contra Fighter (603-5052 PCB, C5052-3, GAL dip: unk conf. SC603-A/SCB603-B maskroms)\r |
386f5371 |
33 | *\r |
34 | */\r |
35 | \r |
36 | #include "mapinc.h"\r |
386f5371 |
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 |
43725da7 |
41 | static uint8 IRQCount,IRQLatch,IRQa;\r |
42 | static uint8 IRQReload;\r |
386f5371 |
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 |
43725da7 |
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 |
386f5371 |
54 | {&IRQReload, 1, "IRQR"},\r |
55 | {&IRQCount, 1, "IRQC"},\r |
56 | {&IRQLatch, 1, "IRQL"},\r |
57 | {&IRQa, 1, "IRQA"},\r |
43725da7 |
58 | {mmc1_regs, 4, "M1RG"},\r |
59 | {&mmc1_buffer, 1, "M1BF"},\r |
60 | {&mmc1_shift, 1, "M1MR"},\r |
386f5371 |
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 |
43725da7 |
176 | // FCEU_printf("%04X:%02X\n",A,V);\r |
386f5371 |
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 |
43725da7 |
196 | // FCEU_printf("%04X:%02X\n",A,V);\r |
386f5371 |
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 |
43725da7 |
270 | case 2: SyncCHR();\r |
386f5371 |
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 |