1 /*  Festalon - NSF Player
2  *  Copyright (C) 2004 Xodnizel
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2.1 of the License, or (at your option) any later version.
8  *
9  *  This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public
15  *  License along with this library; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include "common.h"
24 
25 static DECLFW(FDSWaveWrite);
26 static DECLFR(FDSWaveRead);
27 
28 static DECLFR(FDSSRead);
29 static DECLFW(FDSSWrite);
30 
31 static void RenderSoundHQ(void *);
32 
33 /* Begin FDS sound */
34 
35 #define FDSClock (1789772.7272727272727272/2)
36 
37 typedef struct {
38         int64 cycles;           // Cycles per PCM sample
39         int64 count;		// Cycle counter
40 	int64 envcount;		// Envelope cycle counter
41 	uint32 b19shiftreg60;
42 	uint32 b24adder66;
43 	uint32 b24latch68;
44 	uint32 b17latch76;
45         int32 clockcount;	// Counter to divide frequency by 8.
46 	uint8 b8shiftreg88;	// Modulation register.
47 	uint8 amplitude[2];	// Current amplitudes.
48 	uint8 speedo[2];
49 	uint8 mwcount;
50 	uint8 mwstart;
51         uint8 mwave[0x20];      // Modulation waveform
52         uint8 cwave[0x40];      // Game-defined waveform(carrier)
53         uint8 SPSG[0xB];
54 
55 	int32 FBC;
56    	int counto[2];
57 
58 	int disabled;
59 	int32 curout;
60 	NESAPU *gapu;
61 } FDSSOUND;
62 
63 #define	SPSG	fdso->SPSG
64 #define b19shiftreg60	fdso->b19shiftreg60
65 #define b24adder66	fdso->b24adder66
66 #define b24latch68	fdso->b24latch68
67 #define b17latch76	fdso->b17latch76
68 #define b8shiftreg88	fdso->b8shiftreg88
69 #define clockcount	fdso->clockcount
70 #define amplitude	fdso->amplitude
71 #define speedo		fdso->speedo
72 
RedoCO(FDSSOUND * fdso)73 static INLINE void RedoCO(FDSSOUND *fdso)
74 {
75  int k=amplitude[0];
76  if(k>0x20) k=0x20;
77  fdso->curout = (fdso->cwave[b24latch68>>19]*k)*4/((SPSG[0x9]&0x3)+2);
78 }
79 
DECLFR(FDSSRead)80 static DECLFR(FDSSRead)
81 {
82  FDSSOUND *fdso=private;
83 
84  switch(A&0xF)
85  {
86   case 0x0:return(amplitude[0]|(DB&0xC0));
87   case 0x2:return(amplitude[1]|(DB&0xC0));
88  }
89  return(DB);
90 }
91 
DECLFW(FDSSWrite)92 static DECLFW(FDSSWrite)
93 {
94  FDSSOUND *fdso=private;
95  RenderSoundHQ(fdso);
96 
97  A-=0x4080;
98  switch(A)
99  {
100   case 0x0:
101   case 0x4: if(V&0x80)
102 	     amplitude[(A&0xF)>>2]=V&0x3F; //)>0x20?0x20:(V&0x3F);
103 	    break;
104   case 0x5://printf("$%04x:$%02x\n",A,V);
105 		break;
106   case 0x7: b17latch76=0;SPSG[0x5]=0;//printf("$%04x:$%02x\n",A,V);
107 		break;
108   case 0x8:
109 	   b17latch76=0;
110 	//   printf("%d:$%02x, $%02x\n",SPSG[0x5],V,b17latch76);
111 	   fdso->mwave[SPSG[0x5]&0x1F]=V&0x7;
112            SPSG[0x5]=(SPSG[0x5]+1)&0x1F;
113 	   break;
114  }
115  //if(A>=0x7 && A!=0x8 && A<=0xF)
116  //if(A==0xA || A==0x9)
117  //printf("$%04x:$%02x\n",A,V);
118  SPSG[A]=V;
119 
120  if(A == 0x9) RedoCO(fdso);
121 }
122 
123 // $4080 - Fundamental wave amplitude data register 92
124 // $4082 - Fundamental wave frequency data register 58
125 // $4083 - Same as $4082($4083 is the upper 4 bits).
126 
127 // $4084 - Modulation amplitude data register 78
128 // $4086 - Modulation frequency data register 72
129 // $4087 - Same as $4086($4087 is the upper 4 bits)
130 
131 
DoEnv(FDSSOUND * fdso)132 static void DoEnv(FDSSOUND *fdso)
133 {
134  int x;
135 
136  for(x=0;x<2;x++)
137   if(!(SPSG[x<<2]&0x80) && !(SPSG[0x3]&0x40))
138   {
139    if(fdso->counto[x]<=0)
140    {
141     if(!(SPSG[x<<2]&0x80))
142     {
143      if(SPSG[x<<2]&0x40)
144      {
145       if(amplitude[x]<0x3F)
146        amplitude[x]++;
147      }
148      else
149      {
150       if(amplitude[x]>0)
151        amplitude[x]--;
152      }
153     }
154     fdso->counto[x]=(SPSG[x<<2]&0x3F);
155     if(x == 0) RedoCO(fdso);
156    }
157    else
158     fdso->counto[x]--;
159   }
160 }
161 
DECLFR(FDSWaveRead)162 static DECLFR(FDSWaveRead)
163 {
164  FDSSOUND *fdso=private;
165  return(fdso->cwave[A&0x3f]|(DB&0xC0));
166 }
167 
DECLFW(FDSWaveWrite)168 static DECLFW(FDSWaveWrite)
169 {
170  FDSSOUND *fdso=private;
171  //printf("$%04x:$%02x, %d\n",A,V,SPSG[0x9]&0x80);
172  if(SPSG[0x9]&0x80)
173   fdso->cwave[A&0x3f]=V&0x3F;
174 }
175 
ClockRise(FDSSOUND * fdso)176 static INLINE void ClockRise(FDSSOUND *fdso)
177 {
178  if(!clockcount)
179  {
180   b19shiftreg60=(SPSG[0x2]|((SPSG[0x3]&0xF)<<8));
181   b17latch76=(SPSG[0x6]|((SPSG[0x07]&0xF)<<8))+b17latch76;
182 
183   if(!(SPSG[0x7]&0x80))
184   {
185    int t=fdso->mwave[(b17latch76>>13)&0x1F]&7;
186    int t2=amplitude[1];
187 
188    if(t2>0x20) t2=0x20;
189 
190    /*if(t&4)
191    {
192     if(t==4) t=0;
193     else t=t2*((3-t)&3);
194     t=0x80-t;
195    }
196    else
197    {
198     t=t2*(t&3);
199     t=0x80+t;
200    }
201    */
202    t=0x80+t2*t/2;
203 
204    //if(t&4)
205    // t=0x80-((t&3))*t2;
206    //else
207    // t=0x80+((t&3))*t2; //(0x80+(t&3))*(amplitude[1]); //t; //amplitude[1]*3; //t; //(amplitude[1])*(fdso->mwave[(b17latch76>>13)&0x1F]&7);
208 
209    b8shiftreg88=t; //(t+0x80)*(amplitude[1]);
210 
211    // t=0;
212    //t=(t-4)*(amplitude[1]);
213    //FCEU_DispMessage("%d",amplitude[1]);
214    //b8shiftreg88=((fdso->mwave[(b17latch76>>11)&0x1F]&7))|(amplitude[1]<<3);
215   }
216   else
217   {
218    b8shiftreg88=0x80;
219   }
220   //b8shiftreg88=0x80;
221  }
222  else
223  {
224   b19shiftreg60<<=1;
225   b8shiftreg88>>=1;
226  }
227 // b24adder66=(b24latch68+b19shiftreg60)&0x3FFFFFF;
228  b24adder66=(b24latch68+b19shiftreg60)&0x1FFFFFF;
229 }
230 
ClockFall(FDSSOUND * fdso)231 static INLINE void ClockFall(FDSSOUND *fdso)
232 {
233  //if(!(SPSG[0x7]&0x80))
234  {
235   if((b8shiftreg88&1)) // || clockcount==7)
236   {
237    b24latch68=b24adder66;
238    RedoCO(fdso);
239   }
240  }
241  clockcount=(clockcount+1)&7;
242 }
243 
FDSDoSound(FDSSOUND * fdso)244 static INLINE int32 FDSDoSound(FDSSOUND *fdso)
245 {
246  fdso->count+=fdso->cycles;
247  if(fdso->count>=((int64)1<<40))
248  {
249   dogk:
250   fdso->count-=(int64)1<<40;
251   ClockRise(fdso);
252   ClockFall(fdso);
253   fdso->envcount--;
254   if(fdso->envcount<=0)
255   {
256    fdso->envcount+=SPSG[0xA]*3;
257    DoEnv(fdso);
258   }
259  }
260  if(fdso->count>=32768) goto dogk;
261 
262  return (fdso->curout);
263 }
264 
RenderSoundHQ(void * private)265 static void RenderSoundHQ(void *private)
266 {
267  FDSSOUND *fdso=private;
268  int32 x;
269 
270  if(!(SPSG[0x9]&0x80) && !(fdso->disabled&0x1))
271   for(x=fdso->FBC;x<fdso->gapu->X->timestamp;x++)
272   {
273    uint32 t=FDSDoSound(fdso);
274    t+=t>>1;
275    fdso->gapu->WaveHi[x]+=t;
276   }
277  fdso->FBC=fdso->gapu->X->timestamp;
278 }
279 
HQSync(void * private,int32 ts)280 static void HQSync(void *private, int32 ts)
281 {
282  FDSSOUND *fdso=private;
283  fdso->FBC=ts;
284 }
285 
Kill(void * private)286 static void Kill(void *private)
287 {
288  free(private);
289 }
290 
Disable(void * private,int mask)291 static void Disable(void *private, int mask)
292 {
293  FDSSOUND *fdso=private;
294 
295  fdso->disabled=mask;
296 }
297 
298 
NSFFDS_Init(EXPSOUND * ep,NESAPU * apu)299 int NSFFDS_Init(EXPSOUND *ep, NESAPU *apu)
300 {
301  FDSSOUND *fdso;
302 
303  fdso=malloc(sizeof(FDSSOUND));
304  memset(fdso,0,sizeof(FDSSOUND));
305  fdso->cycles=(int64)1<<39;
306  fdso->gapu=apu;
307 
308  SetReadHandler(apu->X,0x4040,0x407f,FDSWaveRead,fdso);
309  SetWriteHandler(apu->X,0x4040,0x407f,FDSWaveWrite,fdso);
310  SetWriteHandler(apu->X,0x4080,0x408A,FDSSWrite,fdso);
311  SetReadHandler(apu->X,0x4090,0x4092,FDSSRead,fdso);
312 
313  ep->HiSync=HQSync;
314  ep->HiFill=RenderSoundHQ;
315  ep->Disable=Disable;
316  ep->private=fdso;
317  ep->Channels = 1;
318  ep->Kill = Kill;
319 
320  return(1);
321 }
322 
323