098 video fix, 098 sound integrated
[fceu.git] / sound098.c
CommitLineData
a384bf44 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
36static uint32 wlookup1[32];
37static uint32 wlookup2[203];
38
39int32 WaveHi[40000];
40//int32 WaveFinal[2048+512]; // TODO: use WaveFinalMono
41
42static uint8 TriCount=0;
43static uint8 TriMode=0;
44
45static int32 tristep=0;
46
47static int32 wlcount[4]={0,0,0,0}; /* Wave length counters. */
48
49static uint8 IRQFrameMode=0; /* $4017 / xx000000 */
50//static uint8 PSG[0x10];
51static uint8 RawDALatch=0; /* $4011 0xxxxxxx */
52
53static uint8 EnabledChannels=0; /* Byte written to $4015 */
54
55typedef 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
63static ENVUNIT EnvUnits[3];
64
65static const int RectDuties[4]={1,2,4,6};
66
67static 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. */
79static int32 sqacc[2];
80/* LQ variables segment ends. */
81
82static int32 lengthcount[4];
83static 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
89static const uint32 NoiseFreqTable[0x10]=
90{
91 2,4,8,0x10,0x20,0x30,0x40,0x50,0x65,0x7f,0xbe,0xfe,0x17d,0x1fc,0x3f9,0x7f2
92};
93
94static 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
100static 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
111static int32 DMCacc=1;
112static int32 DMCPeriod=0;
113static uint8 DMCBitCount=0;
114
115static uint8 DMCAddressLatch=0,DMCSizeLatch=0; /* writes to 4012 and 4013 */
116static uint8 DMCFormat=0; /* Write to $4010 */
117
118static uint32 DMCAddress=0;
119static int32 DMCSize=0;
120static uint8 DMCShift=0;
121static uint8 SIRQStat=0;
122
123static char DMCHaveDMA=0;
124static uint8 DMCDMABuf=0;
125static char DMCHaveSample=0;
126
127static void Dummyfunc(void) {};
128static void (*DoNoise)(void)=Dummyfunc;
129static void (*DoTriangle)(void)=Dummyfunc;
130static void (*DoPCM)(void)=Dummyfunc;
131static void (*DoSQ1)(void)=Dummyfunc;
132static void (*DoSQ2)(void)=Dummyfunc;
133
134static uint32 ChannelBC[5];
135
136static void LoadDMCPeriod(uint8 V)
137{
138 if(PAL)
139 DMCPeriod=PALDMCTable[V];
140 else
141 DMCPeriod=NTSCDMCTable[V];
142}
143
144static 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
152static 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
164static 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
184static 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
256static 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
286static 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
312static 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
332static 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
435static 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
456static 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
471static 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
501void 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
540static 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. */
551static 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
605static void RDoSQ1(void)
606{
607 RDoSQ(0);
608}
609
610static void RDoSQ2(void)
611{
612 RDoSQ(1);
613}
614#endif
615
616static 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
710static 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
750static 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
880static 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
937static 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
950void 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
960static int32 inbuf=0;
961int 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,
1037due to that whole MegaMan 2 Game Genie thing.
1038*/
1039
1040void 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
1115void 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
1134void 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/*
1196void FCEUI_SetLowPass(int q)
1197{
1198 FSettings.lowpass=q;
1199}
1200
1201void FCEUI_SetSoundQuality(int quality)
1202{
1203 FSettings.soundq=quality;
1204 SetSoundVariables098();
1205}
1206*/
1207
1208
1209SFORMAT 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
1263void FCEUSND_SaveState(void)
1264{
1265
1266}
1267
1268void FCEUSND_LoadState(int version)
1269{
1270 LoadDMCPeriod(DMCFormat&0xF);
1271 RawDALatch&=0x7F;
1272 DMCAddress&=0x7FFF;
1273}