098 video fix, 098 sound integrated
[fceu.git] / sound098.c
1 /* FCE Ultra - NES/Famicom Emulator
2  *
3  * Copyright notice for this file:
4  *  Copyright (C) 2002 Xodnizel
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdlib.h>
22 #include <stdio.h>
23
24 #include <string.h>
25
26 #include "types.h"
27 #include "x6502.h"
28
29 #include "fce.h"
30 #include "sound.h"
31 #include "filter098.h"
32 #include "state.h"
33 #include "svga.h"
34 //#include "wave.h"
35
36 static uint32 wlookup1[32];
37 static uint32 wlookup2[203];
38
39 int32 WaveHi[40000];
40 //int32 WaveFinal[2048+512]; // TODO: use WaveFinalMono
41
42 static uint8 TriCount=0;
43 static uint8 TriMode=0;
44
45 static int32 tristep=0;
46
47 static int32 wlcount[4]={0,0,0,0};      /* Wave length counters.        */
48
49 static uint8 IRQFrameMode=0;    /* $4017 / xx000000 */
50 //static uint8 PSG[0x10];
51 static uint8 RawDALatch=0;      /* $4011 0xxxxxxx */
52
53 static uint8 EnabledChannels=0;         /* Byte written to $4015 */
54
55 typedef struct {
56         uint8 Speed;
57         uint8 Mode;     /* Fixed volume(1), and loop(2) */
58         uint8 DecCountTo1;
59         uint8 decvolume;
60         int reloaddec;
61 } ENVUNIT;
62
63 static ENVUNIT EnvUnits[3];
64
65 static const int RectDuties[4]={1,2,4,6};
66
67 static int32 RectDutyCount[2];
68 //static uint8 sweepon[2];
69 //static int32 curfreq[2];
70 //static uint8 SweepCount[2];
71
72 //static uint16 nreg=0;
73
74 //static uint8 fcnt=0;
75 //static int32 fhcnt=0;
76 //static int32 fhinc=0;
77
78 /* Variables exclusively for low-quality sound. */
79 static int32 sqacc[2];
80 /* LQ variables segment ends. */
81
82 static int32 lengthcount[4];
83 static const uint8 lengthtable[0x20]=
84 {
85  0x5*2,0x7f*2,0xA*2,0x1*2,0x14*2,0x2*2,0x28*2,0x3*2,0x50*2,0x4*2,0x1E*2,0x5*2,0x7*2,0x6*2,0x0E*2,0x7*2,
86  0x6*2,0x08*2,0xC*2,0x9*2,0x18*2,0xa*2,0x30*2,0xb*2,0x60*2,0xc*2,0x24*2,0xd*2,0x8*2,0xe*2,0x10*2,0xf*2
87 };
88
89 static const uint32 NoiseFreqTable[0x10]=
90 {
91  2,4,8,0x10,0x20,0x30,0x40,0x50,0x65,0x7f,0xbe,0xfe,0x17d,0x1fc,0x3f9,0x7f2
92 };
93
94 static const uint32 NTSCDMCTable[0x10]=
95 {
96  428,380,340,320,286,254,226,214,
97  190,160,142,128,106, 84 ,72,54
98 };
99
100 static const uint32 PALDMCTable[0x10]=
101 {
102  397, 353, 315, 297, 265, 235, 209, 198,
103  176, 148, 131, 118, 98, 78, 66, 50,
104 };
105
106 // $4010        -        Frequency
107 // $4011        -        Actual data outputted
108 // $4012        -        Address register: $c000 + V*64
109 // $4013        -        Size register:  Size in bytes = (V+1)*64
110
111 static int32 DMCacc=1;
112 static int32 DMCPeriod=0;
113 static uint8 DMCBitCount=0;
114
115 static uint8 DMCAddressLatch=0,DMCSizeLatch=0; /* writes to 4012 and 4013 */
116 static uint8 DMCFormat=0;       /* Write to $4010 */
117
118 static uint32 DMCAddress=0;
119 static int32 DMCSize=0;
120 static uint8 DMCShift=0;
121 static uint8 SIRQStat=0;
122
123 static char DMCHaveDMA=0;
124 static uint8 DMCDMABuf=0;
125 static char DMCHaveSample=0;
126
127 static void Dummyfunc(void) {};
128 static void (*DoNoise)(void)=Dummyfunc;
129 static void (*DoTriangle)(void)=Dummyfunc;
130 static void (*DoPCM)(void)=Dummyfunc;
131 static void (*DoSQ1)(void)=Dummyfunc;
132 static void (*DoSQ2)(void)=Dummyfunc;
133
134 static uint32 ChannelBC[5];
135
136 static void LoadDMCPeriod(uint8 V)
137 {
138  if(PAL)
139   DMCPeriod=PALDMCTable[V];
140  else
141   DMCPeriod=NTSCDMCTable[V];
142 }
143
144 static void PrepDPCM()
145 {
146  DMCAddress=0x4000+(DMCAddressLatch<<6);
147  DMCSize=(DMCSizeLatch<<4)+1;
148 }
149
150 /* Instantaneous?  Maybe the new freq value is being calculated all of the time... */
151
152 static int FASTAPASS(2) CheckFreq(uint32 cf, uint8 sr)
153 {
154  uint32 mod;
155  if(!(sr&0x8))
156  {
157   mod=cf>>(sr&7);
158   if((mod+cf)&0x800)
159    return(0);
160  }
161  return(1);
162 }
163
164 static void SQReload(int x, uint8 V)
165 {
166            if(EnabledChannels&(1<<x))
167            {
168             if(x)
169              DoSQ2();
170             else
171              DoSQ1();
172             lengthcount[x]=lengthtable[(V>>3)&0x1f];
173            }
174
175            sweepon[x]=PSG[(x<<2)|1]&0x80;
176            curfreq[x]=PSG[(x<<2)|0x2]|((V&7)<<8);
177            SweepCount[x]=((PSG[(x<<2)|0x1]>>4)&7)+1;
178
179            RectDutyCount[x]=7;
180            EnvUnits[x].reloaddec=1;
181            //reloadfreq[x]=1;
182 }
183
184 static DECLFW(Write_PSG)
185 {
186  A&=0x1F;
187  switch(A)
188  {
189   case 0x0:DoSQ1();
190            EnvUnits[0].Mode=(V&0x30)>>4;
191            EnvUnits[0].Speed=(V&0xF);
192            break;
193   case 0x1:
194            sweepon[0]=V&0x80;
195            break;
196   case 0x2:
197            DoSQ1();
198            curfreq[0]&=0xFF00;
199            curfreq[0]|=V;
200            break;
201   case 0x3:
202            SQReload(0,V);
203            break;
204   case 0x4:
205            DoSQ2();
206            EnvUnits[1].Mode=(V&0x30)>>4;
207            EnvUnits[1].Speed=(V&0xF);
208            break;
209   case 0x5:
210           sweepon[1]=V&0x80;
211           break;
212   case 0x6:DoSQ2();
213           curfreq[1]&=0xFF00;
214           curfreq[1]|=V;
215           break;
216   case 0x7:
217           SQReload(1,V);
218           break;
219   case 0xa:DoTriangle();
220            break;
221   case 0xb:
222           DoTriangle();
223           if(EnabledChannels&0x4)
224            lengthcount[2]=lengthtable[(V>>3)&0x1f];
225           TriMode=1;    // Load mode
226           break;
227   case 0xC:DoNoise();
228            EnvUnits[2].Mode=(V&0x30)>>4;
229            EnvUnits[2].Speed=(V&0xF);
230            break;
231   case 0xE:DoNoise();
232            break;
233   case 0xF:
234            DoNoise();
235            if(EnabledChannels&0x8)
236             lengthcount[3]=lengthtable[(V>>3)&0x1f];
237            EnvUnits[2].reloaddec=1;
238            break;
239  case 0x10:DoPCM();
240            LoadDMCPeriod(V&0xF);
241
242            if(SIRQStat&0x80)
243            {
244             if(!(V&0x80))
245             {
246              X6502_IRQEnd(FCEU_IQDPCM);
247              SIRQStat&=~0x80;
248             }
249             else X6502_IRQBegin(FCEU_IQDPCM);
250            }
251            break;
252  }
253  PSG[A]=V;
254 }
255
256 static DECLFW(Write_DMCRegs)
257 {
258  A&=0xF;
259
260  switch(A)
261  {
262   case 0x00:DoPCM();
263             LoadDMCPeriod(V&0xF);
264
265             if(SIRQStat&0x80)
266             {
267              if(!(V&0x80))
268              {
269               X6502_IRQEnd(FCEU_IQDPCM);
270               SIRQStat&=~0x80;
271              }
272              else X6502_IRQBegin(FCEU_IQDPCM);
273             }
274             DMCFormat=V;
275             break;
276   case 0x01:DoPCM();
277             RawDALatch=V&0x7F;
278             break;
279   case 0x02:DMCAddressLatch=V;break;
280   case 0x03:DMCSizeLatch=V;break;
281  }
282
283
284 }
285
286 static DECLFW(StatusWrite)
287 {
288         int x;
289
290         DoSQ1();
291         DoSQ2();
292         DoTriangle();
293         DoNoise();
294         DoPCM();
295         for(x=0;x<4;x++)
296          if(!(V&(1<<x))) lengthcount[x]=0;   /* Force length counters to 0. */
297
298         if(V&0x10)
299         {
300          if(!DMCSize)
301           PrepDPCM();
302         }
303         else
304         {
305          DMCSize=0;
306         }
307         SIRQStat&=~0x80;
308         X6502_IRQEnd(FCEU_IQDPCM);
309         EnabledChannels=V&0x1F;
310 }
311
312 static DECLFR(StatusRead)
313 {
314    int x;
315    uint8 ret;
316
317    ret=SIRQStat;
318
319    for(x=0;x<4;x++) ret|=lengthcount[x]?(1<<x):0;
320    if(DMCSize) ret|=0x10;
321
322    #ifdef FCEUDEF_DEBUGGER
323    if(!fceuindbg)
324    #endif
325    {
326     SIRQStat&=~0x40;
327     X6502_IRQEnd(FCEU_IQFCOUNT);
328    }
329    return ret;
330 }
331
332 static void FASTAPASS(1) FrameSoundStuff(int V)
333 {
334  int P;
335
336  DoSQ1();
337  DoSQ2();
338  DoNoise();
339  DoTriangle();
340
341  if(!(V&1)) /* Envelope decay, linear counter, length counter, freq sweep */
342  {
343   if(!(PSG[8]&0x80))
344    if(lengthcount[2]>0)
345     lengthcount[2]--;
346
347   if(!(PSG[0xC]&0x20))  /* Make sure loop flag is not set. */
348    if(lengthcount[3]>0)
349     lengthcount[3]--;
350
351   for(P=0;P<2;P++)
352   {
353    if(!(PSG[P<<2]&0x20))  /* Make sure loop flag is not set. */
354     if(lengthcount[P]>0)
355      lengthcount[P]--;
356
357    /* Frequency Sweep Code Here */
358    /* xxxx 0000 */
359    /* xxxx = hz.  120/(x+1)*/
360    if(sweepon[P])
361    {
362     int32 mod=0;
363
364     if(SweepCount[P]>0) SweepCount[P]--;
365     if(SweepCount[P]<=0)
366     {
367      SweepCount[P]=((PSG[(P<<2)+0x1]>>4)&7)+1; //+1;
368      if(PSG[(P<<2)+0x1]&0x8)
369      {
370       mod-=(P^1)+((curfreq[P])>>(PSG[(P<<2)+0x1]&7));
371       if(curfreq[P] && (PSG[(P<<2)+0x1]&7)/* && sweepon[P]&0x80*/)
372       {
373        curfreq[P]+=mod;
374       }
375      }
376      else
377      {
378       mod=curfreq[P]>>(PSG[(P<<2)+0x1]&7);
379       if((mod+curfreq[P])&0x800)
380       {
381        sweepon[P]=0;
382        curfreq[P]=0;
383       }
384       else
385       {
386        if(curfreq[P] && (PSG[(P<<2)+0x1]&7)/* && sweepon[P]&0x80*/)
387        {
388         curfreq[P]+=mod;
389        }
390       }
391      }
392     }
393    }
394    else  /* Sweeping is disabled: */
395    {
396     //curfreq[P]&=0xFF00;
397     //curfreq[P]|=PSG[(P<<2)|0x2]; //|((PSG[(P<<2)|3]&7)<<8);
398    }
399   }
400  }
401
402  /* Now do envelope decay + linear counter. */
403
404   if(TriMode) // In load mode?
405    TriCount=PSG[0x8]&0x7F;
406   else if(TriCount)
407    TriCount--;
408
409   if(!(PSG[0x8]&0x80))
410    TriMode=0;
411
412   for(P=0;P<3;P++)
413   {
414    if(EnvUnits[P].reloaddec)
415    {
416     EnvUnits[P].decvolume=0xF;
417     EnvUnits[P].DecCountTo1=EnvUnits[P].Speed+1;
418     EnvUnits[P].reloaddec=0;
419     continue;
420    }
421
422    if(EnvUnits[P].DecCountTo1>0) EnvUnits[P].DecCountTo1--;
423    if(EnvUnits[P].DecCountTo1==0)
424    {
425     EnvUnits[P].DecCountTo1=EnvUnits[P].Speed+1;
426     if(EnvUnits[P].decvolume || (EnvUnits[P].Mode&0x2))
427     {
428      EnvUnits[P].decvolume--;
429      EnvUnits[P].decvolume&=0xF;
430     }
431    }
432   }
433 }
434
435 static void FrameSoundUpdate098(void)
436 {
437  // Linear counter:  Bit 0-6 of $4008
438  // Length counter:  Bit 4-7 of $4003, $4007, $400b, $400f
439
440  if(!fcnt && !(IRQFrameMode&0x3))
441  {
442          SIRQStat|=0x40;
443          X6502_IRQBegin(FCEU_IQFCOUNT);
444  }
445
446  if(fcnt==3)
447  {
448         if(IRQFrameMode&0x2)
449          fhcnt+=fhinc;
450  }
451  FrameSoundStuff(fcnt);
452  fcnt=(fcnt+1)&3;
453 }
454
455
456 static INLINE void tester(void)
457 {
458  if(DMCBitCount==0)
459  {
460   if(!DMCHaveDMA)
461    DMCHaveSample=0;
462   else
463   {
464    DMCHaveSample=1;
465    DMCShift=DMCDMABuf;
466    DMCHaveDMA=0;
467   }
468  }
469 }
470
471 static INLINE void DMCDMA(void)
472 {
473   if(DMCSize && !DMCHaveDMA)
474   {
475    /*
476    X6502_DMR(0x8000+DMCAddress);
477    X6502_DMR(0x8000+DMCAddress);
478    X6502_DMR(0x8000+DMCAddress);
479    DMCDMABuf=X6502_DMR(0x8000+DMCAddress);
480    */
481    DMCDMABuf=ARead[0x8000](0x8000);
482    X6502_AddCycles(4);
483
484    DMCHaveDMA=1;
485    DMCAddress=(DMCAddress+1)&0x7fff;
486    DMCSize--;
487    if(!DMCSize)
488    {
489     if(DMCFormat&0x40)
490      PrepDPCM();
491     else
492     {
493      SIRQStat|=0x80;
494      if(DMCFormat&0x80)
495       X6502_IRQBegin(FCEU_IQDPCM);
496     }
497    }
498  }
499 }
500
501 void FASTAPASS(1) FCEU_SoundCPUHook098(int cycles)
502 {
503  fhcnt-=cycles*48;
504  if(fhcnt<=0)
505  {
506   FrameSoundUpdate098();
507   fhcnt+=fhinc;
508  }
509
510  DMCDMA();
511  DMCacc-=cycles;
512
513  while(DMCacc<=0)
514  {
515   if(DMCHaveSample)
516   {
517    uint8 bah=RawDALatch;
518    int t=((DMCShift&1)<<2)-2;
519
520    /* Unbelievably ugly hack */
521    if(FSettings.SndRate)
522    {
523     soundtsoffs+=DMCacc;
524     DoPCM();
525     soundtsoffs-=DMCacc;
526    }
527    RawDALatch+=t;
528    if(RawDALatch&0x80)
529     RawDALatch=bah;
530   }
531
532   DMCacc+=DMCPeriod;
533   DMCBitCount=(DMCBitCount+1)&7;
534   DMCShift>>=1;
535   tester();
536  }
537 }
538
539 #if 0
540 static void RDoPCM(void)
541 {
542  int32 V;
543
544  for(V=ChannelBC[4];V<SOUNDTS;V++)
545   WaveHi[V]+=RawDALatch<<16;
546
547  ChannelBC[4]=SOUNDTS;
548 }
549
550 /* This has the correct phase.  Don't mess with it. */
551 static INLINE void RDoSQ(int x)
552 {
553    int32 V;
554    int32 amp;
555    int32 rthresh;
556    int32 *D;
557    int32 currdc;
558    int32 cf;
559    int32 rc;
560
561    if(curfreq[x]<8 || curfreq[x]>0x7ff)
562     goto endit;
563    if(!CheckFreq(curfreq[x],PSG[(x<<2)|0x1]))
564     goto endit;
565    if(!lengthcount[x])
566     goto endit;
567
568    if(EnvUnits[x].Mode&0x1)
569     amp=EnvUnits[x].Speed;
570    else
571     amp=EnvUnits[x].decvolume;
572 //   printf("%d\n",amp);
573    amp<<=24;
574
575    rthresh=RectDuties[(PSG[(x<<2)]&0xC0)>>6];
576
577    D=&WaveHi[ChannelBC[x]];
578    V=SOUNDTS-ChannelBC[x];
579
580    currdc=RectDutyCount[x];
581    cf=(curfreq[x]+1)*2;
582    rc=wlcount[x];
583
584    while(V>0)
585    {
586     if(currdc<rthresh)
587      *D+=amp;
588     rc--;
589     if(!rc)
590     {
591      rc=cf;
592      currdc=(currdc+1)&7;
593     }
594     V--;
595     D++;
596    }
597
598    RectDutyCount[x]=currdc;
599    wlcount[x]=rc;
600
601    endit:
602    ChannelBC[x]=SOUNDTS;
603 }
604
605 static void RDoSQ1(void)
606 {
607  RDoSQ(0);
608 }
609
610 static void RDoSQ2(void)
611 {
612  RDoSQ(1);
613 }
614 #endif
615
616 static void RDoSQLQ(void)
617 {
618    int32 start,end;
619    int32 V;
620    int32 amp[2];
621    int32 rthresh[2];
622    int32 freq[2];
623    int x;
624    int32 inie[2];
625
626    int32 ttable[2][8];
627    int32 totalout;
628
629    start=ChannelBC[0];
630    end=(SOUNDTS<<16)/soundtsinc;
631    if(end<=start) return;
632    ChannelBC[0]=end;
633
634    for(x=0;x<2;x++)
635    {
636     int y;
637
638     inie[x]=nesincsize;
639     if(curfreq[x]<8 || curfreq[x]>0x7ff)
640      inie[x]=0;
641     if(!CheckFreq(curfreq[x],PSG[(x<<2)|0x1]))
642      inie[x]=0;
643     if(!lengthcount[x])
644      inie[x]=0;
645
646     if(EnvUnits[x].Mode&0x1)
647      amp[x]=EnvUnits[x].Speed;
648     else
649      amp[x]=EnvUnits[x].decvolume;
650
651     if(!inie[x]) amp[x]=0;    /* Correct? Buzzing in MM2, others otherwise... */
652
653     rthresh[x]=RectDuties[(PSG[x*4]&0xC0)>>6];
654
655     for(y=0;y<8;y++)
656     {
657      if(y < rthresh[x])
658       ttable[x][y] = amp[x];
659      else
660       ttable[x][y] = 0;
661     }
662     freq[x]=(curfreq[x]+1)<<1;
663     freq[x]<<=17;
664    }
665
666    totalout = wlookup1[ ttable[0][RectDutyCount[0]] + ttable[1][RectDutyCount[1]] ];
667
668    if(!inie[0] && !inie[1])
669    {
670     for(V=start;V<end;V++)
671      ((int32 *)Wave)[V>>4]+=totalout;
672    }
673    else
674    for(V=start;V<end;V++)
675    {
676     //int tmpamp=0;
677     //if(RectDutyCount[0]<rthresh[0])
678     // tmpamp=amp[0];
679     //if(RectDutyCount[1]<rthresh[1])
680     // tmpamp+=amp[1];
681     //tmpamp=wlookup1[tmpamp];
682     //tmpamp = wlookup1[ ttable[0][RectDutyCount[0]] + ttable[1][RectDutyCount[1]] ];
683
684     ((int32 *)Wave)[V>>4]+=totalout; //tmpamp;
685
686     sqacc[0]-=inie[0];
687     sqacc[1]-=inie[1];
688
689     if(sqacc[0]<=0)
690     {
691      rea:
692      sqacc[0]+=freq[0];
693      RectDutyCount[0]=(RectDutyCount[0]+1)&7;
694      if(sqacc[0]<=0) goto rea;
695      totalout = wlookup1[ ttable[0][RectDutyCount[0]] + ttable[1][RectDutyCount[1]] ];
696     }
697
698     if(sqacc[1]<=0)
699     {
700      rea2:
701      sqacc[1]+=freq[1];
702      RectDutyCount[1]=(RectDutyCount[1]+1)&7;
703      if(sqacc[1]<=0) goto rea2;
704      totalout = wlookup1[ ttable[0][RectDutyCount[0]] + ttable[1][RectDutyCount[1]] ];
705     }
706    }
707 }
708
709 #if 0
710 static void RDoTriangle(void)
711 {
712  int32 V;
713  int32 tcout;
714
715  tcout=(tristep&0xF);
716  if(!(tristep&0x10)) tcout^=0xF;
717  tcout=(tcout*3) << 16;  //(tcout<<1);
718
719  if(!lengthcount[2] || !TriCount)
720  {           /* Counter is halted, but we still need to output. */
721   int32 *start = &WaveHi[ChannelBC[2]];
722   int32 count = SOUNDTS - ChannelBC[2];
723   while(count--)
724   {
725    *start += tcout;
726    start++;
727   }
728   //for(V=ChannelBC[2];V<SOUNDTS;V++)
729   // WaveHi[V]+=tcout;
730  }
731  else
732   for(V=ChannelBC[2];V<SOUNDTS;V++)
733   {
734     WaveHi[V]+=tcout;
735     wlcount[2]--;
736     if(!wlcount[2])
737     {
738      wlcount[2]=(PSG[0xa]|((PSG[0xb]&7)<<8))+1;
739      tristep++;
740      tcout=(tristep&0xF);
741      if(!(tristep&0x10)) tcout^=0xF;
742      tcout=(tcout*3) << 16;
743     }
744   }
745
746  ChannelBC[2]=SOUNDTS;
747 }
748 #endif
749
750 static void RDoTriangleNoisePCMLQ(void)
751 {
752    static uint32 tcout=0;
753    static int32 triacc=0;
754    static int32 noiseacc=0;
755
756    int32 V;
757    int32 start,end;
758    int32 freq[2];
759    int32 inie[2];
760    uint32 amptab[2];
761    uint32 noiseout;
762    int nshift;
763
764    int32 totalout;
765
766    start=ChannelBC[2];
767    end=(SOUNDTS<<16)/soundtsinc;
768    if(end<=start) return;
769    ChannelBC[2]=end;
770
771    inie[0]=inie[1]=nesincsize;
772
773    freq[0]=(((PSG[0xa]|((PSG[0xb]&7)<<8))+1));
774
775    if(!lengthcount[2] || !TriCount || freq[0]<=4)
776     inie[0]=0;
777
778    freq[0]<<=17;
779    if(EnvUnits[2].Mode&0x1)
780     amptab[0]=EnvUnits[2].Speed;
781    else
782     amptab[0]=EnvUnits[2].decvolume;
783    amptab[1]=0;
784    amptab[0]<<=1;
785
786    if(!lengthcount[3])
787     amptab[0]=inie[1]=0;  /* Quick hack speedup, set inie[1] to 0 */
788
789    noiseout=amptab[(nreg>>0xe)&1];
790
791    if(PSG[0xE]&0x80)
792     nshift=8;
793    else
794     nshift=13;
795
796
797    totalout = wlookup2[tcout+noiseout+RawDALatch];
798
799    if(inie[0] && inie[1])
800    {
801     for(V=start;V<end;V++)
802     {
803      ((int32 *)Wave)[V>>4]+=totalout;
804
805     triacc-=inie[0];
806     noiseacc-=inie[1];
807
808     if(triacc<=0)
809     {
810      rea:
811      triacc+=freq[0]; //t;
812      tristep=(tristep+1)&0x1F;
813      if(triacc<=0) goto rea;
814      tcout=(tristep&0xF);
815      if(!(tristep&0x10)) tcout^=0xF;
816      tcout=tcout*3;
817       totalout = wlookup2[tcout+noiseout+RawDALatch];
818     }
819
820     if(noiseacc<=0)
821     {
822      rea2:
823      noiseacc+=NoiseFreqTable[PSG[0xE]&0xF]<<(16+2);
824      nreg=(nreg<<1)+(((nreg>>nshift)^(nreg>>14))&1);
825      nreg&=0x7fff;
826      noiseout=amptab[(nreg>>0xe)];
827      if(noiseacc<=0) goto rea2;
828       totalout = wlookup2[tcout+noiseout+RawDALatch];
829     } /* noiseacc<=0 */
830     } /* for(V=... */
831   }
832   else if(inie[0])
833   {
834     for(V=start;V<end;V++)
835     {
836      ((int32 *)Wave)[V>>4]+=totalout;
837
838      triacc-=inie[0];
839
840      if(triacc<=0)
841      {
842       area:
843       triacc+=freq[0]; //t;
844       tristep=(tristep+1)&0x1F;
845       if(triacc<=0) goto area;
846       tcout=(tristep&0xF);
847       if(!(tristep&0x10)) tcout^=0xF;
848       tcout=tcout*3;
849       totalout = wlookup2[tcout+noiseout+RawDALatch];
850      }
851     }
852   }
853   else if(inie[1])
854   {
855     for(V=start;V<end;V++)
856     {
857      ((int32 *)Wave)[V>>4]+=totalout;
858      noiseacc-=inie[1];
859      if(noiseacc<=0)
860      {
861       area2:
862       noiseacc+=NoiseFreqTable[PSG[0xE]&0xF]<<(16+2);
863       nreg=(nreg<<1)+(((nreg>>nshift)^(nreg>>14))&1);
864       nreg&=0x7fff;
865       noiseout=amptab[(nreg>>0xe)];
866       if(noiseacc<=0) goto area2;
867       totalout = wlookup2[tcout+noiseout+RawDALatch];
868      } /* noiseacc<=0 */
869     }
870   }
871   else
872   {
873     for(V=start;V<end;V++)
874      ((int32 *)Wave)[V>>4]+=totalout;
875   }
876 }
877
878
879 #if 0
880 static void RDoNoise(void)
881 {
882  int32 V;
883  int32 outo;
884  uint32 amptab[2];
885
886  if(EnvUnits[2].Mode&0x1)
887   amptab[0]=EnvUnits[2].Speed;
888  else
889   amptab[0]=EnvUnits[2].decvolume;
890
891  amptab[0]<<=16;
892  amptab[1]=0;
893
894  amptab[0]<<=1;
895
896  outo=amptab[nreg&1]; //(nreg>>0xe)&1];
897
898  if(!lengthcount[3])
899  {
900   outo=amptab[0]=0;
901  }
902
903  if(PSG[0xE]&0x80)  // "short" noise
904   for(V=ChannelBC[3];V<SOUNDTS;V++)
905   {
906    WaveHi[V]+=outo;
907    wlcount[3]--;
908    if(!wlcount[3])
909    {
910     uint8 feedback;
911     wlcount[3]=NoiseFreqTable[PSG[0xE]&0xF]<<1;
912     feedback=((nreg>>8)&1)^((nreg>>14)&1);
913     nreg=(nreg<<1)+feedback;
914     nreg&=0x7fff;
915     outo=amptab[(nreg>>0xe)&1];
916    }
917   }
918  else
919   for(V=ChannelBC[3];V<SOUNDTS;V++)
920   {
921    WaveHi[V]+=outo;
922    wlcount[3]--;
923    if(!wlcount[3])
924    {
925     uint8 feedback;
926     wlcount[3]=NoiseFreqTable[PSG[0xE]&0xF]<<1;
927     feedback=((nreg>>13)&1)^((nreg>>14)&1);
928     nreg=(nreg<<1)+feedback;
929     nreg&=0x7fff;
930     outo=amptab[(nreg>>0xe)&1];
931    }
932   }
933  ChannelBC[3]=SOUNDTS;
934 }
935 #endif
936
937 static DECLFW(Write_IRQFM)
938 {
939  V=(V&0xC0)>>6;
940  fcnt=0;
941  if(V&0x2)
942   FrameSoundUpdate098();
943  fcnt=1;
944  fhcnt=fhinc;
945  X6502_IRQEnd(FCEU_IQFCOUNT);
946  SIRQStat&=~0x40;
947  IRQFrameMode=V;
948 }
949
950 void SetNESSoundMap098(void)
951 {
952   SetWriteHandler(0x4000,0x400F,Write_PSG);
953   SetWriteHandler(0x4010,0x4013,Write_DMCRegs);
954   SetWriteHandler(0x4017,0x4017,Write_IRQFM);
955
956   SetWriteHandler(0x4015,0x4015,StatusWrite);
957   SetReadHandler(0x4015,0x4015,StatusRead);
958 }
959
960 static int32 inbuf=0;
961 int FlushEmulateSound098(void)
962 {
963   int x;
964   int32 end,left;
965
966   if(!timestamp) return(0);
967
968   if(!FSettings.SndRate)
969   {
970    left=0;
971    end=0;
972    goto nosoundo;
973   }
974
975   DoSQ1();
976   DoSQ2();
977   DoTriangle();
978   DoNoise();
979   DoPCM();
980
981 #if 0
982   if(FSettings.soundq>=1)
983   {
984    int32 *tmpo=&WaveHi[soundtsoffs];
985
986    if(GameExpSound.HiFill) GameExpSound.HiFill();
987
988    for(x=timestamp;x;x--)
989    {
990     uint32 b=*tmpo;
991     *tmpo=(b&65535)+wlookup2[(b>>16)&255]+wlookup1[b>>24];
992     tmpo++;
993    }
994    end=NeoFilterSound(WaveHi,WaveFinal,SOUNDTS,&left);
995
996    memmove(WaveHi,WaveHi+SOUNDTS-left,left*sizeof(uint32));
997    memset(WaveHi+left,0,sizeof(WaveHi)-left*sizeof(uint32));
998
999    if(GameExpSound.HiSync) GameExpSound.HiSync(left);
1000    for(x=0;x<5;x++)
1001     ChannelBC[x]=left;
1002   }
1003   else
1004 #endif
1005   {
1006    end=(SOUNDTS<<16)/soundtsinc;
1007    if(GameExpSound.Fill)
1008     GameExpSound.Fill(end&0xF);
1009
1010    SexyFilter((int32 *)Wave,WaveFinalMono,end>>4);
1011
1012    if(end&0xF)
1013     Wave[0]=Wave[(end>>4)];
1014    Wave[end>>4]=0;
1015   }
1016   nosoundo:
1017
1018 #if 0
1019   if(FSettings.soundq>=1)
1020   {
1021    soundtsoffs=left;
1022   }
1023   else
1024 #endif
1025   {
1026    for(x=0;x<5;x++)
1027     ChannelBC[x]=end&0xF;
1028    soundtsoffs = (soundtsinc*(end&0xF))>>16;
1029    end>>=4;
1030   }
1031   inbuf=end;
1032
1033   return(end);
1034 }
1035
1036 /* FIXME:  Find out what sound registers get reset on reset.  I know $4001/$4005 don't,
1037 due to that whole MegaMan 2 Game Genie thing.
1038 */
1039
1040 void FCEUSND_Reset(void)
1041 {
1042         int x;
1043
1044         IRQFrameMode=0x0;
1045         fhcnt=fhinc;
1046         fcnt=0;
1047
1048         nreg=1;
1049         for(x=0;x<2;x++)
1050         {
1051          wlcount[x]=2048;
1052          if(nesincsize) // lq mode
1053           sqacc[x]=((uint32)2048<<17)/nesincsize;
1054          else
1055           sqacc[x]=1;
1056          sweepon[x]=0;
1057          curfreq[x]=0;
1058         }
1059   wlcount[2]=1;  //2048;
1060         wlcount[3]=2048;
1061         DMCHaveDMA=DMCHaveSample=0;
1062         SIRQStat=0x00;
1063
1064         RawDALatch=0x00;
1065         TriCount=0;
1066         TriMode=0;
1067         tristep=0;
1068         EnabledChannels=0;
1069         for(x=0;x<4;x++)
1070          lengthcount[x]=0;
1071
1072         DMCAddressLatch=0;
1073         DMCSizeLatch=0;
1074         DMCFormat=0;
1075         DMCAddress=0;
1076         DMCSize=0;
1077         DMCShift=0;
1078
1079         // MAJOR BUG WAS HERE: DMCacc and DMCBitCount never got reset...
1080         // so, do some ridiculous hackery if a movie's about to play to keep it in sync...
1081
1082 #if 0
1083         extern int movieSyncHackOn,resetDMCacc,movieConvertOffset1,movieConvertOffset2;
1084         if(movieSyncHackOn)
1085         {
1086                 if(resetDMCacc)
1087                 {
1088                         // no value in movie save state
1089 #ifdef WIN32
1090                         // use editbox fields
1091                         DMCacc=movieConvertOffset1;
1092                         DMCBitCount=movieConvertOffset2;
1093 #else
1094                         // no editbox fields, so leave the values alone
1095                         // and print out a warning that says what they are
1096                         FCEU_PrintError("Warning: These variables were not found in the save state and will keep their current value: DMCacc=%d, DMCBitCount=%d\n", DMCacc, DMCBitCount);
1097 #endif
1098                 }
1099                 else
1100                 {
1101                         // keep values loaded from movie save state or reset earlier
1102                 }
1103         }
1104         else
1105 #endif
1106         {
1107                 // reset these variables like should have done in the first place
1108                 DMCacc=1;
1109                 DMCBitCount=0;
1110         }
1111
1112 //      FCEU_PrintError("DMCacc=%d, DMCBitCount=%d",DMCacc,DMCBitCount);
1113 }
1114
1115 void FCEUSND_Power(void)
1116 {
1117         int x;
1118
1119         SetNESSoundMap098();
1120         memset(PSG,0x00,sizeof(PSG));
1121         FCEUSND_Reset();
1122
1123         memset(Wave,0,sizeof(Wave));
1124         memset(WaveHi,0,sizeof(WaveHi));
1125         memset(&EnvUnits,0,sizeof(EnvUnits));
1126
1127         for(x=0;x<5;x++)
1128          ChannelBC[x]=0;
1129         soundtsoffs=0;
1130         LoadDMCPeriod(DMCFormat&0xF);
1131 }
1132
1133
1134 void SetSoundVariables098(void)
1135 {
1136   int x;
1137
1138   fhinc=PAL?16626:14915;  // *2 CPU clock rate
1139   fhinc*=24;
1140
1141   if(FSettings.SndRate)
1142   {
1143    wlookup1[0]=0;
1144    for(x=1;x<32;x++)
1145    {
1146     wlookup1[x]=(double)16*16*16*4*95.52/((double)8128/(double)x+100);
1147 //    if(!FSettings.soundq) wlookup1[x]>>=4;
1148    }
1149    wlookup2[0]=0;
1150    for(x=1;x<203;x++)
1151    {
1152     wlookup2[x]=(double)16*16*16*4*163.67/((double)24329/(double)x+100);
1153 //    if(!FSettings.soundq) wlookup2[x]>>=4;
1154    }
1155 #if 0
1156    if(FSettings.soundq>=1)
1157    {
1158     DoNoise=RDoNoise;
1159     DoTriangle=RDoTriangle;
1160     DoPCM=RDoPCM;
1161     DoSQ1=RDoSQ1;
1162     DoSQ2=RDoSQ2;
1163    }
1164    else
1165 #endif
1166    {
1167     DoNoise=DoTriangle=DoPCM=DoSQ1=DoSQ2=Dummyfunc;
1168     DoSQ1=RDoSQLQ;
1169     DoSQ2=RDoSQLQ;
1170     DoTriangle=RDoTriangleNoisePCMLQ;
1171     DoNoise=RDoTriangleNoisePCMLQ;
1172     DoPCM=RDoTriangleNoisePCMLQ;
1173    }
1174   }
1175   else
1176   {
1177    DoNoise=DoTriangle=DoPCM=DoSQ1=DoSQ2=Dummyfunc;
1178    return;
1179   }
1180
1181   MakeFilters(FSettings.SndRate);
1182
1183   if(GameExpSound.RChange)
1184    GameExpSound.RChange();
1185
1186   nesincsize=(int32)(((int64)1<<17)*(double)(PAL?PAL_CPU:NTSC_CPU)/(FSettings.SndRate * 16));
1187   memset(sqacc,0,sizeof(sqacc));
1188   memset(ChannelBC,0,sizeof(ChannelBC));
1189
1190   LoadDMCPeriod(DMCFormat&0xF);  // For changing from PAL to NTSC
1191
1192   soundtsinc=(uint32)((uint64)(PAL?(long double)PAL_CPU*65536:(long double)NTSC_CPU*65536)/(FSettings.SndRate * 16));
1193 }
1194
1195 /*
1196 void FCEUI_SetLowPass(int q)
1197 {
1198  FSettings.lowpass=q;
1199 }
1200
1201 void FCEUI_SetSoundQuality(int quality)
1202 {
1203  FSettings.soundq=quality;
1204  SetSoundVariables098();
1205 }
1206 */
1207
1208
1209 SFORMAT FCEUSND_STATEINFO[]={
1210
1211  { &fhcnt, 4|FCEUSTATE_RLSB,"FHCN"},
1212  { &fcnt, 1, "FCNT"},
1213  { PSG, 0x10, "PSG"},
1214  { &EnabledChannels, 1, "ENCH"},
1215  { &IRQFrameMode, 1, "IQFM"},
1216  { &nreg, 2|FCEUSTATE_RLSB, "NREG"},
1217  { &TriMode, 1, "TRIM"},
1218  { &TriCount, 1, "TRIC"},
1219
1220  { &EnvUnits[0].Speed, 1, "E0SP"},
1221  { &EnvUnits[1].Speed, 1, "E1SP"},
1222  { &EnvUnits[2].Speed, 1, "E2SP"},
1223
1224  { &EnvUnits[0].Mode, 1, "E0MO"},
1225  { &EnvUnits[1].Mode, 1, "E1MO"},
1226  { &EnvUnits[2].Mode, 1, "E2MO"},
1227
1228  { &EnvUnits[0].DecCountTo1, 1, "E0D1"},
1229  { &EnvUnits[1].DecCountTo1, 1, "E1D1"},
1230  { &EnvUnits[2].DecCountTo1, 1, "E2D1"},
1231
1232  { &EnvUnits[0].decvolume, 1, "E0DV"},
1233  { &EnvUnits[1].decvolume, 1, "E1DV"},
1234  { &EnvUnits[2].decvolume, 1, "E2DV"},
1235
1236  { &lengthcount[0], 4|FCEUSTATE_RLSB, "LEN0"},
1237  { &lengthcount[1], 4|FCEUSTATE_RLSB, "LEN1"},
1238  { &lengthcount[2], 4|FCEUSTATE_RLSB, "LEN2"},
1239  { &lengthcount[3], 4|FCEUSTATE_RLSB, "LEN3"},
1240  { sweepon, 2, "SWEE"},
1241  { &curfreq[0], 4|FCEUSTATE_RLSB,"CRF1"},
1242  { &curfreq[1], 4|FCEUSTATE_RLSB,"CRF2"},
1243  { SweepCount, 2,"SWCT"},
1244
1245  { &SIRQStat, 1, "SIRQ"},
1246
1247  { &DMCacc, 4|FCEUSTATE_RLSB, "5ACC"},
1248  { &DMCBitCount, 1, "5BIT"},
1249  { &DMCAddress, 4|FCEUSTATE_RLSB, "5ADD"},
1250  { &DMCSize, 4|FCEUSTATE_RLSB, "5SIZ"},
1251  { &DMCShift, 1, "5SHF"},
1252
1253  { &DMCHaveDMA, 1, "5HVDM"},
1254  { &DMCHaveSample, 1, "5HVSP"},
1255
1256  { &DMCSizeLatch, 1, "5SZL"},
1257  { &DMCAddressLatch, 1, "5ADL"},
1258  { &DMCFormat, 1, "5FMT"},
1259  { &RawDALatch, 1, "RWDA"},
1260  { 0 }
1261 };
1262
1263 void FCEUSND_SaveState(void)
1264 {
1265
1266 }
1267
1268 void FCEUSND_LoadState(int version)
1269 {
1270  LoadDMCPeriod(DMCFormat&0xF);
1271  RawDALatch&=0x7F;
1272  DMCAddress&=0x7FFF;
1273 }