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