spu: rework synchronization
[pcsx_rearmed.git] / plugins / dfsound / registers.c
CommitLineData
ef79bbde
P
1/***************************************************************************\r
2 registers.c - description\r
3 -------------------\r
4 begin : Wed May 15 2002\r
5 copyright : (C) 2002 by Pete Bernert\r
6 email : BlackDove@addcom.de\r
7 ***************************************************************************/\r
8/***************************************************************************\r
9 * *\r
10 * This program is free software; you can redistribute it and/or modify *\r
11 * it under the terms of the GNU General Public License as published by *\r
12 * the Free Software Foundation; either version 2 of the License, or *\r
13 * (at your option) any later version. See also the license.txt file for *\r
14 * additional informations. *\r
15 * *\r
16 ***************************************************************************/\r
17\r
18#include "stdafx.h"\r
19\r
20#define _IN_REGISTERS\r
21\r
22#include "externals.h"\r
23#include "registers.h"\r
ef79bbde 24\r
7e44d49d 25static void SoundOn(int start,int end,unsigned short val);\r
26static void SoundOff(int start,int end,unsigned short val);\r
27static void FModOn(int start,int end,unsigned short val);\r
28static void NoiseOn(int start,int end,unsigned short val);\r
29static void SetVolumeL(unsigned char ch,short vol);\r
30static void SetVolumeR(unsigned char ch,short vol);\r
31static void SetPitch(int ch,unsigned short val);\r
32static void ReverbOn(int start,int end,unsigned short val);\r
33\r
ef79bbde
P
34////////////////////////////////////////////////////////////////////////\r
35// WRITE REGISTERS: called by main emu\r
36////////////////////////////////////////////////////////////////////////\r
37\r
650adfd2 38static const uint32_t ignore_dupe[8] = {\r
39 // ch 0-15 c40 c80 cc0\r
40 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f,\r
41 // ch 16-24 d40 control reverb\r
42 0x7f7f7f7f, 0x7f7f7f7f, 0xff05ff0f, 0xffffffff\r
43};\r
44\r
45void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val,\r
46 unsigned int cycles)\r
ef79bbde 47{\r
650adfd2 48 int r = reg & 0xfff;\r
49 int rofs = (r - 0xc00) >> 1;\r
50 int changed = regArea[rofs] != val;\r
51 regArea[rofs] = val;\r
52\r
53 if (!changed && (ignore_dupe[rofs >> 5] & (1 << (rofs & 0x1f))))\r
54 return;\r
55 // zero keyon/keyoff?\r
56 if (val == 0 && (r & 0xff8) == 0xd88)\r
57 return;\r
58\r
59 do_samples_if_needed(cycles);\r
ef79bbde
P
60\r
61 if(r>=0x0c00 && r<0x0d80) // some channel info?\r
62 {\r
63 int ch=(r>>4)-0xc0; // calc channel\r
64 switch(r&0x0f)\r
65 {\r
66 //------------------------------------------------// r volume\r
67 case 0: \r
68 SetVolumeL((unsigned char)ch,val);\r
69 break;\r
70 //------------------------------------------------// l volume\r
71 case 2: \r
72 SetVolumeR((unsigned char)ch,val);\r
73 break;\r
74 //------------------------------------------------// pitch\r
75 case 4: \r
76 SetPitch(ch,val);\r
650adfd2 77 goto upd_irq;\r
ef79bbde
P
78 //------------------------------------------------// start\r
79 case 6: \r
7e44d49d 80 // taken from regArea later\r
ef79bbde
P
81 break;\r
82 //------------------------------------------------// level with pre-calcs\r
83 case 8:\r
84 {\r
6d866bb7 85 const unsigned long lval=val;\r
ef79bbde
P
86 //---------------------------------------------//\r
87 s_chan[ch].ADSRX.AttackModeExp=(lval&0x8000)?1:0; \r
88 s_chan[ch].ADSRX.AttackRate=(lval>>8) & 0x007f;\r
89 s_chan[ch].ADSRX.DecayRate=(lval>>4) & 0x000f;\r
90 s_chan[ch].ADSRX.SustainLevel=lval & 0x000f;\r
91 //---------------------------------------------//\r
ef79bbde
P
92 }\r
93 break;\r
94 //------------------------------------------------// adsr times with pre-calcs\r
95 case 10:\r
96 {\r
6d866bb7 97 const unsigned long lval=val;\r
ef79bbde
P
98\r
99 //----------------------------------------------//\r
100 s_chan[ch].ADSRX.SustainModeExp = (lval&0x8000)?1:0;\r
101 s_chan[ch].ADSRX.SustainIncrease= (lval&0x4000)?0:1;\r
102 s_chan[ch].ADSRX.SustainRate = (lval>>6) & 0x007f;\r
103 s_chan[ch].ADSRX.ReleaseModeExp = (lval&0x0020)?1:0;\r
104 s_chan[ch].ADSRX.ReleaseRate = lval & 0x001f;\r
105 //----------------------------------------------//\r
ef79bbde
P
106 }\r
107 break;\r
108 //------------------------------------------------// adsr volume... mmm have to investigate this\r
109 case 12:\r
110 break;\r
111 //------------------------------------------------//\r
112 case 14: // loop?\r
7e44d49d 113 s_chan[ch].pLoop=spuMemC+((val&~1)<<3);\r
650adfd2 114 goto upd_irq;\r
ef79bbde
P
115 //------------------------------------------------//\r
116 }\r
ef79bbde
P
117 return;\r
118 }\r
119\r
120 switch(r)\r
121 {\r
122 //-------------------------------------------------//\r
123 case H_SPUaddr:\r
124 spuAddr = (unsigned long) val<<3;\r
125 break;\r
126 //-------------------------------------------------//\r
127 case H_SPUdata:\r
128 spuMem[spuAddr>>1] = val;\r
129 spuAddr+=2;\r
130 if(spuAddr>0x7ffff) spuAddr=0;\r
131 break;\r
132 //-------------------------------------------------//\r
133 case H_SPUctrl:\r
650adfd2 134 if (!(spuCtrl & CTRL_IRQ)) {\r
3fc2a4c2 135 spuStat&=~STAT_IRQ;\r
650adfd2 136 if (val & CTRL_IRQ)\r
137 schedule_next_irq();\r
138 }\r
ef79bbde
P
139 spuCtrl=val;\r
140 break;\r
141 //-------------------------------------------------//\r
142 case H_SPUstat:\r
143 spuStat=val & 0xf800;\r
144 break;\r
145 //-------------------------------------------------//\r
146 case H_SPUReverbAddr:\r
147 if(val==0xFFFF || val<=0x200)\r
148 {rvb.StartAddr=rvb.CurrAddr=0;}\r
149 else\r
150 {\r
151 const long iv=(unsigned long)val<<2;\r
152 if(rvb.StartAddr!=iv)\r
153 {\r
154 rvb.StartAddr=(unsigned long)val<<2;\r
155 rvb.CurrAddr=rvb.StartAddr;\r
b72f17a1 156 // sync-with-decode-buffers hack..\r
157 if(rvb.StartAddr==0x3ff00)\r
158 rvb.CurrAddr+=decode_pos/2;\r
ef79bbde
P
159 }\r
160 }\r
650adfd2 161 goto rvbd;\r
ef79bbde
P
162 //-------------------------------------------------//\r
163 case H_SPUirqAddr:\r
164 spuIrq = val;\r
3fc2a4c2 165 pSpuIrq=spuMemC+(((unsigned long) val<<3)&~0xf);\r
650adfd2 166 goto upd_irq;\r
ef79bbde
P
167 //-------------------------------------------------//\r
168 case H_SPUrvolL:\r
169 rvb.VolLeft=val;\r
170 break;\r
171 //-------------------------------------------------//\r
172 case H_SPUrvolR:\r
173 rvb.VolRight=val;\r
174 break;\r
175 //-------------------------------------------------//\r
176\r
177/*\r
178 case H_ExtLeft:\r
179 //auxprintf("EL %d\n",val);\r
180 break;\r
181 //-------------------------------------------------//\r
182 case H_ExtRight:\r
183 //auxprintf("ER %d\n",val);\r
184 break;\r
185 //-------------------------------------------------//\r
186 case H_SPUmvolL:\r
187 //auxprintf("ML %d\n",val);\r
188 break;\r
189 //-------------------------------------------------//\r
190 case H_SPUmvolR:\r
191 //auxprintf("MR %d\n",val);\r
192 break;\r
193 //-------------------------------------------------//\r
194 case H_SPUMute1:\r
195 //auxprintf("M0 %04x\n",val);\r
196 break;\r
197 //-------------------------------------------------//\r
198 case H_SPUMute2:\r
199 //auxprintf("M1 %04x\n",val);\r
200 break;\r
201*/\r
202 //-------------------------------------------------//\r
203 case H_SPUon1:\r
204 SoundOn(0,16,val);\r
205 break;\r
206 //-------------------------------------------------//\r
207 case H_SPUon2:\r
208 SoundOn(16,24,val);\r
209 break;\r
210 //-------------------------------------------------//\r
211 case H_SPUoff1:\r
212 SoundOff(0,16,val);\r
213 break;\r
214 //-------------------------------------------------//\r
215 case H_SPUoff2:\r
216 SoundOff(16,24,val);\r
217 break;\r
218 //-------------------------------------------------//\r
219 case H_CDLeft:\r
220 iLeftXAVol=val & 0x7fff;\r
221 if(cddavCallback) cddavCallback(0,val);\r
222 break;\r
223 case H_CDRight:\r
224 iRightXAVol=val & 0x7fff;\r
225 if(cddavCallback) cddavCallback(1,val);\r
226 break;\r
227 //-------------------------------------------------//\r
228 case H_FMod1:\r
229 FModOn(0,16,val);\r
230 break;\r
231 //-------------------------------------------------//\r
232 case H_FMod2:\r
233 FModOn(16,24,val);\r
234 break;\r
235 //-------------------------------------------------//\r
236 case H_Noise1:\r
237 NoiseOn(0,16,val);\r
238 break;\r
239 //-------------------------------------------------//\r
240 case H_Noise2:\r
241 NoiseOn(16,24,val);\r
242 break;\r
243 //-------------------------------------------------//\r
244 case H_RVBon1:\r
245 ReverbOn(0,16,val);\r
246 break;\r
247 //-------------------------------------------------//\r
248 case H_RVBon2:\r
249 ReverbOn(16,24,val);\r
250 break;\r
251 //-------------------------------------------------//\r
650adfd2 252 case H_Reverb+0 : rvb.FB_SRC_A=val*4; goto rvbd;\r
253 case H_Reverb+2 : rvb.FB_SRC_B=val*4; goto rvbd;\r
254 case H_Reverb+4 : rvb.IIR_ALPHA=(short)val; goto rvbd;\r
255 case H_Reverb+6 : rvb.ACC_COEF_A=(short)val; goto rvbd;\r
256 case H_Reverb+8 : rvb.ACC_COEF_B=(short)val; goto rvbd;\r
257 case H_Reverb+10 : rvb.ACC_COEF_C=(short)val; goto rvbd;\r
258 case H_Reverb+12 : rvb.ACC_COEF_D=(short)val; goto rvbd;\r
259 case H_Reverb+14 : rvb.IIR_COEF=(short)val; goto rvbd;\r
260 case H_Reverb+16 : rvb.FB_ALPHA=(short)val; goto rvbd;\r
261 case H_Reverb+18 : rvb.FB_X=(short)val; goto rvbd;\r
262 case H_Reverb+20 : rvb.IIR_DEST_A0=val*4; goto rvbd;\r
263 case H_Reverb+22 : rvb.IIR_DEST_A1=val*4; goto rvbd;\r
264 case H_Reverb+24 : rvb.ACC_SRC_A0=val*4; goto rvbd;\r
265 case H_Reverb+26 : rvb.ACC_SRC_A1=val*4; goto rvbd;\r
266 case H_Reverb+28 : rvb.ACC_SRC_B0=val*4; goto rvbd;\r
267 case H_Reverb+30 : rvb.ACC_SRC_B1=val*4; goto rvbd;\r
268 case H_Reverb+32 : rvb.IIR_SRC_A0=val*4; goto rvbd;\r
269 case H_Reverb+34 : rvb.IIR_SRC_A1=val*4; goto rvbd;\r
270 case H_Reverb+36 : rvb.IIR_DEST_B0=val*4; goto rvbd;\r
271 case H_Reverb+38 : rvb.IIR_DEST_B1=val*4; goto rvbd;\r
272 case H_Reverb+40 : rvb.ACC_SRC_C0=val*4; goto rvbd;\r
273 case H_Reverb+42 : rvb.ACC_SRC_C1=val*4; goto rvbd;\r
274 case H_Reverb+44 : rvb.ACC_SRC_D0=val*4; goto rvbd;\r
275 case H_Reverb+46 : rvb.ACC_SRC_D1=val*4; goto rvbd;\r
276 case H_Reverb+48 : rvb.IIR_SRC_B1=val*4; goto rvbd;\r
277 case H_Reverb+50 : rvb.IIR_SRC_B0=val*4; goto rvbd;\r
278 case H_Reverb+52 : rvb.MIX_DEST_A0=val*4; goto rvbd;\r
279 case H_Reverb+54 : rvb.MIX_DEST_A1=val*4; goto rvbd;\r
280 case H_Reverb+56 : rvb.MIX_DEST_B0=val*4; goto rvbd;\r
281 case H_Reverb+58 : rvb.MIX_DEST_B1=val*4; goto rvbd;\r
282 case H_Reverb+60 : rvb.IN_COEF_L=(short)val; goto rvbd;\r
283 case H_Reverb+62 : rvb.IN_COEF_R=(short)val; goto rvbd;\r
ef79bbde 284 }\r
650adfd2 285 return;\r
286\r
287upd_irq:\r
288 if (spuCtrl & CTRL_IRQ)\r
289 schedule_next_irq();\r
290 return;\r
ef79bbde 291\r
650adfd2 292rvbd:\r
293 rvb.dirty = 1; // recalculate on next update\r
ef79bbde
P
294}\r
295\r
296////////////////////////////////////////////////////////////////////////\r
297// READ REGISTER: called by main emu\r
298////////////////////////////////////////////////////////////////////////\r
299\r
300unsigned short CALLBACK SPUreadRegister(unsigned long reg)\r
301{\r
302 const unsigned long r=reg&0xfff;\r
303 \r
ef79bbde
P
304 if(r>=0x0c00 && r<0x0d80)\r
305 {\r
306 switch(r&0x0f)\r
307 {\r
308 case 12: // get adsr vol\r
309 {\r
310 const int ch=(r>>4)-0xc0;\r
6d866bb7 311 if(dwNewChannel&(1<<ch)) return 1; // we are started, but not processed? return 1\r
312 if((dwChannelOn&(1<<ch)) && // same here... we haven't decoded one sample yet, so no envelope yet. return 1 as well\r
313 !s_chan[ch].ADSRX.EnvelopeVol)\r
ef79bbde
P
314 return 1;\r
315 return (unsigned short)(s_chan[ch].ADSRX.EnvelopeVol>>16);\r
316 }\r
317\r
318 case 14: // get loop address\r
319 {\r
320 const int ch=(r>>4)-0xc0;\r
ef79bbde
P
321 return (unsigned short)((s_chan[ch].pLoop-spuMemC)>>3);\r
322 }\r
323 }\r
324 }\r
325\r
326 switch(r)\r
327 {\r
328 case H_SPUctrl:\r
329 return spuCtrl;\r
330\r
331 case H_SPUstat:\r
332 return spuStat;\r
333 \r
334 case H_SPUaddr:\r
335 return (unsigned short)(spuAddr>>3);\r
336\r
337 case H_SPUdata:\r
338 {\r
339 unsigned short s=spuMem[spuAddr>>1];\r
340 spuAddr+=2;\r
341 if(spuAddr>0x7ffff) spuAddr=0;\r
342 return s;\r
343 }\r
344\r
345 case H_SPUirqAddr:\r
346 return spuIrq;\r
347\r
348 //case H_SPUIsOn1:\r
349 // return IsSoundOn(0,16);\r
350\r
351 //case H_SPUIsOn2:\r
352 // return IsSoundOn(16,24);\r
353 \r
354 }\r
355\r
356 return regArea[(r-0xc00)>>1];\r
357}\r
358 \r
359////////////////////////////////////////////////////////////////////////\r
360// SOUND ON register write\r
361////////////////////////////////////////////////////////////////////////\r
362\r
7e44d49d 363static void SoundOn(int start,int end,unsigned short val)\r
ef79bbde
P
364{\r
365 int ch;\r
366\r
367 for(ch=start;ch<end;ch++,val>>=1) // loop channels\r
368 {\r
77d6fd63 369 if((val&1) && regAreaGet(ch,6)) // mmm... start has to be set before key on !?!\r
ef79bbde 370 {\r
b00afb77 371 // do this here, not in StartSound\r
372 // - fixes fussy timing issues\r
373 s_chan[ch].bStop=0;\r
77d6fd63 374 s_chan[ch].pCurr=spuMemC+((regAreaGet(ch,6)&~1)<<3); // must be block aligned\r
375 s_chan[ch].pLoop=spuMemC+((regAreaGet(ch,14)&~1)<<3);\r
e4f075af 376 s_chan[ch].prevflags=2;\r
b00afb77 377\r
ef79bbde 378 dwNewChannel|=(1<<ch); // bitfield for faster testing\r
6d866bb7 379 dwChannelOn|=1<<ch;\r
174c454a 380 dwChannelDead&=~(1<<ch);\r
ef79bbde
P
381 }\r
382 }\r
383}\r
384\r
385////////////////////////////////////////////////////////////////////////\r
386// SOUND OFF register write\r
387////////////////////////////////////////////////////////////////////////\r
388\r
7e44d49d 389static void SoundOff(int start,int end,unsigned short val)\r
ef79bbde
P
390{\r
391 int ch;\r
392 for(ch=start;ch<end;ch++,val>>=1) // loop channels\r
393 {\r
394 if(val&1) // && s_chan[i].bOn) mmm...\r
395 {\r
396 s_chan[ch].bStop=1;\r
b00afb77 397\r
398 // Jungle Book - Rhythm 'n Groove\r
399 // - turns off buzzing sound (loop hangs)\r
b00afb77 400 dwNewChannel &= ~(1<<ch);\r
ef79bbde
P
401 } \r
402 }\r
403}\r
404\r
405////////////////////////////////////////////////////////////////////////\r
406// FMOD register write\r
407////////////////////////////////////////////////////////////////////////\r
408\r
7e44d49d 409static void FModOn(int start,int end,unsigned short val)\r
ef79bbde
P
410{\r
411 int ch;\r
412\r
413 for(ch=start;ch<end;ch++,val>>=1) // loop channels\r
414 {\r
415 if(val&1) // -> fmod on/off\r
416 {\r
417 if(ch>0) \r
418 {\r
419 s_chan[ch].bFMod=1; // --> sound channel\r
420 s_chan[ch-1].bFMod=2; // --> freq channel\r
421 }\r
422 }\r
423 else\r
424 {\r
425 s_chan[ch].bFMod=0; // --> turn off fmod\r
07a6dd2c 426 if(ch>0&&s_chan[ch-1].bFMod==2)\r
427 s_chan[ch-1].bFMod=0;\r
ef79bbde
P
428 }\r
429 }\r
430}\r
431\r
432////////////////////////////////////////////////////////////////////////\r
433// NOISE register write\r
434////////////////////////////////////////////////////////////////////////\r
435\r
7e44d49d 436static void NoiseOn(int start,int end,unsigned short val)\r
ef79bbde
P
437{\r
438 int ch;\r
439\r
440 for(ch=start;ch<end;ch++,val>>=1) // loop channels\r
441 {\r
6d866bb7 442 s_chan[ch].bNoise=val&1; // -> noise on/off\r
ef79bbde
P
443 }\r
444}\r
445\r
446////////////////////////////////////////////////////////////////////////\r
447// LEFT VOLUME register write\r
448////////////////////////////////////////////////////////////////////////\r
449\r
450// please note: sweep and phase invert are wrong... but I've never seen\r
451// them used\r
452\r
7e44d49d 453static void SetVolumeL(unsigned char ch,short vol) // LEFT VOLUME\r
ef79bbde 454{\r
ef79bbde
P
455 if(vol&0x8000) // sweep?\r
456 {\r
457 short sInc=1; // -> sweep up?\r
458 if(vol&0x2000) sInc=-1; // -> or down?\r
459 if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this\r
460 vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64\r
461 vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!\r
462 vol*=128;\r
463 }\r
464 else // no sweep:\r
465 {\r
466 if(vol&0x4000) // -> mmm... phase inverted? have to investigate this\r
467 //vol^=0xffff;\r
468 vol=0x3fff-(vol&0x3fff);\r
469 }\r
470\r
471 vol&=0x3fff;\r
472 s_chan[ch].iLeftVolume=vol; // store volume\r
473}\r
474\r
475////////////////////////////////////////////////////////////////////////\r
476// RIGHT VOLUME register write\r
477////////////////////////////////////////////////////////////////////////\r
478\r
7e44d49d 479static void SetVolumeR(unsigned char ch,short vol) // RIGHT VOLUME\r
ef79bbde 480{\r
ef79bbde
P
481 if(vol&0x8000) // comments... see above :)\r
482 {\r
483 short sInc=1;\r
484 if(vol&0x2000) sInc=-1;\r
485 if(vol&0x1000) vol^=0xffff;\r
486 vol=((vol&0x7f)+1)/2; \r
487 vol+=vol/(2*sInc);\r
488 vol*=128;\r
489 }\r
490 else \r
491 {\r
492 if(vol&0x4000) //vol=vol^=0xffff;\r
493 vol=0x3fff-(vol&0x3fff);\r
494 }\r
495\r
496 vol&=0x3fff;\r
497\r
498 s_chan[ch].iRightVolume=vol;\r
499}\r
500\r
501////////////////////////////////////////////////////////////////////////\r
502// PITCH register write\r
503////////////////////////////////////////////////////////////////////////\r
504\r
7e44d49d 505static void SetPitch(int ch,unsigned short val) // SET PITCH\r
ef79bbde
P
506{\r
507 int NP;\r
508 if(val>0x3fff) NP=0x3fff; // get pitch val\r
509 else NP=val;\r
510\r
511 s_chan[ch].iRawPitch=NP;\r
7e44d49d 512 s_chan[ch].sinc=(NP<<4)|8;\r
650adfd2 513 s_chan[ch].sinc_inv=0;\r
7e44d49d 514 if(iUseInterpolation==1) s_chan[ch].SB[32]=1; // -> freq change in simple interpolation mode: set flag\r
ef79bbde
P
515}\r
516\r
517////////////////////////////////////////////////////////////////////////\r
518// REVERB register write\r
519////////////////////////////////////////////////////////////////////////\r
520\r
7e44d49d 521static void ReverbOn(int start,int end,unsigned short val)\r
ef79bbde
P
522{\r
523 int ch;\r
524\r
525 for(ch=start;ch<end;ch++,val>>=1) // loop channels\r
526 {\r
6d866bb7 527 s_chan[ch].bReverb=val&1; // -> reverb on/off\r
ef79bbde
P
528 }\r
529}\r