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