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 int)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 spu.spuStat = (spu.spuStat & ~0x3f) | (val & 0x3f);
\r
148 spu.spuStat &= ~STAT_IRQ | val;
\r
149 if (!(spu.spuCtrl & CTRL_IRQ)) {
\r
150 if (val & CTRL_IRQ)
\r
151 schedule_next_irq();
\r
155 //-------------------------------------------------//
\r
157 //spu.spuStat=val&0xf800;
\r
159 //-------------------------------------------------//
\r
160 case H_SPUReverbAddr:
\r
162 //-------------------------------------------------//
\r
165 // log_unhandled("w irq with lsb: %08lx %04x\n", reg, val);
\r
166 spu.pSpuIrq = spu.spuMemC + (((int)val << 3) & ~0xf);
\r
167 //check_irq_io(spu.spuAddr);
\r
169 //-------------------------------------------------//
\r
171 spu.rvb->VolLeft = (int16_t)val;
\r
173 //-------------------------------------------------//
\r
175 spu.rvb->VolRight = (int16_t)val;
\r
177 //-------------------------------------------------//
\r
182 log_unhandled("w master sweep: %08lx %04x\n", reg, val);
\r
187 log_unhandled("1f801dac %04x\n", val);
\r
192 //auxprintf("EL %d\n",val);
\r
194 //-------------------------------------------------//
\r
196 //auxprintf("ER %d\n",val);
\r
198 //-------------------------------------------------//
\r
200 //auxprintf("ML %d\n",val);
\r
202 //-------------------------------------------------//
\r
204 //auxprintf("MR %d\n",val);
\r
206 //-------------------------------------------------//
\r
208 //auxprintf("M0 %04x\n",val);
\r
210 //-------------------------------------------------//
\r
212 //auxprintf("M1 %04x\n",val);
\r
215 //-------------------------------------------------//
\r
217 spu.last_keyon_cycles = cycles;
\r
218 do_samples_if_needed(cycles, 0, 2);
\r
221 //-------------------------------------------------//
\r
223 spu.last_keyon_cycles = cycles;
\r
224 do_samples_if_needed(cycles, 0, 2);
\r
225 SoundOn(16,24,val);
\r
227 //-------------------------------------------------//
\r
229 if (cycles - spu.last_keyon_cycles < 786u) {
\r
230 if (val & regAreaGet(H_SPUon1))
\r
231 log_unhandled("koff1 %04x %d\n", val, cycles - spu.last_keyon_cycles);
\r
232 val &= ~regAreaGet(H_SPUon1);
\r
234 do_samples_if_needed(cycles, 0, 2);
\r
235 SoundOff(0,16,val);
\r
237 //-------------------------------------------------//
\r
239 if (cycles - spu.last_keyon_cycles < 786u) {
\r
240 if (val & regAreaGet(H_SPUon1))
\r
241 log_unhandled("koff2 %04x %d\n", val, cycles - spu.last_keyon_cycles);
\r
242 val &= ~regAreaGet(H_SPUon2);
\r
244 do_samples_if_needed(cycles, 0, 2);
\r
245 SoundOff(16,24,val);
\r
247 //-------------------------------------------------//
\r
249 spu.iLeftXAVol=(int16_t)val;
\r
250 //if(spu.cddavCallback) spu.cddavCallback(0,(int16_t)val);
\r
253 spu.iRightXAVol=(int16_t)val;
\r
254 //if(spu.cddavCallback) spu.cddavCallback(1,(int16_t)val);
\r
256 //-------------------------------------------------//
\r
260 //-------------------------------------------------//
\r
264 //-------------------------------------------------//
\r
268 //-------------------------------------------------//
\r
270 NoiseOn(16,24,val);
\r
272 //-------------------------------------------------//
\r
274 ReverbOn(0,16,val);
\r
276 //-------------------------------------------------//
\r
278 ReverbOn(16,24,val);
\r
280 //-------------------------------------------------//
\r
281 case H_Reverb+0 : goto rvbd;
\r
282 case H_Reverb+2 : goto rvbd;
\r
283 case H_Reverb+4 : spu.rvb->IIR_ALPHA=(short)val; break;
\r
284 case H_Reverb+6 : spu.rvb->ACC_COEF_A=(short)val; break;
\r
285 case H_Reverb+8 : spu.rvb->ACC_COEF_B=(short)val; break;
\r
286 case H_Reverb+10 : spu.rvb->ACC_COEF_C=(short)val; break;
\r
287 case H_Reverb+12 : spu.rvb->ACC_COEF_D=(short)val; break;
\r
288 case H_Reverb+14 : spu.rvb->IIR_COEF=(short)val; break;
\r
289 case H_Reverb+16 : spu.rvb->FB_ALPHA=(short)val; break;
\r
290 case H_Reverb+18 : spu.rvb->FB_X=(short)val; break;
\r
291 case H_Reverb+20 : goto rvbd;
\r
292 case H_Reverb+22 : goto rvbd;
\r
293 case H_Reverb+24 : goto rvbd;
\r
294 case H_Reverb+26 : goto rvbd;
\r
295 case H_Reverb+28 : goto rvbd;
\r
296 case H_Reverb+30 : goto rvbd;
\r
297 case H_Reverb+32 : goto rvbd;
\r
298 case H_Reverb+34 : goto rvbd;
\r
299 case H_Reverb+36 : goto rvbd;
\r
300 case H_Reverb+38 : goto rvbd;
\r
301 case H_Reverb+40 : goto rvbd;
\r
302 case H_Reverb+42 : goto rvbd;
\r
303 case H_Reverb+44 : goto rvbd;
\r
304 case H_Reverb+46 : goto rvbd;
\r
305 case H_Reverb+48 : goto rvbd;
\r
306 case H_Reverb+50 : goto rvbd;
\r
307 case H_Reverb+52 : goto rvbd;
\r
308 case H_Reverb+54 : goto rvbd;
\r
309 case H_Reverb+56 : goto rvbd;
\r
310 case H_Reverb+58 : goto rvbd;
\r
311 case H_Reverb+60 : spu.rvb->IN_COEF_L=(short)val; break;
\r
312 case H_Reverb+62 : spu.rvb->IN_COEF_R=(short)val; break;
\r
317 if (spu.spuCtrl & CTRL_IRQ)
\r
318 schedule_next_irq();
\r
322 spu.rvb->dirty = 1; // recalculate on next update
\r
325 ////////////////////////////////////////////////////////////////////////
\r
326 // READ REGISTER: called by main emu
\r
327 ////////////////////////////////////////////////////////////////////////
\r
329 unsigned short CALLBACK SPUreadRegister(unsigned long reg, unsigned int cycles)
\r
331 const unsigned long r = reg & 0xffe;
\r
333 if(r>=0x0c00 && r<0x0d80)
\r
337 case 12: // get adsr vol
\r
339 // this used to return 1 immediately after keyon to deal with
\r
340 // some poor timing, but that causes Rayman 2 to lose track of
\r
341 // it's channels on busy scenes and start looping some of them forever
\r
342 const int ch = (r>>4) - 0xc0;
\r
343 if (spu.s_chan[ch].bStarting)
\r
344 do_samples_if_needed(cycles, 0, 2);
\r
345 return (unsigned short)(spu.s_chan[ch].ADSRX.EnvelopeVol >> 16);
\r
348 case 14: // get loop address
\r
350 const int ch=(r>>4)-0xc0;
\r
351 return (unsigned short)((spu.s_chan[ch].pLoop-spu.spuMemC)>>3);
\r
355 else if (0x0e00 <= r && r < 0x0e60)
\r
357 int ch = (r >> 2) & 0x1f;
\r
358 int v = spu.s_chan[ch].iVolume[(r >> 1) & 1] << 1;
\r
359 log_unhandled("c%02d r %cvol %04x\n", ch, (r & 2) ? 'r' : 'l', v);
\r
366 return spu.spuCtrl;
\r
369 return spu.spuStat;
\r
372 return (unsigned short)(spu.spuAddr>>3);
\r
374 // this reportedly doesn't work on real hw
\r
377 unsigned short s = LE16TOH(*(unsigned short *)(spu.spuMemC + spu.spuAddr));
\r
379 spu.spuAddr &= 0x7fffe;
\r
380 //check_irq_io(spu.spuAddr);
\r
385 // return IsSoundOn(0,16);
\r
388 // return IsSoundOn(16,24);
\r
392 log_unhandled("r isOn: %08lx\n", reg);
\r
405 log_unhandled("spu r %08lx\n", reg);
\r
409 return spu.regArea[(r-0xc00)>>1];
\r
412 ////////////////////////////////////////////////////////////////////////
\r
413 // SOUND ON register write
\r
414 ////////////////////////////////////////////////////////////////////////
\r
416 static void SoundOn(int start,int end,unsigned short val)
\r
420 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
422 if((val&1) && regAreaGetCh(ch, 6)) // mmm... start has to be set before key on !?!
\r
424 spu.s_chan[ch].bIgnoreLoop = 0;
\r
425 spu.s_chan[ch].bStarting = 1;
\r
426 spu.dwNewChannel|=(1<<ch);
\r
431 ////////////////////////////////////////////////////////////////////////
\r
432 // SOUND OFF register write
\r
433 ////////////////////////////////////////////////////////////////////////
\r
435 static void SoundOff(int start,int end,unsigned short val)
\r
438 for (ch = start; val && ch < end; ch++, val >>= 1) // loop channels
\r
442 spu.s_chan[ch].ADSRX.State = ADSR_RELEASE;
\r
444 // Jungle Book - Rhythm 'n Groove
\r
445 // - turns off buzzing sound (loop hangs)
\r
446 spu.dwNewChannel &= ~(1<<ch);
\r
451 ////////////////////////////////////////////////////////////////////////
\r
452 // FMOD register write
\r
453 ////////////////////////////////////////////////////////////////////////
\r
455 static void FModOn(int start,int end,unsigned short val)
\r
459 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
461 if(val&1) // -> fmod on/off
\r
465 spu.s_chan[ch].bFMod=1; // --> sound channel
\r
466 spu.s_chan[ch-1].bFMod=2; // --> freq channel
\r
471 spu.s_chan[ch].bFMod=0; // --> turn off fmod
\r
472 if(ch>0&&spu.s_chan[ch-1].bFMod==2)
\r
473 spu.s_chan[ch-1].bFMod=0;
\r
478 ////////////////////////////////////////////////////////////////////////
\r
479 // NOISE register write
\r
480 ////////////////////////////////////////////////////////////////////////
\r
482 static void NoiseOn(int start,int end,unsigned short val)
\r
486 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
488 spu.s_chan[ch].bNoise=val&1; // -> noise on/off
\r
492 ////////////////////////////////////////////////////////////////////////
\r
493 // LEFT VOLUME register write
\r
494 ////////////////////////////////////////////////////////////////////////
\r
496 // please note: sweep and phase invert are wrong... but I've never seen
\r
499 static void SetVolumeL(unsigned char ch,short vol) // LEFT VOLUME
\r
501 if(vol&0x8000) // sweep?
\r
503 short sInc=1; // -> sweep up?
\r
504 log_unhandled("ch%d sweepl %04x\n", ch, vol);
\r
505 if(vol&0x2000) sInc=-1; // -> or down?
\r
506 if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this
\r
507 vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64
\r
508 vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!
\r
513 if(vol&0x4000) // -> mmm... phase inverted? have to investigate this
\r
515 vol=0x3fff-(vol&0x3fff);
\r
519 spu.s_chan[ch].iLeftVolume=vol; // store volume
\r
520 //spu.regArea[(0xe00-0xc00)/2 + ch*2 + 0] = vol << 1;
\r
523 ////////////////////////////////////////////////////////////////////////
\r
524 // RIGHT VOLUME register write
\r
525 ////////////////////////////////////////////////////////////////////////
\r
527 static void SetVolumeR(unsigned char ch,short vol) // RIGHT VOLUME
\r
529 if(vol&0x8000) // comments... see above :)
\r
532 log_unhandled("ch%d sweepr %04x\n", ch, vol);
\r
533 if(vol&0x2000) sInc=-1;
\r
534 if(vol&0x1000) vol^=0xffff;
\r
535 vol=((vol&0x7f)+1)/2;
\r
541 if(vol&0x4000) //vol=vol^=0xffff;
\r
542 vol=0x3fff-(vol&0x3fff);
\r
547 spu.s_chan[ch].iRightVolume=vol;
\r
548 //spu.regArea[(0xe00-0xc00)/2 + ch*2 + 1] = vol << 1;
\r
551 ////////////////////////////////////////////////////////////////////////
\r
552 // PITCH register write
\r
553 ////////////////////////////////////////////////////////////////////////
\r
555 static void SetPitch(int ch,unsigned short val) // SET PITCH
\r
558 if(val>0x3fff) NP=0x3fff; // get pitch val
\r
561 spu.s_chan[ch].iRawPitch = NP;
\r
562 spu.s_chan[ch].sinc = NP << 4;
\r
563 spu.s_chan[ch].sinc_inv = 0;
\r
565 // don't mess spu.dwChannelsAudible as adsr runs independently
\r
568 ////////////////////////////////////////////////////////////////////////
\r
569 // REVERB register write
\r
570 ////////////////////////////////////////////////////////////////////////
\r
572 static void ReverbOn(int start,int end,unsigned short val)
\r
576 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
578 spu.s_chan[ch].bReverb=val&1; // -> reverb on/off
\r
582 // vim:shiftwidth=1:expandtab
\r