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 = (int16_t)val;
\r
172 //-------------------------------------------------//
\r
174 spu.rvb->VolRight = (int16_t)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 spu.last_keyon_cycles = cycles;
\r
217 do_samples_if_needed(cycles, 0, 2);
\r
220 //-------------------------------------------------//
\r
222 spu.last_keyon_cycles = cycles;
\r
223 do_samples_if_needed(cycles, 0, 2);
\r
224 SoundOn(16,24,val);
\r
226 //-------------------------------------------------//
\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
233 SoundOff(0,16,val);
\r
235 //-------------------------------------------------//
\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
242 SoundOff(16,24,val);
\r
244 //-------------------------------------------------//
\r
246 spu.iLeftXAVol=(int16_t)val;
\r
247 //if(spu.cddavCallback) spu.cddavCallback(0,(int16_t)val);
\r
250 spu.iRightXAVol=(int16_t)val;
\r
251 //if(spu.cddavCallback) spu.cddavCallback(1,(int16_t)val);
\r
253 //-------------------------------------------------//
\r
257 //-------------------------------------------------//
\r
261 //-------------------------------------------------//
\r
265 //-------------------------------------------------//
\r
267 NoiseOn(16,24,val);
\r
269 //-------------------------------------------------//
\r
271 ReverbOn(0,16,val);
\r
273 //-------------------------------------------------//
\r
275 ReverbOn(16,24,val);
\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
314 if (spu.spuCtrl & CTRL_IRQ)
\r
315 schedule_next_irq();
\r
319 spu.rvb->dirty = 1; // recalculate on next update
\r
322 ////////////////////////////////////////////////////////////////////////
\r
323 // READ REGISTER: called by main emu
\r
324 ////////////////////////////////////////////////////////////////////////
\r
326 unsigned short CALLBACK SPUreadRegister(unsigned long reg, unsigned int cycles)
\r
328 const unsigned long r = reg & 0xffe;
\r
330 if(r>=0x0c00 && r<0x0d80)
\r
334 case 12: // get adsr vol
\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
345 case 14: // get loop address
\r
347 const int ch=(r>>4)-0xc0;
\r
348 return (unsigned short)((spu.s_chan[ch].pLoop-spu.spuMemC)>>3);
\r
352 else if (0x0e00 <= r && r < 0x0e60)
\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
363 return spu.spuCtrl;
\r
366 return (spu.spuStat & ~0x3F) | (spu.spuCtrl & 0x3F);
\r
369 return (unsigned short)(spu.spuAddr>>3);
\r
371 // this reportedly doesn't work on real hw
\r
374 unsigned short s = LE16TOH(*(unsigned short *)(spu.spuMemC + spu.spuAddr));
\r
376 spu.spuAddr &= 0x7fffe;
\r
377 //check_irq_io(spu.spuAddr);
\r
382 // return IsSoundOn(0,16);
\r
385 // return IsSoundOn(16,24);
\r
389 log_unhandled("r isOn: %08lx\n", reg);
\r
402 log_unhandled("spu r %08lx\n", reg);
\r
406 return spu.regArea[(r-0xc00)>>1];
\r
409 ////////////////////////////////////////////////////////////////////////
\r
410 // SOUND ON register write
\r
411 ////////////////////////////////////////////////////////////////////////
\r
413 static void SoundOn(int start,int end,unsigned short val)
\r
417 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
419 if((val&1) && regAreaGetCh(ch, 6)) // mmm... start has to be set before key on !?!
\r
421 spu.s_chan[ch].bIgnoreLoop = 0;
\r
422 spu.s_chan[ch].bStarting = 1;
\r
423 spu.dwNewChannel|=(1<<ch);
\r
428 ////////////////////////////////////////////////////////////////////////
\r
429 // SOUND OFF register write
\r
430 ////////////////////////////////////////////////////////////////////////
\r
432 static void SoundOff(int start,int end,unsigned short val)
\r
435 for (ch = start; val && ch < end; ch++, val >>= 1) // loop channels
\r
439 spu.s_chan[ch].ADSRX.State = ADSR_RELEASE;
\r
441 // Jungle Book - Rhythm 'n Groove
\r
442 // - turns off buzzing sound (loop hangs)
\r
443 spu.dwNewChannel &= ~(1<<ch);
\r
448 ////////////////////////////////////////////////////////////////////////
\r
449 // FMOD register write
\r
450 ////////////////////////////////////////////////////////////////////////
\r
452 static void FModOn(int start,int end,unsigned short val)
\r
456 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
458 if(val&1) // -> fmod on/off
\r
462 spu.s_chan[ch].bFMod=1; // --> sound channel
\r
463 spu.s_chan[ch-1].bFMod=2; // --> freq channel
\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
475 ////////////////////////////////////////////////////////////////////////
\r
476 // NOISE register write
\r
477 ////////////////////////////////////////////////////////////////////////
\r
479 static void NoiseOn(int start,int end,unsigned short val)
\r
483 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
485 spu.s_chan[ch].bNoise=val&1; // -> noise on/off
\r
489 ////////////////////////////////////////////////////////////////////////
\r
490 // LEFT VOLUME register write
\r
491 ////////////////////////////////////////////////////////////////////////
\r
493 // please note: sweep and phase invert are wrong... but I've never seen
\r
496 static void SetVolumeL(unsigned char ch,short vol) // LEFT VOLUME
\r
498 if(vol&0x8000) // sweep?
\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
510 if(vol&0x4000) // -> mmm... phase inverted? have to investigate this
\r
512 vol=0x3fff-(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
520 ////////////////////////////////////////////////////////////////////////
\r
521 // RIGHT VOLUME register write
\r
522 ////////////////////////////////////////////////////////////////////////
\r
524 static void SetVolumeR(unsigned char ch,short vol) // RIGHT VOLUME
\r
526 if(vol&0x8000) // comments... see above :)
\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
538 if(vol&0x4000) //vol=vol^=0xffff;
\r
539 vol=0x3fff-(vol&0x3fff);
\r
544 spu.s_chan[ch].iRightVolume=vol;
\r
545 //spu.regArea[(0xe00-0xc00)/2 + ch*2 + 1] = vol << 1;
\r
548 ////////////////////////////////////////////////////////////////////////
\r
549 // PITCH register write
\r
550 ////////////////////////////////////////////////////////////////////////
\r
552 static void SetPitch(int ch,unsigned short val) // SET PITCH
\r
555 if(val>0x3fff) NP=0x3fff; // get pitch val
\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
562 // don't mess spu.dwChannelsAudible as adsr runs independently
\r
565 ////////////////////////////////////////////////////////////////////////
\r
566 // REVERB register write
\r
567 ////////////////////////////////////////////////////////////////////////
\r
569 static void ReverbOn(int start,int end,unsigned short val)
\r
573 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
575 spu.s_chan[ch].bReverb=val&1; // -> reverb on/off
\r
579 // vim:shiftwidth=1:expandtab
\r