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 | |
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 | } |