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