merge mapper code from FCEUX
[fceu.git] / boards / mmc1.c
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
24 static void GenMMC1Power(void);\r
25 static void GenMMC1Init(CartInfo *info, int prg, int chr, int wram, int battery);\r
26 \r
27 static uint8 DRegs[4];\r
28 static uint8 Buffer,BufferShift;\r
29 \r
30 static int mmc1opts;\r
31 \r
32 static void (*MMC1CHRHook4)(uint32 A, uint8 V);\r
33 static void (*MMC1PRGHook16)(uint32 A, uint8 V);\r
34 \r
35 static uint8 *WRAM=NULL;\r
36 static uint8 *CHRRAM=NULL;\r
37 static int is155, is171;\r
38 \r
39 static 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
45 static 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
52 static 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
87 static 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
126 static 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
138 static uint64 lreset;\r
139 static 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
180 static 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
188 static 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
206 static 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
224 static uint32 NWCIRQCount;\r
225 static uint8 NWCRec;\r
226 #define NWCDIP 0xE\r
227 \r
228 static 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
241 static 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
256 static 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
264 static void NWCPower(void)\r
265 {\r
266   GenMMC1Power();\r
267   setchr8r(0,0);\r
268 }\r
269 \r
270 void 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
279 static 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
303 static 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
312 static 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
357 void 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
364 void 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
372 void Mapper171_Init(CartInfo *info)\r
373 {\r
374   GenMMC1Init(info,32,32,0,0);\r
375   is171=1;\r
376 }\r
377 \r
378 void SAROM_Init(CartInfo *info)\r
379 {\r
380   GenMMC1Init(info, 128, 64, 8, info->battery);\r
381 }\r
382 \r
383 void SBROM_Init(CartInfo *info)\r
384 {\r
385   GenMMC1Init(info, 128, 64, 0, 0);\r
386 }\r
387 \r
388 void SCROM_Init(CartInfo *info)\r
389 {\r
390   GenMMC1Init(info, 128, 128, 0, 0);\r
391 }\r
392 \r
393 void SEROM_Init(CartInfo *info)\r
394 {\r
395   GenMMC1Init(info, 32, 64, 0, 0);\r
396 }\r
397 \r
398 void SGROM_Init(CartInfo *info)\r
399 {\r
400   GenMMC1Init(info, 256, 0, 0, 0);\r
401 }\r
402 \r
403 void SKROM_Init(CartInfo *info)\r
404 {\r
405   GenMMC1Init(info, 256, 64, 8, info->battery);\r
406 }\r
407 \r
408 void SLROM_Init(CartInfo *info)\r
409 {\r
410   GenMMC1Init(info, 256, 128, 0, 0);\r
411 }\r
412 \r
413 void 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
422 void SL2ROM_Init(CartInfo *info)\r
423 {\r
424   GenMMC1Init(info, 256, 256, 0, 0);\r
425 }\r
426 \r
427 void SFROM_Init(CartInfo *info)\r
428 {\r
429   GenMMC1Init(info, 256, 256, 0, 0);\r
430 }\r
431 \r
432 void 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
441 void SNROM_Init(CartInfo *info)\r
442 {\r
443   GenMMC1Init(info, 256, 0, 8, info->battery);\r
444 }\r
445 \r
446 void SOROM_Init(CartInfo *info)\r
447 {\r
448   GenMMC1Init(info, 256, 0, 16, info->battery);\r
449 }\r
450 \r
451 \r