fix state corruption
[fceu.git] / cart.c
1 /* FCE Ultra - NES/Famicom Emulator
2  *
3  * Copyright notice for this file:
4  *  Copyright (C) 2002 Xodnizel
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <string.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24
25 #ifdef GP2X
26 #include <unistd.h> // for sync()
27 #endif
28
29 #include "types.h"
30 #include "fce.h"
31 #include "ppu.h"
32 #include "ppu098.h"
33
34 #include "cart.h"
35 #include "memory.h"
36 #include "x6502.h"
37
38 #include "general.h"
39
40 #include "svga.h"
41 #include "file.h"
42
43 /*
44    This file contains all code for coordinating the mapping in of the
45    address space external to the NES.
46    It's also (ab)used by the NSF code.
47 */
48
49 uint8 *Page[32],*VPage[8];
50 uint8 **VPageR=VPage;
51 uint8 *VPageG[8];
52 uint8 *MMC5SPRVPage[8];
53 uint8 *MMC5BGVPage[8];
54
55 static uint8 PRGIsRAM[32];  /* This page is/is not PRG RAM. */
56
57 /* 16 are (sort of) reserved for UNIF/iNES and 16 to map other stuff. */
58 static int CHRram[32];
59 static int PRGram[32];
60
61 uint8 *PRGptr[32];
62 uint8 *CHRptr[32];
63
64 uint32 PRGsize[32];
65 uint32 CHRsize[32];
66
67 uint32 PRGmask2[32];
68 uint32 PRGmask4[32];
69 uint32 PRGmask8[32];
70 uint32 PRGmask16[32];
71 uint32 PRGmask32[32];
72
73 uint32 CHRmask1[32];
74 uint32 CHRmask2[32];
75 uint32 CHRmask4[32];
76 uint32 CHRmask8[32];
77
78 int geniestage=0;
79
80 int modcon;
81
82 uint8 genieval[3];
83 uint8 geniech[3];
84
85 uint32 genieaddr[3];
86
87 static INLINE void setpageptr(int s, uint32 A, uint8 *p, int ram)
88 {
89  uint32 AB=A>>11;
90  int x;
91
92  if(p)
93   for(x=(s>>1)-1;x>=0;x--)
94   {
95    PRGIsRAM[AB+x]=ram;
96    Page[AB+x]=p-A;
97   }
98  else
99   for(x=(s>>1)-1;x>=0;x--)
100   {
101    PRGIsRAM[AB+x]=0;
102    Page[AB+x]=0;
103   }
104 }
105
106 static uint8 nothing[8192];
107 void ResetCartMapping(void)
108 {
109  int x;
110
111  for(x=0;x<32;x++)
112  {
113   Page[x]=nothing-x*2048;
114   PRGptr[x]=CHRptr[x]=0;
115   PRGsize[x]=CHRsize[x]=0;
116  }
117  for(x=0;x<8;x++)
118  {
119   MMC5SPRVPage[x]=MMC5BGVPage[x]=VPageR[x]=nothing-0x400*x;
120  }
121
122 }
123
124 void SetupCartPRGMapping(int chip, uint8 *p, uint32 size, int ram)
125 {
126  PRGptr[chip]=p;
127  PRGsize[chip]=size;
128
129  PRGmask2[chip]=(size>>11)-1;
130  PRGmask4[chip]=(size>>12)-1;
131  PRGmask8[chip]=(size>>13)-1;
132  PRGmask16[chip]=(size>>14)-1;
133  PRGmask32[chip]=(size>>15)-1;
134
135  PRGram[chip]=ram?1:0;
136 }
137
138 void SetupCartCHRMapping(int chip, uint8 *p, uint32 size, int ram)
139 {
140  CHRptr[chip]=p;
141  CHRsize[chip]=size;
142
143  CHRmask1[chip]=(size>>10)-1;
144  CHRmask2[chip]=(size>>11)-1;
145  CHRmask4[chip]=(size>>12)-1;
146  CHRmask8[chip]=(size>>13)-1;
147
148  CHRram[chip]=ram;
149 }
150
151 DECLFR(CartBR)
152 {
153  return Page[A>>11][A];
154 }
155
156 DECLFW(CartBW)
157 {
158  //printf("Ok: %04x:%02x, %d\n",A,V,PRGIsRAM[A>>11]);
159  if(PRGIsRAM[A>>11] && Page[A>>11])
160   Page[A>>11][A]=V;
161 }
162
163 DECLFR(CartBROB)
164 {
165  if(!Page[A>>11]) return(X.DB);
166  return Page[A>>11][A];
167 }
168
169 void FASTAPASS(3) setprg2r(int r, unsigned int A, unsigned int V)
170 {
171   V&=PRGmask2[r];
172   setpageptr(2,A,PRGptr[r]?(&PRGptr[r][V<<11]):0,PRGram[r]);
173   X6502_Rebase();
174 }
175
176 void FASTAPASS(2) setprg2(uint32 A, uint32 V)
177 {
178  setprg2r(0,A,V);
179 }
180
181 void FASTAPASS(3) setprg4r(int r, unsigned int A, unsigned int V)
182 {
183   V&=PRGmask4[r];
184   setpageptr(4,A,PRGptr[r]?(&PRGptr[r][V<<12]):0,PRGram[r]);
185   X6502_Rebase();
186 }
187
188 void FASTAPASS(2) setprg4(uint32 A, uint32 V)
189 {
190  setprg4r(0,A,V);
191 }
192
193 void FASTAPASS(3) setprg8r(int r, unsigned int A, unsigned int V)
194 {
195   if(PRGsize[r]>=8192)
196   {
197    V&=PRGmask8[r];
198    setpageptr(8,A,PRGptr[r]?(&PRGptr[r][V<<13]):0,PRGram[r]);
199   }
200   else
201   {
202    uint32 VA=V<<2;
203    int x;
204    for(x=0;x<4;x++)
205     setpageptr(2,A+(x<<11),PRGptr[r]?(&PRGptr[r][((VA+x)&PRGmask2[r])<<11]):0,PRGram[r]);
206   }
207   X6502_Rebase();
208 }
209
210 void FASTAPASS(2) setprg8(uint32 A, uint32 V)
211 {
212  setprg8r(0,A,V);
213 }
214
215 void FASTAPASS(3) setprg16r(int r, unsigned int A, unsigned int V)
216 {
217   if(PRGsize[r]>=16384)
218   {
219    V&=PRGmask16[r];
220    setpageptr(16,A,PRGptr[r]?(&PRGptr[r][V<<14]):0,PRGram[r]);
221   }
222   else
223   {
224    uint32 VA=V<<3;
225    int x;
226
227    for(x=0;x<8;x++)
228     setpageptr(2,A+(x<<11),PRGptr[r]?(&PRGptr[r][((VA+x)&PRGmask2[r])<<11]):0,PRGram[r]);
229   }
230   X6502_Rebase();
231 }
232
233 void FASTAPASS(2) setprg16(uint32 A, uint32 V)
234 {
235  setprg16r(0,A,V);
236 }
237
238 void FASTAPASS(3) setprg32r(int r,unsigned int A, unsigned int V)
239 {
240   if(PRGsize[r]>=32768)
241   {
242    V&=PRGmask32[r];
243    setpageptr(32,A,PRGptr[r]?(&PRGptr[r][V<<15]):0,PRGram[r]);
244   }
245   else
246   {
247    uint32 VA=V<<4;
248    int x;
249
250    for(x=0;x<16;x++)
251     setpageptr(2,A+(x<<11),PRGptr[r]?(&PRGptr[r][((VA+x)&PRGmask2[r])<<11]):0,PRGram[r]);
252   }
253   X6502_Rebase();
254 }
255
256 void FASTAPASS(2) setprg32(uint32 A, uint32 V)
257 {
258  setprg32r(0,A,V);
259 }
260
261 void FASTAPASS(3) setchr1r(int r, unsigned int A, unsigned int V)
262 {
263   if(!CHRptr[r]) return;
264   FCEUPPU_LineUpdate();
265   V&=CHRmask1[r];
266   if(CHRram[r])
267    PPUCHRRAM|=(1<<(A>>10));
268   else
269    PPUCHRRAM&=~(1<<(A>>10));
270   VPageR[(A)>>10]=&CHRptr[r][(V)<<10]-(A);
271 }
272
273 void FASTAPASS(3) setchr2r(int r, unsigned int A, unsigned int V)
274 {
275   if(!CHRptr[r]) return;
276   FCEUPPU_LineUpdate();
277   V&=CHRmask2[r];
278   VPageR[(A)>>10]=VPageR[((A)>>10)+1]=&CHRptr[r][(V)<<11]-(A);
279   if(CHRram[r])
280    PPUCHRRAM|=(3<<(A>>10));
281   else
282    PPUCHRRAM&=~(3<<(A>>10));
283 }
284
285 void FASTAPASS(3) setchr4r(int r, unsigned int A, unsigned int V)
286 {
287   if(!CHRptr[r]) return;
288   FCEUPPU_LineUpdate();
289   V&=CHRmask4[r];
290   VPageR[(A)>>10]=VPageR[((A)>>10)+1]=
291   VPageR[((A)>>10)+2]=VPageR[((A)>>10)+3]=&CHRptr[r][(V)<<12]-(A);
292   if(CHRram[r])
293    PPUCHRRAM|=(15<<(A>>10));
294   else
295    PPUCHRRAM&=~(15<<(A>>10));
296 }
297
298 void FASTAPASS(2) setchr8r(int r, unsigned int V)
299 {
300   int x;
301
302   if(!CHRptr[r]) return;
303   FCEUPPU_LineUpdate();
304   V&=CHRmask8[r];
305   for(x=7;x>=0;x--)
306    VPageR[x]=&CHRptr[r][V<<13];
307   if(CHRram[r])
308    PPUCHRRAM|=(255);
309   else
310    PPUCHRRAM&=~(255);
311 }
312
313 void FASTAPASS(2) setchr1(unsigned int A, unsigned int V)
314 {
315  setchr1r(0,A,V);
316 }
317
318 void FASTAPASS(2) setchr2(unsigned int A, unsigned int V)
319 {
320  setchr2r(0,A,V);
321 }
322
323 void FASTAPASS(2) setchr4(unsigned int A, unsigned int V)
324 {
325  setchr4r(0,A,V);
326 }
327
328 void FASTAPASS(1) setchr8(unsigned int V)
329 {
330  setchr8r(0,V);
331 }
332
333 void FASTAPASS(1) setvram8(uint8 *p)
334 {
335   int x;
336   for(x=7;x>=0;x--)
337    VPageR[x]=p;
338   PPUCHRRAM|=255;
339 }
340
341 void FASTAPASS(2) setvram4(uint32 A, uint8 *p)
342 {
343   int x;
344   for(x=3;x>=0;x--)
345    VPageR[(A>>10)+x]=p-A;
346   PPUCHRRAM|=(15<<(A>>10));
347 }
348
349 void FASTAPASS(3) setvramb1(uint8 *p, uint32 A, uint32 b)
350 {
351   FCEUPPU_LineUpdate();
352   VPageR[A>>10]=p-A+(b<<10);
353   PPUCHRRAM|=(1<<(A>>10));
354 }
355
356 void FASTAPASS(3) setvramb2(uint8 *p, uint32 A, uint32 b)
357 {
358   FCEUPPU_LineUpdate();
359   VPageR[(A>>10)]=VPageR[(A>>10)+1]=p-A+(b<<11);
360   PPUCHRRAM|=(3<<(A>>10));
361 }
362
363 void FASTAPASS(3) setvramb4(uint8 *p, uint32 A, uint32 b)
364 {
365   int x;
366
367   FCEUPPU_LineUpdate();
368   for(x=3;x>=0;x--)
369    VPageR[(A>>10)+x]=p-A+(b<<12);
370   PPUCHRRAM|=(15<<(A>>10));
371 }
372
373 void FASTAPASS(2) setvramb8(uint8 *p, uint32 b)
374 {
375   int x;
376
377   FCEUPPU_LineUpdate();
378   for(x=7;x>=0;x--)
379    VPageR[x]=p+(b<<13);
380   PPUCHRRAM|=255;
381 }
382
383 /* This function can be called without calling SetupCartMirroring(). */
384
385 void FASTAPASS(3) setntamem(uint8 *p, int ram, uint32 b)
386 {
387  FCEUPPU_LineUpdate();
388  vnapage[b]=p;
389  PPUNTARAM&=~(1<<b);
390  if(ram)
391   PPUNTARAM|=1<<b;
392 }
393
394 static int mirrorhard=0;
395 void setmirrorw(int a, int b, int c, int d)
396 {
397  FCEUPPU_LineUpdate();
398  vnapage[0]=NTARAM+a*0x400;
399  vnapage[1]=NTARAM+b*0x400;
400  vnapage[2]=NTARAM+c*0x400;
401  vnapage[3]=NTARAM+d*0x400;
402 }
403
404 void FASTAPASS(1) setmirror(int t)
405 {
406   FCEUPPU_LineUpdate();
407   if(!mirrorhard)
408   {
409    switch(t)
410    {
411     case MI_H:
412      vnapage[0]=vnapage[1]=NTARAM;vnapage[2]=vnapage[3]=NTARAM+0x400;
413      break;
414     case MI_V:
415      vnapage[0]=vnapage[2]=NTARAM;vnapage[1]=vnapage[3]=NTARAM+0x400;
416      break;
417     case MI_0:
418      vnapage[0]=vnapage[1]=vnapage[2]=vnapage[3]=NTARAM;
419      break;
420     case MI_1:
421      vnapage[0]=vnapage[1]=vnapage[2]=vnapage[3]=NTARAM+0x400;
422      break;
423    }
424   PPUNTARAM=0xF;
425  }
426 }
427
428 void SetupCartMirroring(int m, int hard, uint8 *extra)
429 {
430  if(m<4)
431  {
432   mirrorhard = 0;
433   setmirror(m);
434  }
435  else
436  {
437   vnapage[0]=NTARAM;
438   vnapage[1]=NTARAM+0x400;
439   vnapage[2]=extra;
440   vnapage[3]=extra+0x400;
441   PPUNTARAM=0xF;
442  }
443  mirrorhard=hard;
444 }
445
446 static uint8 *GENIEROM=0;
447
448 void FixGenieMap(void);
449
450 /* Called when a game(file) is opened successfully. */
451 void OpenGenie(void)
452 {
453  FILE *fp;
454  int x;
455
456  if(!GENIEROM)
457  {
458   char *fn;
459
460   if(!(GENIEROM=(uint8 *)FCEU_malloc(4096+1024))) return;
461
462   fn=FCEU_MakeFName(FCEUMKF_GGROM,0,0);
463   fp=fopen(fn,"rb");
464   free(fn);
465   if(!fp)
466   {
467    FCEU_PrintError("Error opening Game Genie ROM image!");
468    free(GENIEROM);
469    GENIEROM=0;
470    return;
471   }
472   if(fread(GENIEROM,1,16,fp)!=16)
473   {
474    grerr:
475    FCEU_PrintError("Error reading from Game Genie ROM image!");
476    free(GENIEROM);
477    GENIEROM=0;
478    fclose(fp);
479    return;
480   }
481   if(GENIEROM[0]==0x4E)  /* iNES ROM image */
482   {
483    if(fread(GENIEROM,1,4096,fp)!=4096)
484     goto grerr;
485    if(fseek(fp,16384-4096,SEEK_CUR))
486     goto grerr;
487    if(fread(GENIEROM+4096,1,256,fp)!=256)
488     goto grerr;
489   }
490   else
491   {
492    if(fread(GENIEROM+16,1,4352-16,fp)!=(4352-16))
493     goto grerr;
494   }
495   fclose(fp);
496
497   /* Workaround for the FCE Ultra CHR page size only being 1KB */
498   for(x=0;x<4;x++)
499    memcpy(GENIEROM+4096+(x<<8),GENIEROM+4096,256);
500  }
501
502  geniestage=1;
503 }
504
505 /* Called when a game is closed. */
506 void CloseGenie(void)
507 {
508  /* No good reason to free() the Game Genie ROM image data. */
509  geniestage=0;
510  FlushGenieRW();
511  VPageR=VPage;
512 }
513
514 void FCEU_KillGenie(void)
515 {
516  if(GENIEROM)
517  {
518   free(GENIEROM);
519   GENIEROM=0;
520  }
521 }
522
523 static DECLFR(GenieRead)
524 {
525  return GENIEROM[A&4095];
526 }
527
528 static DECLFW(GenieWrite)
529 {
530  switch(A)
531  {
532   case 0x800c:
533   case 0x8008:
534   case 0x8004:genieval[((A-4)&0xF)>>2]=V;break;
535
536   case 0x800b:
537   case 0x8007:
538   case 0x8003:geniech[((A-3)&0xF)>>2]=V;break;
539
540   case 0x800a:
541   case 0x8006:
542   case 0x8002:genieaddr[((A-2)&0xF)>>2]&=0xFF00;genieaddr[((A-2)&0xF)>>2]|=V;break;
543
544   case 0x8009:
545   case 0x8005:
546   case 0x8001:genieaddr[((A-1)&0xF)>>2]&=0xFF;genieaddr[((A-1)&0xF)>>2]|=(V|0x80)<<8;break;
547
548   case 0x8000:if(!V)
549          FixGenieMap();
550         else
551         {
552          modcon=V^0xFF;
553          if(V==0x71)
554     modcon=0;
555         }
556         break;
557  }
558 }
559
560 static readfunc GenieBackup[3];
561
562 static DECLFR(GenieFix1)
563 {
564  uint8 r=GenieBackup[0](A);
565
566  if((modcon>>1)&1)    // No check
567   return genieval[0];
568  else if(r==geniech[0])
569   return genieval[0];
570
571  return r;
572 }
573
574 static DECLFR(GenieFix2)
575 {
576  uint8 r=GenieBackup[1](A);
577
578  if((modcon>>2)&1)        // No check
579   return genieval[1];
580  else if(r==geniech[1])
581   return genieval[1];
582
583  return r;
584 }
585
586 static DECLFR(GenieFix3)
587 {
588  uint8 r=GenieBackup[2](A);
589
590  if((modcon>>3)&1)        // No check
591   return genieval[2];
592  else if(r==geniech[2])
593   return genieval[2];
594
595  return r;
596 }
597
598
599 void FixGenieMap(void)
600 {
601  int x;
602
603  geniestage=2;
604
605  for(x=0;x<8;x++)
606   VPage[x]=VPageG[x];
607
608  VPageR=VPage;
609  FlushGenieRW();
610  //printf("Rightyo\n");
611  for(x=0;x<3;x++)
612   if((modcon>>(4+x))&1)
613   {
614    readfunc tmp[3]={GenieFix1,GenieFix2,GenieFix3};
615    GenieBackup[x]=GetReadHandler(genieaddr[x]);
616    SetReadHandler(genieaddr[x],genieaddr[x],tmp[x]);
617   }
618 }
619
620 void GeniePower(void)
621 {
622  uint32 x;
623
624  if(!geniestage)
625   return;
626
627  geniestage=1;
628  for(x=0;x<3;x++)
629  {
630   genieval[x]=0xFF;
631   geniech[x]=0xFF;
632   genieaddr[x]=0xFFFF;
633  }
634  modcon=0;
635
636  SetWriteHandler(0x8000,0xFFFF,GenieWrite);
637  SetReadHandler(0x8000,0xFFFF,GenieRead);
638
639  for(x=0;x<8;x++)
640   VPage[x]=GENIEROM+4096-0x400*x;
641
642  if(AllocGenieRW())
643   VPageR=VPageG;
644  else
645   geniestage=2;
646 }
647
648 static uint8 *real_pages[16];
649
650 void GenieSetPages(int restore)
651 {
652  int page;
653  if (restore)
654  {
655   for (page=16; page<32; page++)
656    Page[page] = real_pages[page-16];
657  }
658  else
659  {
660   for (page=16; page<32; page++) {
661    real_pages[page-16] = Page[page];
662    Page[page]=GENIEROM - (page<<11) + ((page&1)<<11);
663   }
664  }
665 }
666
667 void FCEU_SaveGameSave(CartInfo *LocalHWInfo)
668 {
669  if(LocalHWInfo->battery && LocalHWInfo->SaveGame[0])
670  {
671   FILE *sp;
672   char *soot;
673
674   soot=FCEU_MakeFName(FCEUMKF_SAV,0,"sav");
675   if((sp=FCEUD_UTF8fopen(soot,"wb"))==NULL)
676   {
677    FCEU_PrintError("WRAM file \"%s\" cannot be written to.\n",soot);
678   }
679   else
680   {
681    int x;
682
683    for(x=0;x<4;x++)
684     if(LocalHWInfo->SaveGame[x])
685     {
686      fwrite(LocalHWInfo->SaveGame[x],1,
687       LocalHWInfo->SaveGameLen[x],sp);
688     }
689    fclose(sp);
690 #ifdef GP2X
691    sync();
692 #endif
693   }
694   free(soot);
695  }
696 }
697
698 // hack, movie.c has to communicate with this function somehow
699 int disableBatteryLoading=0;
700
701 void FCEU_LoadGameSave(CartInfo *LocalHWInfo)
702 {
703  if(LocalHWInfo->battery && LocalHWInfo->SaveGame[0] && !disableBatteryLoading)
704  {
705   FILE *sp;
706   char *soot;
707
708   soot=FCEU_MakeFName(FCEUMKF_SAV,0,"sav");
709   sp=FCEUD_UTF8fopen(soot,"rb");
710   if(sp!=NULL)
711   {
712    int x;
713    for(x=0;x<4;x++)
714     if(LocalHWInfo->SaveGame[x])
715      fread(LocalHWInfo->SaveGame[x],1,LocalHWInfo->SaveGameLen[x],sp);
716    fclose(sp);
717   }
718   free(soot);
719  }
720 }
721
722 void DumpEmptyCartMapping(void)
723 {
724  int x, st=0, end=-1;
725
726  for(x=8;x<32;x++)
727  {
728   if (Page[x] == (nothing-x*2048) || Page[x] == 0)
729   {
730    if (end != x) st=x;
731    end=x+1;
732   }
733   if (end == x)
734    printf("DumpEmptyCartMapping: %04x-%04x\n", st*2048, end*2048-1);
735  }
736  if (end==32)
737   printf("DumpEmptyCartMapping: %04x-%04x\n", st*2048, end*2048-1);
738 }
739
740