more menu work, scalers, sound
[fceu.git] / boards / mmc1.c
CommitLineData
d97315ac 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
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;\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 if(MMC1CHRHook4)\r
62 {\r
63 if(DRegs[0]&0x10)\r
64 {\r
65 MMC1CHRHook4(0x0000,DRegs[1]);\r
66 MMC1CHRHook4(0x1000,DRegs[2]);\r
67 }\r
68 else\r
69 {\r
70 MMC1CHRHook4(0x0000,(DRegs[1]&0xFE));\r
71 MMC1CHRHook4(0x1000,DRegs[1]|1);\r
72 }\r
73 }\r
74 else\r
75 {\r
76 if(DRegs[0]&0x10)\r
77 {\r
78 setchr4(0x0000,DRegs[1]);\r
79 setchr4(0x1000,DRegs[2]);\r
80 }\r
81 else\r
82 setchr8(DRegs[1]>>1);\r
83 }\r
84}\r
85\r
86static void MMC1PRG(void)\r
87{\r
88 uint8 offs=DRegs[1]&0x10;\r
89\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 switch(DRegs[0]&0xC)\r
108 {\r
109 case 0xC: setprg16(0x8000,(DRegs[3]+offs));\r
110 setprg16(0xC000,0xF+offs);\r
111 break;\r
112 case 0x8: setprg16(0xC000,(DRegs[3]+offs));\r
113 setprg16(0x8000,offs);\r
114 break;\r
115 case 0x0:\r
116 case 0x4:\r
117 setprg16(0x8000,((DRegs[3]&~1)+offs));\r
118 setprg16(0xc000,((DRegs[3]&~1)+offs+1));\r
119 break;\r
120 }\r
121}\r
122\r
123static void MMC1MIRROR(void)\r
124{\r
125 switch(DRegs[0]&3)\r
126 {\r
127 case 2: setmirror(MI_V); break;\r
128 case 3: setmirror(MI_H); break;\r
129 case 0: setmirror(MI_0); break;\r
130 case 1: setmirror(MI_1); break;\r
131 }\r
132}\r
133\r
134\r
135static uint64 lreset;\r
136static DECLFW(MMC1_write)\r
137{\r
138 int n=(A>>13)-4;\r
139 //FCEU_DispMessage("%016x",timestampbase+timestamp);\r
140 //printf("$%04x:$%02x, $%04x\n",A,V,X.PC);\r
141 //DumpMem("out",0xe000,0xffff);\r
142\r
143 /* The MMC1 is busy so ignore the write. */\r
144 /* As of version FCE Ultra 0.81, the timestamp is only\r
145 increased before each instruction is executed(in other words\r
146 precision isn't that great), but this should still work to\r
147 deal with 2 writes in a row from a single RMW instruction. */\r
148 if((timestampbase+timestamp)<(lreset+2)) return;\r
149\r
150 if(V&0x80)\r
151 {\r
152 DRegs[0]|=0xC;\r
153 BufferShift=Buffer=0;\r
154 MMC1PRG();\r
155 lreset=timestampbase+timestamp;\r
156 return;\r
157 }\r
158 Buffer|=(V&1)<<(BufferShift++);\r
159 if(BufferShift==5)\r
160 {\r
161 DRegs[n] = Buffer;\r
162 BufferShift = Buffer = 0;\r
163 switch(n)\r
164 {\r
165 case 0: MMC1MIRROR(); MMC1CHR(); MMC1PRG(); break;\r
166 case 1: MMC1CHR(); MMC1PRG(); break;\r
167 case 2: MMC1CHR(); break;\r
168 case 3: MMC1PRG(); break;\r
169 }\r
170 }\r
171}\r
172\r
173static void MMC1_Restore(int version)\r
174{\r
175 MMC1MIRROR();\r
176 MMC1CHR();\r
177 MMC1PRG();\r
178 //lreset=0; /* timestamp(base) is not stored in save states. */ // it is now!\r
179}\r
180\r
181static void MMC1CMReset(void)\r
182{\r
183 int i;\r
184 for(i=0;i<4;i++)\r
185 DRegs[i]=0;\r
186 Buffer = BufferShift = 0;\r
187 DRegs[0]=0x1F;\r
188 DRegs[1]=0;\r
189 DRegs[2]=0; // Should this be something other than 0?\r
190 DRegs[3]=0;\r
191\r
192 MMC1MIRROR();\r
193 MMC1CHR();\r
194 MMC1PRG();\r
195}\r
196\r
197static int DetectMMC1WRAMSize(uint32 crc32)\r
198{\r
199 switch(crc32)\r
200 {\r
201 case 0xc6182024: /* Romance of the 3 Kingdoms */\r
202 case 0x2225c20f: /* Genghis Khan */\r
203 case 0x4642dda6: /* Nobunaga's Ambition */\r
204 case 0x29449ba9: /* "" "" (J) */\r
205 case 0x2b11e0b0: /* "" "" (J) */\r
206 FCEU_printf(" >8KB external WRAM present. Use UNIF if you hack the ROM image.\n");\r
207 return(16);\r
208 break;\r
209 default:return(8);\r
210 }\r
211}\r
212\r
213static uint32 NWCIRQCount;\r
214static uint8 NWCRec;\r
215#define NWCDIP 0xE\r
216\r
217static void FP_FASTAPASS(1) NWCIRQHook(int a)\r
218{\r
219 if(!(NWCRec&0x10))\r
220 {\r
221 NWCIRQCount+=a;\r
222 if((NWCIRQCount|(NWCDIP<<25))>=0x3e000000)\r
223 {\r
224 NWCIRQCount=0;\r
225 X6502_IRQBegin(FCEU_IQEXT);\r
226 }\r
227 }\r
228}\r
229\r
230static void NWCCHRHook(uint32 A, uint8 V)\r
231{\r
232 if((V&0x10)) // && !(NWCRec&0x10))\r
233 {\r
234 NWCIRQCount=0;\r
235 X6502_IRQEnd(FCEU_IQEXT);\r
236 }\r
237 NWCRec=V;\r
238 if(V&0x08)\r
239 MMC1PRG();\r
240 else\r
241 setprg32(0x8000,(V>>1)&3);\r
242}\r
243\r
244static void NWCPRGHook(uint32 A, uint8 V)\r
245{\r
246 if(NWCRec&0x8)\r
247 setprg16(A,8|(V&0x7));\r
248 else\r
249 setprg32(0x8000,(NWCRec>>1)&3);\r
250}\r
251\r
252static void NWCPower(void)\r
253{\r
254 GenMMC1Power();\r
255 setchr8r(0,0);\r
256}\r
257\r
258void Mapper105_Init(CartInfo *info)\r
259{\r
260 GenMMC1Init(info, 256, 256, 8, 0);\r
261 MMC1CHRHook4=NWCCHRHook;\r
262 MMC1PRGHook16=NWCPRGHook;\r
263 MapIRQHook=NWCIRQHook;\r
264 info->Power=NWCPower;\r
265}\r
266\r
267static void GenMMC1Power(void)\r
268{\r
269 lreset=0;\r
270 if(mmc1opts&1)\r
271 {\r
272 FCEU_CheatAddRAM(8,0x6000,WRAM);\r
273 if(mmc1opts&4)\r
274 FCEU_dwmemset(WRAM,0,8192)\r
275 else if(!(mmc1opts&2))\r
276 FCEU_dwmemset(WRAM,0,8192);\r
277 }\r
278 SetWriteHandler(0x8000,0xFFFF,MMC1_write);\r
279 SetReadHandler(0x8000,0xFFFF,CartBR);\r
280\r
281 if(mmc1opts&1)\r
282 {\r
283 SetReadHandler(0x6000,0x7FFF,MAWRAM);\r
284 SetWriteHandler(0x6000,0x7FFF,MBWRAM);\r
285 setprg8r(0x10,0x6000,0);\r
286 }\r
287\r
288 MMC1CMReset();\r
289}\r
290\r
291static void GenMMC1Close(void)\r
292{\r
293 if(CHRRAM)\r
294 FCEU_gfree(CHRRAM);\r
295 if(WRAM)\r
296 FCEU_gfree(WRAM);\r
297 CHRRAM=WRAM=NULL;\r
298}\r
299\r
300static void GenMMC1Init(CartInfo *info, int prg, int chr, int wram, int battery)\r
301{\r
302 is155=0;\r
303\r
304 info->Close=GenMMC1Close;\r
305 MMC1PRGHook16=MMC1CHRHook4=0;\r
306 mmc1opts=0;\r
307 PRGmask16[0]&=(prg>>14)-1;\r
308 CHRmask4[0]&=(chr>>12)-1;\r
309 CHRmask8[0]&=(chr>>13)-1;\r
310\r
311 if(wram)\r
312 {\r
313 WRAM=(uint8*)FCEU_gmalloc(wram*1024);\r
314 mmc1opts|=1;\r
315 if(wram>8) mmc1opts|=4;\r
316 SetupCartPRGMapping(0x10,WRAM,wram*1024,1);\r
317 AddExState(WRAM, wram*1024, 0, "WRAM");\r
318 if(battery)\r
319 {\r
320 mmc1opts|=2;\r
321 info->SaveGame[0]=WRAM+((mmc1opts&4)?8192:0);\r
322 info->SaveGameLen[0]=8192;\r
323 }\r
324 }\r
325 if(!chr)\r
326 {\r
327 CHRRAM=(uint8*)FCEU_gmalloc(8192);\r
328 SetupCartCHRMapping(0, CHRRAM, 8192, 1);\r
329 AddExState(CHRRAM, 8192, 0, "CHRR");\r
330 }\r
331 AddExState(DRegs, 4, 0, "DREG");\r
332\r
333 info->Power=GenMMC1Power;\r
334 GameStateRestore=MMC1_Restore;\r
335 AddExState(&lreset, 8, 1, "LRST");\r
336 AddExState(&Buffer, 1, 1, "BFFR");\r
337 AddExState(&BufferShift, 1, 1, "BFRS");\r
338}\r
339\r
340void Mapper1_Init(CartInfo *info)\r
341{\r
342 int ws=DetectMMC1WRAMSize(info->CRC32);\r
343 GenMMC1Init(info, 512, 256, ws, info->battery);\r
344}\r
345\r
346/* Same as mapper 1, without respect for WRAM enable bit. */\r
347void Mapper155_Init(CartInfo *info)\r
348{\r
349 GenMMC1Init(info,512,256,8,info->battery);\r
350 is155=1;\r
351}\r
352\r
353void SAROM_Init(CartInfo *info)\r
354{\r
355 GenMMC1Init(info, 128, 64, 8, info->battery);\r
356}\r
357\r
358void SBROM_Init(CartInfo *info)\r
359{\r
360 GenMMC1Init(info, 128, 64, 0, 0);\r
361}\r
362\r
363void SCROM_Init(CartInfo *info)\r
364{\r
365 GenMMC1Init(info, 128, 128, 0, 0);\r
366}\r
367\r
368void SEROM_Init(CartInfo *info)\r
369{\r
370 GenMMC1Init(info, 32, 64, 0, 0);\r
371}\r
372\r
373void SGROM_Init(CartInfo *info)\r
374{\r
375 GenMMC1Init(info, 256, 0, 0, 0);\r
376}\r
377\r
378void SKROM_Init(CartInfo *info)\r
379{\r
380 GenMMC1Init(info, 256, 64, 8, info->battery);\r
381}\r
382\r
383void SLROM_Init(CartInfo *info)\r
384{\r
385 GenMMC1Init(info, 256, 128, 0, 0);\r
386}\r
387\r
388void SL1ROM_Init(CartInfo *info)\r
389{\r
390 GenMMC1Init(info, 128, 128, 0, 0);\r
391}\r
392\r
393/* Begin unknown - may be wrong - perhaps they use different MMC1s from the\r
394 similarly functioning boards?\r
395*/\r
396\r
397void SL2ROM_Init(CartInfo *info)\r
398{\r
399 GenMMC1Init(info, 256, 256, 0, 0);\r
400}\r
401\r
402void SFROM_Init(CartInfo *info)\r
403{\r
404 GenMMC1Init(info, 256, 256, 0, 0);\r
405}\r
406\r
407void SHROM_Init(CartInfo *info)\r
408{\r
409 GenMMC1Init(info, 256, 256, 0, 0);\r
410}\r
411\r
412/* End unknown */\r
413/* */\r
414/* */\r
415\r
416void SNROM_Init(CartInfo *info)\r
417{\r
418 GenMMC1Init(info, 256, 0, 8, info->battery);\r
419}\r
420\r
421void SOROM_Init(CartInfo *info)\r
422{\r
423 GenMMC1Init(info, 256, 0, 16, info->battery);\r
424}\r
425\r
426\r