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
25 static void SoundOn(int start,int end,unsigned short val);
\r
26 static void SoundOff(int start,int end,unsigned short val);
\r
27 static void FModOn(int start,int end,unsigned short val);
\r
28 static void NoiseOn(int start,int end,unsigned short val);
\r
29 static void SetVolumeL(unsigned char ch,short vol);
\r
30 static void SetVolumeR(unsigned char ch,short vol);
\r
31 static void SetPitch(int ch,unsigned short val);
\r
32 static void ReverbOn(int start,int end,unsigned short val);
\r
34 ////////////////////////////////////////////////////////////////////////
\r
35 // WRITE REGISTERS: called by main emu
\r
36 ////////////////////////////////////////////////////////////////////////
\r
38 void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val)
\r
40 const unsigned long r=reg&0xfff;
\r
41 regArea[(r-0xc00)>>1] = val;
\r
43 if(r>=0x0c00 && r<0x0d80) // some channel info?
\r
45 int ch=(r>>4)-0xc0; // calc channel
\r
48 //------------------------------------------------// r volume
\r
50 SetVolumeL((unsigned char)ch,val);
\r
52 //------------------------------------------------// l volume
\r
54 SetVolumeR((unsigned char)ch,val);
\r
56 //------------------------------------------------// pitch
\r
60 //------------------------------------------------// start
\r
62 // taken from regArea later
\r
64 //------------------------------------------------// level with pre-calcs
\r
67 const unsigned long lval=val;
\r
68 //---------------------------------------------//
\r
69 s_chan[ch].ADSRX.AttackModeExp=(lval&0x8000)?1:0;
\r
70 s_chan[ch].ADSRX.AttackRate=(lval>>8) & 0x007f;
\r
71 s_chan[ch].ADSRX.DecayRate=(lval>>4) & 0x000f;
\r
72 s_chan[ch].ADSRX.SustainLevel=lval & 0x000f;
\r
73 //---------------------------------------------//
\r
76 //------------------------------------------------// adsr times with pre-calcs
\r
79 const unsigned long lval=val;
\r
81 //----------------------------------------------//
\r
82 s_chan[ch].ADSRX.SustainModeExp = (lval&0x8000)?1:0;
\r
83 s_chan[ch].ADSRX.SustainIncrease= (lval&0x4000)?0:1;
\r
84 s_chan[ch].ADSRX.SustainRate = (lval>>6) & 0x007f;
\r
85 s_chan[ch].ADSRX.ReleaseModeExp = (lval&0x0020)?1:0;
\r
86 s_chan[ch].ADSRX.ReleaseRate = lval & 0x001f;
\r
87 //----------------------------------------------//
\r
90 //------------------------------------------------// adsr volume... mmm have to investigate this
\r
93 //------------------------------------------------//
\r
95 s_chan[ch].pLoop=spuMemC+((val&~1)<<3);
\r
97 //------------------------------------------------//
\r
104 //-------------------------------------------------//
\r
106 spuAddr = (unsigned long) val<<3;
\r
108 //-------------------------------------------------//
\r
110 spuMem[spuAddr>>1] = val;
\r
112 if(spuAddr>0x7ffff) spuAddr=0;
\r
114 //-------------------------------------------------//
\r
116 if(!(spuCtrl & CTRL_IRQ))
\r
117 spuStat&=~STAT_IRQ;
\r
120 //-------------------------------------------------//
\r
122 spuStat=val & 0xf800;
\r
124 //-------------------------------------------------//
\r
125 case H_SPUReverbAddr:
\r
126 if(val==0xFFFF || val<=0x200)
\r
127 {rvb.StartAddr=rvb.CurrAddr=0;}
\r
130 const long iv=(unsigned long)val<<2;
\r
131 if(rvb.StartAddr!=iv)
\r
133 rvb.StartAddr=(unsigned long)val<<2;
\r
134 rvb.CurrAddr=rvb.StartAddr;
\r
135 // sync-with-decode-buffers hack..
\r
136 if(rvb.StartAddr==0x3ff00)
\r
137 rvb.CurrAddr+=decode_pos/2;
\r
142 //-------------------------------------------------//
\r
145 pSpuIrq=spuMemC+(((unsigned long) val<<3)&~0xf);
\r
147 //-------------------------------------------------//
\r
151 //-------------------------------------------------//
\r
155 //-------------------------------------------------//
\r
159 //auxprintf("EL %d\n",val);
\r
161 //-------------------------------------------------//
\r
163 //auxprintf("ER %d\n",val);
\r
165 //-------------------------------------------------//
\r
167 //auxprintf("ML %d\n",val);
\r
169 //-------------------------------------------------//
\r
171 //auxprintf("MR %d\n",val);
\r
173 //-------------------------------------------------//
\r
175 //auxprintf("M0 %04x\n",val);
\r
177 //-------------------------------------------------//
\r
179 //auxprintf("M1 %04x\n",val);
\r
182 //-------------------------------------------------//
\r
186 //-------------------------------------------------//
\r
188 SoundOn(16,24,val);
\r
190 //-------------------------------------------------//
\r
192 SoundOff(0,16,val);
\r
194 //-------------------------------------------------//
\r
196 SoundOff(16,24,val);
\r
198 //-------------------------------------------------//
\r
200 iLeftXAVol=val & 0x7fff;
\r
201 if(cddavCallback) cddavCallback(0,val);
\r
204 iRightXAVol=val & 0x7fff;
\r
205 if(cddavCallback) cddavCallback(1,val);
\r
207 //-------------------------------------------------//
\r
211 //-------------------------------------------------//
\r
215 //-------------------------------------------------//
\r
219 //-------------------------------------------------//
\r
221 NoiseOn(16,24,val);
\r
223 //-------------------------------------------------//
\r
225 ReverbOn(0,16,val);
\r
227 //-------------------------------------------------//
\r
229 ReverbOn(16,24,val);
\r
231 //-------------------------------------------------//
\r
232 case H_Reverb+0 : rvb.FB_SRC_A=val*4; break;
\r
233 case H_Reverb+2 : rvb.FB_SRC_B=val*4; break;
\r
234 case H_Reverb+4 : rvb.IIR_ALPHA=(short)val; break;
\r
235 case H_Reverb+6 : rvb.ACC_COEF_A=(short)val; break;
\r
236 case H_Reverb+8 : rvb.ACC_COEF_B=(short)val; break;
\r
237 case H_Reverb+10 : rvb.ACC_COEF_C=(short)val; break;
\r
238 case H_Reverb+12 : rvb.ACC_COEF_D=(short)val; break;
\r
239 case H_Reverb+14 : rvb.IIR_COEF=(short)val; break;
\r
240 case H_Reverb+16 : rvb.FB_ALPHA=(short)val; break;
\r
241 case H_Reverb+18 : rvb.FB_X=(short)val; break;
\r
242 case H_Reverb+20 : rvb.IIR_DEST_A0=val*4; break;
\r
243 case H_Reverb+22 : rvb.IIR_DEST_A1=val*4; break;
\r
244 case H_Reverb+24 : rvb.ACC_SRC_A0=val*4; break;
\r
245 case H_Reverb+26 : rvb.ACC_SRC_A1=val*4; break;
\r
246 case H_Reverb+28 : rvb.ACC_SRC_B0=val*4; break;
\r
247 case H_Reverb+30 : rvb.ACC_SRC_B1=val*4; break;
\r
248 case H_Reverb+32 : rvb.IIR_SRC_A0=val*4; break;
\r
249 case H_Reverb+34 : rvb.IIR_SRC_A1=val*4; break;
\r
250 case H_Reverb+36 : rvb.IIR_DEST_B0=val*4; break;
\r
251 case H_Reverb+38 : rvb.IIR_DEST_B1=val*4; break;
\r
252 case H_Reverb+40 : rvb.ACC_SRC_C0=val*4; break;
\r
253 case H_Reverb+42 : rvb.ACC_SRC_C1=val*4; break;
\r
254 case H_Reverb+44 : rvb.ACC_SRC_D0=val*4; break;
\r
255 case H_Reverb+46 : rvb.ACC_SRC_D1=val*4; break;
\r
256 case H_Reverb+48 : rvb.IIR_SRC_B1=val*4; break;
\r
257 case H_Reverb+50 : rvb.IIR_SRC_B0=val*4; break;
\r
258 case H_Reverb+52 : rvb.MIX_DEST_A0=val*4; break;
\r
259 case H_Reverb+54 : rvb.MIX_DEST_A1=val*4; break;
\r
260 case H_Reverb+56 : rvb.MIX_DEST_B0=val*4; break;
\r
261 case H_Reverb+58 : rvb.MIX_DEST_B1=val*4; break;
\r
262 case H_Reverb+60 : rvb.IN_COEF_L=(short)val; break;
\r
263 case H_Reverb+62 : rvb.IN_COEF_R=(short)val; break;
\r
266 if ((r & ~0x3f) == H_Reverb)
\r
267 rvb.dirty = 1; // recalculate on next update
\r
270 ////////////////////////////////////////////////////////////////////////
\r
271 // READ REGISTER: called by main emu
\r
272 ////////////////////////////////////////////////////////////////////////
\r
274 unsigned short CALLBACK SPUreadRegister(unsigned long reg)
\r
276 const unsigned long r=reg&0xfff;
\r
278 if(r>=0x0c00 && r<0x0d80)
\r
282 case 12: // get adsr vol
\r
284 const int ch=(r>>4)-0xc0;
\r
285 if(dwNewChannel&(1<<ch)) return 1; // we are started, but not processed? return 1
\r
286 if((dwChannelOn&(1<<ch)) && // same here... we haven't decoded one sample yet, so no envelope yet. return 1 as well
\r
287 !s_chan[ch].ADSRX.EnvelopeVol)
\r
289 return (unsigned short)(s_chan[ch].ADSRX.EnvelopeVol>>16);
\r
292 case 14: // get loop address
\r
294 const int ch=(r>>4)-0xc0;
\r
295 return (unsigned short)((s_chan[ch].pLoop-spuMemC)>>3);
\r
309 return (unsigned short)(spuAddr>>3);
\r
313 unsigned short s=spuMem[spuAddr>>1];
\r
315 if(spuAddr>0x7ffff) spuAddr=0;
\r
323 // return IsSoundOn(0,16);
\r
326 // return IsSoundOn(16,24);
\r
330 return regArea[(r-0xc00)>>1];
\r
333 ////////////////////////////////////////////////////////////////////////
\r
334 // SOUND ON register write
\r
335 ////////////////////////////////////////////////////////////////////////
\r
337 static void SoundOn(int start,int end,unsigned short val)
\r
341 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
343 if((val&1) && regAreaGet(ch,6)) // mmm... start has to be set before key on !?!
\r
345 // do this here, not in StartSound
\r
346 // - fixes fussy timing issues
\r
347 s_chan[ch].bStop=0;
\r
348 s_chan[ch].pCurr=spuMemC+((regAreaGet(ch,6)&~1)<<3); // must be block aligned
\r
349 s_chan[ch].pLoop=spuMemC+((regAreaGet(ch,14)&~1)<<3);
\r
350 s_chan[ch].prevflags=2;
\r
352 dwNewChannel|=(1<<ch); // bitfield for faster testing
\r
353 dwChannelOn|=1<<ch;
\r
354 dwChannelDead&=~(1<<ch);
\r
359 ////////////////////////////////////////////////////////////////////////
\r
360 // SOUND OFF register write
\r
361 ////////////////////////////////////////////////////////////////////////
\r
363 static void SoundOff(int start,int end,unsigned short val)
\r
366 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
368 if(val&1) // && s_chan[i].bOn) mmm...
\r
370 s_chan[ch].bStop=1;
\r
372 // Jungle Book - Rhythm 'n Groove
\r
373 // - turns off buzzing sound (loop hangs)
\r
374 dwNewChannel &= ~(1<<ch);
\r
379 ////////////////////////////////////////////////////////////////////////
\r
380 // FMOD register write
\r
381 ////////////////////////////////////////////////////////////////////////
\r
383 static void FModOn(int start,int end,unsigned short val)
\r
387 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
389 if(val&1) // -> fmod on/off
\r
393 s_chan[ch].bFMod=1; // --> sound channel
\r
394 s_chan[ch-1].bFMod=2; // --> freq channel
\r
399 s_chan[ch].bFMod=0; // --> turn off fmod
\r
400 if(ch>0&&s_chan[ch-1].bFMod==2)
\r
401 s_chan[ch-1].bFMod=0;
\r
406 ////////////////////////////////////////////////////////////////////////
\r
407 // NOISE register write
\r
408 ////////////////////////////////////////////////////////////////////////
\r
410 static void NoiseOn(int start,int end,unsigned short val)
\r
414 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
416 s_chan[ch].bNoise=val&1; // -> noise on/off
\r
420 ////////////////////////////////////////////////////////////////////////
\r
421 // LEFT VOLUME register write
\r
422 ////////////////////////////////////////////////////////////////////////
\r
424 // please note: sweep and phase invert are wrong... but I've never seen
\r
427 static void SetVolumeL(unsigned char ch,short vol) // LEFT VOLUME
\r
429 if(vol&0x8000) // sweep?
\r
431 short sInc=1; // -> sweep up?
\r
432 if(vol&0x2000) sInc=-1; // -> or down?
\r
433 if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this
\r
434 vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64
\r
435 vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!
\r
440 if(vol&0x4000) // -> mmm... phase inverted? have to investigate this
\r
442 vol=0x3fff-(vol&0x3fff);
\r
446 s_chan[ch].iLeftVolume=vol; // store volume
\r
449 ////////////////////////////////////////////////////////////////////////
\r
450 // RIGHT VOLUME register write
\r
451 ////////////////////////////////////////////////////////////////////////
\r
453 static void SetVolumeR(unsigned char ch,short vol) // RIGHT VOLUME
\r
455 if(vol&0x8000) // comments... see above :)
\r
458 if(vol&0x2000) sInc=-1;
\r
459 if(vol&0x1000) vol^=0xffff;
\r
460 vol=((vol&0x7f)+1)/2;
\r
466 if(vol&0x4000) //vol=vol^=0xffff;
\r
467 vol=0x3fff-(vol&0x3fff);
\r
472 s_chan[ch].iRightVolume=vol;
\r
475 ////////////////////////////////////////////////////////////////////////
\r
476 // PITCH register write
\r
477 ////////////////////////////////////////////////////////////////////////
\r
479 static void SetPitch(int ch,unsigned short val) // SET PITCH
\r
482 if(val>0x3fff) NP=0x3fff; // get pitch val
\r
485 s_chan[ch].iRawPitch=NP;
\r
486 s_chan[ch].sinc=(NP<<4)|8;
\r
487 if(iUseInterpolation==1) s_chan[ch].SB[32]=1; // -> freq change in simple interpolation mode: set flag
\r
490 ////////////////////////////////////////////////////////////////////////
\r
491 // REVERB register write
\r
492 ////////////////////////////////////////////////////////////////////////
\r
494 static void ReverbOn(int start,int end,unsigned short val)
\r
498 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
500 s_chan[ch].bReverb=val&1; // -> reverb on/off
\r