merge mappers from FCEU-mm
[fceu.git] / boards / mmc5.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 /* None of this code should use any of the iNES bank switching wrappers. */
22
23 #include "mapinc.h"
24
25 static void (*sfun)(int P);
26 static void (*psfun)(void);
27
28 void MMC5RunSound(int Count);
29 void MMC5RunSoundHQ(void);
30
31 static INLINE void MMC5SPRVROM_BANK1(uint32 A,uint32 V)
32 {
33   if(CHRptr[0])
34   {
35     V&=CHRmask1[0];
36     MMC5SPRVPage[(A)>>10]=&CHRptr[0][(V)<<10]-(A);
37   }
38 }
39
40 static INLINE void MMC5BGVROM_BANK1(uint32 A,uint32 V) {if(CHRptr[0]){V&=CHRmask1[0];MMC5BGVPage[(A)>>10]=&CHRptr[0][(V)<<10]-(A);}}
41
42 static INLINE void MMC5SPRVROM_BANK2(uint32 A,uint32 V) {if(CHRptr[0]){V&=CHRmask2[0];MMC5SPRVPage[(A)>>10]=MMC5SPRVPage[((A)>>10)+1]=&CHRptr[0][(V)<<11]-(A);}}
43 static INLINE void MMC5BGVROM_BANK2(uint32 A,uint32 V) {if(CHRptr[0]){V&=CHRmask2[0];MMC5BGVPage[(A)>>10]=MMC5BGVPage[((A)>>10)+1]=&CHRptr[0][(V)<<11]-(A);}}
44
45 static INLINE void MMC5SPRVROM_BANK4(uint32 A,uint32 V) {if(CHRptr[0]){V&=CHRmask4[0];MMC5SPRVPage[(A)>>10]=MMC5SPRVPage[((A)>>10)+1]= MMC5SPRVPage[((A)>>10)+2]=MMC5SPRVPage[((A)>>10)+3]=&CHRptr[0][(V)<<12]-(A);}}
46 static INLINE void MMC5BGVROM_BANK4(uint32 A,uint32 V) {if(CHRptr[0]){V&=CHRmask4[0];MMC5BGVPage[(A)>>10]=MMC5BGVPage[((A)>>10)+1]=MMC5BGVPage[((A)>>10)+2]=MMC5BGVPage[((A)>>10)+3]=&CHRptr[0][(V)<<12]-(A);}}
47
48 static INLINE void MMC5SPRVROM_BANK8(uint32 V) {if(CHRptr[0]){V&=CHRmask8[0];MMC5SPRVPage[0]=MMC5SPRVPage[1]=MMC5SPRVPage[2]=MMC5SPRVPage[3]=MMC5SPRVPage[4]=MMC5SPRVPage[5]=MMC5SPRVPage[6]=MMC5SPRVPage[7]=&CHRptr[0][(V)<<13];}}
49 static INLINE void MMC5BGVROM_BANK8(uint32 V) {if(CHRptr[0]){V&=CHRmask8[0];MMC5BGVPage[0]=MMC5BGVPage[1]=MMC5BGVPage[2]=MMC5BGVPage[3]=MMC5BGVPage[4]=MMC5BGVPage[5]=MMC5BGVPage[6]=MMC5BGVPage[7]=&CHRptr[0][(V)<<13];}}
50
51 static uint8 PRGBanks[4];
52 static uint8 WRAMPage;
53 static uint8 CHRBanksA[8], CHRBanksB[4];
54 static uint8 WRAMMaskEnable[2];
55 static uint8 ABMode;                /* A=0, B=1 */
56
57 static uint8 IRQScanline,IRQEnable;
58 static uint8 CHRMode, NTAMirroring, NTFill, ATFill;
59
60 static uint8 MMC5IRQR;
61 static uint8 MMC5LineCounter;
62 static uint8 mmc5psize, mmc5vsize;
63 static uint8 mul[2];
64
65 static uint8 *WRAM=NULL;
66 static uint8 *MMC5fill=NULL;
67 static uint8 *ExRAM=NULL;
68
69 static uint8 MMC5WRAMsize;
70 static uint8 MMC5WRAMIndex[8];
71
72 static uint8 MMC5ROMWrProtect[4];
73 static uint8 MMC5MemIn[5];
74
75 static void MMC5CHRA(void);
76 static void MMC5CHRB(void);
77
78 typedef struct __cartdata {
79   uint32 crc32;
80   uint8 size;
81 } cartdata;
82
83
84 // ETROM seems to have 16KB of WRAM, ELROM seems to have 8KB
85 // EWROM seems to have 32KB of WRAM
86
87 cartdata MMC5CartList[]=
88 {
89   {0x9c18762b,2},         /* L'Empereur */
90   {0x26533405,2},
91   {0x6396b988,2},
92
93   {0xaca15643,2},        /* Uncharted Waters */
94   {0xfe3488d1,2},        /* Dai Koukai Jidai */
95
96   {0x15fe6d0f,2},        /* BKAC             */
97   {0x39f2ce4b,2},        /* Suikoden              */
98
99   {0x8ce478db,2},        /* Nobunaga's Ambition 2 */
100   {0xeee9a682,2},
101   {0xf9b4240f,2},
102
103   {0x1ced086f,2},        /* Ishin no Arashi */
104
105   {0xf540677b,4},        /* Nobunaga...Bushou Fuuun Roku */
106
107   {0x6f4e4312,4},        /* Aoki Ookami..Genchou */
108
109   {0xf011e490,4},        /* Romance of the 3 Kingdoms 2 */
110   {0x184c2124,4},        /* Sangokushi 2 */
111   {0xee8e6553,4},
112 };
113
114 #define MMC5_NOCARTS            (sizeof(MMC5CartList)/sizeof(MMC5CartList[0]))
115 int DetectMMC5WRAMSize(uint32 crc32)
116 {
117   int x;
118   for(x=0;x<MMC5_NOCARTS;x++)
119      if(crc32==MMC5CartList[x].crc32)
120      {
121        FCEU_printf(" >8KB external WRAM present.  Use UNIF if you hack the ROM image.\n");
122        return(MMC5CartList[x].size*8);
123      }
124   return(8);
125 }
126
127 static void BuildWRAMSizeTable(void)
128 {
129   int x;
130   for(x=0;x<8;x++)
131   {
132     switch(MMC5WRAMsize)
133     {
134       case 0: MMC5WRAMIndex[x]=255; break;
135       case 1: MMC5WRAMIndex[x]=(x>3)?255:0; break;
136       case 2: MMC5WRAMIndex[x]=(x&4)>>2; break;
137       case 4: MMC5WRAMIndex[x]=(x>3)?255:(x&3); break;
138     }
139   }
140 }
141
142 static void MMC5CHRA(void)
143 {
144   int x;
145   switch(mmc5vsize&3)
146   {
147     case 0: setchr8(CHRBanksA[7]);
148             MMC5SPRVROM_BANK8(CHRBanksA[7]);
149             break;
150     case 1: setchr4(0x0000,CHRBanksA[3]);
151             setchr4(0x1000,CHRBanksA[7]);
152             MMC5SPRVROM_BANK4(0x0000,CHRBanksA[3]);
153             MMC5SPRVROM_BANK4(0x1000,CHRBanksA[7]);
154             break;
155     case 2: setchr2(0x0000,CHRBanksA[1]);
156             setchr2(0x0800,CHRBanksA[3]);
157             setchr2(0x1000,CHRBanksA[5]);
158             setchr2(0x1800,CHRBanksA[7]);
159             MMC5SPRVROM_BANK2(0x0000,CHRBanksA[1]);
160             MMC5SPRVROM_BANK2(0x0800,CHRBanksA[3]);
161             MMC5SPRVROM_BANK2(0x1000,CHRBanksA[5]);
162             MMC5SPRVROM_BANK2(0x1800,CHRBanksA[7]);
163             break;
164     case 3: for(x=0;x<8;x++)
165             {
166               setchr1(x<<10,CHRBanksA[x]);
167               MMC5SPRVROM_BANK1(x<<10,CHRBanksA[x]);
168             }
169             break;
170   }
171 }
172
173 static void MMC5CHRB(void)
174 {
175   int x;
176   switch(mmc5vsize&3)
177   {
178     case 0: setchr8(CHRBanksB[3]);
179             MMC5BGVROM_BANK8(CHRBanksB[3]);
180             break;
181     case 1: setchr4(0x0000,CHRBanksB[3]);
182             setchr4(0x1000,CHRBanksB[3]);
183             MMC5BGVROM_BANK4(0x0000,CHRBanksB[3]);
184             MMC5BGVROM_BANK4(0x1000,CHRBanksB[3]);
185             break;
186     case 2: setchr2(0x0000,CHRBanksB[1]);
187             setchr2(0x0800,CHRBanksB[3]);
188             setchr2(0x1000,CHRBanksB[1]);
189             setchr2(0x1800,CHRBanksB[3]);
190             MMC5BGVROM_BANK2(0x0000,CHRBanksB[1]);
191             MMC5BGVROM_BANK2(0x0800,CHRBanksB[3]);
192             MMC5BGVROM_BANK2(0x1000,CHRBanksB[1]);
193             MMC5BGVROM_BANK2(0x1800,CHRBanksB[3]);
194             break;
195     case 3: for(x=0;x<8;x++)
196             {
197               setchr1(x<<10,CHRBanksB[x&3]);
198               MMC5BGVROM_BANK1(x<<10,CHRBanksB[x&3]);
199             }
200             break;
201   }
202 }
203
204 static void FASTAPASS(2) MMC5WRAM(uint32 A, uint32 V)
205 {
206   //printf("%02x\n",V);
207   V=MMC5WRAMIndex[V&7];
208   if(V!=255)
209   {
210     setprg8r(0x10,A,V);
211     MMC5MemIn[(A-0x6000)>>13]=1;
212   }
213   else
214     MMC5MemIn[(A-0x6000)>>13]=0;
215 }
216
217 static void MMC5PRG(void)
218 {
219   int x;
220   switch(mmc5psize&3)
221   {
222     case 0: MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=
223             MMC5ROMWrProtect[2]=MMC5ROMWrProtect[3]=1;
224             setprg32(0x8000,((PRGBanks[1]&0x7F)>>2));
225             for(x=0;x<4;x++)
226                MMC5MemIn[1+x]=1;
227             break;
228     case 1: if(PRGBanks[1]&0x80)
229             {
230               MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=1;
231               setprg16(0x8000,(PRGBanks[1]>>1));
232               MMC5MemIn[1]=MMC5MemIn[2]=1;
233             }
234             else
235             {
236               MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=0;
237               MMC5WRAM(0x8000,PRGBanks[1]&7&0xFE);
238               MMC5WRAM(0xA000,(PRGBanks[1]&7&0xFE)+1);
239             }
240             MMC5MemIn[3]=MMC5MemIn[4]=1;
241             MMC5ROMWrProtect[2]=MMC5ROMWrProtect[3]=1;
242             setprg16(0xC000,(PRGBanks[3]&0x7F)>>1);
243             break;
244     case 2: if(PRGBanks[1]&0x80)
245             {
246               MMC5MemIn[1]=MMC5MemIn[2]=1;
247               MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=1;
248               setprg16(0x8000,(PRGBanks[1]&0x7F)>>1);
249             }
250             else
251             {
252               MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=0;
253               MMC5WRAM(0x8000,PRGBanks[1]&7&0xFE);
254               MMC5WRAM(0xA000,(PRGBanks[1]&7&0xFE)+1);
255             }
256             if(PRGBanks[2]&0x80)
257             {
258               MMC5ROMWrProtect[2]=1;
259               MMC5MemIn[3]=1;
260               setprg8(0xC000,PRGBanks[2]&0x7F);
261             }
262             else
263             {
264               MMC5ROMWrProtect[2]=0;
265               MMC5WRAM(0xC000,PRGBanks[2]&7);
266             }
267             MMC5MemIn[4]=1;
268             MMC5ROMWrProtect[3]=1;
269             setprg8(0xE000,PRGBanks[3]&0x7F);
270             break;
271     case 3: for(x=0;x<3;x++)
272             if(PRGBanks[x]&0x80)
273             {
274               MMC5ROMWrProtect[x]=1;
275               setprg8(0x8000+(x<<13),PRGBanks[x]&0x7F);
276               MMC5MemIn[1+x]=1;
277             }
278             else
279             {
280               MMC5ROMWrProtect[x]=0;
281               MMC5WRAM(0x8000+(x<<13),PRGBanks[x]&7);
282             }
283             MMC5MemIn[4]=1;
284             MMC5ROMWrProtect[3]=1;
285             setprg8(0xE000,PRGBanks[3]&0x7F);
286             break;
287   }
288 }
289
290 static DECLFW(Mapper5_write)
291 {
292   if(A>=0x5120&&A<=0x5127)
293   {
294     ABMode = 0;
295     CHRBanksA[A&7]=V;
296     MMC5CHRA();
297   }
298   else switch(A)
299   {
300     case 0x5105: {
301                    int x;
302                    for(x=0;x<4;x++)
303                    {
304                      switch((V>>(x<<1))&3)
305                      {
306                        case 0:PPUNTARAM|=1<<x;vnapage[x]=NTARAM;break;
307                        case 1:PPUNTARAM|=1<<x;vnapage[x]=NTARAM+0x400;break;
308                        case 2:PPUNTARAM|=1<<x;vnapage[x]=ExRAM;break;
309                        case 3:PPUNTARAM&=~(1<<x);vnapage[x]=MMC5fill;break;
310                      }
311                    }
312                  }
313                  NTAMirroring=V;
314                  break;
315     case 0x5113: WRAMPage=V;MMC5WRAM(0x6000,V&7);break;
316     case 0x5100: mmc5psize=V;MMC5PRG();break;
317     case 0x5101: mmc5vsize=V;
318                  if(!ABMode)
319                  { MMC5CHRB();MMC5CHRA();}
320                  else
321                  { MMC5CHRA();MMC5CHRB();}
322                  break;
323     case 0x5114:
324     case 0x5115:
325     case 0x5116:
326     case 0x5117: PRGBanks[A&3]=V;MMC5PRG();break;
327     case 0x5128:
328     case 0x5129:
329     case 0x512a:
330     case 0x512b: ABMode=1;
331                  CHRBanksB[A&3]=V;
332                  MMC5CHRB();
333                  break;
334     case 0x5102: WRAMMaskEnable[0]=V;break;
335     case 0x5103: WRAMMaskEnable[1]=V;break;
336     case 0x5104: CHRMode=V;MMC5HackCHRMode=V&3;break;
337     case 0x5106: if(V!=NTFill)
338                  {
339                    uint32 t;
340                    t=V|(V<<8)|(V<<16)|(V<<24);
341                    FCEU_dwmemset(MMC5fill,t,0x3c0);
342                  }
343                  NTFill=V;
344                  break;
345     case 0x5107: if(V!=ATFill)
346                  {
347                    unsigned char moop;
348                    uint32 t;
349                    moop=V|(V<<2)|(V<<4)|(V<<6);
350                    t=moop|(moop<<8)|(moop<<16)|(moop<<24);
351                    FCEU_dwmemset(MMC5fill+0x3c0,t,0x40);
352                  }
353                  ATFill=V;
354                  break;
355     case 0x5200: MMC5HackSPMode=V;break;
356     case 0x5201: MMC5HackSPScroll=(V>>3)&0x1F;break;
357     case 0x5202: MMC5HackSPPage=V&0x3F;break;
358     case 0x5203: X6502_IRQEnd(FCEU_IQEXT);IRQScanline=V;break;
359     case 0x5204: X6502_IRQEnd(FCEU_IQEXT);IRQEnable=V&0x80;break;
360     case 0x5205: mul[0]=V;break;
361     case 0x5206: mul[1]=V;break;
362   }
363 }
364
365 static DECLFR(MMC5_ReadROMRAM)
366 {
367   if(MMC5MemIn[(A-0x6000)>>13])
368     return Page[A>>11][A];
369   else
370     return X.DB;
371 }
372
373 static DECLFW(MMC5_WriteROMRAM)
374 {
375   if(A>=0x8000)
376     if(MMC5ROMWrProtect[(A-0x8000)>>13]) return;
377   if(MMC5MemIn[(A-0x6000)>>13])
378     if(((WRAMMaskEnable[0]&3)|((WRAMMaskEnable[1]&3)<<2)) == 6) Page[A>>11][A]=V;
379 }
380
381 static DECLFW(MMC5_ExRAMWr)
382 {
383   if(MMC5HackCHRMode!=3)
384     ExRAM[A&0x3ff]=V;
385 }
386
387 static DECLFR(MMC5_ExRAMRd)
388 {
389  /* Not sure if this is correct, so I'll comment it out for now. */
390  //if(MMC5HackCHRMode>=2)
391   return ExRAM[A&0x3ff];
392  //else
393  // return(X.DB);
394 }
395
396 static DECLFR(MMC5_read)
397 {
398   switch(A)
399   {
400     case 0x5204: X6502_IRQEnd(FCEU_IQEXT);
401                  {
402                    uint8 x;
403                    x=MMC5IRQR;
404                    #ifdef FCEUDEF_DEBUGGER
405                    if(!fceuindbg)
406                    #endif
407                      MMC5IRQR&=0x40;
408                    return x;
409                  }
410     case 0x5205: return (mul[0]*mul[1]);
411     case 0x5206: return ((mul[0]*mul[1])>>8);
412   }
413   return(X.DB);
414 }
415
416 void MMC5Synco(void)
417 {
418   int x;
419
420   MMC5PRG();
421   for(x=0;x<4;x++)
422   {
423     switch((NTAMirroring>>(x<<1))&3)
424     {
425       case 0:PPUNTARAM|=1<<x;vnapage[x]=NTARAM;break;
426       case 1:PPUNTARAM|=1<<x;vnapage[x]=NTARAM+0x400;break;
427       case 2:PPUNTARAM|=1<<x;vnapage[x]=ExRAM;break;
428       case 3:PPUNTARAM&=~(1<<x);vnapage[x]=MMC5fill;break;
429     }
430   }
431   MMC5WRAM(0x6000,WRAMPage&7);
432   if(!ABMode)
433   {
434     MMC5CHRB();
435     MMC5CHRA();
436   }
437   else
438   {
439     MMC5CHRA();
440     MMC5CHRB();
441   }
442   {
443     uint32 t;
444     t=NTFill|(NTFill<<8)|(NTFill<<16)|(NTFill<<24);
445     FCEU_dwmemset(MMC5fill,t,0x3c0);
446   }
447   {
448     unsigned char moop;
449     uint32 t;
450     moop=ATFill|(ATFill<<2)|(ATFill<<4)|(ATFill<<6);
451     t=moop|(moop<<8)|(moop<<16)|(moop<<24);
452     FCEU_dwmemset(MMC5fill+0x3c0,t,0x40);
453   }
454   X6502_IRQEnd(FCEU_IQEXT);
455   MMC5HackCHRMode=CHRMode&3;
456 }
457
458 void MMC5_hb(int scanline)
459 {
460   if(scanline==240)
461   {
462     MMC5LineCounter=0;
463     MMC5IRQR=0x40;
464     return;
465   }
466   if(MMC5LineCounter<240)
467   {
468     if(MMC5LineCounter==IRQScanline)
469     {
470       MMC5IRQR|=0x80;
471       if(IRQEnable&0x80)
472         X6502_IRQBegin(FCEU_IQEXT);
473     }
474     MMC5LineCounter++;
475   }
476   if(MMC5LineCounter==240)
477     MMC5IRQR=0;
478 }
479
480 void MMC5_StateRestore(int version)
481 {
482   MMC5Synco();
483 }
484
485 typedef struct {
486   uint16 wl[2];
487   uint8 env[2];
488   uint8 enable;
489   uint8 running;
490   uint8 raw;
491   uint8 rawcontrol;
492   int32 dcount[2];
493   int32 BC[3];
494   int32 vcount[2];
495 } MMC5APU;
496
497 static MMC5APU MMC5Sound;
498
499
500 static void Do5PCM()
501 {
502   int32 V;
503   int32 start,end;
504
505   start=MMC5Sound.BC[2];
506   end=(SOUNDTS<<16)/soundtsinc;
507   if(end<=start) return;
508   MMC5Sound.BC[2]=end;
509
510   if(!(MMC5Sound.rawcontrol&0x40) && MMC5Sound.raw)
511     for(V=start;V<end;V++)
512        Wave[V>>4]+=MMC5Sound.raw<<1;
513 }
514
515 static void Do5PCMHQ()
516 {
517   int32 V;
518   if(!(MMC5Sound.rawcontrol&0x40) && MMC5Sound.raw)
519     for(V=MMC5Sound.BC[2];V<SOUNDTS;V++)
520        WaveHi[V]+=MMC5Sound.raw<<5;
521   MMC5Sound.BC[2]=SOUNDTS;
522 }
523
524
525 static DECLFW(Mapper5_SW)
526 {
527   A&=0x1F;
528
529   GameExpSound.Fill=MMC5RunSound;
530   GameExpSound.HiFill=MMC5RunSoundHQ;
531
532   switch(A)
533   {
534     case 0x10:if(psfun) psfun();MMC5Sound.rawcontrol=V;break;
535     case 0x11:if(psfun) psfun();MMC5Sound.raw=V;break;
536
537     case 0x0:
538     case 0x4://printf("%04x:$%02x\n",A,V&0x30);
539               if(sfun) sfun(A>>2);
540               MMC5Sound.env[A>>2]=V;
541               break;
542     case 0x2:
543     case 0x6: if(sfun) sfun(A>>2);
544               MMC5Sound.wl[A>>2]&=~0x00FF;
545               MMC5Sound.wl[A>>2]|=V&0xFF;
546               break;
547     case 0x3:
548     case 0x7://printf("%04x:$%02x\n",A,V>>3);
549            MMC5Sound.wl[A>>2]&=~0x0700;
550            MMC5Sound.wl[A>>2]|=(V&0x07)<<8;
551            MMC5Sound.running|=1<<(A>>2);
552            break;
553   case 0x15:if(sfun)
554             {
555              sfun(0);
556              sfun(1);
557             }
558             MMC5Sound.running&=V;
559             MMC5Sound.enable=V;
560                 //printf("%02x\n",V);
561             break;
562  }
563 }
564
565 static void Do5SQ(int P)
566 {
567  static int tal[4]={1,2,4,6};
568  int32 V,amp,rthresh,wl;
569  int32 start,end;
570
571  start=MMC5Sound.BC[P];
572  end=(SOUNDTS<<16)/soundtsinc;
573  if(end<=start) return;
574  MMC5Sound.BC[P]=end;
575
576  wl=MMC5Sound.wl[P]+1;
577  amp=(MMC5Sound.env[P]&0xF)<<4;
578  rthresh=tal[(MMC5Sound.env[P]&0xC0)>>6];
579
580  if(wl>=8 && (MMC5Sound.running&(P+1)))
581  {
582   int dc,vc;
583
584   wl<<=18;
585   dc=MMC5Sound.dcount[P];
586   vc=MMC5Sound.vcount[P];
587
588   for(V=start;V<end;V++)
589   {
590     if(dc<rthresh)
591      Wave[V>>4]+=amp;
592     vc-=nesincsize;
593     while(vc<=0)
594     {
595      vc+=wl;
596      dc=(dc+1)&7;
597     }
598   }
599   MMC5Sound.dcount[P]=dc;
600   MMC5Sound.vcount[P]=vc;
601  }
602 }
603
604 static void Do5SQHQ(int P)
605 {
606  static int tal[4]={1,2,4,6};
607  int32 V,amp,rthresh,wl;
608
609  wl=MMC5Sound.wl[P]+1;
610  amp=((MMC5Sound.env[P]&0xF)<<8);
611  rthresh=tal[(MMC5Sound.env[P]&0xC0)>>6];
612
613  if(wl>=8 && (MMC5Sound.running&(P+1)))
614  {
615   int dc,vc;
616
617   wl<<=1;
618
619   dc=MMC5Sound.dcount[P];
620   vc=MMC5Sound.vcount[P];
621   for(V=MMC5Sound.BC[P];V<SOUNDTS;V++)
622   {
623     if(dc<rthresh)
624      WaveHi[V]+=amp;
625     vc--;
626     if(vc<=0)   /* Less than zero when first started. */
627     {
628      vc=wl;
629      dc=(dc+1)&7;
630     }
631   }
632   MMC5Sound.dcount[P]=dc;
633   MMC5Sound.vcount[P]=vc;
634  }
635  MMC5Sound.BC[P]=SOUNDTS;
636 }
637
638 void MMC5RunSoundHQ(void)
639 {
640   Do5SQHQ(0);
641   Do5SQHQ(1);
642   Do5PCMHQ();
643 }
644
645 void MMC5HiSync(int32 ts)
646 {
647  int x;
648  for(x=0;x<3;x++) MMC5Sound.BC[x]=ts;
649 }
650
651 void MMC5RunSound(int Count)
652 {
653   int x;
654   Do5SQ(0);
655   Do5SQ(1);
656   Do5PCM();
657   for(x=0;x<3;x++)
658    MMC5Sound.BC[x]=Count;
659 }
660
661 void Mapper5_ESI(void)
662 {
663  GameExpSound.RChange=Mapper5_ESI;
664  if(FSettings.SndRate)
665  {
666   if(FSettings.soundq>=1)
667   {
668    sfun=Do5SQHQ;
669    psfun=Do5PCMHQ;
670   }
671   else
672   {
673    sfun=Do5SQ;
674    psfun=Do5PCM;
675   }
676  }
677  else
678  {
679   sfun=0;
680   psfun=0;
681  }
682  memset(MMC5Sound.BC,0,sizeof(MMC5Sound.BC));
683  memset(MMC5Sound.vcount,0,sizeof(MMC5Sound.vcount));
684  GameExpSound.HiSync=MMC5HiSync;
685 }
686
687 void NSFMMC5_Init(void)
688 {
689   memset(&MMC5Sound,0,sizeof(MMC5Sound));
690   mul[0]=mul[1]=0;
691   ExRAM=(uint8*)FCEU_gmalloc(1024);
692   Mapper5_ESI();
693   SetWriteHandler(0x5c00,0x5fef,MMC5_ExRAMWr);
694   SetReadHandler(0x5c00,0x5fef,MMC5_ExRAMRd);
695   MMC5HackCHRMode=2;
696   SetWriteHandler(0x5000,0x5015,Mapper5_SW);
697   SetWriteHandler(0x5205,0x5206,Mapper5_write);
698   SetReadHandler(0x5205,0x5206,MMC5_read);
699 }
700
701 void NSFMMC5_Close(void)
702 {
703  FCEU_gfree(ExRAM);
704  ExRAM=0;
705 }
706
707 static void GenMMC5Reset(void)
708 {
709  int x;
710
711  for(x=0;x<4;x++) PRGBanks[x]=~0;
712  for(x=0;x<8;x++) CHRBanksA[x]=~0;
713  for(x=0;x<4;x++) CHRBanksB[x]=~0;
714  WRAMMaskEnable[0]=WRAMMaskEnable[1]=~0;
715
716  mmc5psize=mmc5vsize=3;
717  CHRMode=0;
718
719  NTAMirroring=NTFill=ATFill=0xFF;
720
721  MMC5Synco();
722
723  SetWriteHandler(0x4020,0x5bff,Mapper5_write);
724  SetReadHandler(0x4020,0x5bff,MMC5_read);
725
726  SetWriteHandler(0x5c00,0x5fff,MMC5_ExRAMWr);
727  SetReadHandler(0x5c00,0x5fff,MMC5_ExRAMRd);
728
729  SetWriteHandler(0x6000,0xFFFF,MMC5_WriteROMRAM);
730  SetReadHandler(0x6000,0xFFFF,MMC5_ReadROMRAM);
731
732  SetWriteHandler(0x5000,0x5015,Mapper5_SW);
733  SetWriteHandler(0x5205,0x5206,Mapper5_write);
734  SetReadHandler(0x5205,0x5206,MMC5_read);
735
736  //GameHBIRQHook=MMC5_hb;
737  FCEU_CheatAddRAM(8,0x6000,WRAM);
738  FCEU_CheatAddRAM(1,0x5c00,ExRAM);
739 }
740
741 static SFORMAT MMC5_StateRegs[]={
742         { PRGBanks, 4, "PRGB"},
743         { CHRBanksA, 8, "CHRA"},
744         { CHRBanksB, 4, "CHRB"},
745         { &WRAMPage, 1, "WRMP"},
746         { WRAMMaskEnable, 2, "WRME"},
747         { &ABMode, 1, "ABMD"},
748         { &IRQScanline, 1, "IRQS"},
749         { &IRQEnable, 1, "IRQE"},
750         { &CHRMode, 1, "CHRM"},
751         { &NTAMirroring, 1, "NTAM"},
752         { &NTFill, 1, "NTFL"},
753         { &ATFill, 1, "ATFL"},
754
755         { &MMC5Sound.wl[0], 2|FCEUSTATE_RLSB, "SDW0"},
756         { &MMC5Sound.wl[1], 2|FCEUSTATE_RLSB, "SDW1"},
757         { MMC5Sound.env, 2, "SDEV"},
758         { &MMC5Sound.enable, 1, "SDEN"},
759         { &MMC5Sound.running, 1, "SDRU"},
760         { &MMC5Sound.raw, 1, "SDRW"},
761         { &MMC5Sound.rawcontrol, 1, "SDRC"},
762         {0}
763 };
764
765 static void GenMMC5_Init(CartInfo *info, int wsize, int battery)
766 {
767  if(wsize)
768  {
769   WRAM=(uint8*)FCEU_gmalloc(wsize*1024);
770   SetupCartPRGMapping(0x10,WRAM,wsize*1024,1);
771   AddExState(WRAM, wsize*1024, 0, "WRAM");
772  }
773
774  MMC5fill=(uint8*)FCEU_gmalloc(1024);
775  ExRAM=(uint8*)FCEU_gmalloc(1024);
776
777  AddExState(MMC5_StateRegs, ~0, 0, 0);
778  AddExState(WRAM, wsize*1024, 0, "WRAM");
779  AddExState(ExRAM, 1024, 0, "ERAM");
780  AddExState(&MMC5HackSPMode, 1, 0, "SPLM");
781  AddExState(&MMC5HackSPScroll, 1, 0, "SPLS");
782  AddExState(&MMC5HackSPPage, 1, 0, "SPLP");
783
784  MMC5WRAMsize=wsize/8;
785  BuildWRAMSizeTable();
786  GameStateRestore=MMC5_StateRestore;
787  info->Power=GenMMC5Reset;
788
789  if(battery)
790  {
791   info->SaveGame[0]=WRAM;
792   if(wsize<=16)
793    info->SaveGameLen[0]=8192;
794   else
795    info->SaveGameLen[0]=32768;
796  }
797
798  MMC5HackVROMMask=CHRmask4[0];
799  MMC5HackExNTARAMPtr=ExRAM;
800  MMC5Hack=1;
801  MMC5HackVROMPTR=CHRptr[0];
802  MMC5HackCHRMode=0;
803  MMC5HackSPMode=MMC5HackSPScroll=MMC5HackSPPage=0;
804  Mapper5_ESI();
805 }
806
807 void Mapper5_Init(CartInfo *info)
808 {
809  GenMMC5_Init(info, DetectMMC5WRAMSize(info->CRC32), info->battery);
810 }
811
812 // ELROM seems to have 0KB of WRAM
813 // EKROM seems to have 8KB of WRAM
814 // ETROM seems to have 16KB of WRAM
815 // EWROM seems to have 32KB of WRAM
816
817 // ETROM and EWROM are battery-backed, EKROM isn't.
818
819 void ETROM_Init(CartInfo *info)
820 {
821  GenMMC5_Init(info, 16,info->battery);
822 }
823
824 void ELROM_Init(CartInfo *info)
825 {
826  GenMMC5_Init(info,0,0);
827 }
828
829 void EWROM_Init(CartInfo *info)
830 {
831  GenMMC5_Init(info,32,info->battery);
832 }
833
834 void EKROM_Init(CartInfo *info)
835 {
836  GenMMC5_Init(info,8,info->battery);
837 }