merge mapper code from FCEUX
[fceu.git] / boards / mmc1.c
CommitLineData
386f5371 1/* FCE Ultra - NES/Famicom Emulator\r
2 *\r
3 * Copyright notice for this file:\r
4 * Copyright (C) 1998 BERO\r
5 * Copyright (C) 2002 Xodnizel\r
6 *\r
7 * This program is free software; you can redistribute it and/or modify\r
8 * it under the terms of the GNU General Public License as published by\r
9 * the Free Software Foundation; either version 2 of the License, or\r
10 * (at your option) any later version.\r
11 *\r
12 * This program is distributed in the hope that it will be useful,\r
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
15 * GNU General Public License for more details.\r
16 *\r
17 * You should have received a copy of the GNU General Public License\r
18 * along with this program; if not, write to the Free Software\r
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\r
20 */\r
21\r
22#include "mapinc.h"\r
23\r
24static void GenMMC1Power(void);\r
25static void GenMMC1Init(CartInfo *info, int prg, int chr, int wram, int battery);\r
26\r
27static uint8 DRegs[4];\r
28static uint8 Buffer,BufferShift;\r
29\r
30static int mmc1opts;\r
31\r
32static void (*MMC1CHRHook4)(uint32 A, uint8 V);\r
33static void (*MMC1PRGHook16)(uint32 A, uint8 V);\r
34\r
35static uint8 *WRAM=NULL;\r
36static uint8 *CHRRAM=NULL;\r
37static int is155, is171;\r
38\r
39static DECLFW(MBWRAM)\r
40{\r
41 if(!(DRegs[3]&0x10)||is155)\r
42 Page[A>>11][A]=V; // WRAM is enabled.\r
43}\r
44\r
45static DECLFR(MAWRAM)\r
46{\r
47 if((DRegs[3]&0x10)&&!is155)\r
48 return X.DB; // WRAM is disabled\r
49 return(Page[A>>11][A]);\r
50}\r
51\r
52static void MMC1CHR(void)\r
53{\r
54 if(mmc1opts&4)\r
55 {\r
56 if(DRegs[0]&0x10)\r
57 setprg8r(0x10,0x6000,(DRegs[1]>>4)&1);\r
58 else\r
59 setprg8r(0x10,0x6000,(DRegs[1]>>3)&1);\r
60 }\r
61\r
62 if(MMC1CHRHook4)\r
63 {\r
64 if(DRegs[0]&0x10)\r
65 {\r
66 MMC1CHRHook4(0x0000,DRegs[1]);\r
67 MMC1CHRHook4(0x1000,DRegs[2]);\r
68 }\r
69 else\r
70 {\r
71 MMC1CHRHook4(0x0000,(DRegs[1]&0xFE));\r
72 MMC1CHRHook4(0x1000,DRegs[1]|1);\r
73 }\r
74 }\r
75 else\r
76 {\r
77 if(DRegs[0]&0x10)\r
78 {\r
79 setchr4(0x0000,DRegs[1]);\r
80 setchr4(0x1000,DRegs[2]);\r
81 }\r
82 else\r
83 setchr8(DRegs[1]>>1);\r
84 }\r
85}\r
86\r
87static void MMC1PRG(void)\r
88{\r
89 uint8 offs=DRegs[1]&0x10;\r
90 if(MMC1PRGHook16)\r
91 {\r
92 switch(DRegs[0]&0xC)\r
93 {\r
94 case 0xC: MMC1PRGHook16(0x8000,(DRegs[3]+offs));\r
95 MMC1PRGHook16(0xC000,0xF+offs);\r
96 break;\r
97 case 0x8: MMC1PRGHook16(0xC000,(DRegs[3]+offs));\r
98 MMC1PRGHook16(0x8000,offs);\r
99 break;\r
100 case 0x0:\r
101 case 0x4:\r
102 MMC1PRGHook16(0x8000,((DRegs[3]&~1)+offs));\r
103 MMC1PRGHook16(0xc000,((DRegs[3]&~1)+offs+1));\r
104 break;\r
105 }\r
106 }\r
107 else\r
108 {\r
109 switch(DRegs[0]&0xC)\r
110 {\r
111 case 0xC: setprg16(0x8000,(DRegs[3]+offs));\r
112 setprg16(0xC000,0xF+offs);\r
113 break;\r
114 case 0x8: setprg16(0xC000,(DRegs[3]+offs));\r
115 setprg16(0x8000,offs);\r
116 break;\r
117 case 0x0:\r
118 case 0x4:\r
119 setprg16(0x8000,((DRegs[3]&~1)+offs));\r
120 setprg16(0xc000,((DRegs[3]&~1)+offs+1));\r
121 break;\r
122 }\r
123}\r
124}\r
125\r
126static void MMC1MIRROR(void)\r
127{\r
128 if(!is171) \r
129 switch(DRegs[0]&3)\r
130 {\r
131 case 2: setmirror(MI_V); break;\r
132 case 3: setmirror(MI_H); break;\r
133 case 0: setmirror(MI_0); break;\r
134 case 1: setmirror(MI_1); break;\r
135 }\r
136}\r
137\r
138static uint64 lreset;\r
139static DECLFW(MMC1_write)\r
140{\r
141 int n=(A>>13)-4;\r
142 //FCEU_DispMessage("%016x",0,timestampbase+timestamp);\r
143// FCEU_printf("$%04x:$%02x, $%04x\n",A,V,X.PC);\r
144 //DumpMem("out",0xe000,0xffff);\r
145\r
146 /* The MMC1 is busy so ignore the write. */\r
147 /* As of version FCE Ultra 0.81, the timestamp is only\r
148 increased before each instruction is executed(in other words\r
149 precision isn't that great), but this should still work to\r
150 deal with 2 writes in a row from a single RMW instruction.\r
151 */\r
152 if((timestampbase+timestamp)<(lreset+2))\r
153 return;\r
154// FCEU_printf("Write %04x:%02x\n",A,V);\r
155 if(V&0x80)\r
156 {\r
157 DRegs[0]|=0xC;\r
158 BufferShift=Buffer=0;\r
159 MMC1PRG();\r
160 lreset=timestampbase+timestamp;\r
161 return;\r
162 }\r
163\r
164 Buffer|=(V&1)<<(BufferShift++);\r
165\r
166 if(BufferShift==5)\r
167 {\r
168 DRegs[n] = Buffer;\r
169 BufferShift = Buffer = 0;\r
170 switch(n)\r
171 {\r
172 case 0: MMC1MIRROR(); MMC1CHR(); MMC1PRG(); break;\r
173 case 1: MMC1CHR(); MMC1PRG(); break;\r
174 case 2: MMC1CHR(); break;\r
175 case 3: MMC1PRG(); break;\r
176 }\r
177 }\r
178}\r
179\r
180static void MMC1_Restore(int version)\r
181{\r
182 MMC1MIRROR();\r
183 MMC1CHR();\r
184 MMC1PRG();\r
185 lreset=0; /* timestamp(base) is not stored in save states. */\r
186}\r
187\r
188static void MMC1CMReset(void)\r
189{\r
190 int i;\r
191\r
192 for(i=0;i<4;i++)\r
193 DRegs[i]=0;\r
194 Buffer = BufferShift = 0;\r
195 DRegs[0]=0x1F;\r
196\r
197 DRegs[1]=0;\r
198 DRegs[2]=0; // Should this be something other than 0?\r
199 DRegs[3]=0;\r
200\r
201 MMC1MIRROR();\r
202 MMC1CHR();\r
203 MMC1PRG();\r
204}\r
205\r
206static int DetectMMC1WRAMSize(uint32 crc32)\r
207{\r
208 switch(crc32)\r
209 {\r
210 case 0xc6182024: /* Romance of the 3 Kingdoms */\r
211 case 0x2225c20f: /* Genghis Khan */\r
212 case 0x4642dda6: /* Nobunaga's Ambition */\r
213 case 0x29449ba9: /* "" "" (J) */\r
214 case 0x2b11e0b0: /* "" "" (J) */\r
215 case 0xb8747abf: /* Best Play Pro Yakyuu Special (J) */\r
216 case 0xc9556b36: /* Final Fantasy I & II (J) [!] */\r
217 FCEU_printf(" >8KB external WRAM present. Use UNIF if you hack the ROM image.\n");\r
218 return(16);\r
219 break;\r
220 default:return(8);\r
221 }\r
222}\r
223\r
224static uint32 NWCIRQCount;\r
225static uint8 NWCRec;\r
226#define NWCDIP 0xE\r
227\r
228static void NWCIRQHook(int a)\r
229{\r
230 if(!(NWCRec&0x10))\r
231 {\r
232 NWCIRQCount+=a;\r
233 if((NWCIRQCount|(NWCDIP<<25))>=0x3e000000)\r
234 {\r
235 NWCIRQCount=0;\r
236 X6502_IRQBegin(FCEU_IQEXT);\r
237 }\r
238 }\r
239}\r
240\r
241static void NWCCHRHook(uint32 A, uint8 V)\r
242{\r
243 if((V&0x10)) // && !(NWCRec&0x10))\r
244 {\r
245 NWCIRQCount=0;\r
246 X6502_IRQEnd(FCEU_IQEXT);\r
247 }\r
248\r
249 NWCRec=V;\r
250 if(V&0x08)\r
251 MMC1PRG();\r
252 else\r
253 setprg32(0x8000,(V>>1)&3);\r
254}\r
255\r
256static void NWCPRGHook(uint32 A, uint8 V)\r
257{\r
258 if(NWCRec&0x8)\r
259 setprg16(A,8|(V&0x7));\r
260 else\r
261 setprg32(0x8000,(NWCRec>>1)&3);\r
262}\r
263\r
264static void NWCPower(void)\r
265{\r
266 GenMMC1Power();\r
267 setchr8r(0,0);\r
268}\r
269\r
270void Mapper105_Init(CartInfo *info)\r
271{\r
272 GenMMC1Init(info, 256, 256, 8, 0);\r
273 MMC1CHRHook4=NWCCHRHook;\r
274 MMC1PRGHook16=NWCPRGHook;\r
275 MapIRQHook=NWCIRQHook;\r
276 info->Power=NWCPower;\r
277}\r
278\r
279static void GenMMC1Power(void)\r
280{\r
281 lreset=0;\r
282 if(mmc1opts&1)\r
283 {\r
284 FCEU_CheatAddRAM(8,0x6000,WRAM);\r
285 if(mmc1opts&4)\r
286 FCEU_dwmemset(WRAM,0,8192)\r
287 else if(!(mmc1opts&2))\r
288 FCEU_dwmemset(WRAM,0,8192);\r
289 }\r
290 SetWriteHandler(0x8000,0xFFFF,MMC1_write);\r
291 SetReadHandler(0x8000,0xFFFF,CartBR);\r
292\r
293 if(mmc1opts&1)\r
294 {\r
295 SetReadHandler(0x6000,0x7FFF,MAWRAM);\r
296 SetWriteHandler(0x6000,0x7FFF,MBWRAM);\r
297 setprg8r(0x10,0x6000,0);\r
298 }\r
299\r
300 MMC1CMReset();\r
301}\r
302\r
303static void GenMMC1Close(void)\r
304{\r
305 if(CHRRAM)\r
306 FCEU_gfree(CHRRAM);\r
307 if(WRAM)\r
308 FCEU_gfree(WRAM);\r
309 CHRRAM=WRAM=NULL;\r
310}\r
311\r
312static void GenMMC1Init(CartInfo *info, int prg, int chr, int wram, int battery)\r
313{\r
314 is155=0;\r
315\r
316 info->Close=GenMMC1Close;\r
317 MMC1PRGHook16=MMC1CHRHook4=0;\r
318 mmc1opts=0;\r
319 PRGmask16[0]&=(prg>>14)-1;\r
320 CHRmask4[0]&=(chr>>12)-1;\r
321 CHRmask8[0]&=(chr>>13)-1;\r
322\r
323 if(wram)\r
324 {\r
325 WRAM=(uint8*)FCEU_gmalloc(wram*1024);\r
326 //mbg 17-jun-08 - this shouldve been cleared to re-initialize save ram\r
327 //ch4 10-dec-08 - nope, this souldn't\r
328 //mbg 29-mar-09 - no time to debate this, we need to keep from breaking some old stuff.\r
329 //we really need to make up a policy for how compatibility and accuracy can be resolved.\r
330 memset(WRAM,0,wram*1024);\r
331 mmc1opts|=1;\r
332 if(wram>8) mmc1opts|=4;\r
333 SetupCartPRGMapping(0x10,WRAM,wram*1024,1);\r
334 AddExState(WRAM, wram*1024, 0, "WRAM");\r
335 if(battery)\r
336 {\r
337 mmc1opts|=2;\r
338 info->SaveGame[0]=WRAM+((mmc1opts&4)?8192:0);\r
339 info->SaveGameLen[0]=8192;\r
340 }\r
341 }\r
342 if(!chr)\r
343 {\r
344 CHRRAM=(uint8*)FCEU_gmalloc(8192);\r
345 SetupCartCHRMapping(0, CHRRAM, 8192, 1);\r
346 AddExState(CHRRAM, 8192, 0, "CHRR");\r
347 }\r
348 AddExState(DRegs, 4, 0, "DREG");\r
349\r
350 info->Power=GenMMC1Power;\r
351 GameStateRestore=MMC1_Restore;\r
352 AddExState(&lreset, 8, 1, "LRST");\r
353 AddExState(&Buffer, 1, 1, "BFFR");\r
354 AddExState(&BufferShift, 1, 1, "BFRS");\r
355}\r
356\r
357void Mapper1_Init(CartInfo *info)\r
358{\r
359 int ws=DetectMMC1WRAMSize(info->CRC32);\r
360 GenMMC1Init(info, 512, 256, ws, info->battery);\r
361}\r
362\r
363/* Same as mapper 1, without respect for WRAM enable bit. */\r
364void Mapper155_Init(CartInfo *info)\r
365{\r
366 GenMMC1Init(info,512,256,8,info->battery);\r
367 is155=1;\r
368}\r
369\r
370/* Same as mapper 1, with different (or without) mirroring control. */\r
371/* Kaiser KS7058 board, KS203 custom chip */\r
372void Mapper171_Init(CartInfo *info)\r
373{\r
374 GenMMC1Init(info,32,32,0,0);\r
375 is171=1;\r
376}\r
377\r
378void SAROM_Init(CartInfo *info)\r
379{\r
380 GenMMC1Init(info, 128, 64, 8, info->battery);\r
381}\r
382\r
383void SBROM_Init(CartInfo *info)\r
384{\r
385 GenMMC1Init(info, 128, 64, 0, 0);\r
386}\r
387\r
388void SCROM_Init(CartInfo *info)\r
389{\r
390 GenMMC1Init(info, 128, 128, 0, 0);\r
391}\r
392\r
393void SEROM_Init(CartInfo *info)\r
394{\r
395 GenMMC1Init(info, 32, 64, 0, 0);\r
396}\r
397\r
398void SGROM_Init(CartInfo *info)\r
399{\r
400 GenMMC1Init(info, 256, 0, 0, 0);\r
401}\r
402\r
403void SKROM_Init(CartInfo *info)\r
404{\r
405 GenMMC1Init(info, 256, 64, 8, info->battery);\r
406}\r
407\r
408void SLROM_Init(CartInfo *info)\r
409{\r
410 GenMMC1Init(info, 256, 128, 0, 0);\r
411}\r
412\r
413void SL1ROM_Init(CartInfo *info)\r
414{\r
415 GenMMC1Init(info, 128, 128, 0, 0);\r
416}\r
417\r
418/* Begin unknown - may be wrong - perhaps they use different MMC1s from the\r
419 similarly functioning boards?\r
420*/\r
421\r
422void SL2ROM_Init(CartInfo *info)\r
423{\r
424 GenMMC1Init(info, 256, 256, 0, 0);\r
425}\r
426\r
427void SFROM_Init(CartInfo *info)\r
428{\r
429 GenMMC1Init(info, 256, 256, 0, 0);\r
430}\r
431\r
432void SHROM_Init(CartInfo *info)\r
433{\r
434 GenMMC1Init(info, 256, 256, 0, 0);\r
435}\r
436\r
437/* End unknown */\r
438/* */\r
439/* */\r
440\r
441void SNROM_Init(CartInfo *info)\r
442{\r
443 GenMMC1Init(info, 256, 0, 8, info->battery);\r
444}\r
445\r
446void SOROM_Init(CartInfo *info)\r
447{\r
448 GenMMC1Init(info, 256, 0, 16, info->battery);\r
449}\r
450\r
451\r