mappers updated to 0.98.16
[fceu.git] / boards / mmc1.c
CommitLineData
e2d0dd92 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
24static void GenMMC1Power(void);
25static void GenMMC1Init(CartInfo *info, int prg, int chr, int wram, int battery);
26
27static uint8 DRegs[4];
28static uint8 Buffer,BufferShift;
29
30static int mmc1opts;
31
32static void (*MMC1CHRHook4)(uint32 A, uint8 V);
33static void (*MMC1PRGHook16)(uint32 A, uint8 V);
34
35static uint8 *WRAM=NULL;
36static uint8 *CHRRAM=NULL;
37static int is155;
38
39static DECLFW(MBWRAM)
40{
41 if(!(DRegs[3]&0x10)||is155)
42 Page[A>>11][A]=V; // WRAM is enabled.
43}
44
45static DECLFR(MAWRAM)
46{
47 if((DRegs[3]&0x10)&&!is155)
48 return X.DB; // WRAM is disabled
49 return(Page[A>>11][A]);
50}
51
52static 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
86static 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
122static 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
133static uint64 lreset;
134static 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
170static 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
178static 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
194static 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
212static uint32 NWCIRQCount;
213static uint8 NWCRec;
214#define NWCDIP 0xE
215
216static 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
229static 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
243static 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
251static void NWCPower(void)
252{
253 GenMMC1Power();
254 setchr8r(0,0);
255}
256
257void 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
266static 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
290static void GenMMC1Close(void)
291{
292 if(CHRRAM)
293 FCEU_gfree(CHRRAM);
294 if(WRAM)
295 FCEU_gfree(WRAM);
296 CHRRAM=WRAM=NULL;
297}
298
299static 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
337void 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. */
344void Mapper155_Init(CartInfo *info)
345{
346 GenMMC1Init(info,512,256,8,info->battery);
347 is155=1;
348}
349
350void SAROM_Init(CartInfo *info)
351{
352 GenMMC1Init(info, 128, 64, 8, info->battery);
353}
354
355void SBROM_Init(CartInfo *info)
356{
357 GenMMC1Init(info, 128, 64, 0, 0);
358}
359
360void SCROM_Init(CartInfo *info)
361{
362 GenMMC1Init(info, 128, 128, 0, 0);
363}
364
365void SEROM_Init(CartInfo *info)
366{
367 GenMMC1Init(info, 32, 64, 0, 0);
368}
369
370void SGROM_Init(CartInfo *info)
371{
372 GenMMC1Init(info, 256, 0, 0, 0);
373}
374
375void SKROM_Init(CartInfo *info)
376{
377 GenMMC1Init(info, 256, 64, 8, info->battery);
378}
379
380void SLROM_Init(CartInfo *info)
381{
382 GenMMC1Init(info, 256, 128, 0, 0);
383}
384
385void 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
394void SL2ROM_Init(CartInfo *info)
395{
396 GenMMC1Init(info, 256, 256, 0, 0);
397}
398
399void SFROM_Init(CartInfo *info)
400{
401 GenMMC1Init(info, 256, 256, 0, 0);
402}
403
404void SHROM_Init(CartInfo *info)
405{
406 GenMMC1Init(info, 256, 256, 0, 0);
407}
408
409/* End unknown */
410/* */
411/* */
412
413void SNROM_Init(CartInfo *info)
414{
415 GenMMC1Init(info, 256, 0, 8, info->battery);
416}
417
418void SOROM_Init(CartInfo *info)
419{
420 GenMMC1Init(info, 256, 0, 16, info->battery);
421}
422
423