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