merge mapper code from FCEUX
[fceu.git] / boards / n106.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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21 #include "mapinc.h"
22
23 static uint16 IRQCount;
24 static uint8 IRQa;
25
26 static uint8 WRAM[8192];
27 static uint8 IRAM[128];
28
29 static DECLFR(AWRAM)
30 {
31   return(WRAM[A-0x6000]);
32 }
33
34 static DECLFW(BWRAM)
35 {
36   WRAM[A-0x6000]=V;
37 }
38
39 void Mapper19_ESI(void);
40
41 static uint8 NTAPage[4];
42
43 static uint8 dopol;
44 static uint8 gorfus;
45 static uint8 gorko;
46
47 static void NamcoSound(int Count);
48 static void NamcoSoundHack(void);
49 static void DoNamcoSound(int32 *Wave, int Count);
50 static void DoNamcoSoundHQ(void);
51 static void SyncHQ(int32 ts);
52
53 static int is210;        /* Lesser mapper. */
54
55 static uint8 PRG[3];
56 static uint8 CHR[8];
57
58 static SFORMAT N106_StateRegs[]={
59   {PRG,3,"PRG"},
60   {CHR,8,"CHR"},
61   {NTAPage,4,"NTA"},
62   {0}
63 };
64
65 static void SyncPRG(void)
66 {
67   setprg8(0x8000,PRG[0]);
68   setprg8(0xa000,PRG[1]);
69   setprg8(0xc000,PRG[2]);
70   setprg8(0xe000,0x3F);
71 }
72
73 static void NamcoIRQHook(int a)
74 {
75   if(IRQa)
76   {
77     IRQCount+=a;
78     if(IRQCount>=0x7FFF)
79     {
80       X6502_IRQBegin(FCEU_IQEXT);
81       IRQa=0;
82       IRQCount=0x7FFF; //7FFF;
83     }
84   }
85 }
86
87 static DECLFR(Namco_Read4800)
88 {
89   uint8 ret=IRAM[dopol&0x7f];
90   /* Maybe I should call NamcoSoundHack() here? */
91   if(!fceuindbg)
92     if(dopol&0x80)
93       dopol=(dopol&0x80)|((dopol+1)&0x7f);
94   return ret;
95 }
96
97 static DECLFR(Namco_Read5000)
98 {
99   return(IRQCount);
100 }
101
102 static DECLFR(Namco_Read5800)
103 {
104   return(IRQCount>>8);
105 }
106
107 static void DoNTARAMROM(int w, uint8 V)
108 {
109   NTAPage[w]=V;
110   if(V>=0xE0)
111     setntamem(NTARAM+((V&1)<<10), 1, w);
112   else
113   {
114     V&=CHRmask1[0];
115     setntamem(CHRptr[0]+(V<<10), 0, w);
116   }
117 }
118
119 static void FixNTAR(void)
120 {
121   int x;
122   for(x=0;x<4;x++)
123      DoNTARAMROM(x,NTAPage[x]);
124 }
125
126 static void DoCHRRAMROM(int x, uint8 V)
127 {
128   CHR[x]=V;
129   if(!is210 && !((gorfus>>((x>>2)+6))&1) && (V>=0xE0))
130   {
131     // printf("BLAHAHA: %d, %02x\n",x,V);
132     //setchr1r(0x10,x<<10,V&7);
133   }
134   else
135     setchr1(x<<10,V);
136 }
137
138 static void FixCRR(void)
139 {
140   int x;
141   for(x=0;x<8;x++)
142      DoCHRRAMROM(x,CHR[x]);
143 }
144
145 static DECLFW(Mapper19C0D8_write)
146 {
147   DoNTARAMROM((A-0xC000)>>11,V);
148 }
149
150 static uint32 FreqCache[8];
151 static uint32 EnvCache[8];
152 static uint32 LengthCache[8];
153
154 static void FixCache(int a,int V)
155 {
156   int w=(a>>3)&0x7;
157   switch(a&0x07)
158   {
159     case 0x00:FreqCache[w]&=~0x000000FF;FreqCache[w]|=V;break;
160     case 0x02:FreqCache[w]&=~0x0000FF00;FreqCache[w]|=V<<8;break;
161     case 0x04:FreqCache[w]&=~0x00030000;FreqCache[w]|=(V&3)<<16;
162               LengthCache[w]=(8-((V>>2)&7))<<2;
163               break;
164     case 0x07:EnvCache[w]=(double)(V&0xF)*576716;break;
165   }
166 }
167
168 static DECLFW(Mapper19_write)
169 {
170   A&=0xF800;
171   if(A>=0x8000 && A<=0xb800)
172     DoCHRRAMROM((A-0x8000)>>11,V);
173   else switch(A)
174   {
175     case 0x4800:
176          if(dopol&0x40)
177          {
178            if(FSettings.SndRate)
179            {
180              NamcoSoundHack();
181              GameExpSound.Fill=NamcoSound;
182              GameExpSound.HiFill=DoNamcoSoundHQ;
183              GameExpSound.HiSync=SyncHQ;
184            }
185            FixCache(dopol,V);
186          }
187          IRAM[dopol&0x7f]=V;
188          if(dopol&0x80)
189            dopol=(dopol&0x80)|((dopol+1)&0x7f);
190          break;
191     case 0xf800:
192          dopol=V;break;
193     case 0x5000:
194          IRQCount&=0xFF00;IRQCount|=V;X6502_IRQEnd(FCEU_IQEXT);break;
195     case 0x5800:
196          IRQCount&=0x00ff;IRQCount|=(V&0x7F)<<8;
197          IRQa=V&0x80;
198          X6502_IRQEnd(FCEU_IQEXT);
199          break;
200     case 0xE000:
201          gorko=V&0xC0;
202          PRG[0]=V&0x3F;
203          SyncPRG();
204          break;
205     case 0xE800:
206          gorfus=V&0xC0;
207          FixCRR();
208          PRG[1]=V&0x3F;
209          SyncPRG();
210          break;
211     case 0xF000:
212          PRG[2]=V&0x3F;
213          SyncPRG();
214          break;
215   }
216 }
217
218 static int dwave=0;
219
220 static void NamcoSoundHack(void)
221 {
222   int32 z,a;
223   if(FSettings.soundq>=1)
224   {
225     DoNamcoSoundHQ();
226     return;
227   }
228   z=((SOUNDTS<<16)/soundtsinc)>>4;
229   a=z-dwave;
230   if(a) DoNamcoSound(&Wave[dwave], a);
231   dwave+=a;
232 }
233
234 static void NamcoSound(int Count)
235 {
236   int32 z,a;
237   z=((SOUNDTS<<16)/soundtsinc)>>4;
238   a=z-dwave;
239   if(a) DoNamcoSound(&Wave[dwave], a);
240   dwave=0;
241 }
242
243 static uint32 PlayIndex[8];
244 static int32 vcount[8];
245 static int32 CVBC;
246
247 #define TOINDEX        (16+1)
248
249 // 16:15
250 static void SyncHQ(int32 ts)
251 {
252   CVBC=ts;
253 }
254
255
256 /* Things to do:
257         1        Read freq low
258         2        Read freq mid
259         3        Read freq high
260         4        Read envelope
261         ...?
262 */
263
264 static INLINE uint32 FetchDuff(uint32 P, uint32 envelope)
265 {
266   uint32 duff;
267   duff=IRAM[((IRAM[0x46+(P<<3)]+(PlayIndex[P]>>TOINDEX))&0xFF)>>1];
268   if((IRAM[0x46+(P<<3)]+(PlayIndex[P]>>TOINDEX))&1)
269     duff>>=4;
270   duff&=0xF;
271   duff=(duff*envelope)>>16;
272   return(duff);
273 }
274
275 static void DoNamcoSoundHQ(void)
276 {
277   uint32 V; //mbg merge 7/17/06 made uint32
278   int32 P; 
279   int32 cyclesuck=(((IRAM[0x7F]>>4)&7)+1)*15;
280
281   for(P=7;P>=(7-((IRAM[0x7F]>>4)&7));P--)
282   {
283     if((IRAM[0x44+(P<<3)]&0xE0) && (IRAM[0x47+(P<<3)]&0xF))
284     {
285       uint32 freq;
286       int32 vco;
287       uint32 duff2,lengo,envelope;
288
289       vco=vcount[P];
290       freq=FreqCache[P];
291       envelope=EnvCache[P];
292       lengo=LengthCache[P];
293
294       duff2=FetchDuff(P,envelope);
295       for(V=CVBC<<1;V<SOUNDTS<<1;V++)
296       {
297         WaveHi[V>>1]+=duff2;
298         if(!vco)
299         {
300           PlayIndex[P]+=freq;
301           while((PlayIndex[P]>>TOINDEX)>=lengo) PlayIndex[P]-=lengo<<TOINDEX;
302           duff2=FetchDuff(P,envelope);
303           vco=cyclesuck;
304         }
305         vco--;
306       }
307       vcount[P]=vco;
308     }
309   }
310   CVBC=SOUNDTS;
311 }
312
313
314 static void DoNamcoSound(int32 *Wave, int Count)
315 {
316   int P,V;
317   for(P=7;P>=7-((IRAM[0x7F]>>4)&7);P--)
318   {
319     if((IRAM[0x44+(P<<3)]&0xE0) && (IRAM[0x47+(P<<3)]&0xF))
320     {
321       int32 inc;
322       uint32 freq;
323       int32 vco;
324       uint32 duff,duff2,lengo,envelope;
325
326       vco=vcount[P];
327       freq=FreqCache[P];
328       envelope=EnvCache[P];
329       lengo=LengthCache[P];
330
331       if(!freq) {/*printf("Ack");*/  continue;}
332
333       {
334         int c=((IRAM[0x7F]>>4)&7)+1;
335         inc=(long double)(FSettings.SndRate<<15)/((long double)freq*21477272/((long double)0x400000*c*45));
336       }
337
338       duff=IRAM[(((IRAM[0x46+(P<<3)]+PlayIndex[P])&0xFF)>>1)];
339       if((IRAM[0x46+(P<<3)]+PlayIndex[P])&1)
340         duff>>=4;
341       duff&=0xF;
342       duff2=(duff*envelope)>>19;
343       for(V=0;V<Count*16;V++)
344       {
345         if(vco>=inc)
346         {
347           PlayIndex[P]++;
348           if(PlayIndex[P]>=lengo)
349             PlayIndex[P]=0;
350           vco-=inc;
351           duff=IRAM[(((IRAM[0x46+(P<<3)]+PlayIndex[P])&0xFF)>>1)];
352           if((IRAM[0x46+(P<<3)]+PlayIndex[P])&1)
353             duff>>=4;
354           duff&=0xF;
355           duff2=(duff*envelope)>>19;
356         }
357         Wave[V>>4]+=duff2;
358         vco+=0x8000;
359       }
360       vcount[P]=vco;
361     }
362   }
363 }
364
365 static void Mapper19_StateRestore(int version)
366 {
367   int x;
368   SyncPRG();
369   FixNTAR();
370   FixCRR();
371   for(x=0x40;x<0x80;x++)
372      FixCache(x,IRAM[x]);
373 }
374
375 static void M19SC(void)
376 {
377   if(FSettings.SndRate)
378     Mapper19_ESI();
379 }
380
381 void Mapper19_ESI(void)
382 {
383   GameExpSound.RChange=M19SC;
384   memset(vcount,0,sizeof(vcount));
385   memset(PlayIndex,0,sizeof(PlayIndex));
386   CVBC=0;
387 }
388
389 void NSFN106_Init(void)
390 {
391   SetWriteHandler(0xf800,0xffff,Mapper19_write);
392   SetWriteHandler(0x4800,0x4fff,Mapper19_write);
393   SetReadHandler(0x4800,0x4fff,Namco_Read4800);
394   Mapper19_ESI();
395 }
396
397 static int battery=0;
398
399 static void N106_Power(void)
400 {
401   int x;
402   SetReadHandler(0x8000,0xFFFF,CartBR);
403   SetWriteHandler(0x8000,0xffff,Mapper19_write);
404   SetWriteHandler(0x4020,0x5fff,Mapper19_write);
405   if(!is210)
406   {
407     SetWriteHandler(0xc000,0xdfff,Mapper19C0D8_write);
408     SetReadHandler(0x4800,0x4fff,Namco_Read4800);
409     SetReadHandler(0x5000,0x57ff,Namco_Read5000);
410     SetReadHandler(0x5800,0x5fff,Namco_Read5800);
411     NTAPage[0]=NTAPage[1]=NTAPage[2]=NTAPage[3]=0xFF;
412     FixNTAR();
413   }
414
415   SetReadHandler(0x6000,0x7FFF,AWRAM);
416   SetWriteHandler(0x6000,0x7FFF,BWRAM);
417   FCEU_CheatAddRAM(8,0x6000,WRAM);
418
419   gorfus=0xFF;
420   SyncPRG();
421   FixCRR();
422
423   if(!battery)
424   {
425     FCEU_dwmemset(WRAM,0,8192);
426     FCEU_dwmemset(IRAM,0,128);
427   }
428   for(x=0x40;x<0x80;x++)
429      FixCache(x,IRAM[x]);
430 }
431
432 void Mapper19_Init(CartInfo *info)
433 {
434   is210=0;
435   battery=info->battery;
436   info->Power=N106_Power;
437
438   MapIRQHook=NamcoIRQHook;
439   GameStateRestore=Mapper19_StateRestore;
440   GameExpSound.RChange=M19SC;
441
442   if(FSettings.SndRate)
443     Mapper19_ESI();
444
445   AddExState(WRAM, 8192, 0, "WRAM");
446   AddExState(IRAM, 128, 0, "IRAM");
447   AddExState(N106_StateRegs, ~0, 0, 0);
448
449   if(info->battery)
450   {
451     info->SaveGame[0]=WRAM;
452     info->SaveGameLen[0]=8192;
453     info->SaveGame[1]=IRAM;
454     info->SaveGameLen[1]=128;
455   }
456 }
457
458 static void Mapper210_StateRestore(int version)
459 {
460   SyncPRG();
461   FixCRR();
462 }
463
464 void Mapper210_Init(CartInfo *info)
465 {
466   is210=1;
467   GameStateRestore=Mapper210_StateRestore;
468   info->Power=N106_Power;
469   AddExState(WRAM, 8192, 0, "WRAM");
470   AddExState(N106_StateRegs, ~0, 0, 0);
471 }