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 | |
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 | |