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
26 static void SoundOn(int start,int end,unsigned short val);
\r
27 static void SoundOff(int start,int end,unsigned short val);
\r
28 static void FModOn(int start,int end,unsigned short val);
\r
29 static void NoiseOn(int start,int end,unsigned short val);
\r
30 static void SetVolumeL(unsigned char ch,short vol);
\r
31 static void SetVolumeR(unsigned char ch,short vol);
\r
32 static void SetPitch(int ch,unsigned short val);
\r
33 static void ReverbOn(int start,int end,unsigned short val);
\r
35 ////////////////////////////////////////////////////////////////////////
\r
36 // WRITE REGISTERS: called by main emu
\r
37 ////////////////////////////////////////////////////////////////////////
\r
39 static 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
46 void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val,
\r
47 unsigned int cycles)
\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
54 if (!changed && (ignore_dupe[rofs >> 5] & (1 << (rofs & 0x1f))))
\r
56 // zero keyon/keyoff?
\r
57 if (val == 0 && (r & 0xff8) == 0xd88)
\r
60 do_samples_if_needed(cycles, 0);
\r
62 if(r>=0x0c00 && r<0x0d80) // some channel info?
\r
64 int ch=(r>>4)-0xc0; // calc channel
\r
67 //------------------------------------------------// r volume
\r
69 SetVolumeL((unsigned char)ch,val);
\r
71 //------------------------------------------------// l volume
\r
73 SetVolumeR((unsigned char)ch,val);
\r
75 //------------------------------------------------// pitch
\r
79 //------------------------------------------------// start
\r
81 // taken from regArea later
\r
83 //------------------------------------------------// level with pre-calcs
\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
95 //------------------------------------------------// adsr times with pre-calcs
\r
98 const unsigned long lval=val;
\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
109 //------------------------------------------------// adsr volume... mmm have to investigate this
\r
112 //------------------------------------------------//
\r
114 spu.s_chan[ch].pLoop=spu.spuMemC+((val&~1)<<3);
\r
115 spu.s_chan[ch].bIgnoreLoop = 1;
\r
117 //------------------------------------------------//
\r
124 //-------------------------------------------------//
\r
126 spu.spuAddr = (unsigned long) val<<3;
\r
128 //-------------------------------------------------//
\r
130 *(unsigned short *)(spu.spuMemC + spu.spuAddr) = val;
\r
132 spu.spuAddr &= 0x7fffe;
\r
134 //-------------------------------------------------//
\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
143 //-------------------------------------------------//
\r
145 spu.spuStat=val&0xf800;
\r
147 //-------------------------------------------------//
\r
148 case H_SPUReverbAddr:
\r
150 //-------------------------------------------------//
\r
152 spu.pSpuIrq=spu.spuMemC+(((unsigned long) val<<3)&~0xf);
\r
154 //-------------------------------------------------//
\r
156 spu.rvb->VolLeft=val;
\r
158 //-------------------------------------------------//
\r
160 spu.rvb->VolRight=val;
\r
162 //-------------------------------------------------//
\r
166 //auxprintf("EL %d\n",val);
\r
168 //-------------------------------------------------//
\r
170 //auxprintf("ER %d\n",val);
\r
172 //-------------------------------------------------//
\r
174 //auxprintf("ML %d\n",val);
\r
176 //-------------------------------------------------//
\r
178 //auxprintf("MR %d\n",val);
\r
180 //-------------------------------------------------//
\r
182 //auxprintf("M0 %04x\n",val);
\r
184 //-------------------------------------------------//
\r
186 //auxprintf("M1 %04x\n",val);
\r
189 //-------------------------------------------------//
\r
193 //-------------------------------------------------//
\r
195 SoundOn(16,24,val);
\r
197 //-------------------------------------------------//
\r
199 SoundOff(0,16,val);
\r
201 //-------------------------------------------------//
\r
203 SoundOff(16,24,val);
\r
205 //-------------------------------------------------//
\r
207 spu.iLeftXAVol=(int16_t)val;
\r
208 if(spu.cddavCallback) spu.cddavCallback(0,(int16_t)val);
\r
211 spu.iRightXAVol=(int16_t)val;
\r
212 if(spu.cddavCallback) spu.cddavCallback(1,(int16_t)val);
\r
214 //-------------------------------------------------//
\r
218 //-------------------------------------------------//
\r
222 //-------------------------------------------------//
\r
226 //-------------------------------------------------//
\r
228 NoiseOn(16,24,val);
\r
230 //-------------------------------------------------//
\r
232 ReverbOn(0,16,val);
\r
234 //-------------------------------------------------//
\r
236 ReverbOn(16,24,val);
\r
238 //-------------------------------------------------//
\r
239 case H_Reverb+0 : goto rvbd;
\r
240 case H_Reverb+2 : goto rvbd;
\r
241 case H_Reverb+4 : spu.rvb->IIR_ALPHA=(short)val; break;
\r
242 case H_Reverb+6 : spu.rvb->ACC_COEF_A=(short)val; break;
\r
243 case H_Reverb+8 : spu.rvb->ACC_COEF_B=(short)val; break;
\r
244 case H_Reverb+10 : spu.rvb->ACC_COEF_C=(short)val; break;
\r
245 case H_Reverb+12 : spu.rvb->ACC_COEF_D=(short)val; break;
\r
246 case H_Reverb+14 : spu.rvb->IIR_COEF=(short)val; break;
\r
247 case H_Reverb+16 : spu.rvb->FB_ALPHA=(short)val; break;
\r
248 case H_Reverb+18 : spu.rvb->FB_X=(short)val; break;
\r
249 case H_Reverb+20 : goto rvbd;
\r
250 case H_Reverb+22 : goto rvbd;
\r
251 case H_Reverb+24 : goto rvbd;
\r
252 case H_Reverb+26 : goto rvbd;
\r
253 case H_Reverb+28 : goto rvbd;
\r
254 case H_Reverb+30 : goto rvbd;
\r
255 case H_Reverb+32 : goto rvbd;
\r
256 case H_Reverb+34 : goto rvbd;
\r
257 case H_Reverb+36 : goto rvbd;
\r
258 case H_Reverb+38 : goto rvbd;
\r
259 case H_Reverb+40 : goto rvbd;
\r
260 case H_Reverb+42 : goto rvbd;
\r
261 case H_Reverb+44 : goto rvbd;
\r
262 case H_Reverb+46 : goto rvbd;
\r
263 case H_Reverb+48 : goto rvbd;
\r
264 case H_Reverb+50 : goto rvbd;
\r
265 case H_Reverb+52 : goto rvbd;
\r
266 case H_Reverb+54 : goto rvbd;
\r
267 case H_Reverb+56 : goto rvbd;
\r
268 case H_Reverb+58 : goto rvbd;
\r
269 case H_Reverb+60 : spu.rvb->IN_COEF_L=(short)val; break;
\r
270 case H_Reverb+62 : spu.rvb->IN_COEF_R=(short)val; break;
\r
275 if (spu.spuCtrl & CTRL_IRQ)
\r
276 schedule_next_irq();
\r
280 spu.rvb->dirty = 1; // recalculate on next update
\r
283 ////////////////////////////////////////////////////////////////////////
\r
284 // READ REGISTER: called by main emu
\r
285 ////////////////////////////////////////////////////////////////////////
\r
287 unsigned short CALLBACK SPUreadRegister(unsigned long reg)
\r
289 const unsigned long r=reg&0xfff;
\r
291 if(r>=0x0c00 && r<0x0d80)
\r
295 case 12: // get adsr vol
\r
297 const int ch=(r>>4)-0xc0;
\r
298 if(spu.dwNewChannel&(1<<ch)) return 1; // we are started, but not processed? return 1
\r
299 if((spu.dwChannelOn&(1<<ch)) && // same here... we haven't decoded one sample yet, so no envelope yet. return 1 as well
\r
300 !spu.s_chan[ch].ADSRX.EnvelopeVol)
\r
302 return (unsigned short)(spu.s_chan[ch].ADSRX.EnvelopeVol>>16);
\r
305 case 14: // get loop address
\r
307 const int ch=(r>>4)-0xc0;
\r
308 return (unsigned short)((spu.s_chan[ch].pLoop-spu.spuMemC)>>3);
\r
316 return spu.spuCtrl;
\r
319 return (spu.spuStat & ~0x3F) | (spu.spuCtrl & 0x3F);
\r
322 return (unsigned short)(spu.spuAddr>>3);
\r
326 unsigned short s = *(unsigned short *)(spu.spuMemC + spu.spuAddr);
\r
328 spu.spuAddr &= 0x7fffe;
\r
333 // return IsSoundOn(0,16);
\r
336 // return IsSoundOn(16,24);
\r
340 return spu.regArea[(r-0xc00)>>1];
\r
343 ////////////////////////////////////////////////////////////////////////
\r
344 // SOUND ON register write
\r
345 ////////////////////////////////////////////////////////////////////////
\r
347 static void SoundOn(int start,int end,unsigned short val)
\r
351 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
353 if((val&1) && regAreaGet(ch,6)) // mmm... start has to be set before key on !?!
\r
355 spu.s_chan[ch].bIgnoreLoop = 0;
\r
356 spu.dwNewChannel|=(1<<ch);
\r
361 ////////////////////////////////////////////////////////////////////////
\r
362 // SOUND OFF register write
\r
363 ////////////////////////////////////////////////////////////////////////
\r
365 static void SoundOff(int start,int end,unsigned short val)
\r
368 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
372 spu.s_chan[ch].ADSRX.State = ADSR_RELEASE;
\r
374 // Jungle Book - Rhythm 'n Groove
\r
375 // - turns off buzzing sound (loop hangs)
\r
376 spu.dwNewChannel &= ~(1<<ch);
\r
381 ////////////////////////////////////////////////////////////////////////
\r
382 // FMOD register write
\r
383 ////////////////////////////////////////////////////////////////////////
\r
385 static void FModOn(int start,int end,unsigned short val)
\r
389 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
391 if(val&1) // -> fmod on/off
\r
395 spu.s_chan[ch].bFMod=1; // --> sound channel
\r
396 spu.s_chan[ch-1].bFMod=2; // --> freq channel
\r
401 spu.s_chan[ch].bFMod=0; // --> turn off fmod
\r
402 if(ch>0&&spu.s_chan[ch-1].bFMod==2)
\r
403 spu.s_chan[ch-1].bFMod=0;
\r
408 ////////////////////////////////////////////////////////////////////////
\r
409 // NOISE register write
\r
410 ////////////////////////////////////////////////////////////////////////
\r
412 static void NoiseOn(int start,int end,unsigned short val)
\r
416 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
418 spu.s_chan[ch].bNoise=val&1; // -> noise on/off
\r
422 ////////////////////////////////////////////////////////////////////////
\r
423 // LEFT VOLUME register write
\r
424 ////////////////////////////////////////////////////////////////////////
\r
426 // please note: sweep and phase invert are wrong... but I've never seen
\r
429 static void SetVolumeL(unsigned char ch,short vol) // LEFT VOLUME
\r
431 if(vol&0x8000) // sweep?
\r
433 short sInc=1; // -> sweep up?
\r
434 if(vol&0x2000) sInc=-1; // -> or down?
\r
435 if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this
\r
436 vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64
\r
437 vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!
\r
442 if(vol&0x4000) // -> mmm... phase inverted? have to investigate this
\r
444 vol=0x3fff-(vol&0x3fff);
\r
448 spu.s_chan[ch].iLeftVolume=vol; // store volume
\r
451 ////////////////////////////////////////////////////////////////////////
\r
452 // RIGHT VOLUME register write
\r
453 ////////////////////////////////////////////////////////////////////////
\r
455 static void SetVolumeR(unsigned char ch,short vol) // RIGHT VOLUME
\r
457 if(vol&0x8000) // comments... see above :)
\r
460 if(vol&0x2000) sInc=-1;
\r
461 if(vol&0x1000) vol^=0xffff;
\r
462 vol=((vol&0x7f)+1)/2;
\r
468 if(vol&0x4000) //vol=vol^=0xffff;
\r
469 vol=0x3fff-(vol&0x3fff);
\r
474 spu.s_chan[ch].iRightVolume=vol;
\r
477 ////////////////////////////////////////////////////////////////////////
\r
478 // PITCH register write
\r
479 ////////////////////////////////////////////////////////////////////////
\r
481 static void SetPitch(int ch,unsigned short val) // SET PITCH
\r
484 if(val>0x3fff) NP=0x3fff; // get pitch val
\r
487 spu.s_chan[ch].iRawPitch=NP;
\r
488 spu.s_chan[ch].sinc=(NP<<4)|8;
\r
489 spu.s_chan[ch].sinc_inv=0;
\r
490 if (spu_config.iUseInterpolation == 1)
\r
491 spu.SB[ch * SB_SIZE + 32] = 1; // -> freq change in simple interpolation mode: set flag
\r
494 ////////////////////////////////////////////////////////////////////////
\r
495 // REVERB register write
\r
496 ////////////////////////////////////////////////////////////////////////
\r
498 static void ReverbOn(int start,int end,unsigned short val)
\r
502 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
504 spu.s_chan[ch].bReverb=val&1; // -> reverb on/off
\r