spu: irq adjustments according to MiSTer
[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\r
26static void SoundOn(int start,int end,unsigned short val);\r
27static void SoundOff(int start,int end,unsigned short val);\r
28static void FModOn(int start,int end,unsigned short val);\r
29static void NoiseOn(int start,int end,unsigned short val);\r
30static void SetVolumeL(unsigned char ch,short vol);\r
31static void SetVolumeR(unsigned char ch,short vol);\r
32static void SetPitch(int ch,unsigned short val);\r
33static 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
39static 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
46void 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
287upd_irq:\r
288 if (spu.spuCtrl & CTRL_IRQ)\r
289 schedule_next_irq();\r
290 return;\r
291\r
292rvbd:\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
300unsigned 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
364static 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
382static 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
402static 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
429static 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
446static 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
473static 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
500static 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
518static 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