1 /***************************************************************************
\r
2 registers.c - description
\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
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
16 ***************************************************************************/
\r
20 #define _IN_REGISTERS
\r
22 #include "externals.h"
\r
23 #include "registers.h"
\r
24 #include "spu_config.h"
\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
36 ////////////////////////////////////////////////////////////////////////
\r
37 // WRITE REGISTERS: called by main emu
\r
38 ////////////////////////////////////////////////////////////////////////
\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
49 void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val,
\r
50 unsigned int cycles)
\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
57 if (!changed && (ignore_dupe[rofs >> 5] & (1 << (rofs & 0x1f))))
\r
59 // zero keyon/keyoff?
\r
60 if (val == 0 && (r & 0xff8) == 0xd88)
\r
63 do_samples_if_needed(cycles, 0, 16);
\r
65 if(r>=0x0c00 && r<0x0d80) // some channel info?
\r
67 int ch=(r>>4)-0xc0; // calc channel
\r
70 //------------------------------------------------// r volume
\r
72 SetVolumeL((unsigned char)ch,val);
\r
74 //------------------------------------------------// l volume
\r
76 SetVolumeR((unsigned char)ch,val);
\r
78 //------------------------------------------------// pitch
\r
82 //------------------------------------------------// start
\r
84 // taken from regArea later
\r
86 //------------------------------------------------// level with pre-calcs
\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
98 //------------------------------------------------// adsr times with pre-calcs
\r
101 const unsigned long lval=val;
\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
112 //------------------------------------------------// adsr volume... mmm have to investigate this
\r
115 //------------------------------------------------//
\r
117 spu.s_chan[ch].pLoop=spu.spuMemC+((val&~1)<<3);
\r
118 spu.s_chan[ch].bIgnoreLoop = 1;
\r
120 //------------------------------------------------//
\r
124 else if (0x0e00 <= r && r < 0x0e60)
\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
133 //-------------------------------------------------//
\r
135 spu.spuAddr = (unsigned long) val<<3;
\r
136 //check_irq_io(spu.spuAddr);
\r
138 //-------------------------------------------------//
\r
140 *(unsigned short *)(spu.spuMemC + spu.spuAddr) = HTOLE16(val);
\r
142 spu.spuAddr &= 0x7fffe;
\r
143 check_irq_io(spu.spuAddr);
\r
145 //-------------------------------------------------//
\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
154 //-------------------------------------------------//
\r
156 spu.spuStat=val&0xf800;
\r
158 //-------------------------------------------------//
\r
159 case H_SPUReverbAddr:
\r
161 //-------------------------------------------------//
\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
168 //-------------------------------------------------//
\r
170 spu.rvb->VolLeft=val;
\r
172 //-------------------------------------------------//
\r
174 spu.rvb->VolRight=val;
\r
176 //-------------------------------------------------//
\r
181 log_unhandled("w master sweep: %08lx %04x\n", reg, val);
\r
186 log_unhandled("1f801dac %04x\n", val);
\r
191 //auxprintf("EL %d\n",val);
\r
193 //-------------------------------------------------//
\r
195 //auxprintf("ER %d\n",val);
\r
197 //-------------------------------------------------//
\r
199 //auxprintf("ML %d\n",val);
\r
201 //-------------------------------------------------//
\r
203 //auxprintf("MR %d\n",val);
\r
205 //-------------------------------------------------//
\r
207 //auxprintf("M0 %04x\n",val);
\r
209 //-------------------------------------------------//
\r
211 //auxprintf("M1 %04x\n",val);
\r
214 //-------------------------------------------------//
\r
216 do_samples_if_needed(cycles, 0, 2);
\r
219 //-------------------------------------------------//
\r
221 do_samples_if_needed(cycles, 0, 2);
\r
222 SoundOn(16,24,val);
\r
224 //-------------------------------------------------//
\r
226 SoundOff(0,16,val);
\r
228 //-------------------------------------------------//
\r
230 SoundOff(16,24,val);
\r
232 //-------------------------------------------------//
\r
234 spu.iLeftXAVol=(int16_t)val;
\r
235 if(spu.cddavCallback) spu.cddavCallback(0,(int16_t)val);
\r
238 spu.iRightXAVol=(int16_t)val;
\r
239 if(spu.cddavCallback) spu.cddavCallback(1,(int16_t)val);
\r
241 //-------------------------------------------------//
\r
245 //-------------------------------------------------//
\r
249 //-------------------------------------------------//
\r
253 //-------------------------------------------------//
\r
255 NoiseOn(16,24,val);
\r
257 //-------------------------------------------------//
\r
259 ReverbOn(0,16,val);
\r
261 //-------------------------------------------------//
\r
263 ReverbOn(16,24,val);
\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
302 if (spu.spuCtrl & CTRL_IRQ)
\r
303 schedule_next_irq();
\r
307 spu.rvb->dirty = 1; // recalculate on next update
\r
310 ////////////////////////////////////////////////////////////////////////
\r
311 // READ REGISTER: called by main emu
\r
312 ////////////////////////////////////////////////////////////////////////
\r
314 unsigned short CALLBACK SPUreadRegister(unsigned long reg, unsigned int cycles)
\r
316 const unsigned long r = reg & 0xffe;
\r
318 if(r>=0x0c00 && r<0x0d80)
\r
322 case 12: // get adsr vol
\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
333 case 14: // get loop address
\r
335 const int ch=(r>>4)-0xc0;
\r
336 return (unsigned short)((spu.s_chan[ch].pLoop-spu.spuMemC)>>3);
\r
340 else if (0x0e00 <= r && r < 0x0e60)
\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
351 return spu.spuCtrl;
\r
354 return (spu.spuStat & ~0x3F) | (spu.spuCtrl & 0x3F);
\r
357 return (unsigned short)(spu.spuAddr>>3);
\r
359 // this reportedly doesn't work on real hw
\r
362 unsigned short s = LE16TOH(*(unsigned short *)(spu.spuMemC + spu.spuAddr));
\r
364 spu.spuAddr &= 0x7fffe;
\r
365 //check_irq_io(spu.spuAddr);
\r
370 // return IsSoundOn(0,16);
\r
373 // return IsSoundOn(16,24);
\r
377 log_unhandled("r isOn: %08lx\n", reg);
\r
390 log_unhandled("spu r %08lx\n", reg);
\r
394 return spu.regArea[(r-0xc00)>>1];
\r
397 ////////////////////////////////////////////////////////////////////////
\r
398 // SOUND ON register write
\r
399 ////////////////////////////////////////////////////////////////////////
\r
401 static void SoundOn(int start,int end,unsigned short val)
\r
405 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
407 if((val&1) && regAreaGetCh(ch, 6)) // mmm... start has to be set before key on !?!
\r
409 spu.s_chan[ch].bIgnoreLoop = 0;
\r
410 spu.s_chan[ch].bStarting = 1;
\r
411 spu.dwNewChannel|=(1<<ch);
\r
416 ////////////////////////////////////////////////////////////////////////
\r
417 // SOUND OFF register write
\r
418 ////////////////////////////////////////////////////////////////////////
\r
420 static void SoundOff(int start,int end,unsigned short val)
\r
423 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
427 spu.s_chan[ch].ADSRX.State = ADSR_RELEASE;
\r
429 // Jungle Book - Rhythm 'n Groove
\r
430 // - turns off buzzing sound (loop hangs)
\r
431 spu.dwNewChannel &= ~(1<<ch);
\r
436 ////////////////////////////////////////////////////////////////////////
\r
437 // FMOD register write
\r
438 ////////////////////////////////////////////////////////////////////////
\r
440 static void FModOn(int start,int end,unsigned short val)
\r
444 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
446 if(val&1) // -> fmod on/off
\r
450 spu.s_chan[ch].bFMod=1; // --> sound channel
\r
451 spu.s_chan[ch-1].bFMod=2; // --> freq channel
\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
463 ////////////////////////////////////////////////////////////////////////
\r
464 // NOISE register write
\r
465 ////////////////////////////////////////////////////////////////////////
\r
467 static void NoiseOn(int start,int end,unsigned short val)
\r
471 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
473 spu.s_chan[ch].bNoise=val&1; // -> noise on/off
\r
477 ////////////////////////////////////////////////////////////////////////
\r
478 // LEFT VOLUME register write
\r
479 ////////////////////////////////////////////////////////////////////////
\r
481 // please note: sweep and phase invert are wrong... but I've never seen
\r
484 static void SetVolumeL(unsigned char ch,short vol) // LEFT VOLUME
\r
486 if(vol&0x8000) // sweep?
\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
498 if(vol&0x4000) // -> mmm... phase inverted? have to investigate this
\r
500 vol=0x3fff-(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
508 ////////////////////////////////////////////////////////////////////////
\r
509 // RIGHT VOLUME register write
\r
510 ////////////////////////////////////////////////////////////////////////
\r
512 static void SetVolumeR(unsigned char ch,short vol) // RIGHT VOLUME
\r
514 if(vol&0x8000) // comments... see above :)
\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
526 if(vol&0x4000) //vol=vol^=0xffff;
\r
527 vol=0x3fff-(vol&0x3fff);
\r
532 spu.s_chan[ch].iRightVolume=vol;
\r
533 //spu.regArea[(0xe00-0xc00)/2 + ch*2 + 1] = vol << 1;
\r
536 ////////////////////////////////////////////////////////////////////////
\r
537 // PITCH register write
\r
538 ////////////////////////////////////////////////////////////////////////
\r
540 static void SetPitch(int ch,unsigned short val) // SET PITCH
\r
543 if(val>0x3fff) NP=0x3fff; // get pitch val
\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
551 // don't mess spu.dwChannelsAudible as adsr runs independently
\r
554 ////////////////////////////////////////////////////////////////////////
\r
555 // REVERB register write
\r
556 ////////////////////////////////////////////////////////////////////////
\r
558 static void ReverbOn(int start,int end,unsigned short val)
\r
562 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
564 spu.s_chan[ch].bReverb=val&1; // -> reverb on/off
\r