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