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