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 do_samples_if_needed(cycles, 0, 2);
\r
234 SoundOff(0,16,val);
\r
236 //-------------------------------------------------//
\r
238 if (cycles - spu.last_keyon_cycles < 786u) {
\r
239 if (val & regAreaGet(H_SPUon1))
\r
240 log_unhandled("koff2 %04x %d\n", val, cycles - spu.last_keyon_cycles);
\r
241 val &= ~regAreaGet(H_SPUon2);
\r
243 do_samples_if_needed(cycles, 0, 2);
\r
244 SoundOff(16,24,val);
\r
246 //-------------------------------------------------//
\r
248 spu.iLeftXAVol=(int16_t)val;
\r
249 //if(spu.cddavCallback) spu.cddavCallback(0,(int16_t)val);
\r
252 spu.iRightXAVol=(int16_t)val;
\r
253 //if(spu.cddavCallback) spu.cddavCallback(1,(int16_t)val);
\r
255 //-------------------------------------------------//
\r
259 //-------------------------------------------------//
\r
263 //-------------------------------------------------//
\r
267 //-------------------------------------------------//
\r
269 NoiseOn(16,24,val);
\r
271 //-------------------------------------------------//
\r
273 ReverbOn(0,16,val);
\r
275 //-------------------------------------------------//
\r
277 ReverbOn(16,24,val);
\r
279 //-------------------------------------------------//
\r
280 case H_Reverb+0 : goto rvbd;
\r
281 case H_Reverb+2 : goto rvbd;
\r
282 case H_Reverb+4 : spu.rvb->IIR_ALPHA=(short)val; break;
\r
283 case H_Reverb+6 : spu.rvb->ACC_COEF_A=(short)val; break;
\r
284 case H_Reverb+8 : spu.rvb->ACC_COEF_B=(short)val; break;
\r
285 case H_Reverb+10 : spu.rvb->ACC_COEF_C=(short)val; break;
\r
286 case H_Reverb+12 : spu.rvb->ACC_COEF_D=(short)val; break;
\r
287 case H_Reverb+14 : spu.rvb->IIR_COEF=(short)val; break;
\r
288 case H_Reverb+16 : spu.rvb->FB_ALPHA=(short)val; break;
\r
289 case H_Reverb+18 : spu.rvb->FB_X=(short)val; break;
\r
290 case H_Reverb+20 : goto rvbd;
\r
291 case H_Reverb+22 : goto rvbd;
\r
292 case H_Reverb+24 : goto rvbd;
\r
293 case H_Reverb+26 : goto rvbd;
\r
294 case H_Reverb+28 : goto rvbd;
\r
295 case H_Reverb+30 : goto rvbd;
\r
296 case H_Reverb+32 : goto rvbd;
\r
297 case H_Reverb+34 : goto rvbd;
\r
298 case H_Reverb+36 : goto rvbd;
\r
299 case H_Reverb+38 : goto rvbd;
\r
300 case H_Reverb+40 : goto rvbd;
\r
301 case H_Reverb+42 : goto rvbd;
\r
302 case H_Reverb+44 : goto rvbd;
\r
303 case H_Reverb+46 : goto rvbd;
\r
304 case H_Reverb+48 : goto rvbd;
\r
305 case H_Reverb+50 : goto rvbd;
\r
306 case H_Reverb+52 : goto rvbd;
\r
307 case H_Reverb+54 : goto rvbd;
\r
308 case H_Reverb+56 : goto rvbd;
\r
309 case H_Reverb+58 : goto rvbd;
\r
310 case H_Reverb+60 : spu.rvb->IN_COEF_L=(short)val; break;
\r
311 case H_Reverb+62 : spu.rvb->IN_COEF_R=(short)val; break;
\r
316 if (spu.spuCtrl & CTRL_IRQ)
\r
317 schedule_next_irq();
\r
321 spu.rvb->dirty = 1; // recalculate on next update
\r
324 ////////////////////////////////////////////////////////////////////////
\r
325 // READ REGISTER: called by main emu
\r
326 ////////////////////////////////////////////////////////////////////////
\r
328 unsigned short CALLBACK SPUreadRegister(unsigned long reg, unsigned int cycles)
\r
330 const unsigned long r = reg & 0xffe;
\r
332 if(r>=0x0c00 && r<0x0d80)
\r
336 case 12: // get adsr vol
\r
338 // this used to return 1 immediately after keyon to deal with
\r
339 // some poor timing, but that causes Rayman 2 to lose track of
\r
340 // it's channels on busy scenes and start looping some of them forever
\r
341 const int ch = (r>>4) - 0xc0;
\r
342 if (spu.s_chan[ch].bStarting)
\r
343 do_samples_if_needed(cycles, 0, 2);
\r
344 return (unsigned short)(spu.s_chan[ch].ADSRX.EnvelopeVol >> 16);
\r
347 case 14: // get loop address
\r
349 const int ch=(r>>4)-0xc0;
\r
350 return (unsigned short)((spu.s_chan[ch].pLoop-spu.spuMemC)>>3);
\r
354 else if (0x0e00 <= r && r < 0x0e60)
\r
356 int ch = (r >> 2) & 0x1f;
\r
357 int v = spu.s_chan[ch].iVolume[(r >> 1) & 1] << 1;
\r
358 log_unhandled("c%02d r %cvol %04x\n", ch, (r & 2) ? 'r' : 'l', v);
\r
365 return spu.spuCtrl;
\r
368 return (spu.spuStat & ~0x3F) | (spu.spuCtrl & 0x3F);
\r
371 return (unsigned short)(spu.spuAddr>>3);
\r
373 // this reportedly doesn't work on real hw
\r
376 unsigned short s = LE16TOH(*(unsigned short *)(spu.spuMemC + spu.spuAddr));
\r
378 spu.spuAddr &= 0x7fffe;
\r
379 //check_irq_io(spu.spuAddr);
\r
384 // return IsSoundOn(0,16);
\r
387 // return IsSoundOn(16,24);
\r
391 log_unhandled("r isOn: %08lx\n", reg);
\r
404 log_unhandled("spu r %08lx\n", reg);
\r
408 return spu.regArea[(r-0xc00)>>1];
\r
411 ////////////////////////////////////////////////////////////////////////
\r
412 // SOUND ON register write
\r
413 ////////////////////////////////////////////////////////////////////////
\r
415 static void SoundOn(int start,int end,unsigned short val)
\r
419 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
421 if((val&1) && regAreaGetCh(ch, 6)) // mmm... start has to be set before key on !?!
\r
423 spu.s_chan[ch].bIgnoreLoop = 0;
\r
424 spu.s_chan[ch].bStarting = 1;
\r
425 spu.dwNewChannel|=(1<<ch);
\r
430 ////////////////////////////////////////////////////////////////////////
\r
431 // SOUND OFF register write
\r
432 ////////////////////////////////////////////////////////////////////////
\r
434 static void SoundOff(int start,int end,unsigned short val)
\r
437 for (ch = start; val && ch < end; ch++, val >>= 1) // loop channels
\r
441 spu.s_chan[ch].ADSRX.State = ADSR_RELEASE;
\r
443 // Jungle Book - Rhythm 'n Groove
\r
444 // - turns off buzzing sound (loop hangs)
\r
445 spu.dwNewChannel &= ~(1<<ch);
\r
450 ////////////////////////////////////////////////////////////////////////
\r
451 // FMOD register write
\r
452 ////////////////////////////////////////////////////////////////////////
\r
454 static void FModOn(int start,int end,unsigned short val)
\r
458 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
460 if(val&1) // -> fmod on/off
\r
464 spu.s_chan[ch].bFMod=1; // --> sound channel
\r
465 spu.s_chan[ch-1].bFMod=2; // --> freq channel
\r
470 spu.s_chan[ch].bFMod=0; // --> turn off fmod
\r
471 if(ch>0&&spu.s_chan[ch-1].bFMod==2)
\r
472 spu.s_chan[ch-1].bFMod=0;
\r
477 ////////////////////////////////////////////////////////////////////////
\r
478 // NOISE register write
\r
479 ////////////////////////////////////////////////////////////////////////
\r
481 static void NoiseOn(int start,int end,unsigned short val)
\r
485 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
487 spu.s_chan[ch].bNoise=val&1; // -> noise on/off
\r
491 ////////////////////////////////////////////////////////////////////////
\r
492 // LEFT VOLUME register write
\r
493 ////////////////////////////////////////////////////////////////////////
\r
495 // please note: sweep and phase invert are wrong... but I've never seen
\r
498 static void SetVolumeL(unsigned char ch,short vol) // LEFT VOLUME
\r
500 if(vol&0x8000) // sweep?
\r
502 short sInc=1; // -> sweep up?
\r
503 log_unhandled("ch%d sweepl %04x\n", ch, vol);
\r
504 if(vol&0x2000) sInc=-1; // -> or down?
\r
505 if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this
\r
506 vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64
\r
507 vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!
\r
512 if(vol&0x4000) // -> mmm... phase inverted? have to investigate this
\r
514 vol=0x3fff-(vol&0x3fff);
\r
518 spu.s_chan[ch].iLeftVolume=vol; // store volume
\r
519 //spu.regArea[(0xe00-0xc00)/2 + ch*2 + 0] = vol << 1;
\r
522 ////////////////////////////////////////////////////////////////////////
\r
523 // RIGHT VOLUME register write
\r
524 ////////////////////////////////////////////////////////////////////////
\r
526 static void SetVolumeR(unsigned char ch,short vol) // RIGHT VOLUME
\r
528 if(vol&0x8000) // comments... see above :)
\r
531 log_unhandled("ch%d sweepr %04x\n", ch, vol);
\r
532 if(vol&0x2000) sInc=-1;
\r
533 if(vol&0x1000) vol^=0xffff;
\r
534 vol=((vol&0x7f)+1)/2;
\r
540 if(vol&0x4000) //vol=vol^=0xffff;
\r
541 vol=0x3fff-(vol&0x3fff);
\r
546 spu.s_chan[ch].iRightVolume=vol;
\r
547 //spu.regArea[(0xe00-0xc00)/2 + ch*2 + 1] = vol << 1;
\r
550 ////////////////////////////////////////////////////////////////////////
\r
551 // PITCH register write
\r
552 ////////////////////////////////////////////////////////////////////////
\r
554 static void SetPitch(int ch,unsigned short val) // SET PITCH
\r
557 if(val>0x3fff) NP=0x3fff; // get pitch val
\r
560 spu.s_chan[ch].iRawPitch = NP;
\r
561 spu.s_chan[ch].sinc = NP << 4;
\r
562 spu.s_chan[ch].sinc_inv = 0;
\r
564 // don't mess spu.dwChannelsAudible as adsr runs independently
\r
567 ////////////////////////////////////////////////////////////////////////
\r
568 // REVERB register write
\r
569 ////////////////////////////////////////////////////////////////////////
\r
571 static void ReverbOn(int start,int end,unsigned short val)
\r
575 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
577 spu.s_chan[ch].bReverb=val&1; // -> reverb on/off
\r
581 // vim:shiftwidth=1:expandtab
\r