merge mapper code from FCEUX
[fceu.git] / boards / mmc5.c
index d14ca0e..1916df9 100644 (file)
-/* FCE Ultra - NES/Famicom Emulator
- *
- * Copyright notice for this file:
- *  Copyright (C) 2002 Xodnizel
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/* None of this code should use any of the iNES bank switching wrappers. */
-
-#include "mapinc.h"
-
-static void (*sfun)(int P);
-static void (*psfun)(void);
-
-void MMC5RunSound(int Count);
-//void MMC5RunSoundHQ(void);
-
-static INLINE void MMC5SPRVROM_BANK1(uint32 A,uint32 V)
-{
- if(CHRptr[0])
- {
-  V&=CHRmask1[0];
-  MMC5SPRVPage[(A)>>10]=&CHRptr[0][(V)<<10]-(A);
- }
-}
-
-static INLINE void MMC5BGVROM_BANK1(uint32 A,uint32 V) {if(CHRptr[0]){V&=CHRmask1[0];MMC5BGVPage[(A)>>10]=&CHRptr[0][(V)<<10]-(A);}}
-
-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);}}
-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);}}
-
-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);}}
-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);}}
-
-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];}}
-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];}}
-
-static uint8 PRGBanks[4];
-static uint8 WRAMPage;
-static uint8 CHRBanksA[8], CHRBanksB[4];
-static uint8 WRAMMaskEnable[2];
-static uint8 ABMode;                /* A=0, B=1 */
-
-static uint8 IRQScanline,IRQEnable;
-static uint8 CHRMode, NTAMirroring, NTFill, ATFill;
-
-static uint8 MMC5IRQR;
-static uint8 MMC5LineCounter;
-static uint8 mmc5psize, mmc5vsize;
-static uint8 mul[2];
-
-static uint8 *WRAM=NULL;
-static uint8 *MMC5fill=NULL;
-static uint8 *ExRAM=NULL;
-
-static uint8 MMC5WRAMsize;
-static uint8 MMC5WRAMIndex[8];
-
-static uint8 MMC5ROMWrProtect[4];
-static uint8 MMC5MemIn[5];
-
-static void MMC5CHRA(void);
-static void MMC5CHRB(void);
-
-typedef struct __cartdata {
-        uint32 crc32;
-        uint8 size;
-} cartdata;
-
-
-// ETROM seems to have 16KB of WRAM, ELROM seems to have 8KB
-// EWROM seems to have 32KB of WRAM
-
-#define MMC5_NOCARTS 14
-cartdata MMC5CartList[MMC5_NOCARTS]=
-{
- {0x9c18762b,2},         /* L'Empereur */
- {0x26533405,2},
- {0x6396b988,2},
-
- {0xaca15643,2},        /* Uncharted Waters */
- {0xfe3488d1,2},        /* Dai Koukai Jidai */
-
- {0x15fe6d0f,2},        /* BKAC             */
- {0x39f2ce4b,2},        /* Suikoden              */
-
- {0x8ce478db,2},        /* Nobunaga's Ambition 2 */
- {0xeee9a682,2},
-
- {0x1ced086f,2},        /* Ishin no Arashi */
-
- {0xf540677b,4},        /* Nobunaga...Bushou Fuuun Roku */
-
- {0x6f4e4312,4},        /* Aoki Ookami..Genchou */
-
- {0xf011e490,4},        /* Romance of the 3 Kingdoms 2 */
- {0x184c2124,4},        /* Sangokushi 2 */
-};
-
-
-int DetectMMC5WRAMSize(uint32 crc32)
-{
- int x;
- for(x=0;x<MMC5_NOCARTS;x++)
-  if(crc32==MMC5CartList[x].crc32)
-  {
-    FCEU_printf(" >8KB external WRAM present.  Use UNIF if you hack the ROM image.\n");
-    return(MMC5CartList[x].size*8);
-  }
- return(8);
-}
-
-static void BuildWRAMSizeTable(void)
-{
- int x;
- for(x=0;x<8;x++)
- {
-  switch(MMC5WRAMsize)
-  {
-      case 0: MMC5WRAMIndex[x]=255; break;
-      case 1: MMC5WRAMIndex[x]=(x>3)?255:0; break;
-      case 2: MMC5WRAMIndex[x]=(x&4)>>2; break;
-      case 4: MMC5WRAMIndex[x]=(x>3)?255:(x&3); break;
-  }
- }
-}
-
-static void MMC5CHRA(void)
-{
-        int x;
-        switch(mmc5vsize&3)
-        {
-    case 0: setchr8(CHRBanksA[7]);
-                MMC5SPRVROM_BANK8(CHRBanksA[7]);
-                break;
-    case 1: setchr4(0x0000,CHRBanksA[3]);
-                setchr4(0x1000,CHRBanksA[7]);
-                MMC5SPRVROM_BANK4(0x0000,CHRBanksA[3]);
-                MMC5SPRVROM_BANK4(0x1000,CHRBanksA[7]);
-                break;
-    case 2: setchr2(0x0000,CHRBanksA[1]);
-                setchr2(0x0800,CHRBanksA[3]);
-                setchr2(0x1000,CHRBanksA[5]);
-                setchr2(0x1800,CHRBanksA[7]);
-                MMC5SPRVROM_BANK2(0x0000,CHRBanksA[1]);
-                MMC5SPRVROM_BANK2(0x0800,CHRBanksA[3]);
-                MMC5SPRVROM_BANK2(0x1000,CHRBanksA[5]);
-                MMC5SPRVROM_BANK2(0x1800,CHRBanksA[7]);
-                break;
-    case 3: for(x=0;x<8;x++)
-                {
-                 setchr1(x<<10,CHRBanksA[x]);
-                 MMC5SPRVROM_BANK1(x<<10,CHRBanksA[x]);
-                }
-                break;
- }
-}
-
-static void MMC5CHRB(void)
-{
- int x;
- switch(mmc5vsize&3)
- {
-    case 0: setchr8(CHRBanksB[3]);
-                MMC5BGVROM_BANK8(CHRBanksB[3]);
-                break;
-    case 1: setchr4(0x0000,CHRBanksB[3]);
-                       setchr4(0x1000,CHRBanksB[3]);
-                MMC5BGVROM_BANK4(0x0000,CHRBanksB[3]);
-                MMC5BGVROM_BANK4(0x1000,CHRBanksB[3]);
-                break;
-    case 2: setchr2(0x0000,CHRBanksB[1]);
-                setchr2(0x0800,CHRBanksB[3]);
-                setchr2(0x1000,CHRBanksB[1]);
-                setchr2(0x1800,CHRBanksB[3]);
-                MMC5BGVROM_BANK2(0x0000,CHRBanksB[1]);
-                MMC5BGVROM_BANK2(0x0800,CHRBanksB[3]);
-                MMC5BGVROM_BANK2(0x1000,CHRBanksB[1]);
-                MMC5BGVROM_BANK2(0x1800,CHRBanksB[3]);
-                break;
-    case 3: for(x=0;x<8;x++)
-                {
-                 setchr1(x<<10,CHRBanksB[x&3]);
-                 MMC5BGVROM_BANK1(x<<10,CHRBanksB[x&3]);
-                }
-                break;
- }
-}
-
-static void FASTAPASS(2) MMC5WRAM(uint32 A, uint32 V)
-{
-   //printf("%02x\n",V);
-   V=MMC5WRAMIndex[V&7];
-   if(V!=255)
-   {
-    setprg8r(0x10,A,V);
-    MMC5MemIn[(A-0x6000)>>13]=1;
-   }
-   else
-    MMC5MemIn[(A-0x6000)>>13]=0;
-}
-
-static void MMC5PRG(void)
-{
- int x;
- switch(mmc5psize&3)
-  {
-    case 0: MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=
-           MMC5ROMWrProtect[2]=MMC5ROMWrProtect[3]=1;
-           setprg32(0x8000,((PRGBanks[1]&0x7F)>>2));
-           for(x=0;x<4;x++)
-             MMC5MemIn[1+x]=1;
-           break;
-    case 1: if(PRGBanks[1]&0x80)
-          {
-           MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=1;
-           setprg16(0x8000,(PRGBanks[1]>>1));
-           MMC5MemIn[1]=MMC5MemIn[2]=1;
-          }
-         else
-          {
-           MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=0;
-           MMC5WRAM(0x8000,PRGBanks[1]&7&0xFE);
-           MMC5WRAM(0xA000,(PRGBanks[1]&7&0xFE)+1);
-          }
-          MMC5MemIn[3]=MMC5MemIn[4]=1;
-          MMC5ROMWrProtect[2]=MMC5ROMWrProtect[3]=1;
-          setprg16(0xC000,(PRGBanks[3]&0x7F)>>1);
-         break;
-    case 2: if(PRGBanks[1]&0x80)
-          {
-           MMC5MemIn[1]=MMC5MemIn[2]=1;
-           MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=1;
-           setprg16(0x8000,(PRGBanks[1]&0x7F)>>1);
-          }
-         else
-          {
-           MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=0;
-           MMC5WRAM(0x8000,PRGBanks[1]&7&0xFE);
-           MMC5WRAM(0xA000,(PRGBanks[1]&7&0xFE)+1);
-          }
-         if(PRGBanks[2]&0x80)
-            {
-              MMC5ROMWrProtect[2]=1;
-              MMC5MemIn[3]=1;
-              setprg8(0xC000,PRGBanks[2]&0x7F);
-            }
-            else
-            {
-              MMC5ROMWrProtect[2]=0;
-              MMC5WRAM(0xC000,PRGBanks[2]&7);
-            }
-            MMC5MemIn[4]=1;
-            MMC5ROMWrProtect[3]=1;
-            setprg8(0xE000,PRGBanks[3]&0x7F);
-            break;
-    case 3: for(x=0;x<3;x++)
-          if(PRGBanks[x]&0x80)
-          {
-           MMC5ROMWrProtect[x]=1;
-           setprg8(0x8000+(x<<13),PRGBanks[x]&0x7F);
-           MMC5MemIn[1+x]=1;
-          }
-          else
-          {
-           MMC5ROMWrProtect[x]=0;
-           MMC5WRAM(0x8000+(x<<13),PRGBanks[x]&7);
-          }
-         MMC5MemIn[4]=1;
-         MMC5ROMWrProtect[3]=1;
-         setprg8(0xE000,PRGBanks[3]&0x7F);
-         break;
-  }
-}
-
-static DECLFW(Mapper5_write)
-{
-  if(A>=0x5120&&A<=0x5127)
- {
-  ABMode = 0;
-  CHRBanksA[A&7]=V;
-  MMC5CHRA();
- }
- else switch(A)
- {
-    case 0x5105: {
-                int x;
-                for(x=0;x<4;x++)
-                {
-                 switch((V>>(x<<1))&3)
-                 {
-                  case 0:PPUNTARAM|=1<<x;vnapage[x]=NTARAM;break;
-                  case 1:PPUNTARAM|=1<<x;vnapage[x]=NTARAM+0x400;break;
-                  case 2:PPUNTARAM|=1<<x;vnapage[x]=ExRAM;break;
-                  case 3:PPUNTARAM&=~(1<<x);vnapage[x]=MMC5fill;break;
-                 }
-                }
-               }
-               NTAMirroring=V;
-               break;
-    case 0x5113: WRAMPage=V;MMC5WRAM(0x6000,V&7);break;
-    case 0x5100: mmc5psize=V;MMC5PRG();break;
-    case 0x5101: mmc5vsize=V;
-                 if(!ABMode)
-                 { MMC5CHRB();MMC5CHRA();}
-                 else
-                 { MMC5CHRA();MMC5CHRB();}
-                 break;
-   case 0x5114:
-   case 0x5115:
-   case 0x5116:
-    case 0x5117: PRGBanks[A&3]=V;MMC5PRG();break;
-   case 0x5128:
-   case 0x5129:
-   case 0x512a:
-    case 0x512b: ABMode=1;
-               CHRBanksB[A&3]=V;
-               MMC5CHRB();
-               break;
-    case 0x5102: WRAMMaskEnable[0]=V;break;
-    case 0x5103: WRAMMaskEnable[1]=V;break;
-    case 0x5104: CHRMode=V;MMC5HackCHRMode=V&3;break;
-    case 0x5106: if(V!=NTFill)
-               {
-                uint32 t;
-                t=V|(V<<8)|(V<<16)|(V<<24);
-                FCEU_dwmemset(MMC5fill,t,0x3c0);
-               }
-               NTFill=V;
-               break;
-    case 0x5107: if(V!=ATFill)
-               {
-                unsigned char moop;
-                uint32 t;
-                moop=V|(V<<2)|(V<<4)|(V<<6);
-                t=moop|(moop<<8)|(moop<<16)|(moop<<24);
-                FCEU_dwmemset(MMC5fill+0x3c0,t,0x40);
-               }
-               ATFill=V;
-               break;
-    case 0x5200: MMC5HackSPMode=V;break;
-    case 0x5201: MMC5HackSPScroll=(V>>3)&0x1F;break;
-    case 0x5202: MMC5HackSPPage=V&0x3F;break;
-    case 0x5203: X6502_IRQEnd(FCEU_IQEXT);IRQScanline=V;break;
-    case 0x5204: X6502_IRQEnd(FCEU_IQEXT);IRQEnable=V&0x80;break;
-    case 0x5205: mul[0]=V;break;
-    case 0x5206: mul[1]=V;break;
-  }
-}
-
-static DECLFR(MMC5_ReadROMRAM)
-{
-        if(MMC5MemIn[(A-0x6000)>>13])
-         return Page[A>>11][A];
-        else
-         return X.DB;
-}
-
-static DECLFW(MMC5_WriteROMRAM)
-{
-       if(A>=0x8000)
-    if(MMC5ROMWrProtect[(A-0x8000)>>13]) return;
-  if(MMC5MemIn[(A-0x6000)>>13])
-    if(((WRAMMaskEnable[0]&3)|((WRAMMaskEnable[1]&3)<<2)) == 6) Page[A>>11][A]=V;
-}
-
-static DECLFW(MMC5_ExRAMWr)
-{
- if(MMC5HackCHRMode!=3)
-  ExRAM[A&0x3ff]=V;
-}
-
-static DECLFR(MMC5_ExRAMRd)
-{
- /* Not sure if this is correct, so I'll comment it out for now. */
- //if(MMC5HackCHRMode>=2)
-  return ExRAM[A&0x3ff];
- //else
- // return(X.DB);
-}
-
-static DECLFR(MMC5_read)
-{
- switch(A)
- {
-    case 0x5204: X6502_IRQEnd(FCEU_IQEXT);
-              {
-                uint8 x;
-                x=MMC5IRQR;
-                if(!fceuindbg)
-                 MMC5IRQR&=0x40;
-                return x;
-              }
-    case 0x5205: return (mul[0]*mul[1]);
-    case 0x5206: return ((mul[0]*mul[1])>>8);
- }
- return(X.DB);
-}
-
-void MMC5Synco(void)
-{
- int x;
-
- MMC5PRG();
- for(x=0;x<4;x++)
- {
-  switch((NTAMirroring>>(x<<1))&3)
-   {
-    case 0:PPUNTARAM|=1<<x;vnapage[x]=NTARAM;break;
-    case 1:PPUNTARAM|=1<<x;vnapage[x]=NTARAM+0x400;break;
-    case 2:PPUNTARAM|=1<<x;vnapage[x]=ExRAM;break;
-    case 3:PPUNTARAM&=~(1<<x);vnapage[x]=MMC5fill;break;
-   }
- }
- MMC5WRAM(0x6000,WRAMPage&7);
- if(!ABMode)
- {
-  MMC5CHRB();
-  MMC5CHRA();
- }
- else
- {
-   MMC5CHRA();
-   MMC5CHRB();
- }
-  {
-    uint32 t;
-    t=NTFill|(NTFill<<8)|(NTFill<<16)|(NTFill<<24);
-    FCEU_dwmemset(MMC5fill,t,0x3c0);
-  }
-  {
-   unsigned char moop;
-   uint32 t;
-   moop=ATFill|(ATFill<<2)|(ATFill<<4)|(ATFill<<6);
-   t=moop|(moop<<8)|(moop<<16)|(moop<<24);
-   FCEU_dwmemset(MMC5fill+0x3c0,t,0x40);
-  }
-  X6502_IRQEnd(FCEU_IQEXT);
-  MMC5HackCHRMode=CHRMode&3;
-}
-
-void MMC5_hb(int scanline)
-{
-  if(scanline==240)
-  {
-   MMC5LineCounter=0;
-   MMC5IRQR=0x40;
-   return;
-  }
-  if(MMC5LineCounter<240)
-  {
-   if(MMC5LineCounter==IRQScanline)
-   {
-    MMC5IRQR|=0x80;
-    if(IRQEnable&0x80)
-     X6502_IRQBegin(FCEU_IQEXT);
-   }
-   MMC5LineCounter++;
-  }
- if(MMC5LineCounter==240)
-   MMC5IRQR=0;
-}
-
-void MMC5_StateRestore(int version)
-{
- MMC5Synco();
-}
-
-typedef struct {
-        uint16 wl[2];
-        uint8 env[2];
-        uint8 enable;
-        uint8 running;
-        uint8 raw;
-        uint8 rawcontrol;
-        int32 dcount[2];
-        int32 BC[3];
-        int32 vcount[2];
-} MMC5APU;
-
-static MMC5APU MMC5Sound;
-
-
-static void Do5PCM()
-{
-   int32 V;
-   int32 start,end;
-
-   start=MMC5Sound.BC[2];
-   end=(SOUNDTS<<16)/soundtsinc;
-   if(end<=start) return;
-   MMC5Sound.BC[2]=end;
-
-   if(!(MMC5Sound.rawcontrol&0x40) && MMC5Sound.raw)
-    for(V=start;V<end;V++)
-     Wave[V>>4]+=MMC5Sound.raw<<1;
-}
-
-#if 0
-static void Do5PCMHQ()
-{
-   int32 V;
-   if(!(MMC5Sound.rawcontrol&0x40) && MMC5Sound.raw)
-    for(V=MMC5Sound.BC[2];V<SOUNDTS;V++)
-     WaveHi[V]+=MMC5Sound.raw<<5;
-   MMC5Sound.BC[2]=SOUNDTS;
-}
-#endif
-
-static DECLFW(Mapper5_SW)
-{
- A&=0x1F;
-
- GameExpSound.Fill=MMC5RunSound;
- GameExpSound.HiFill=0;//MMC5RunSoundHQ;
-
- switch(A)
- {
-  case 0x10:if(psfun) psfun();MMC5Sound.rawcontrol=V;break;
-  case 0x11:if(psfun) psfun();MMC5Sound.raw=V;break;
-
-  case 0x0:
-  case 0x4://printf("%04x:$%02x\n",A,V&0x30);
-           if(sfun) sfun(A>>2);
-           MMC5Sound.env[A>>2]=V;
-           break;
-  case 0x2:
-    case 0x6: if(sfun) sfun(A>>2);
-           MMC5Sound.wl[A>>2]&=~0x00FF;
-           MMC5Sound.wl[A>>2]|=V&0xFF;
-           break;
-  case 0x3:
-  case 0x7://printf("%04x:$%02x\n",A,V>>3);
-           MMC5Sound.wl[A>>2]&=~0x0700;
-           MMC5Sound.wl[A>>2]|=(V&0x07)<<8;
-           MMC5Sound.running|=1<<(A>>2);
-           break;
-  case 0x15:if(sfun)
-            {
-             sfun(0);
-             sfun(1);
-            }
-            MMC5Sound.running&=V;
-            MMC5Sound.enable=V;
-                //printf("%02x\n",V);
-            break;
- }
-}
-
-static void Do5SQ(int P)
-{
- static int tal[4]={1,2,4,6};
- int32 V,amp,rthresh,wl;
- int32 start,end;
-
- start=MMC5Sound.BC[P];
- end=(SOUNDTS<<16)/soundtsinc;
- if(end<=start) return;
- MMC5Sound.BC[P]=end;
-
- wl=MMC5Sound.wl[P]+1;
- amp=(MMC5Sound.env[P]&0xF)<<4;
- rthresh=tal[(MMC5Sound.env[P]&0xC0)>>6];
-
- if(wl>=8 && (MMC5Sound.running&(P+1)))
- {
-  int dc,vc;
-
-  wl<<=18;
-  dc=MMC5Sound.dcount[P];
-  vc=MMC5Sound.vcount[P];
-
-  for(V=start;V<end;V++)
-  {
-    if(dc<rthresh)
-     Wave[V>>4]+=amp;
-    vc-=nesincsize;
-    while(vc<=0)
-    {
-     vc+=wl;
-     dc=(dc+1)&7;
-    }
-  }
-  MMC5Sound.dcount[P]=dc;
-  MMC5Sound.vcount[P]=vc;
- }
-}
-
-#if 0
-static void Do5SQHQ(int P)
-{
- static int tal[4]={1,2,4,6};
- int32 V,amp,rthresh,wl;
-
- wl=MMC5Sound.wl[P]+1;
- amp=((MMC5Sound.env[P]&0xF)<<8);
- rthresh=tal[(MMC5Sound.env[P]&0xC0)>>6];
-
- if(wl>=8 && (MMC5Sound.running&(P+1)))
- {
-  int dc,vc;
-
-  wl<<=1;
-
-  dc=MMC5Sound.dcount[P];
-  vc=MMC5Sound.vcount[P];
-  for(V=MMC5Sound.BC[P];V<SOUNDTS;V++)
-  {
-    if(dc<rthresh)
-     WaveHi[V]+=amp;
-    vc--;
-    if(vc<=0)   /* Less than zero when first started. */
-    {
-     vc=wl;
-     dc=(dc+1)&7;
-    }
-  }
-  MMC5Sound.dcount[P]=dc;
-  MMC5Sound.vcount[P]=vc;
- }
- MMC5Sound.BC[P]=SOUNDTS;
-}
-
-void MMC5RunSoundHQ(void)
-{
-  Do5SQHQ(0);
-  Do5SQHQ(1);
-  Do5PCMHQ();
-}
-
-void MMC5HiSync(int32 ts)
-{
- int x;
- for(x=0;x<3;x++) MMC5Sound.BC[x]=ts;
-}
-#endif
-
-void MMC5RunSound(int Count)
-{
-  int x;
-  Do5SQ(0);
-  Do5SQ(1);
-  Do5PCM();
-  for(x=0;x<3;x++)
-   MMC5Sound.BC[x]=Count;
-}
-
-void Mapper5_ESI(void)
-{
- GameExpSound.RChange=Mapper5_ESI;
- if(FSettings.SndRate)
- {
-#if 0
-  if(FSettings.soundq>=1)
-  {
-   sfun=Do5SQHQ;
-   psfun=Do5PCMHQ;
-  }
-  else
-#endif
-  {
-   sfun=Do5SQ;
-   psfun=Do5PCM;
-  }
- }
- else
- {
-  sfun=0;
-  psfun=0;
- }
- memset(MMC5Sound.BC,0,sizeof(MMC5Sound.BC));
- memset(MMC5Sound.vcount,0,sizeof(MMC5Sound.vcount));
- GameExpSound.HiSync=0;//MMC5HiSync;
-}
-
-void NSFMMC5_Init(void)
-{
-  memset(&MMC5Sound,0,sizeof(MMC5Sound));
-  mul[0]=mul[1]=0;
-  ExRAM=(uint8*)FCEU_gmalloc(1024);
-  Mapper5_ESI();
-  SetWriteHandler(0x5c00,0x5fef,MMC5_ExRAMWr);
-  SetReadHandler(0x5c00,0x5fef,MMC5_ExRAMRd);
-  MMC5HackCHRMode=2;
-  SetWriteHandler(0x5000,0x5015,Mapper5_SW);
-  SetWriteHandler(0x5205,0x5206,Mapper5_write);
-  SetReadHandler(0x5205,0x5206,MMC5_read);
-}
-
-void NSFMMC5_Close(void)
-{
- FCEU_gfree(ExRAM);
- ExRAM=0;
-}
-
-static void GenMMC5Reset(void)
-{
- int x;
-
- for(x=0;x<4;x++) PRGBanks[x]=~0;
- for(x=0;x<8;x++) CHRBanksA[x]=~0;
- for(x=0;x<4;x++) CHRBanksB[x]=~0;
- WRAMMaskEnable[0]=WRAMMaskEnable[1]=~0;
-
- mmc5psize=mmc5vsize=3;
- CHRMode=0;
-
- NTAMirroring=NTFill=ATFill=0xFF;
-
- MMC5Synco();
-
- SetWriteHandler(0x4020,0x5bff,Mapper5_write);
- SetReadHandler(0x4020,0x5bff,MMC5_read);
-
- SetWriteHandler(0x5c00,0x5fff,MMC5_ExRAMWr);
- SetReadHandler(0x5c00,0x5fff,MMC5_ExRAMRd);
-
- SetWriteHandler(0x6000,0xFFFF,MMC5_WriteROMRAM);
- SetReadHandler(0x6000,0xFFFF,MMC5_ReadROMRAM);
-
- SetWriteHandler(0x5000,0x5015,Mapper5_SW);
- SetWriteHandler(0x5205,0x5206,Mapper5_write);
- SetReadHandler(0x5205,0x5206,MMC5_read);
-
- //GameHBIRQHook=MMC5_hb;
- FCEU_CheatAddRAM(8,0x6000,WRAM);
- FCEU_CheatAddRAM(1,0x5c00,ExRAM);
-}
-
-static SFORMAT MMC5_StateRegs[]={
-        { PRGBanks, 4, "PRGB"},
-        { CHRBanksA, 8, "CHRA"},
-        { CHRBanksB, 4, "CHRB"},
-        { &WRAMPage, 1, "WRMP"},
-        { WRAMMaskEnable, 2, "WRME"},
-        { &ABMode, 1, "ABMD"},
-        { &IRQScanline, 1, "IRQS"},
-        { &IRQEnable, 1, "IRQE"},
-        { &CHRMode, 1, "CHRM"},
-        { &NTAMirroring, 1, "NTAM"},
-        { &NTFill, 1, "NTFL"},
-        { &ATFill, 1, "ATFL"},
-
-        { &MMC5Sound.wl[0], 2|FCEUSTATE_RLSB, "SDW0"},
-        { &MMC5Sound.wl[1], 2|FCEUSTATE_RLSB, "SDW1"},
-        { MMC5Sound.env, 2, "SDEV"},
-        { &MMC5Sound.enable, 1, "SDEN"},
-        { &MMC5Sound.running, 1, "SDRU"},
-        { &MMC5Sound.raw, 1, "SDRW"},
-        { &MMC5Sound.rawcontrol, 1, "SDRC"},
-        {0}
-};
-
-static void GenMMC5_Init(CartInfo *info, int wsize, int battery)
-{
- if(wsize)
- {
-  WRAM=(uint8*)FCEU_gmalloc(wsize*1024);
-  SetupCartPRGMapping(0x10,WRAM,wsize*1024,1);
-  AddExState(WRAM, wsize*1024, 0, "WRAM");
- }
-
- MMC5fill=(uint8*)FCEU_gmalloc(1024);
- ExRAM=(uint8*)FCEU_gmalloc(1024);
-
- AddExState(MMC5_StateRegs, ~0, 0, 0);
- AddExState(WRAM, wsize*1024, 0, "WRAM");
- AddExState(ExRAM, 1024, 0, "ERAM");
- AddExState(&MMC5HackSPMode, 1, 0, "SPLM");
- AddExState(&MMC5HackSPScroll, 1, 0, "SPLS");
- AddExState(&MMC5HackSPPage, 1, 0, "SPLP");
-
- MMC5WRAMsize=wsize/8;
- BuildWRAMSizeTable();
- GameStateRestore=MMC5_StateRestore;
- info->Power=GenMMC5Reset;
-
- if(battery)
- {
-  info->SaveGame[0]=WRAM;
-  if(wsize<=16)
-   info->SaveGameLen[0]=8192;
-  else
-   info->SaveGameLen[0]=32768;
- }
-
- MMC5HackVROMMask=CHRmask4[0];
- MMC5HackExNTARAMPtr=ExRAM;
- MMC5Hack=1;
- MMC5HackVROMPTR=CHRptr[0];
- MMC5HackCHRMode=0;
- MMC5HackSPMode=MMC5HackSPScroll=MMC5HackSPPage=0;
- Mapper5_ESI();
-}
-
-void Mapper5_Init(CartInfo *info)
-{
- GenMMC5_Init(info, DetectMMC5WRAMSize(info->CRC32), info->battery);
-}
-
-// ELROM seems to have 0KB of WRAM
-// EKROM seems to have 8KB of WRAM
-// ETROM seems to have 16KB of WRAM
-// EWROM seems to have 32KB of WRAM
-
-// ETROM and EWROM are battery-backed, EKROM isn't.
-
-void ETROM_Init(CartInfo *info)
-{
- GenMMC5_Init(info, 16,info->battery);
-}
-
-void ELROM_Init(CartInfo *info)
-{
- GenMMC5_Init(info,0,0);
-}
-
-void EWROM_Init(CartInfo *info)
-{
- GenMMC5_Init(info,32,info->battery);
-}
-
-void EKROM_Init(CartInfo *info)
-{
- GenMMC5_Init(info,8,info->battery);
-}
+/* FCE Ultra - NES/Famicom Emulator\r
+ *\r
+ * Copyright notice for this file:\r
+ *  Copyright (C) 2002 Xodnizel\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
+ */\r
+\r
+/* None of this code should use any of the iNES bank switching wrappers. */\r
+\r
+#include "mapinc.h"\r
+\r
+static void (*sfun)(int P);\r
+static void (*psfun)(void);\r
+\r
+void MMC5RunSound(int Count);\r
+void MMC5RunSoundHQ(void);\r
+\r
+static INLINE void MMC5SPRVROM_BANK1(uint32 A,uint32 V)\r
+{\r
+  if(CHRptr[0])\r
+  {\r
+    V&=CHRmask1[0];\r
+    MMC5SPRVPage[(A)>>10]=&CHRptr[0][(V)<<10]-(A);\r
+  }\r
+}\r
+\r
+static INLINE void MMC5BGVROM_BANK1(uint32 A,uint32 V) {if(CHRptr[0]){V&=CHRmask1[0];MMC5BGVPage[(A)>>10]=&CHRptr[0][(V)<<10]-(A);}}\r
+\r
+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);}}\r
+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);}}\r
+\r
+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);}}\r
+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);}}\r
+\r
+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];}}\r
+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];}}\r
+\r
+static uint8 PRGBanks[4];\r
+static uint8 WRAMPage;\r
+static uint16 CHRBanksA[8], CHRBanksB[4];\r
+static uint8 WRAMMaskEnable[2];\r
+uint8 mmc5ABMode;                /* A=0, B=1 */\r
+\r
+static uint8 IRQScanline,IRQEnable;\r
+static uint8 CHRMode, NTAMirroring, NTFill, ATFill;\r
+\r
+static uint8 MMC5IRQR;\r
+static uint8 MMC5LineCounter;\r
+static uint8 mmc5psize, mmc5vsize;\r
+static uint8 mul[2];\r
+\r
+static uint8 *WRAM=NULL;\r
+static uint8 *MMC5fill=NULL;\r
+static uint8 *ExRAM=NULL;\r
+\r
+static uint8 MMC5WRAMsize;\r
+static uint8 MMC5WRAMIndex[8];\r
+\r
+static uint8 MMC5ROMWrProtect[4];\r
+static uint8 MMC5MemIn[5];\r
+\r
+static void MMC5CHRA(void);\r
+static void MMC5CHRB(void);\r
+\r
+typedef struct __cartdata {\r
+  uint32 crc32;\r
+  uint8 size;\r
+} cartdata;\r
+\r
+#define Sprite16  (PPU[0]&0x20)   //Sprites 8x16/8x8 \r
+//#define MMC5SPRVRAMADR(V)      &MMC5SPRVPage[(V)>>10][(V)]\r
+static inline uint8 *  MMC5BGVRAMADR(uint32 A)\r
+{\r
+       if(!Sprite16) {\r
+               if(mmc5ABMode==0)\r
+                       return &MMC5SPRVPage[(A)>>10][(A)];\r
+               else \r
+                       return &MMC5BGVPage[(A)>>10][(A)];\r
+       } else return &MMC5BGVPage[(A)>>10][(A)];\r
+}\r
+\r
+static void mmc5_PPUWrite(uint32 A, uint8 V) {\r
+       uint32 tmp = A;\r
+       extern uint8 PALRAM[0x20];\r
+\r
+       if(tmp>=0x3F00)\r
+               {\r
+                       // hmmm....\r
+                       if(!(tmp&0xf))\r
+                               PALRAM[0x00]=PALRAM[0x04]=PALRAM[0x08]=PALRAM[0x0C]=V&0x3F;\r
+                       else if(tmp&3) PALRAM[(tmp&0x1f)]=V&0x3f;\r
+               }\r
+               else if(tmp<0x2000)\r
+               {\r
+                       if(PPUCHRRAM&(1<<(tmp>>10)))\r
+                               VPage[tmp>>10][tmp]=V;\r
+               }   \r
+               else\r
+               {\r
+                       if(PPUNTARAM&(1<<((tmp&0xF00)>>10)))\r
+                               vnapage[((tmp&0xF00)>>10)][tmp&0x3FF]=V;\r
+               }\r
+}\r
+\r
+uint8 FASTCALL mmc5_PPURead(uint32 A) {\r
+       if(A<0x2000)\r
+       {\r
+               if(ppuphase == PPUPHASE_BG)\r
+                       return *MMC5BGVRAMADR(A);\r
+               else return MMC5SPRVPage[(A)>>10][(A)];\r
+       }\r
+       else\r
+       {   \r
+               return vnapage[(A>>10)&0x3][A&0x3FF];\r
+       }\r
+}\r
+\r
+\r
+\r
+// ELROM seems to have 8KB of RAM\r
+// ETROM seems to have 16KB of WRAM\r
+// EWROM seems to have 32KB of WRAM\r
+\r
+cartdata MMC5CartList[]=\r
+{\r
+  {0x9c18762b,2},         /* L'Empereur */\r
+  {0x26533405,2},\r
+  {0x6396b988,2},\r
+  {0xaca15643,2},        /* Uncharted Waters */\r
+  {0xfe3488d1,2},        /* Dai Koukai Jidai */\r
+  {0x15fe6d0f,2},        /* BKAC             */\r
+  {0x39f2ce4b,2},        /* Suikoden              */\r
+  {0x8ce478db,2},        /* Nobunaga's Ambition 2 */\r
+  {0xeee9a682,2},\r
+  {0xf9b4240f,2},\r
+  {0x1ced086f,2},        /* Ishin no Arashi */\r
+  {0xf540677b,4},        /* Nobunaga...Bushou Fuuun Roku */\r
+  {0x6f4e4312,4},        /* Aoki Ookami..Genchou */\r
+  {0xf011e490,4},        /* Romance of the 3 Kingdoms 2 */\r
+  {0x184c2124,4},        /* Sangokushi 2 */\r
+  {0xee8e6553,4},\r
+};\r
+\r
+#define MMC5_NOCARTS           (sizeof(MMC5CartList)/sizeof(MMC5CartList[0]))\r
+int DetectMMC5WRAMSize(uint32 crc32)\r
+{\r
+       int x;\r
+       for(x=0;x<MMC5_NOCARTS;x++) {\r
+               if(crc32==MMC5CartList[x].crc32) {\r
+                       FCEU_printf(" >8KB external WRAM present.  Use UNIF if you hack the ROM image.\n");\r
+                       return(MMC5CartList[x].size*8);\r
+               }\r
+       }\r
+\r
+       //mbg 04-aug-08 - previously, this was returning 8KB\r
+       //but I changed it to return 64 because unlisted carts are probably homebrews, and they should probably use 64 (why not use it all?)\r
+       //ch4 10-dec-08 - then f***ng for what all this shit above? let's give em all this 64k shit! Damn\r
+       //               homebrew must use it's own emulators or standart features.\r
+       //adelikat 20-dec-08 - reverting back to return 64, sounds like it was changed back to 8 simply on principle.  FCEUX is all encompassing, and that include\r
+       //rom-hacking.  We want it to be the best emulator for such purposes.  So unless return 64 harms compatibility with anything else, I see now reason not to have it\r
+       //mbg 29-mar-09 - I should note that mmc5 is in principle capable of 64KB, even if no real carts ever supported it.\r
+       //This does not in principle break any games which share this mapper, and it should be OK for homebrew.\r
+       //if there are games which need 8KB instead of 64KB default then lets add them to the list\r
+       return 64;\r
+}\r
+\r
+static void BuildWRAMSizeTable(void)\r
+{\r
+       int x;\r
+       for(x=0;x<8;x++)\r
+       {\r
+               switch(MMC5WRAMsize)\r
+               {\r
+               case 0: MMC5WRAMIndex[x]=255; break; //X,X,X,X,X,X,X,X\r
+               case 1: MMC5WRAMIndex[x]=(x>3)?255:0; break; //0,0,0,0,X,X,X,X\r
+               case 2: MMC5WRAMIndex[x]=(x&4)>>2; break; //0,0,0,0,1,1,1,1\r
+               case 4: MMC5WRAMIndex[x]=(x>3)?255:(x&3); break; //0,1,2,3,X,X,X,X\r
+               case 8: MMC5WRAMIndex[x]=x; break; //0,1,2,3,4,5,6,7,8  \r
+                       //mbg 8/6/08 - i added this to support 64KB of wram\r
+                       //now, I have at least one example (laser invasion) which actually uses size 1 but isnt in the crc list\r
+                       //so, whereas before my change on 8/4/08 we would have selected size 1, now we select size 8\r
+                       //this means that we could have just introduced an emulation bug, in case those games happened to \r
+                       //address, say, page 3. with size 1 that would resolve to [0] but in size 8 it resolves to [3].\r
+                       //so, you know what to do if there are problems.\r
+               }\r
+       }\r
+}\r
+\r
+static void MMC5CHRA(void)\r
+{\r
+  int x;\r
+  switch(mmc5vsize&3)\r
+  {\r
+    case 0: setchr8(CHRBanksA[7]);\r
+            MMC5SPRVROM_BANK8(CHRBanksA[7]);\r
+            break;\r
+    case 1: setchr4(0x0000,CHRBanksA[3]);\r
+            setchr4(0x1000,CHRBanksA[7]);\r
+            MMC5SPRVROM_BANK4(0x0000,CHRBanksA[3]);\r
+            MMC5SPRVROM_BANK4(0x1000,CHRBanksA[7]);\r
+            break;\r
+    case 2: setchr2(0x0000,CHRBanksA[1]);\r
+            setchr2(0x0800,CHRBanksA[3]);\r
+            setchr2(0x1000,CHRBanksA[5]);\r
+            setchr2(0x1800,CHRBanksA[7]);\r
+            MMC5SPRVROM_BANK2(0x0000,CHRBanksA[1]);\r
+            MMC5SPRVROM_BANK2(0x0800,CHRBanksA[3]);\r
+            MMC5SPRVROM_BANK2(0x1000,CHRBanksA[5]);\r
+            MMC5SPRVROM_BANK2(0x1800,CHRBanksA[7]);\r
+            break;\r
+    case 3: for(x=0;x<8;x++)\r
+            {\r
+              setchr1(x<<10,CHRBanksA[x]);\r
+              MMC5SPRVROM_BANK1(x<<10,CHRBanksA[x]);\r
+            }\r
+            break;\r
+  }\r
+}\r
+\r
+static void MMC5CHRB(void)\r
+{\r
+  int x;\r
+  switch(mmc5vsize&3)\r
+  {\r
+    case 0: setchr8(CHRBanksB[3]);\r
+            MMC5BGVROM_BANK8(CHRBanksB[3]);\r
+            break;\r
+    case 1: setchr4(0x0000,CHRBanksB[3]);\r
+            setchr4(0x1000,CHRBanksB[3]);\r
+            MMC5BGVROM_BANK4(0x0000,CHRBanksB[3]);\r
+            MMC5BGVROM_BANK4(0x1000,CHRBanksB[3]);\r
+            break;\r
+    case 2: setchr2(0x0000,CHRBanksB[1]);\r
+            setchr2(0x0800,CHRBanksB[3]);\r
+            setchr2(0x1000,CHRBanksB[1]);\r
+            setchr2(0x1800,CHRBanksB[3]);\r
+            MMC5BGVROM_BANK2(0x0000,CHRBanksB[1]);\r
+            MMC5BGVROM_BANK2(0x0800,CHRBanksB[3]);\r
+            MMC5BGVROM_BANK2(0x1000,CHRBanksB[1]);\r
+            MMC5BGVROM_BANK2(0x1800,CHRBanksB[3]);\r
+            break;\r
+    case 3: for(x=0;x<8;x++)\r
+            {\r
+              setchr1(x<<10,CHRBanksB[x&3]);\r
+              MMC5BGVROM_BANK1(x<<10,CHRBanksB[x&3]);\r
+            }\r
+            break;\r
+  }\r
+}\r
+\r
+static void MMC5WRAM(uint32 A, uint32 V)\r
+{\r
+  //printf("%02x\n",V);\r
+  V=MMC5WRAMIndex[V&7];\r
+  if(V!=255)\r
+  {\r
+    setprg8r(0x10,A,V);\r
+    MMC5MemIn[(A-0x6000)>>13]=1;\r
+  }\r
+  else\r
+    MMC5MemIn[(A-0x6000)>>13]=0;\r
+}\r
+\r
+static void MMC5PRG(void)\r
+{\r
+  int x;\r
+  switch(mmc5psize&3)\r
+  {\r
+    case 0: MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=\r
+            MMC5ROMWrProtect[2]=MMC5ROMWrProtect[3]=1;\r
+            setprg32(0x8000,((PRGBanks[1]&0x7F)>>2));\r
+            for(x=0;x<4;x++)\r
+               MMC5MemIn[1+x]=1;\r
+            break;\r
+    case 1: if(PRGBanks[1]&0x80)\r
+            {\r
+              MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=1;\r
+              setprg16(0x8000,(PRGBanks[1]>>1));\r
+              MMC5MemIn[1]=MMC5MemIn[2]=1;\r
+            }\r
+            else\r
+            {\r
+              MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=0;\r
+              MMC5WRAM(0x8000,PRGBanks[1]&7&0xFE);\r
+              MMC5WRAM(0xA000,(PRGBanks[1]&7&0xFE)+1);\r
+            }\r
+            MMC5MemIn[3]=MMC5MemIn[4]=1;\r
+            MMC5ROMWrProtect[2]=MMC5ROMWrProtect[3]=1;\r
+            setprg16(0xC000,(PRGBanks[3]&0x7F)>>1);\r
+            break;\r
+    case 2: if(PRGBanks[1]&0x80)\r
+            {\r
+              MMC5MemIn[1]=MMC5MemIn[2]=1;\r
+              MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=1;\r
+              setprg16(0x8000,(PRGBanks[1]&0x7F)>>1);\r
+            }\r
+            else\r
+            {\r
+              MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=0;\r
+              MMC5WRAM(0x8000,PRGBanks[1]&7&0xFE);\r
+              MMC5WRAM(0xA000,(PRGBanks[1]&7&0xFE)+1);\r
+            }\r
+            if(PRGBanks[2]&0x80)\r
+            {\r
+              MMC5ROMWrProtect[2]=1;\r
+              MMC5MemIn[3]=1;\r
+              setprg8(0xC000,PRGBanks[2]&0x7F);\r
+            }\r
+            else\r
+            {\r
+              MMC5ROMWrProtect[2]=0;\r
+              MMC5WRAM(0xC000,PRGBanks[2]&7);\r
+            }\r
+            MMC5MemIn[4]=1;\r
+            MMC5ROMWrProtect[3]=1;\r
+            setprg8(0xE000,PRGBanks[3]&0x7F);\r
+            break;\r
+    case 3: for(x=0;x<3;x++)\r
+            if(PRGBanks[x]&0x80)\r
+            {\r
+              MMC5ROMWrProtect[x]=1;\r
+              setprg8(0x8000+(x<<13),PRGBanks[x]&0x7F);\r
+              MMC5MemIn[1+x]=1;\r
+            }\r
+            else\r
+            {\r
+              MMC5ROMWrProtect[x]=0;\r
+              MMC5WRAM(0x8000+(x<<13),PRGBanks[x]&7);\r
+            }\r
+            MMC5MemIn[4]=1;\r
+            MMC5ROMWrProtect[3]=1;\r
+            setprg8(0xE000,PRGBanks[3]&0x7F);\r
+            break;\r
+  }\r
+}\r
+\r
+static DECLFW(Mapper5_write)\r
+{\r
+  if(A>=0x5120&&A<=0x5127)\r
+  {\r
+    mmc5ABMode = 0;\r
+    CHRBanksA[A&7]=V | ((MMC50x5130&0x3)<<8); //if we had a test case for this then we could test this, but it hasnt been verified\r
+       //CHRBanksA[A&7]=V;\r
+    MMC5CHRA();\r
+  }\r
+  else switch(A)\r
+  {\r
+    case 0x5105: {\r
+                   int x;\r
+                   for(x=0;x<4;x++)\r
+                   {\r
+                     switch((V>>(x<<1))&3)\r
+                     {\r
+                       case 0:\r
+                                                  PPUNTARAM|=1<<x;vnapage[x]=NTARAM;break;\r
+                       case 1:\r
+                                                  PPUNTARAM|=1<<x;vnapage[x]=NTARAM+0x400;break;\r
+                       case 2:\r
+                                                  PPUNTARAM|=1<<x;vnapage[x]=ExRAM;break;\r
+                       case 3:\r
+                                                  PPUNTARAM&=~(1<<x);vnapage[x]=MMC5fill;break;\r
+                     }\r
+                   }\r
+                 }\r
+                 NTAMirroring=V;\r
+                 break;\r
+    case 0x5113: WRAMPage=V;MMC5WRAM(0x6000,V&7);break;\r
+    case 0x5100: mmc5psize=V;MMC5PRG();break;\r
+    case 0x5101: mmc5vsize=V;\r
+                 if(!mmc5ABMode)\r
+                 { MMC5CHRB();MMC5CHRA();}\r
+                 else\r
+                 { MMC5CHRA();MMC5CHRB();}\r
+                 break;\r
+    case 0x5114:\r
+    case 0x5115:\r
+    case 0x5116:\r
+    case 0x5117: PRGBanks[A&3]=V;MMC5PRG();break;\r
+    case 0x5128:\r
+    case 0x5129:\r
+    case 0x512a:\r
+    case 0x512b: mmc5ABMode=1;\r
+                 CHRBanksB[A&3]=V;\r
+                 MMC5CHRB();\r
+                 break;\r
+    case 0x5102: WRAMMaskEnable[0]=V;break;\r
+    case 0x5103: WRAMMaskEnable[1]=V;break;\r
+    case 0x5104: CHRMode=V;MMC5HackCHRMode=V&3;break;\r
+    case 0x5106: if(V!=NTFill)\r
+                 {\r
+                   uint32 t;\r
+                   t=V|(V<<8)|(V<<16)|(V<<24);\r
+                   FCEU_dwmemset(MMC5fill,t,0x3c0);\r
+                 }\r
+                 NTFill=V;\r
+                 break;\r
+    case 0x5107: if(V!=ATFill)\r
+                 {\r
+                   unsigned char moop;\r
+                   uint32 t;\r
+                   moop=V|(V<<2)|(V<<4)|(V<<6);\r
+                   t=moop|(moop<<8)|(moop<<16)|(moop<<24);\r
+                   FCEU_dwmemset(MMC5fill+0x3c0,t,0x40);\r
+                 }\r
+                 ATFill=V;\r
+                 break;\r
+    case 0x5130: MMC50x5130=V;break;\r
+\r
+    case 0x5200: MMC5HackSPMode=V;break;\r
+    case 0x5201: MMC5HackSPScroll=(V>>3)&0x1F;break;\r
+    case 0x5202: MMC5HackSPPage=V&0x3F;break;\r
+    case 0x5203: X6502_IRQEnd(FCEU_IQEXT);IRQScanline=V;break;\r
+    case 0x5204: X6502_IRQEnd(FCEU_IQEXT);IRQEnable=V&0x80;break;\r
+    case 0x5205: mul[0]=V;break;\r
+    case 0x5206: mul[1]=V;break;\r
+  }\r
+}\r
+\r
+static DECLFR(MMC5_ReadROMRAM)\r
+{\r
+  if(MMC5MemIn[(A-0x6000)>>13])\r
+    return Page[A>>11][A];\r
+  else\r
+    return X.DB;\r
+}\r
+\r
+static DECLFW(MMC5_WriteROMRAM)\r
+{\r
+  if(A>=0x8000)\r
+    if(MMC5ROMWrProtect[(A-0x8000)>>13]) return;\r
+  if(MMC5MemIn[(A-0x6000)>>13])\r
+    if(((WRAMMaskEnable[0]&3)|((WRAMMaskEnable[1]&3)<<2)) == 6) \r
+               Page[A>>11][A]=V;\r
+}\r
+\r
+static DECLFW(MMC5_ExRAMWr)\r
+{\r
+  if(MMC5HackCHRMode!=3)\r
+    ExRAM[A&0x3ff]=V;\r
+}\r
+\r
+static DECLFR(MMC5_ExRAMRd)\r
+{\r
+ /* Not sure if this is correct, so I'll comment it out for now. */\r
+ //if(MMC5HackCHRMode>=2)\r
+  return ExRAM[A&0x3ff];\r
+ //else\r
+ // return(X.DB);\r
+}\r
+\r
+static DECLFR(MMC5_read)\r
+{\r
+  switch(A)\r
+  {\r
+    case 0x5204: X6502_IRQEnd(FCEU_IQEXT);\r
+                 {\r
+                   uint8 x;\r
+                   x=MMC5IRQR;\r
+                   if(!fceuindbg)\r
+                     MMC5IRQR&=0x40;\r
+                   return x;\r
+                 }\r
+    case 0x5205: return (mul[0]*mul[1]);\r
+    case 0x5206: return ((mul[0]*mul[1])>>8);\r
+  }\r
+  return(X.DB);\r
+}\r
+\r
+void MMC5Synco(void)\r
+{\r
+  int x;\r
+\r
+  MMC5PRG();\r
+  for(x=0;x<4;x++)\r
+  {\r
+    switch((NTAMirroring>>(x<<1))&3)\r
+    {\r
+      case 0:PPUNTARAM|=1<<x;vnapage[x]=NTARAM;break;\r
+      case 1:PPUNTARAM|=1<<x;vnapage[x]=NTARAM+0x400;break;\r
+      case 2:PPUNTARAM|=1<<x;vnapage[x]=ExRAM;break;\r
+      case 3:PPUNTARAM&=~(1<<x);vnapage[x]=MMC5fill;break;\r
+    }\r
+  }\r
+  MMC5WRAM(0x6000,WRAMPage&7);\r
+  if(!mmc5ABMode)\r
+  {\r
+    MMC5CHRB();\r
+    MMC5CHRA();\r
+  }\r
+  else\r
+  {\r
+    MMC5CHRA();\r
+    MMC5CHRB();\r
+  }\r
+  {\r
+    uint32 t;\r
+    t=NTFill|(NTFill<<8)|(NTFill<<16)|(NTFill<<24);\r
+    FCEU_dwmemset(MMC5fill,t,0x3c0);\r
+  }\r
+  {\r
+    unsigned char moop;\r
+    uint32 t;\r
+    moop=ATFill|(ATFill<<2)|(ATFill<<4)|(ATFill<<6);\r
+    t=moop|(moop<<8)|(moop<<16)|(moop<<24);\r
+    FCEU_dwmemset(MMC5fill+0x3c0,t,0x40);\r
+  }\r
+  X6502_IRQEnd(FCEU_IQEXT);\r
+  MMC5HackCHRMode=CHRMode&3;\r
+}\r
+\r
+void MMC5_hb(int scanline)\r
+{\r
+  if(scanline==240)\r
+  {\r
+    MMC5LineCounter=0;\r
+    MMC5IRQR=0x40;\r
+    return;\r
+  }\r
+  if(MMC5LineCounter<240)\r
+  {\r
+    if(MMC5LineCounter==IRQScanline)\r
+    {\r
+      MMC5IRQR|=0x80;\r
+      if(IRQEnable&0x80)\r
+        X6502_IRQBegin(FCEU_IQEXT);\r
+    }\r
+    MMC5LineCounter++;\r
+  }\r
+  if(MMC5LineCounter==240)\r
+    MMC5IRQR=0;\r
+}\r
+\r
+void MMC5_StateRestore(int version)\r
+{\r
+  MMC5Synco();\r
+}\r
+\r
+typedef struct {\r
+  uint16 wl[2];\r
+  uint8 env[2];\r
+  uint8 enable;\r
+  uint8 running;\r
+  uint8 raw;\r
+  uint8 rawcontrol;\r
+  int32 dcount[2];\r
+  int32 BC[3];\r
+  int32 vcount[2];\r
+} MMC5APU;\r
+\r
+static MMC5APU MMC5Sound;\r
+\r
+\r
+static void Do5PCM()\r
+{\r
+  int32 V;\r
+  int32 start,end;\r
+\r
+  start=MMC5Sound.BC[2];\r
+  end=(SOUNDTS<<16)/soundtsinc;\r
+  if(end<=start) return;\r
+  MMC5Sound.BC[2]=end;\r
+\r
+  if(!(MMC5Sound.rawcontrol&0x40) && MMC5Sound.raw)\r
+    for(V=start;V<end;V++)\r
+       Wave[V>>4]+=MMC5Sound.raw<<1;\r
+}\r
+\r
+static void Do5PCMHQ()\r
+{\r
+  uint32 V; //mbg merge 7/17/06 made uint32\r
+  if(!(MMC5Sound.rawcontrol&0x40) && MMC5Sound.raw)\r
+    for(V=MMC5Sound.BC[2];V<SOUNDTS;V++)\r
+       WaveHi[V]+=MMC5Sound.raw<<5;\r
+  MMC5Sound.BC[2]=SOUNDTS;\r
+}\r
+\r
+\r
+static DECLFW(Mapper5_SW)\r
+{\r
+  A&=0x1F;\r
+\r
+  GameExpSound.Fill=MMC5RunSound;\r
+  GameExpSound.HiFill=MMC5RunSoundHQ;\r
+\r
+  switch(A)\r
+  {\r
+    case 0x10:if(psfun) psfun();MMC5Sound.rawcontrol=V;break;\r
+    case 0x11:if(psfun) psfun();MMC5Sound.raw=V;break;\r
+\r
+    case 0x0:\r
+    case 0x4://printf("%04x:$%02x\n",A,V&0x30);\r
+              if(sfun) sfun(A>>2);\r
+              MMC5Sound.env[A>>2]=V;\r
+              break;\r
+    case 0x2:\r
+    case 0x6: if(sfun) sfun(A>>2);\r
+              MMC5Sound.wl[A>>2]&=~0x00FF;\r
+              MMC5Sound.wl[A>>2]|=V&0xFF;\r
+              break;\r
+    case 0x3:\r
+    case 0x7://printf("%04x:$%02x\n",A,V>>3);\r
+           MMC5Sound.wl[A>>2]&=~0x0700;\r
+           MMC5Sound.wl[A>>2]|=(V&0x07)<<8;\r
+           MMC5Sound.running|=1<<(A>>2);\r
+           break;\r
+  case 0x15:if(sfun)\r
+            {\r
+             sfun(0);\r
+             sfun(1);\r
+            }\r
+            MMC5Sound.running&=V;\r
+            MMC5Sound.enable=V;\r
+                //printf("%02x\n",V);\r
+            break;\r
+ }\r
+}\r
+\r
+static void Do5SQ(int P)\r
+{\r
+ static int tal[4]={1,2,4,6};\r
+ int32 V,amp,rthresh,wl;\r
+ int32 start,end;\r
+\r
+ start=MMC5Sound.BC[P];\r
+ end=(SOUNDTS<<16)/soundtsinc;\r
+ if(end<=start) return;\r
+ MMC5Sound.BC[P]=end;\r
+\r
+ wl=MMC5Sound.wl[P]+1;\r
+ amp=(MMC5Sound.env[P]&0xF)<<4;\r
+ rthresh=tal[(MMC5Sound.env[P]&0xC0)>>6];\r
+\r
+ if(wl>=8 && (MMC5Sound.running&(P+1)))\r
+ {\r
+  int dc,vc;\r
+\r
+  wl<<=18;\r
+  dc=MMC5Sound.dcount[P];\r
+  vc=MMC5Sound.vcount[P];\r
+\r
+  for(V=start;V<end;V++)\r
+  {\r
+    if(dc<rthresh)\r
+     Wave[V>>4]+=amp;\r
+    vc-=nesincsize;\r
+    while(vc<=0)\r
+    {\r
+     vc+=wl;\r
+     dc=(dc+1)&7;\r
+    }\r
+  }\r
+  MMC5Sound.dcount[P]=dc;\r
+  MMC5Sound.vcount[P]=vc;\r
+ }\r
+}\r
+\r
+static void Do5SQHQ(int P)\r
+{\r
+ static int tal[4]={1,2,4,6};\r
+ uint32 V; //mbg merge 7/17/06 made uint32\r
+ int32 amp,rthresh,wl;\r
+\r
+ wl=MMC5Sound.wl[P]+1;\r
+ amp=((MMC5Sound.env[P]&0xF)<<8);\r
+ rthresh=tal[(MMC5Sound.env[P]&0xC0)>>6];\r
+\r
+ if(wl>=8 && (MMC5Sound.running&(P+1)))\r
+ {\r
+  int dc,vc;\r
+\r
+  wl<<=1;\r
+\r
+  dc=MMC5Sound.dcount[P];\r
+  vc=MMC5Sound.vcount[P];\r
+  for(V=MMC5Sound.BC[P];V<SOUNDTS;V++)\r
+  {\r
+    if(dc<rthresh)\r
+     WaveHi[V]+=amp;\r
+    vc--;\r
+    if(vc<=0)   /* Less than zero when first started. */\r
+    {\r
+     vc=wl;\r
+     dc=(dc+1)&7;\r
+    }\r
+  }\r
+  MMC5Sound.dcount[P]=dc;\r
+  MMC5Sound.vcount[P]=vc;\r
+ }\r
+ MMC5Sound.BC[P]=SOUNDTS;\r
+}\r
+\r
+void MMC5RunSoundHQ(void)\r
+{\r
+  Do5SQHQ(0);\r
+  Do5SQHQ(1);\r
+  Do5PCMHQ();\r
+}\r
+\r
+void MMC5HiSync(int32 ts)\r
+{\r
+ int x;\r
+ for(x=0;x<3;x++) MMC5Sound.BC[x]=ts;\r
+}\r
+\r
+void MMC5RunSound(int Count)\r
+{\r
+  int x;\r
+  Do5SQ(0);\r
+  Do5SQ(1);\r
+  Do5PCM();\r
+  for(x=0;x<3;x++)\r
+   MMC5Sound.BC[x]=Count;\r
+}\r
+\r
+void Mapper5_ESI(void)\r
+{\r
+ GameExpSound.RChange=Mapper5_ESI;\r
+ if(FSettings.SndRate)\r
+ {\r
+  if(FSettings.soundq>=1)\r
+  {\r
+   sfun=Do5SQHQ;\r
+   psfun=Do5PCMHQ;\r
+  }\r
+  else\r
+  {\r
+   sfun=Do5SQ;\r
+   psfun=Do5PCM;\r
+  }\r
+ }\r
+ else\r
+ {\r
+  sfun=0;\r
+  psfun=0;\r
+ }\r
+ memset(MMC5Sound.BC,0,sizeof(MMC5Sound.BC));\r
+ memset(MMC5Sound.vcount,0,sizeof(MMC5Sound.vcount));\r
+ GameExpSound.HiSync=MMC5HiSync;\r
+}\r
+\r
+void NSFMMC5_Init(void)\r
+{\r
+  memset(&MMC5Sound,0,sizeof(MMC5Sound));\r
+  mul[0]=mul[1]=0;\r
+  ExRAM=(uint8*)FCEU_gmalloc(1024);\r
+  Mapper5_ESI();\r
+  SetWriteHandler(0x5c00,0x5fef,MMC5_ExRAMWr);\r
+  SetReadHandler(0x5c00,0x5fef,MMC5_ExRAMRd);\r
+  MMC5HackCHRMode=2;\r
+  SetWriteHandler(0x5000,0x5015,Mapper5_SW);\r
+  SetWriteHandler(0x5205,0x5206,Mapper5_write);\r
+  SetReadHandler(0x5205,0x5206,MMC5_read);\r
+}\r
+\r
+void NSFMMC5_Close(void)\r
+{\r
+ FCEU_gfree(ExRAM);\r
+ ExRAM=0;\r
+}\r
+\r
+static void GenMMC5Reset(void)\r
+{\r
+ int x;\r
+\r
+ for(x=0;x<4;x++) PRGBanks[x]=~0;\r
+ for(x=0;x<8;x++) CHRBanksA[x]=~0;\r
+ for(x=0;x<4;x++) CHRBanksB[x]=~0;\r
+ WRAMMaskEnable[0]=WRAMMaskEnable[1]=~0;\r
+\r
+ mmc5psize=mmc5vsize=3;\r
+ CHRMode=0;\r
+\r
+ NTAMirroring=NTFill=ATFill=0xFF;\r
+\r
+ MMC5Synco();\r
+\r
+ SetWriteHandler(0x4020,0x5bff,Mapper5_write);\r
+ SetReadHandler(0x4020,0x5bff,MMC5_read);\r
+\r
+ SetWriteHandler(0x5c00,0x5fff,MMC5_ExRAMWr);\r
+ SetReadHandler(0x5c00,0x5fff,MMC5_ExRAMRd);\r
+\r
+ SetWriteHandler(0x6000,0xFFFF,MMC5_WriteROMRAM);\r
+ SetReadHandler(0x6000,0xFFFF,MMC5_ReadROMRAM);\r
+\r
+ SetWriteHandler(0x5000,0x5015,Mapper5_SW);\r
+ SetWriteHandler(0x5205,0x5206,Mapper5_write);\r
+ SetReadHandler(0x5205,0x5206,MMC5_read);\r
+\r
+ //GameHBIRQHook=MMC5_hb;\r
+ FCEU_CheatAddRAM(8,0x6000,WRAM);\r
+ FCEU_CheatAddRAM(1,0x5c00,ExRAM);\r
+}\r
+\r
+static SFORMAT MMC5_StateRegs[]={\r
+        { PRGBanks, 4, "PRGB"},\r
+        { CHRBanksA, 16, "CHRA"},\r
+        { CHRBanksB, 8, "CHRB"},\r
+        { &WRAMPage, 1, "WRMP"},\r
+        { WRAMMaskEnable, 2, "WRME"},\r
+        { &mmc5ABMode, 1, "ABMD"},\r
+        { &IRQScanline, 1, "IRQS"},\r
+        { &IRQEnable, 1, "IRQE"},\r
+        { &CHRMode, 1, "CHRM"},\r
+        { &NTAMirroring, 1, "NTAM"},\r
+        { &NTFill, 1, "NTFL"},\r
+        { &ATFill, 1, "ATFL"},\r
+\r
+        { &MMC5Sound.wl[0], 2|FCEUSTATE_RLSB, "SDW0"},\r
+        { &MMC5Sound.wl[1], 2|FCEUSTATE_RLSB, "SDW1"},\r
+        { MMC5Sound.env, 2, "SDEV"},\r
+        { &MMC5Sound.enable, 1, "SDEN"},\r
+        { &MMC5Sound.running, 1, "SDRU"},\r
+        { &MMC5Sound.raw, 1, "SDRW"},\r
+        { &MMC5Sound.rawcontrol, 1, "SDRC"},\r
+        {0}\r
+};\r
+\r
+static void GenMMC5_Init(CartInfo *info, int wsize, int battery)\r
+{\r
+ if(wsize)\r
+ {\r
+  WRAM=(uint8*)FCEU_gmalloc(wsize*1024);\r
+  SetupCartPRGMapping(0x10,WRAM,wsize*1024,1);\r
+  AddExState(WRAM, wsize*1024, 0, "WRAM");\r
+ }\r
+\r
+ MMC5fill=(uint8*)FCEU_gmalloc(1024);\r
+ ExRAM=(uint8*)FCEU_gmalloc(1024);\r
+\r
+ AddExState(MMC5_StateRegs, ~0, 0, 0);\r
+ AddExState(WRAM, wsize*1024, 0, "WRAM");\r
+ AddExState(ExRAM, 1024, 0, "ERAM");\r
+ AddExState(&MMC5HackSPMode, 1, 0, "SPLM");\r
+ AddExState(&MMC5HackSPScroll, 1, 0, "SPLS");\r
+ AddExState(&MMC5HackSPPage, 1, 0, "SPLP");\r
+ AddExState(&MMC50x5130, 1, 0, "5130");\r
+\r
+ MMC5WRAMsize=wsize/8;\r
+ BuildWRAMSizeTable();\r
+ GameStateRestore=MMC5_StateRestore;\r
+ info->Power=GenMMC5Reset;\r
+\r
+ if(battery)\r
+ {\r
+  info->SaveGame[0]=WRAM;\r
+  if(wsize<=16)\r
+   info->SaveGameLen[0]=8192;\r
+  else\r
+   info->SaveGameLen[0]=32768;\r
+ }\r
+\r
+ MMC5HackVROMMask=CHRmask4[0];\r
+ MMC5HackExNTARAMPtr=ExRAM;\r
+ MMC5Hack=1;\r
+ MMC5HackVROMPTR=CHRptr[0];\r
+ MMC5HackCHRMode=0;\r
+ MMC5HackSPMode=MMC5HackSPScroll=MMC5HackSPPage=0;\r
+ Mapper5_ESI();\r
+\r
+ FFCEUX_PPURead = mmc5_PPURead;\r
+ FFCEUX_PPUWrite = mmc5_PPUWrite;\r
+}\r
+\r
+void Mapper5_Init(CartInfo *info)\r
+{\r
+ GenMMC5_Init(info, DetectMMC5WRAMSize(info->CRC32), info->battery);\r
+}\r
+\r
+// ELROM seems to have 0KB of WRAM\r
+// EKROM seems to have 8KB of WRAM\r
+// ETROM seems to have 16KB of WRAM\r
+// EWROM seems to have 32KB of WRAM\r
+\r
+// ETROM and EWROM are battery-backed, EKROM isn't.\r
+\r
+void ETROM_Init(CartInfo *info)\r
+{\r
+ GenMMC5_Init(info, 16,info->battery);\r
+}\r
+\r
+void ELROM_Init(CartInfo *info)\r
+{\r
+ GenMMC5_Init(info,0,0);\r
+}\r
+\r
+void EWROM_Init(CartInfo *info)\r
+{\r
+ GenMMC5_Init(info,32,info->battery);\r
+}\r
+\r
+void EKROM_Init(CartInfo *info)\r
+{\r
+ GenMMC5_Init(info,8,info->battery);\r
+}\r