gpu_neon: fix some missing ebuf updates
[pcsx_rearmed.git] / plugins / dfsound / registers.c
... / ...
CommitLineData
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
27static void SoundOn(int start,int end,unsigned short val);\r
28static void SoundOff(int start,int end,unsigned short val);\r
29static void FModOn(int start,int end,unsigned short val);\r
30static void NoiseOn(int start,int end,unsigned short val);\r
31static void SetVolumeL(unsigned char ch,short vol);\r
32static void SetVolumeR(unsigned char ch,short vol);\r
33static void SetPitch(int ch,unsigned short val);\r
34static 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
40static 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
49void 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 = (int16_t)val;\r
171 break;\r
172 //-------------------------------------------------//\r
173 case H_SPUrvolR:\r
174 spu.rvb->VolRight = (int16_t)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 spu.last_keyon_cycles = cycles;\r
217 do_samples_if_needed(cycles, 0, 2);\r
218 SoundOn(0,16,val);\r
219 break;\r
220 //-------------------------------------------------//\r
221 case H_SPUon2:\r
222 spu.last_keyon_cycles = cycles;\r
223 do_samples_if_needed(cycles, 0, 2);\r
224 SoundOn(16,24,val);\r
225 break;\r
226 //-------------------------------------------------//\r
227 case H_SPUoff1:\r
228 if (cycles - spu.last_keyon_cycles < 786u) {\r
229 if (val & regAreaGet(H_SPUon1))\r
230 log_unhandled("koff1 %04x %d\n", val, cycles - spu.last_keyon_cycles);\r
231 val &= ~regAreaGet(H_SPUon1);\r
232 }\r
233 SoundOff(0,16,val);\r
234 break;\r
235 //-------------------------------------------------//\r
236 case H_SPUoff2:\r
237 if (cycles - spu.last_keyon_cycles < 786u) {\r
238 if (val & regAreaGet(H_SPUon1))\r
239 log_unhandled("koff2 %04x %d\n", val, cycles - spu.last_keyon_cycles);\r
240 val &= ~regAreaGet(H_SPUon2);\r
241 }\r
242 SoundOff(16,24,val);\r
243 break;\r
244 //-------------------------------------------------//\r
245 case H_CDLeft:\r
246 spu.iLeftXAVol=(int16_t)val;\r
247 //if(spu.cddavCallback) spu.cddavCallback(0,(int16_t)val);\r
248 break;\r
249 case H_CDRight:\r
250 spu.iRightXAVol=(int16_t)val;\r
251 //if(spu.cddavCallback) spu.cddavCallback(1,(int16_t)val);\r
252 break;\r
253 //-------------------------------------------------//\r
254 case H_FMod1:\r
255 FModOn(0,16,val);\r
256 break;\r
257 //-------------------------------------------------//\r
258 case H_FMod2:\r
259 FModOn(16,24,val);\r
260 break;\r
261 //-------------------------------------------------//\r
262 case H_Noise1:\r
263 NoiseOn(0,16,val);\r
264 break;\r
265 //-------------------------------------------------//\r
266 case H_Noise2:\r
267 NoiseOn(16,24,val);\r
268 break;\r
269 //-------------------------------------------------//\r
270 case H_RVBon1:\r
271 ReverbOn(0,16,val);\r
272 break;\r
273 //-------------------------------------------------//\r
274 case H_RVBon2:\r
275 ReverbOn(16,24,val);\r
276 break;\r
277 //-------------------------------------------------//\r
278 case H_Reverb+0 : goto rvbd;\r
279 case H_Reverb+2 : goto rvbd;\r
280 case H_Reverb+4 : spu.rvb->IIR_ALPHA=(short)val; break;\r
281 case H_Reverb+6 : spu.rvb->ACC_COEF_A=(short)val; break;\r
282 case H_Reverb+8 : spu.rvb->ACC_COEF_B=(short)val; break;\r
283 case H_Reverb+10 : spu.rvb->ACC_COEF_C=(short)val; break;\r
284 case H_Reverb+12 : spu.rvb->ACC_COEF_D=(short)val; break;\r
285 case H_Reverb+14 : spu.rvb->IIR_COEF=(short)val; break;\r
286 case H_Reverb+16 : spu.rvb->FB_ALPHA=(short)val; break;\r
287 case H_Reverb+18 : spu.rvb->FB_X=(short)val; break;\r
288 case H_Reverb+20 : goto rvbd;\r
289 case H_Reverb+22 : goto rvbd;\r
290 case H_Reverb+24 : goto rvbd;\r
291 case H_Reverb+26 : goto rvbd;\r
292 case H_Reverb+28 : goto rvbd;\r
293 case H_Reverb+30 : goto rvbd;\r
294 case H_Reverb+32 : goto rvbd;\r
295 case H_Reverb+34 : goto rvbd;\r
296 case H_Reverb+36 : goto rvbd;\r
297 case H_Reverb+38 : goto rvbd;\r
298 case H_Reverb+40 : goto rvbd;\r
299 case H_Reverb+42 : goto rvbd;\r
300 case H_Reverb+44 : goto rvbd;\r
301 case H_Reverb+46 : goto rvbd;\r
302 case H_Reverb+48 : goto rvbd;\r
303 case H_Reverb+50 : goto rvbd;\r
304 case H_Reverb+52 : goto rvbd;\r
305 case H_Reverb+54 : goto rvbd;\r
306 case H_Reverb+56 : goto rvbd;\r
307 case H_Reverb+58 : goto rvbd;\r
308 case H_Reverb+60 : spu.rvb->IN_COEF_L=(short)val; break;\r
309 case H_Reverb+62 : spu.rvb->IN_COEF_R=(short)val; break;\r
310 }\r
311 return;\r
312\r
313upd_irq:\r
314 if (spu.spuCtrl & CTRL_IRQ)\r
315 schedule_next_irq();\r
316 return;\r
317\r
318rvbd:\r
319 spu.rvb->dirty = 1; // recalculate on next update\r
320}\r
321\r
322////////////////////////////////////////////////////////////////////////\r
323// READ REGISTER: called by main emu\r
324////////////////////////////////////////////////////////////////////////\r
325\r
326unsigned short CALLBACK SPUreadRegister(unsigned long reg, unsigned int cycles)\r
327{\r
328 const unsigned long r = reg & 0xffe;\r
329 \r
330 if(r>=0x0c00 && r<0x0d80)\r
331 {\r
332 switch(r&0x0f)\r
333 {\r
334 case 12: // get adsr vol\r
335 {\r
336 // this used to return 1 immediately after keyon to deal with\r
337 // some poor timing, but that causes Rayman 2 to lose track of\r
338 // it's channels on busy scenes and start looping some of them forever\r
339 const int ch = (r>>4) - 0xc0;\r
340 if (spu.s_chan[ch].bStarting)\r
341 do_samples_if_needed(cycles, 0, 2);\r
342 return (unsigned short)(spu.s_chan[ch].ADSRX.EnvelopeVol >> 16);\r
343 }\r
344\r
345 case 14: // get loop address\r
346 {\r
347 const int ch=(r>>4)-0xc0;\r
348 return (unsigned short)((spu.s_chan[ch].pLoop-spu.spuMemC)>>3);\r
349 }\r
350 }\r
351 }\r
352 else if (0x0e00 <= r && r < 0x0e60)\r
353 {\r
354 int ch = (r >> 2) & 0x1f;\r
355 int v = spu.s_chan[ch].iVolume[(r >> 1) & 1] << 1;\r
356 log_unhandled("c%02d r %cvol %04x\n", ch, (r & 2) ? 'r' : 'l', v);\r
357 return v;\r
358 }\r
359\r
360 switch(r)\r
361 {\r
362 case H_SPUctrl:\r
363 return spu.spuCtrl;\r
364\r
365 case H_SPUstat:\r
366 return (spu.spuStat & ~0x3F) | (spu.spuCtrl & 0x3F);\r
367 \r
368 case H_SPUaddr:\r
369 return (unsigned short)(spu.spuAddr>>3);\r
370\r
371 // this reportedly doesn't work on real hw\r
372 case H_SPUdata:\r
373 {\r
374 unsigned short s = LE16TOH(*(unsigned short *)(spu.spuMemC + spu.spuAddr));\r
375 spu.spuAddr += 2;\r
376 spu.spuAddr &= 0x7fffe;\r
377 //check_irq_io(spu.spuAddr);\r
378 return s;\r
379 }\r
380\r
381 //case H_SPUIsOn1:\r
382 // return IsSoundOn(0,16);\r
383\r
384 //case H_SPUIsOn2:\r
385 // return IsSoundOn(16,24);\r
386 \r
387 case H_SPUMute1:\r
388 case H_SPUMute2:\r
389 log_unhandled("r isOn: %08lx\n", reg);\r
390 break;\r
391\r
392 case 0x0dac:\r
393 case H_SPUirqAddr:\r
394 case H_CDLeft:\r
395 case H_CDRight:\r
396 case H_ExtLeft:\r
397 case H_ExtRight:\r
398 break;\r
399\r
400 default:\r
401 if (r >= 0xda0)\r
402 log_unhandled("spu r %08lx\n", reg);\r
403 break;\r
404 }\r
405\r
406 return spu.regArea[(r-0xc00)>>1];\r
407}\r
408 \r
409////////////////////////////////////////////////////////////////////////\r
410// SOUND ON register write\r
411////////////////////////////////////////////////////////////////////////\r
412\r
413static void SoundOn(int start,int end,unsigned short val)\r
414{\r
415 int ch;\r
416\r
417 for(ch=start;ch<end;ch++,val>>=1) // loop channels\r
418 {\r
419 if((val&1) && regAreaGetCh(ch, 6)) // mmm... start has to be set before key on !?!\r
420 {\r
421 spu.s_chan[ch].bIgnoreLoop = 0;\r
422 spu.s_chan[ch].bStarting = 1;\r
423 spu.dwNewChannel|=(1<<ch);\r
424 }\r
425 }\r
426}\r
427\r
428////////////////////////////////////////////////////////////////////////\r
429// SOUND OFF register write\r
430////////////////////////////////////////////////////////////////////////\r
431\r
432static void SoundOff(int start,int end,unsigned short val)\r
433{\r
434 int ch;\r
435 for (ch = start; val && ch < end; ch++, val >>= 1) // loop channels\r
436 {\r
437 if(val&1)\r
438 {\r
439 spu.s_chan[ch].ADSRX.State = ADSR_RELEASE;\r
440\r
441 // Jungle Book - Rhythm 'n Groove\r
442 // - turns off buzzing sound (loop hangs)\r
443 spu.dwNewChannel &= ~(1<<ch);\r
444 } \r
445 }\r
446}\r
447\r
448////////////////////////////////////////////////////////////////////////\r
449// FMOD register write\r
450////////////////////////////////////////////////////////////////////////\r
451\r
452static void FModOn(int start,int end,unsigned short val)\r
453{\r
454 int ch;\r
455\r
456 for(ch=start;ch<end;ch++,val>>=1) // loop channels\r
457 {\r
458 if(val&1) // -> fmod on/off\r
459 {\r
460 if(ch>0) \r
461 {\r
462 spu.s_chan[ch].bFMod=1; // --> sound channel\r
463 spu.s_chan[ch-1].bFMod=2; // --> freq channel\r
464 }\r
465 }\r
466 else\r
467 {\r
468 spu.s_chan[ch].bFMod=0; // --> turn off fmod\r
469 if(ch>0&&spu.s_chan[ch-1].bFMod==2)\r
470 spu.s_chan[ch-1].bFMod=0;\r
471 }\r
472 }\r
473}\r
474\r
475////////////////////////////////////////////////////////////////////////\r
476// NOISE register write\r
477////////////////////////////////////////////////////////////////////////\r
478\r
479static void NoiseOn(int start,int end,unsigned short val)\r
480{\r
481 int ch;\r
482\r
483 for(ch=start;ch<end;ch++,val>>=1) // loop channels\r
484 {\r
485 spu.s_chan[ch].bNoise=val&1; // -> noise on/off\r
486 }\r
487}\r
488\r
489////////////////////////////////////////////////////////////////////////\r
490// LEFT VOLUME register write\r
491////////////////////////////////////////////////////////////////////////\r
492\r
493// please note: sweep and phase invert are wrong... but I've never seen\r
494// them used\r
495\r
496static void SetVolumeL(unsigned char ch,short vol) // LEFT VOLUME\r
497{\r
498 if(vol&0x8000) // sweep?\r
499 {\r
500 short sInc=1; // -> sweep up?\r
501 log_unhandled("ch%d sweepl %04x\n", ch, vol);\r
502 if(vol&0x2000) sInc=-1; // -> or down?\r
503 if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this\r
504 vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64\r
505 vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!\r
506 vol*=128;\r
507 }\r
508 else // no sweep:\r
509 {\r
510 if(vol&0x4000) // -> mmm... phase inverted? have to investigate this\r
511 //vol^=0xffff;\r
512 vol=0x3fff-(vol&0x3fff);\r
513 }\r
514\r
515 vol&=0x3fff;\r
516 spu.s_chan[ch].iLeftVolume=vol; // store volume\r
517 //spu.regArea[(0xe00-0xc00)/2 + ch*2 + 0] = vol << 1;\r
518}\r
519\r
520////////////////////////////////////////////////////////////////////////\r
521// RIGHT VOLUME register write\r
522////////////////////////////////////////////////////////////////////////\r
523\r
524static void SetVolumeR(unsigned char ch,short vol) // RIGHT VOLUME\r
525{\r
526 if(vol&0x8000) // comments... see above :)\r
527 {\r
528 short sInc=1;\r
529 log_unhandled("ch%d sweepr %04x\n", ch, vol);\r
530 if(vol&0x2000) sInc=-1;\r
531 if(vol&0x1000) vol^=0xffff;\r
532 vol=((vol&0x7f)+1)/2; \r
533 vol+=vol/(2*sInc);\r
534 vol*=128;\r
535 }\r
536 else \r
537 {\r
538 if(vol&0x4000) //vol=vol^=0xffff;\r
539 vol=0x3fff-(vol&0x3fff);\r
540 }\r
541\r
542 vol&=0x3fff;\r
543\r
544 spu.s_chan[ch].iRightVolume=vol;\r
545 //spu.regArea[(0xe00-0xc00)/2 + ch*2 + 1] = vol << 1;\r
546}\r
547\r
548////////////////////////////////////////////////////////////////////////\r
549// PITCH register write\r
550////////////////////////////////////////////////////////////////////////\r
551\r
552static void SetPitch(int ch,unsigned short val) // SET PITCH\r
553{\r
554 int NP;\r
555 if(val>0x3fff) NP=0x3fff; // get pitch val\r
556 else NP=val;\r
557\r
558 spu.s_chan[ch].iRawPitch = NP;\r
559 spu.s_chan[ch].sinc = NP << 4;\r
560 spu.s_chan[ch].sinc_inv = 0;\r
561\r
562 // don't mess spu.dwChannelsAudible as adsr runs independently\r
563}\r
564\r
565////////////////////////////////////////////////////////////////////////\r
566// REVERB register write\r
567////////////////////////////////////////////////////////////////////////\r
568\r
569static void ReverbOn(int start,int end,unsigned short val)\r
570{\r
571 int ch;\r
572\r
573 for(ch=start;ch<end;ch++,val>>=1) // loop channels\r
574 {\r
575 spu.s_chan[ch].bReverb=val&1; // -> reverb on/off\r
576 }\r
577}\r
578\r
579// vim:shiftwidth=1:expandtab\r