spu: irq adjustments according to MiSTer
[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 #include "spu_config.h"\r
25 \r
26 static void SoundOn(int start,int end,unsigned short val);\r
27 static void SoundOff(int start,int end,unsigned short val);\r
28 static void FModOn(int start,int end,unsigned short val);\r
29 static void NoiseOn(int start,int end,unsigned short val);\r
30 static void SetVolumeL(unsigned char ch,short vol);\r
31 static void SetVolumeR(unsigned char ch,short vol);\r
32 static void SetPitch(int ch,unsigned short val);\r
33 static void ReverbOn(int start,int end,unsigned short val);\r
34 \r
35 ////////////////////////////////////////////////////////////////////////\r
36 // WRITE REGISTERS: called by main emu\r
37 ////////////////////////////////////////////////////////////////////////\r
38 \r
39 static const uint32_t ignore_dupe[8] = {\r
40  // ch 0-15  c40         c80         cc0\r
41  0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f,\r
42  // ch 16-24 d40         control     reverb\r
43  0x7f7f7f7f, 0x7f7f7f7f, 0xff05ff0f, 0xffffffff\r
44 };\r
45 \r
46 void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val,\r
47  unsigned int cycles)\r
48 {\r
49  int r = reg & 0xfff;\r
50  int rofs = (r - 0xc00) >> 1;\r
51  int changed = spu.regArea[rofs] != val;\r
52  spu.regArea[rofs] = val;\r
53 \r
54  if (!changed && (ignore_dupe[rofs >> 5] & (1 << (rofs & 0x1f))))\r
55   return;\r
56  // zero keyon/keyoff?\r
57  if (val == 0 && (r & 0xff8) == 0xd88)\r
58   return;\r
59 \r
60  do_samples_if_needed(cycles, 0);\r
61 \r
62  if(r>=0x0c00 && r<0x0d80)                             // some channel info?\r
63   {\r
64    int ch=(r>>4)-0xc0;                                 // calc channel\r
65    switch(r&0x0f)\r
66     {\r
67      //------------------------------------------------// r volume\r
68      case 0:                                           \r
69        SetVolumeL((unsigned char)ch,val);\r
70        break;\r
71      //------------------------------------------------// l volume\r
72      case 2:                                           \r
73        SetVolumeR((unsigned char)ch,val);\r
74        break;\r
75      //------------------------------------------------// pitch\r
76      case 4:                                           \r
77        SetPitch(ch,val);\r
78        goto upd_irq;\r
79      //------------------------------------------------// start\r
80      case 6:      \r
81        // taken from regArea later\r
82        break;\r
83      //------------------------------------------------// level with pre-calcs\r
84      case 8:\r
85        {\r
86         const unsigned long lval=val;\r
87         //---------------------------------------------//\r
88         spu.s_chan[ch].ADSRX.AttackModeExp=(lval&0x8000)?1:0;\r
89         spu.s_chan[ch].ADSRX.AttackRate=(lval>>8) & 0x007f;\r
90         spu.s_chan[ch].ADSRX.DecayRate=(lval>>4) & 0x000f;\r
91         spu.s_chan[ch].ADSRX.SustainLevel=lval & 0x000f;\r
92         //---------------------------------------------//\r
93        }\r
94       break;\r
95      //------------------------------------------------// adsr times with pre-calcs\r
96      case 10:\r
97       {\r
98        const unsigned long lval=val;\r
99 \r
100        //----------------------------------------------//\r
101        spu.s_chan[ch].ADSRX.SustainModeExp = (lval&0x8000)?1:0;\r
102        spu.s_chan[ch].ADSRX.SustainIncrease= (lval&0x4000)?0:1;\r
103        spu.s_chan[ch].ADSRX.SustainRate = (lval>>6) & 0x007f;\r
104        spu.s_chan[ch].ADSRX.ReleaseModeExp = (lval&0x0020)?1:0;\r
105        spu.s_chan[ch].ADSRX.ReleaseRate = lval & 0x001f;\r
106        //----------------------------------------------//\r
107       }\r
108      break;\r
109      //------------------------------------------------// adsr volume... mmm have to investigate this\r
110      case 12:\r
111        break;\r
112      //------------------------------------------------//\r
113      case 14:                                          // loop?\r
114        spu.s_chan[ch].pLoop=spu.spuMemC+((val&~1)<<3);\r
115        spu.s_chan[ch].bIgnoreLoop = 1;\r
116        goto upd_irq;\r
117      //------------------------------------------------//\r
118     }\r
119    return;\r
120   }\r
121 \r
122  switch(r)\r
123    {\r
124     //-------------------------------------------------//\r
125     case H_SPUaddr:\r
126       spu.spuAddr = (unsigned long) val<<3;\r
127       break;\r
128     //-------------------------------------------------//\r
129     case H_SPUdata:\r
130       *(unsigned short *)(spu.spuMemC + spu.spuAddr) = val;\r
131       spu.spuAddr += 2;\r
132       spu.spuAddr &= 0x7fffe;\r
133       break;\r
134     //-------------------------------------------------//\r
135     case H_SPUctrl:\r
136       if (!(spu.spuCtrl & CTRL_IRQ)) {\r
137         spu.spuStat&=~STAT_IRQ;\r
138         if (val & CTRL_IRQ)\r
139          schedule_next_irq();\r
140       }\r
141       spu.spuCtrl=val;\r
142       break;\r
143     //-------------------------------------------------//\r
144     case H_SPUstat:\r
145       spu.spuStat=val&0xf800;\r
146       break;\r
147     //-------------------------------------------------//\r
148     case H_SPUReverbAddr:\r
149       goto rvbd;\r
150     //-------------------------------------------------//\r
151     case H_SPUirqAddr:\r
152       //if (val & 1)\r
153       //  log_unhandled("w irq with lsb: %08lx %04x\n", reg, val);\r
154       spu.pSpuIrq=spu.spuMemC+(((unsigned long) val<<3)&~0xf);\r
155       goto upd_irq;\r
156     //-------------------------------------------------//\r
157     case H_SPUrvolL:\r
158       spu.rvb->VolLeft=val;\r
159       break;\r
160     //-------------------------------------------------//\r
161     case H_SPUrvolR:\r
162       spu.rvb->VolRight=val;\r
163       break;\r
164     //-------------------------------------------------//\r
165 \r
166     case H_SPUmvolL:\r
167     case H_SPUmvolR:\r
168       if (val & 0x8000)\r
169         log_unhandled("w master sweep: %08lx %04x\n", reg, val);\r
170       break;\r
171 \r
172     case 0x0dac:\r
173      if (val != 4)\r
174        log_unhandled("1f801dac %04x\n", val);\r
175      break;\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       spu.iLeftXAVol=(int16_t)val;\r
221       if(spu.cddavCallback) spu.cddavCallback(0,(int16_t)val);\r
222       break;\r
223     case H_CDRight:\r
224       spu.iRightXAVol=(int16_t)val;\r
225       if(spu.cddavCallback) spu.cddavCallback(1,(int16_t)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   : goto rvbd;\r
253     case H_Reverb+2   : goto rvbd;\r
254     case H_Reverb+4   : spu.rvb->IIR_ALPHA=(short)val;   break;\r
255     case H_Reverb+6   : spu.rvb->ACC_COEF_A=(short)val;  break;\r
256     case H_Reverb+8   : spu.rvb->ACC_COEF_B=(short)val;  break;\r
257     case H_Reverb+10  : spu.rvb->ACC_COEF_C=(short)val;  break;\r
258     case H_Reverb+12  : spu.rvb->ACC_COEF_D=(short)val;  break;\r
259     case H_Reverb+14  : spu.rvb->IIR_COEF=(short)val;    break;\r
260     case H_Reverb+16  : spu.rvb->FB_ALPHA=(short)val;    break;\r
261     case H_Reverb+18  : spu.rvb->FB_X=(short)val;        break;\r
262     case H_Reverb+20  : goto rvbd;\r
263     case H_Reverb+22  : goto rvbd;\r
264     case H_Reverb+24  : goto rvbd;\r
265     case H_Reverb+26  : goto rvbd;\r
266     case H_Reverb+28  : goto rvbd;\r
267     case H_Reverb+30  : goto rvbd;\r
268     case H_Reverb+32  : goto rvbd;\r
269     case H_Reverb+34  : goto rvbd;\r
270     case H_Reverb+36  : goto rvbd;\r
271     case H_Reverb+38  : goto rvbd;\r
272     case H_Reverb+40  : goto rvbd;\r
273     case H_Reverb+42  : goto rvbd;\r
274     case H_Reverb+44  : goto rvbd;\r
275     case H_Reverb+46  : goto rvbd;\r
276     case H_Reverb+48  : goto rvbd;\r
277     case H_Reverb+50  : goto rvbd;\r
278     case H_Reverb+52  : goto rvbd;\r
279     case H_Reverb+54  : goto rvbd;\r
280     case H_Reverb+56  : goto rvbd;\r
281     case H_Reverb+58  : goto rvbd;\r
282     case H_Reverb+60  : spu.rvb->IN_COEF_L=(short)val;   break;\r
283     case H_Reverb+62  : spu.rvb->IN_COEF_R=(short)val;   break;\r
284    }\r
285  return;\r
286 \r
287 upd_irq:\r
288  if (spu.spuCtrl & CTRL_IRQ)\r
289   schedule_next_irq();\r
290  return;\r
291 \r
292 rvbd:\r
293  spu.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(spu.dwNewChannel&(1<<ch)) return 1;          // we are started, but not processed? return 1\r
312        if((spu.dwChannelsAudible&(1<<ch)) &&           // same here... we haven't decoded one sample yet, so no envelope yet. return 1 as well\r
313           !spu.s_chan[ch].ADSRX.EnvelopeVol)\r
314         return 1;\r
315        return (unsigned short)(spu.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)((spu.s_chan[ch].pLoop-spu.spuMemC)>>3);\r
322       }\r
323     }\r
324   }\r
325 \r
326  switch(r)\r
327   {\r
328     case H_SPUctrl:\r
329      return spu.spuCtrl;\r
330 \r
331     case H_SPUstat:\r
332      return (spu.spuStat & ~0x3F) | (spu.spuCtrl & 0x3F);\r
333         \r
334     case H_SPUaddr:\r
335      return (unsigned short)(spu.spuAddr>>3);\r
336 \r
337     case H_SPUdata:\r
338      {\r
339       unsigned short s = *(unsigned short *)(spu.spuMemC + spu.spuAddr);\r
340       spu.spuAddr += 2;\r
341       spu.spuAddr &= 0x7fffe;\r
342       return s;\r
343      }\r
344 \r
345     //case H_SPUIsOn1:\r
346     // return IsSoundOn(0,16);\r
347 \r
348     //case H_SPUIsOn2:\r
349     // return IsSoundOn(16,24);\r
350  \r
351     case H_SPUMute1:\r
352     case H_SPUMute2:\r
353      log_unhandled("r isOn: %08lx\n", reg);\r
354      break;\r
355   }\r
356 \r
357  return spu.regArea[(r-0xc00)>>1];\r
358 }\r
359  \r
360 ////////////////////////////////////////////////////////////////////////\r
361 // SOUND ON register write\r
362 ////////////////////////////////////////////////////////////////////////\r
363 \r
364 static void SoundOn(int start,int end,unsigned short val)\r
365 {\r
366  int ch;\r
367 \r
368  for(ch=start;ch<end;ch++,val>>=1)                     // loop channels\r
369   {\r
370    if((val&1) && regAreaGetCh(ch, 6))                  // mmm... start has to be set before key on !?!\r
371     {\r
372      spu.s_chan[ch].bIgnoreLoop = 0;\r
373      spu.dwNewChannel|=(1<<ch);\r
374     }\r
375   }\r
376 }\r
377 \r
378 ////////////////////////////////////////////////////////////////////////\r
379 // SOUND OFF register write\r
380 ////////////////////////////////////////////////////////////////////////\r
381 \r
382 static void SoundOff(int start,int end,unsigned short val)\r
383 {\r
384  int ch;\r
385  for(ch=start;ch<end;ch++,val>>=1)                     // loop channels\r
386   {\r
387    if(val&1)\r
388     {\r
389      spu.s_chan[ch].ADSRX.State = ADSR_RELEASE;\r
390 \r
391      // Jungle Book - Rhythm 'n Groove\r
392      // - turns off buzzing sound (loop hangs)\r
393      spu.dwNewChannel &= ~(1<<ch);\r
394     }                                                  \r
395   }\r
396 }\r
397 \r
398 ////////////////////////////////////////////////////////////////////////\r
399 // FMOD register write\r
400 ////////////////////////////////////////////////////////////////////////\r
401 \r
402 static void FModOn(int start,int end,unsigned short val)\r
403 {\r
404  int ch;\r
405 \r
406  for(ch=start;ch<end;ch++,val>>=1)                     // loop channels\r
407   {\r
408    if(val&1)                                           // -> fmod on/off\r
409     {\r
410      if(ch>0) \r
411       {\r
412        spu.s_chan[ch].bFMod=1;                         // --> sound channel\r
413        spu.s_chan[ch-1].bFMod=2;                       // --> freq channel\r
414       }\r
415     }\r
416    else\r
417     {\r
418      spu.s_chan[ch].bFMod=0;                           // --> turn off fmod\r
419      if(ch>0&&spu.s_chan[ch-1].bFMod==2)\r
420       spu.s_chan[ch-1].bFMod=0;\r
421     }\r
422   }\r
423 }\r
424 \r
425 ////////////////////////////////////////////////////////////////////////\r
426 // NOISE register write\r
427 ////////////////////////////////////////////////////////////////////////\r
428 \r
429 static void NoiseOn(int start,int end,unsigned short val)\r
430 {\r
431  int ch;\r
432 \r
433  for(ch=start;ch<end;ch++,val>>=1)                     // loop channels\r
434   {\r
435    spu.s_chan[ch].bNoise=val&1;                        // -> noise on/off\r
436   }\r
437 }\r
438 \r
439 ////////////////////////////////////////////////////////////////////////\r
440 // LEFT VOLUME register write\r
441 ////////////////////////////////////////////////////////////////////////\r
442 \r
443 // please note: sweep and phase invert are wrong... but I've never seen\r
444 // them used\r
445 \r
446 static void SetVolumeL(unsigned char ch,short vol)     // LEFT VOLUME\r
447 {\r
448  if(vol&0x8000)                                        // sweep?\r
449   {\r
450    short sInc=1;                                       // -> sweep up?\r
451    log_unhandled("ch%d sweepl %04x\n", ch, vol);\r
452    if(vol&0x2000) sInc=-1;                             // -> or down?\r
453    if(vol&0x1000) vol^=0xffff;                         // -> mmm... phase inverted? have to investigate this\r
454    vol=((vol&0x7f)+1)/2;                               // -> sweep: 0..127 -> 0..64\r
455    vol+=vol/(2*sInc);                                  // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!\r
456    vol*=128;\r
457   }\r
458  else                                                  // no sweep:\r
459   {\r
460    if(vol&0x4000)                                      // -> mmm... phase inverted? have to investigate this\r
461     //vol^=0xffff;\r
462     vol=0x3fff-(vol&0x3fff);\r
463   }\r
464 \r
465  vol&=0x3fff;\r
466  spu.s_chan[ch].iLeftVolume=vol;                       // store volume\r
467 }\r
468 \r
469 ////////////////////////////////////////////////////////////////////////\r
470 // RIGHT VOLUME register write\r
471 ////////////////////////////////////////////////////////////////////////\r
472 \r
473 static void SetVolumeR(unsigned char ch,short vol)     // RIGHT VOLUME\r
474 {\r
475  if(vol&0x8000)                                        // comments... see above :)\r
476   {\r
477    short sInc=1;\r
478    log_unhandled("ch%d sweepr %04x\n", ch, vol);\r
479    if(vol&0x2000) sInc=-1;\r
480    if(vol&0x1000) vol^=0xffff;\r
481    vol=((vol&0x7f)+1)/2;        \r
482    vol+=vol/(2*sInc);\r
483    vol*=128;\r
484   }\r
485  else            \r
486   {\r
487    if(vol&0x4000) //vol=vol^=0xffff;\r
488     vol=0x3fff-(vol&0x3fff);\r
489   }\r
490 \r
491  vol&=0x3fff;\r
492 \r
493  spu.s_chan[ch].iRightVolume=vol;\r
494 }\r
495 \r
496 ////////////////////////////////////////////////////////////////////////\r
497 // PITCH register write\r
498 ////////////////////////////////////////////////////////////////////////\r
499 \r
500 static void SetPitch(int ch,unsigned short val)               // SET PITCH\r
501 {\r
502  int NP;\r
503  if(val>0x3fff) NP=0x3fff;                             // get pitch val\r
504  else           NP=val;\r
505 \r
506  spu.s_chan[ch].iRawPitch = NP;\r
507  spu.s_chan[ch].sinc = NP << 4;\r
508  spu.s_chan[ch].sinc_inv = 0;\r
509  spu.SB[ch * SB_SIZE + 32] = 1; // -> freq change in simple interpolation mode: set flag\r
510 \r
511  // don't mess spu.dwChannelsAudible as adsr runs independently\r
512 }\r
513 \r
514 ////////////////////////////////////////////////////////////////////////\r
515 // REVERB register write\r
516 ////////////////////////////////////////////////////////////////////////\r
517 \r
518 static void ReverbOn(int start,int end,unsigned short val)\r
519 {\r
520  int ch;\r
521 \r
522  for(ch=start;ch<end;ch++,val>>=1)                     // loop channels\r
523   {\r
524    spu.s_chan[ch].bReverb=val&1;                       // -> reverb on/off\r
525   }\r
526 }\r