merge mappers from FCEU-mm
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 BufferShift,DRegs[4];\r
28 static uint8 Buffer;\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",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 //    FCEU_printf("REG[%d]=%02x\n",n,Buffer);\r
169     DRegs[n] = Buffer;\r
170     BufferShift = Buffer = 0;\r
171     switch(n)\r
172     {\r
173       case 0: MMC1MIRROR(); MMC1CHR(); MMC1PRG(); break;\r
174       case 1: MMC1CHR(); MMC1PRG(); break;\r
175       case 2: MMC1CHR(); break;\r
176       case 3: MMC1PRG(); break;\r
177     }\r
178   }\r
179 }\r
180 \r
181 static void MMC1_Restore(int version)\r
182 {\r
183   MMC1MIRROR();\r
184   MMC1CHR();\r
185   MMC1PRG();\r
186   lreset=0;        /* timestamp(base) is not stored in save states. */\r
187 }\r
188 \r
189 static void MMC1CMReset(void)\r
190 {\r
191   int i;\r
192 \r
193   for(i=0;i<4;i++)\r
194      DRegs[i]=0;\r
195   Buffer = BufferShift = 0;\r
196   DRegs[0]=0x1F;\r
197 \r
198   DRegs[1]=0;\r
199   DRegs[2]=0;                  // Should this be something other than 0?\r
200   DRegs[3]=0;\r
201 \r
202   MMC1MIRROR();\r
203   MMC1CHR();\r
204   MMC1PRG();\r
205 }\r
206 \r
207 static int DetectMMC1WRAMSize(uint32 crc32)\r
208 {\r
209   switch(crc32)\r
210   {\r
211     case 0xc6182024:       /* Romance of the 3 Kingdoms */\r
212     case 0x2225c20f:       /* Genghis Khan */\r
213     case 0x4642dda6:       /* Nobunaga's Ambition */\r
214     case 0x29449ba9:       /* ""        "" (J) */\r
215     case 0x2b11e0b0:       /* ""        "" (J) */\r
216     case 0xb8747abf:       /* Best Play Pro Yakyuu Special (J) */\r
217     case 0xc9556b36:       /* Final Fantasy I & II (J) [!] */\r
218          FCEU_printf(" >8KB external WRAM present.  Use UNIF if you hack the ROM image.\n");\r
219          return(16);\r
220          break;\r
221     default:return(8);\r
222   }\r
223 }\r
224 \r
225 static uint32 NWCIRQCount;\r
226 static uint8 NWCRec;\r
227 #define NWCDIP 0xE\r
228 \r
229 static void NWCIRQHook(int a)\r
230 {\r
231   if(!(NWCRec&0x10))\r
232   {\r
233     NWCIRQCount+=a;\r
234     if((NWCIRQCount|(NWCDIP<<25))>=0x3e000000)\r
235     {\r
236       NWCIRQCount=0;\r
237       X6502_IRQBegin(FCEU_IQEXT);\r
238     }\r
239   }\r
240 }\r
241 \r
242 static void NWCCHRHook(uint32 A, uint8 V)\r
243 {\r
244   if((V&0x10)) // && !(NWCRec&0x10))\r
245   {\r
246     NWCIRQCount=0;\r
247     X6502_IRQEnd(FCEU_IQEXT);\r
248   }\r
249 \r
250   NWCRec=V;\r
251   if(V&0x08)\r
252     MMC1PRG();\r
253   else\r
254     setprg32(0x8000,(V>>1)&3);\r
255 }\r
256 \r
257 static void NWCPRGHook(uint32 A, uint8 V)\r
258 {\r
259   if(NWCRec&0x8)\r
260     setprg16(A,8|(V&0x7));\r
261   else\r
262     setprg32(0x8000,(NWCRec>>1)&3);\r
263 }\r
264 \r
265 static void NWCPower(void)\r
266 {\r
267   GenMMC1Power();\r
268   setchr8r(0,0);\r
269 }\r
270 \r
271 void Mapper105_Init(CartInfo *info)\r
272 {\r
273   GenMMC1Init(info, 256, 256, 8, 0);\r
274   MMC1CHRHook4=NWCCHRHook;\r
275   MMC1PRGHook16=NWCPRGHook;\r
276   MapIRQHook=NWCIRQHook;\r
277   info->Power=NWCPower;\r
278 }\r
279 \r
280 static void GenMMC1Power(void)\r
281 {\r
282   lreset=0;\r
283   if(mmc1opts&1)\r
284   {\r
285     FCEU_CheatAddRAM(8,0x6000,WRAM);\r
286     if(mmc1opts&4)\r
287       FCEU_dwmemset(WRAM,0,8192)\r
288     else if(!(mmc1opts&2))\r
289       FCEU_dwmemset(WRAM,0,8192);\r
290   }\r
291   SetWriteHandler(0x8000,0xFFFF,MMC1_write);\r
292   SetReadHandler(0x8000,0xFFFF,CartBR);\r
293 \r
294   if(mmc1opts&1)\r
295   {\r
296     SetReadHandler(0x6000,0x7FFF,MAWRAM);\r
297     SetWriteHandler(0x6000,0x7FFF,MBWRAM);\r
298     setprg8r(0x10,0x6000,0);\r
299   }\r
300 \r
301   MMC1CMReset();\r
302 }\r
303 \r
304 static void GenMMC1Close(void)\r
305 {\r
306   if(CHRRAM)\r
307     FCEU_gfree(CHRRAM);\r
308   if(WRAM)\r
309     FCEU_gfree(WRAM);\r
310   CHRRAM=WRAM=NULL;\r
311 }\r
312 \r
313 static void GenMMC1Init(CartInfo *info, int prg, int chr, int wram, int battery)\r
314 {\r
315   is155=0;\r
316 \r
317   info->Close=GenMMC1Close;\r
318   MMC1PRGHook16=MMC1CHRHook4=0;\r
319   mmc1opts=0;\r
320   PRGmask16[0]&=(prg>>14)-1;\r
321   CHRmask4[0]&=(chr>>12)-1;\r
322   CHRmask8[0]&=(chr>>13)-1;\r
323 \r
324   if(wram)\r
325   {\r
326     WRAM=(uint8*)FCEU_gmalloc(wram*1024);\r
327     mmc1opts|=1;\r
328     if(wram>8) mmc1opts|=4;\r
329     SetupCartPRGMapping(0x10,WRAM,wram*1024,1);\r
330     AddExState(WRAM, wram*1024, 0, "WRAM");\r
331     if(battery)\r
332     {\r
333       mmc1opts|=2;\r
334       info->SaveGame[0]=WRAM+((mmc1opts&4)?8192:0);\r
335       info->SaveGameLen[0]=8192;\r
336     }\r
337   }\r
338   if(!chr)\r
339   {\r
340     CHRRAM=(uint8*)FCEU_gmalloc(8192);\r
341     SetupCartCHRMapping(0, CHRRAM, 8192, 1);\r
342     AddExState(CHRRAM, 8192, 0, "CHRR");\r
343   }\r
344   AddExState(DRegs, 4, 0, "DREG");\r
345 \r
346   info->Power=GenMMC1Power;\r
347   GameStateRestore=MMC1_Restore;\r
348   AddExState(&lreset, 8, 1, "LRST");\r
349 }\r
350 \r
351 void Mapper1_Init(CartInfo *info)\r
352 {\r
353   int ws=DetectMMC1WRAMSize(info->CRC32);\r
354   GenMMC1Init(info, 512, 256, ws, info->battery);\r
355 }\r
356 \r
357 /* Same as mapper 1, without respect for WRAM enable bit. */\r
358 void Mapper155_Init(CartInfo *info)\r
359 {\r
360   GenMMC1Init(info,512,256,8,info->battery);\r
361   is155=1;\r
362 }\r
363 \r
364 /* Same as mapper 1, with different (or without) mirroring control. */\r
365 /* Kaiser KS7058 board, KS203 custom chip */\r
366 void Mapper171_Init(CartInfo *info)\r
367 {\r
368   GenMMC1Init(info,32,32,0,0);\r
369   is171=1;\r
370 }\r
371 \r
372 void SAROM_Init(CartInfo *info)\r
373 {\r
374   GenMMC1Init(info, 128, 64, 8, info->battery);\r
375 }\r
376 \r
377 void SBROM_Init(CartInfo *info)\r
378 {\r
379   GenMMC1Init(info, 128, 64, 0, 0);\r
380 }\r
381 \r
382 void SCROM_Init(CartInfo *info)\r
383 {\r
384   GenMMC1Init(info, 128, 128, 0, 0);\r
385 }\r
386 \r
387 void SEROM_Init(CartInfo *info)\r
388 {\r
389   GenMMC1Init(info, 32, 64, 0, 0);\r
390 }\r
391 \r
392 void SGROM_Init(CartInfo *info)\r
393 {\r
394   GenMMC1Init(info, 256, 0, 0, 0);\r
395 }\r
396 \r
397 void SKROM_Init(CartInfo *info)\r
398 {\r
399   GenMMC1Init(info, 256, 64, 8, info->battery);\r
400 }\r
401 \r
402 void SLROM_Init(CartInfo *info)\r
403 {\r
404   GenMMC1Init(info, 256, 128, 0, 0);\r
405 }\r
406 \r
407 void SL1ROM_Init(CartInfo *info)\r
408 {\r
409   GenMMC1Init(info, 128, 128, 0, 0);\r
410 }\r
411 \r
412 /* Begin unknown - may be wrong - perhaps they use different MMC1s from the\r
413    similarly functioning boards?\r
414 */\r
415 \r
416 void SL2ROM_Init(CartInfo *info)\r
417 {\r
418   GenMMC1Init(info, 256, 256, 0, 0);\r
419 }\r
420 \r
421 void SFROM_Init(CartInfo *info)\r
422 {\r
423   GenMMC1Init(info, 256, 256, 0, 0);\r
424 }\r
425 \r
426 void SHROM_Init(CartInfo *info)\r
427 {\r
428   GenMMC1Init(info, 256, 256, 0, 0);\r
429 }\r
430 \r
431 /* End unknown  */\r
432 /*              */\r
433 /*              */\r
434 \r
435 void SNROM_Init(CartInfo *info)\r
436 {\r
437   GenMMC1Init(info, 256, 0, 8, info->battery);\r
438 }\r
439 \r
440 void SOROM_Init(CartInfo *info)\r
441 {\r
442   GenMMC1Init(info, 256, 0, 16, info->battery);\r
443 }\r
444 \r
445 \r