spu: rework synchronization
[pcsx_rearmed.git] / plugins / dfsound / registers.c
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
24 \r
25 static void SoundOn(int start,int end,unsigned short val);\r
26 static void SoundOff(int start,int end,unsigned short val);\r
27 static void FModOn(int start,int end,unsigned short val);\r
28 static void NoiseOn(int start,int end,unsigned short val);\r
29 static void SetVolumeL(unsigned char ch,short vol);\r
30 static void SetVolumeR(unsigned char ch,short vol);\r
31 static void SetPitch(int ch,unsigned short val);\r
32 static void ReverbOn(int start,int end,unsigned short val);\r
33 \r
34 ////////////////////////////////////////////////////////////////////////\r
35 // WRITE REGISTERS: called by main emu\r
36 ////////////////////////////////////////////////////////////////////////\r
37 \r
38 static 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
45 void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val,\r
46  unsigned int cycles)\r
47 {\r
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
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
77        goto upd_irq;\r
78      //------------------------------------------------// start\r
79      case 6:      \r
80        // taken from regArea later\r
81        break;\r
82      //------------------------------------------------// level with pre-calcs\r
83      case 8:\r
84        {\r
85         const unsigned long lval=val;\r
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
92        }\r
93       break;\r
94      //------------------------------------------------// adsr times with pre-calcs\r
95      case 10:\r
96       {\r
97        const unsigned long lval=val;\r
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
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
113        s_chan[ch].pLoop=spuMemC+((val&~1)<<3);\r
114        goto upd_irq;\r
115      //------------------------------------------------//\r
116     }\r
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
134       if (!(spuCtrl & CTRL_IRQ)) {\r
135         spuStat&=~STAT_IRQ;\r
136         if (val & CTRL_IRQ)\r
137          schedule_next_irq();\r
138       }\r
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
156           // sync-with-decode-buffers hack..\r
157           if(rvb.StartAddr==0x3ff00)\r
158             rvb.CurrAddr+=decode_pos/2;\r
159          }\r
160        }\r
161       goto rvbd;\r
162     //-------------------------------------------------//\r
163     case H_SPUirqAddr:\r
164       spuIrq = val;\r
165       pSpuIrq=spuMemC+(((unsigned long) val<<3)&~0xf);\r
166       goto upd_irq;\r
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
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
284    }\r
285  return;\r
286 \r
287 upd_irq:\r
288  if (spuCtrl & CTRL_IRQ)\r
289   schedule_next_irq();\r
290  return;\r
291 \r
292 rvbd:\r
293  rvb.dirty = 1; // recalculate on next update\r
294 }\r
295 \r
296 ////////////////////////////////////////////////////////////////////////\r
297 // READ REGISTER: called by main emu\r
298 ////////////////////////////////////////////////////////////////////////\r
299 \r
300 unsigned short CALLBACK SPUreadRegister(unsigned long reg)\r
301 {\r
302  const unsigned long r=reg&0xfff;\r
303         \r
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
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
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
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
363 static void SoundOn(int start,int end,unsigned short val)\r
364 {\r
365  int ch;\r
366 \r
367  for(ch=start;ch<end;ch++,val>>=1)                     // loop channels\r
368   {\r
369    if((val&1) && regAreaGet(ch,6))                     // mmm... start has to be set before key on !?!\r
370     {\r
371      // do this here, not in StartSound\r
372      // - fixes fussy timing issues\r
373      s_chan[ch].bStop=0;\r
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
376      s_chan[ch].prevflags=2;\r
377 \r
378      dwNewChannel|=(1<<ch);                            // bitfield for faster testing\r
379      dwChannelOn|=1<<ch;\r
380      dwChannelDead&=~(1<<ch);\r
381     }\r
382   }\r
383 }\r
384 \r
385 ////////////////////////////////////////////////////////////////////////\r
386 // SOUND OFF register write\r
387 ////////////////////////////////////////////////////////////////////////\r
388 \r
389 static void SoundOff(int start,int end,unsigned short val)\r
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
397 \r
398      // Jungle Book - Rhythm 'n Groove\r
399      // - turns off buzzing sound (loop hangs)\r
400      dwNewChannel &= ~(1<<ch);\r
401     }                                                  \r
402   }\r
403 }\r
404 \r
405 ////////////////////////////////////////////////////////////////////////\r
406 // FMOD register write\r
407 ////////////////////////////////////////////////////////////////////////\r
408 \r
409 static void FModOn(int start,int end,unsigned short val)\r
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
426      if(ch>0&&s_chan[ch-1].bFMod==2)\r
427       s_chan[ch-1].bFMod=0;\r
428     }\r
429   }\r
430 }\r
431 \r
432 ////////////////////////////////////////////////////////////////////////\r
433 // NOISE register write\r
434 ////////////////////////////////////////////////////////////////////////\r
435 \r
436 static void NoiseOn(int start,int end,unsigned short val)\r
437 {\r
438  int ch;\r
439 \r
440  for(ch=start;ch<end;ch++,val>>=1)                     // loop channels\r
441   {\r
442    s_chan[ch].bNoise=val&1;                            // -> noise on/off\r
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
453 static void SetVolumeL(unsigned char ch,short vol)     // LEFT VOLUME\r
454 {\r
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
479 static void SetVolumeR(unsigned char ch,short vol)     // RIGHT VOLUME\r
480 {\r
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
505 static void SetPitch(int ch,unsigned short val)               // SET PITCH\r
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
512  s_chan[ch].sinc=(NP<<4)|8;\r
513  s_chan[ch].sinc_inv=0;\r
514  if(iUseInterpolation==1) s_chan[ch].SB[32]=1;         // -> freq change in simple interpolation mode: set flag\r
515 }\r
516 \r
517 ////////////////////////////////////////////////////////////////////////\r
518 // REVERB register write\r
519 ////////////////////////////////////////////////////////////////////////\r
520 \r
521 static void ReverbOn(int start,int end,unsigned short val)\r
522 {\r
523  int ch;\r
524 \r
525  for(ch=start;ch<end;ch++,val>>=1)                     // loop channels\r
526   {\r
527    s_chan[ch].bReverb=val&1;                           // -> reverb on/off\r
528   }\r
529 }\r