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
27 // adsr time values (in ms) by James Higgs ... see the end of
\r
28 // the adsr.c source for details
\r
30 #define ATTACK_MS 514L
\r
31 #define DECAYHALF_MS 292L
\r
32 #define DECAY_MS 584L
\r
33 #define SUSTAIN_MS 450L
\r
34 #define RELEASE_MS 446L
\r
37 // we have a timebase of 1.020408f ms, not 1 ms... so adjust adsr defines
\r
38 #define ATTACK_MS 494L
\r
39 #define DECAYHALF_MS 286L
\r
40 #define DECAY_MS 572L
\r
41 #define SUSTAIN_MS 441L
\r
42 #define RELEASE_MS 437L
\r
44 ////////////////////////////////////////////////////////////////////////
\r
45 // WRITE REGISTERS: called by main emu
\r
46 ////////////////////////////////////////////////////////////////////////
\r
48 void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val)
\r
50 const unsigned long r=reg&0xfff;
\r
51 regArea[(r-0xc00)>>1] = val;
\r
53 if(r>=0x0c00 && r<0x0d80) // some channel info?
\r
55 int ch=(r>>4)-0xc0; // calc channel
\r
58 //------------------------------------------------// r volume
\r
60 SetVolumeL((unsigned char)ch,val);
\r
62 //------------------------------------------------// l volume
\r
64 SetVolumeR((unsigned char)ch,val);
\r
66 //------------------------------------------------// pitch
\r
70 //------------------------------------------------// start
\r
72 // Brain Dead 13 - align to 16 boundary
\r
73 s_chan[ch].pStart= spuMemC+(unsigned long)((val<<3)&~0xf);
\r
75 //------------------------------------------------// level with pre-calcs
\r
78 const unsigned long lval=val;
\r
79 //---------------------------------------------//
\r
80 s_chan[ch].ADSRX.AttackModeExp=(lval&0x8000)?1:0;
\r
81 s_chan[ch].ADSRX.AttackRate=(lval>>8) & 0x007f;
\r
82 s_chan[ch].ADSRX.DecayRate=(lval>>4) & 0x000f;
\r
83 s_chan[ch].ADSRX.SustainLevel=lval & 0x000f;
\r
84 //---------------------------------------------//
\r
86 if(!iDebugMode) break;
\r
87 //---------------------------------------------// stuff below is only for debug mode
\r
89 s_chan[ch].ADSR.AttackModeExp=(lval&0x8000)?1:0; //0x007f
\r
91 lx=(((lval>>8) & 0x007f)>>2); // attack time to run from 0 to 100% volume
\r
92 lx=min(31,lx); // no overflow on shift!
\r
96 if(lx<2147483) lx=(lx*ATTACK_MS)/10000L; // another overflow check
\r
97 else lx=(lx/10000L)*ATTACK_MS;
\r
100 s_chan[ch].ADSR.AttackTime=lx;
\r
102 s_chan[ch].ADSR.SustainLevel= // our adsr vol runs from 0 to 1024, so scale the sustain level
\r
103 (1024*((lval) & 0x000f))/15;
\r
105 lx=(lval>>4) & 0x000f; // decay:
\r
106 if(lx) // our const decay value is time it takes from 100% to 0% of volume
\r
108 lx = ((1<<(lx))*DECAY_MS)/10000L;
\r
111 s_chan[ch].ADSR.DecayTime = // so calc how long does it take to run from 100% to the wanted sus level
\r
112 (lx*(1024-s_chan[ch].ADSR.SustainLevel))/1024;
\r
116 //------------------------------------------------// adsr times with pre-calcs
\r
119 const unsigned long lval=val;
\r
121 //----------------------------------------------//
\r
122 s_chan[ch].ADSRX.SustainModeExp = (lval&0x8000)?1:0;
\r
123 s_chan[ch].ADSRX.SustainIncrease= (lval&0x4000)?0:1;
\r
124 s_chan[ch].ADSRX.SustainRate = (lval>>6) & 0x007f;
\r
125 s_chan[ch].ADSRX.ReleaseModeExp = (lval&0x0020)?1:0;
\r
126 s_chan[ch].ADSRX.ReleaseRate = lval & 0x001f;
\r
127 //----------------------------------------------//
\r
129 if(!iDebugMode) break;
\r
130 //----------------------------------------------// stuff below is only for debug mode
\r
132 s_chan[ch].ADSR.SustainModeExp = (lval&0x8000)?1:0;
\r
133 s_chan[ch].ADSR.ReleaseModeExp = (lval&0x0020)?1:0;
\r
135 lx=((((lval>>6) & 0x007f)>>2)); // sustain time... often very high
\r
136 lx=min(31,lx); // values are used to hold the volume
\r
137 if(lx) // until a sound stop occurs
\r
138 { // the highest value we reach (due to
\r
139 lx = (1<<lx); // overflow checking) is:
\r
140 if(lx<2147483) lx=(lx*SUSTAIN_MS)/10000L; // 94704 seconds = 1578 minutes = 26 hours...
\r
141 else lx=(lx/10000L)*SUSTAIN_MS; // should be enuff... if the stop doesn't
\r
142 if(!lx) lx=1; // come in this time span, I don't care :)
\r
144 s_chan[ch].ADSR.SustainTime = lx;
\r
146 lx=(lval & 0x001f);
\r
147 s_chan[ch].ADSR.ReleaseVal =lx;
\r
148 if(lx) // release time from 100% to 0%
\r
149 { // note: the release time will be
\r
150 lx = (1<<lx); // adjusted when a stop is coming,
\r
151 if(lx<2147483) lx=(lx*RELEASE_MS)/10000L; // so at this time the adsr vol will
\r
152 else lx=(lx/10000L)*RELEASE_MS; // run from (current volume) to 0%
\r
155 s_chan[ch].ADSR.ReleaseTime=lx;
\r
157 if(lval & 0x4000) // add/dec flag
\r
158 s_chan[ch].ADSR.SustainModeDec=-1;
\r
159 else s_chan[ch].ADSR.SustainModeDec=1;
\r
163 //------------------------------------------------// adsr volume... mmm have to investigate this
\r
166 //------------------------------------------------//
\r
168 //WaitForSingleObject(s_chan[ch].hMutex,2000); // -> no multithread fuckups
\r
169 s_chan[ch].pLoop=spuMemC+((unsigned long)((val<<3)&~0xf));
\r
170 //s_chan[ch].bIgnoreLoop=1;
\r
171 //ReleaseMutex(s_chan[ch].hMutex); // -> oki, on with the thread
\r
172 dwChannelDead&=~(1<<ch);
\r
174 //------------------------------------------------//
\r
182 //-------------------------------------------------//
\r
184 spuAddr = (unsigned long) val<<3;
\r
186 //-------------------------------------------------//
\r
188 spuMem[spuAddr>>1] = val;
\r
190 if(spuAddr>0x7ffff) spuAddr=0;
\r
192 //-------------------------------------------------//
\r
194 if(!(spuCtrl & CTRL_IRQ))
\r
195 spuStat&=~STAT_IRQ;
\r
198 //-------------------------------------------------//
\r
200 spuStat=val & 0xf800;
\r
202 //-------------------------------------------------//
\r
203 case H_SPUReverbAddr:
\r
204 if(val==0xFFFF || val<=0x200)
\r
205 {rvb.StartAddr=rvb.CurrAddr=0;}
\r
208 const long iv=(unsigned long)val<<2;
\r
209 if(rvb.StartAddr!=iv)
\r
211 rvb.StartAddr=(unsigned long)val<<2;
\r
212 rvb.CurrAddr=rvb.StartAddr;
\r
217 //-------------------------------------------------//
\r
220 pSpuIrq=spuMemC+(((unsigned long) val<<3)&~0xf);
\r
222 //-------------------------------------------------//
\r
226 //-------------------------------------------------//
\r
230 //-------------------------------------------------//
\r
234 //auxprintf("EL %d\n",val);
\r
236 //-------------------------------------------------//
\r
238 //auxprintf("ER %d\n",val);
\r
240 //-------------------------------------------------//
\r
242 //auxprintf("ML %d\n",val);
\r
244 //-------------------------------------------------//
\r
246 //auxprintf("MR %d\n",val);
\r
248 //-------------------------------------------------//
\r
250 //auxprintf("M0 %04x\n",val);
\r
252 //-------------------------------------------------//
\r
254 //auxprintf("M1 %04x\n",val);
\r
257 //-------------------------------------------------//
\r
261 //-------------------------------------------------//
\r
263 SoundOn(16,24,val);
\r
265 //-------------------------------------------------//
\r
267 SoundOff(0,16,val);
\r
269 //-------------------------------------------------//
\r
271 SoundOff(16,24,val);
\r
273 //-------------------------------------------------//
\r
275 iLeftXAVol=val & 0x7fff;
\r
276 if(cddavCallback) cddavCallback(0,val);
\r
279 iRightXAVol=val & 0x7fff;
\r
280 if(cddavCallback) cddavCallback(1,val);
\r
282 //-------------------------------------------------//
\r
286 //-------------------------------------------------//
\r
290 //-------------------------------------------------//
\r
294 //-------------------------------------------------//
\r
296 NoiseOn(16,24,val);
\r
298 //-------------------------------------------------//
\r
300 ReverbOn(0,16,val);
\r
302 //-------------------------------------------------//
\r
304 ReverbOn(16,24,val);
\r
306 //-------------------------------------------------//
\r
307 case H_Reverb+0 : rvb.FB_SRC_A=val*4; break;
\r
308 case H_Reverb+2 : rvb.FB_SRC_B=val*4; break;
\r
309 case H_Reverb+4 : rvb.IIR_ALPHA=(short)val; break;
\r
310 case H_Reverb+6 : rvb.ACC_COEF_A=(short)val; break;
\r
311 case H_Reverb+8 : rvb.ACC_COEF_B=(short)val; break;
\r
312 case H_Reverb+10 : rvb.ACC_COEF_C=(short)val; break;
\r
313 case H_Reverb+12 : rvb.ACC_COEF_D=(short)val; break;
\r
314 case H_Reverb+14 : rvb.IIR_COEF=(short)val; break;
\r
315 case H_Reverb+16 : rvb.FB_ALPHA=(short)val; break;
\r
316 case H_Reverb+18 : rvb.FB_X=(short)val; break;
\r
317 case H_Reverb+20 : rvb.IIR_DEST_A0=val*4; break;
\r
318 case H_Reverb+22 : rvb.IIR_DEST_A1=val*4; break;
\r
319 case H_Reverb+24 : rvb.ACC_SRC_A0=val*4; break;
\r
320 case H_Reverb+26 : rvb.ACC_SRC_A1=val*4; break;
\r
321 case H_Reverb+28 : rvb.ACC_SRC_B0=val*4; break;
\r
322 case H_Reverb+30 : rvb.ACC_SRC_B1=val*4; break;
\r
323 case H_Reverb+32 : rvb.IIR_SRC_A0=val*4; break;
\r
324 case H_Reverb+34 : rvb.IIR_SRC_A1=val*4; break;
\r
325 case H_Reverb+36 : rvb.IIR_DEST_B0=val*4; break;
\r
326 case H_Reverb+38 : rvb.IIR_DEST_B1=val*4; break;
\r
327 case H_Reverb+40 : rvb.ACC_SRC_C0=val*4; break;
\r
328 case H_Reverb+42 : rvb.ACC_SRC_C1=val*4; break;
\r
329 case H_Reverb+44 : rvb.ACC_SRC_D0=val*4; break;
\r
330 case H_Reverb+46 : rvb.ACC_SRC_D1=val*4; break;
\r
331 case H_Reverb+48 : rvb.IIR_SRC_B1=val*4; break;
\r
332 case H_Reverb+50 : rvb.IIR_SRC_B0=val*4; break;
\r
333 case H_Reverb+52 : rvb.MIX_DEST_A0=val*4; break;
\r
334 case H_Reverb+54 : rvb.MIX_DEST_A1=val*4; break;
\r
335 case H_Reverb+56 : rvb.MIX_DEST_B0=val*4; break;
\r
336 case H_Reverb+58 : rvb.MIX_DEST_B1=val*4; break;
\r
337 case H_Reverb+60 : rvb.IN_COEF_L=(short)val; break;
\r
338 case H_Reverb+62 : rvb.IN_COEF_R=(short)val; break;
\r
341 if ((r & ~0x3f) == H_Reverb)
\r
342 rvb.dirty = 1; // recalculate on next update
\r
347 ////////////////////////////////////////////////////////////////////////
\r
348 // READ REGISTER: called by main emu
\r
349 ////////////////////////////////////////////////////////////////////////
\r
351 unsigned short CALLBACK SPUreadRegister(unsigned long reg)
\r
353 const unsigned long r=reg&0xfff;
\r
357 if(r>=0x0c00 && r<0x0d80)
\r
361 case 12: // get adsr vol
\r
363 const int ch=(r>>4)-0xc0;
\r
364 if(dwNewChannel&(1<<ch)) return 1; // we are started, but not processed? return 1
\r
365 if((dwChannelOn&(1<<ch)) && // same here... we haven't decoded one sample yet, so no envelope yet. return 1 as well
\r
366 !s_chan[ch].ADSRX.EnvelopeVol)
\r
368 return (unsigned short)(s_chan[ch].ADSRX.EnvelopeVol>>16);
\r
371 case 14: // get loop address
\r
373 const int ch=(r>>4)-0xc0;
\r
374 return (unsigned short)((s_chan[ch].pLoop-spuMemC)>>3);
\r
388 return (unsigned short)(spuAddr>>3);
\r
392 unsigned short s=spuMem[spuAddr>>1];
\r
394 if(spuAddr>0x7ffff) spuAddr=0;
\r
402 // return IsSoundOn(0,16);
\r
405 // return IsSoundOn(16,24);
\r
409 return regArea[(r-0xc00)>>1];
\r
412 ////////////////////////////////////////////////////////////////////////
\r
413 // SOUND ON register write
\r
414 ////////////////////////////////////////////////////////////////////////
\r
416 void SoundOn(int start,int end,unsigned short val) // SOUND ON PSX COMAND
\r
420 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
422 if((val&1) && s_chan[ch].pStart) // mmm... start has to be set before key on !?!
\r
424 s_chan[ch].bIgnoreLoop=0;
\r
426 // do this here, not in StartSound
\r
427 // - fixes fussy timing issues
\r
428 s_chan[ch].bStop=0;
\r
429 s_chan[ch].pCurr=s_chan[ch].pStart;
\r
431 dwNewChannel|=(1<<ch); // bitfield for faster testing
\r
432 dwChannelOn|=1<<ch;
\r
433 dwChannelDead&=~(1<<ch);
\r
438 ////////////////////////////////////////////////////////////////////////
\r
439 // SOUND OFF register write
\r
440 ////////////////////////////////////////////////////////////////////////
\r
442 void SoundOff(int start,int end,unsigned short val) // SOUND OFF PSX COMMAND
\r
445 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
447 if(val&1) // && s_chan[i].bOn) mmm...
\r
449 s_chan[ch].bStop=1;
\r
451 // Jungle Book - Rhythm 'n Groove
\r
452 // - turns off buzzing sound (loop hangs)
\r
453 dwNewChannel &= ~(1<<ch);
\r
458 ////////////////////////////////////////////////////////////////////////
\r
459 // FMOD register write
\r
460 ////////////////////////////////////////////////////////////////////////
\r
462 void FModOn(int start,int end,unsigned short val) // FMOD ON PSX COMMAND
\r
466 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
468 if(val&1) // -> fmod on/off
\r
472 s_chan[ch].bFMod=1; // --> sound channel
\r
473 s_chan[ch-1].bFMod=2; // --> freq channel
\r
478 s_chan[ch].bFMod=0; // --> turn off fmod
\r
479 if(ch>0&&s_chan[ch-1].bFMod==2)
\r
480 s_chan[ch-1].bFMod=0;
\r
485 ////////////////////////////////////////////////////////////////////////
\r
486 // NOISE register write
\r
487 ////////////////////////////////////////////////////////////////////////
\r
489 void NoiseOn(int start,int end,unsigned short val) // NOISE ON PSX COMMAND
\r
493 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
495 s_chan[ch].bNoise=val&1; // -> noise on/off
\r
499 ////////////////////////////////////////////////////////////////////////
\r
500 // LEFT VOLUME register write
\r
501 ////////////////////////////////////////////////////////////////////////
\r
503 // please note: sweep and phase invert are wrong... but I've never seen
\r
506 void SetVolumeL(unsigned char ch,short vol) // LEFT VOLUME
\r
508 if(vol&0x8000) // sweep?
\r
510 short sInc=1; // -> sweep up?
\r
511 if(vol&0x2000) sInc=-1; // -> or down?
\r
512 if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this
\r
513 vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64
\r
514 vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!
\r
519 if(vol&0x4000) // -> mmm... phase inverted? have to investigate this
\r
521 vol=0x3fff-(vol&0x3fff);
\r
525 s_chan[ch].iLeftVolume=vol; // store volume
\r
528 ////////////////////////////////////////////////////////////////////////
\r
529 // RIGHT VOLUME register write
\r
530 ////////////////////////////////////////////////////////////////////////
\r
532 void SetVolumeR(unsigned char ch,short vol) // RIGHT VOLUME
\r
534 if(vol&0x8000) // comments... see above :)
\r
537 if(vol&0x2000) sInc=-1;
\r
538 if(vol&0x1000) vol^=0xffff;
\r
539 vol=((vol&0x7f)+1)/2;
\r
545 if(vol&0x4000) //vol=vol^=0xffff;
\r
546 vol=0x3fff-(vol&0x3fff);
\r
551 s_chan[ch].iRightVolume=vol;
\r
554 ////////////////////////////////////////////////////////////////////////
\r
555 // PITCH register write
\r
556 ////////////////////////////////////////////////////////////////////////
\r
558 void SetPitch(int ch,unsigned short val) // SET PITCH
\r
561 if(val>0x3fff) NP=0x3fff; // get pitch val
\r
564 s_chan[ch].iRawPitch=NP;
\r
566 NP=(44100L*NP)/4096L; // calc frequency
\r
567 if(NP<1) NP=1; // some security
\r
568 s_chan[ch].iActFreq=NP; // store frequency
\r
571 ////////////////////////////////////////////////////////////////////////
\r
572 // REVERB register write
\r
573 ////////////////////////////////////////////////////////////////////////
\r
575 void ReverbOn(int start,int end,unsigned short val) // REVERB ON PSX COMMAND
\r
579 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
581 s_chan[ch].bReverb=val&1; // -> reverb on/off
\r