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);
\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
137 //-------------------------------------------------//
\r
139 *(unsigned short *)(spu.spuMemC + spu.spuAddr) = HTOLE16(val);
\r
141 spu.spuAddr &= 0x7fffe;
\r
143 //-------------------------------------------------//
\r
145 if (!(spu.spuCtrl & CTRL_IRQ)) {
\r
146 spu.spuStat&=~STAT_IRQ;
\r
147 if (val & CTRL_IRQ)
\r
148 schedule_next_irq();
\r
152 //-------------------------------------------------//
\r
154 spu.spuStat=val&0xf800;
\r
156 //-------------------------------------------------//
\r
157 case H_SPUReverbAddr:
\r
159 //-------------------------------------------------//
\r
162 // log_unhandled("w irq with lsb: %08lx %04x\n", reg, val);
\r
163 spu.pSpuIrq=spu.spuMemC+(((unsigned long) val<<3)&~0xf);
\r
165 //-------------------------------------------------//
\r
167 spu.rvb->VolLeft=val;
\r
169 //-------------------------------------------------//
\r
171 spu.rvb->VolRight=val;
\r
173 //-------------------------------------------------//
\r
178 log_unhandled("w master sweep: %08lx %04x\n", reg, val);
\r
183 log_unhandled("1f801dac %04x\n", val);
\r
188 //auxprintf("EL %d\n",val);
\r
190 //-------------------------------------------------//
\r
192 //auxprintf("ER %d\n",val);
\r
194 //-------------------------------------------------//
\r
196 //auxprintf("ML %d\n",val);
\r
198 //-------------------------------------------------//
\r
200 //auxprintf("MR %d\n",val);
\r
202 //-------------------------------------------------//
\r
204 //auxprintf("M0 %04x\n",val);
\r
206 //-------------------------------------------------//
\r
208 //auxprintf("M1 %04x\n",val);
\r
211 //-------------------------------------------------//
\r
215 //-------------------------------------------------//
\r
217 SoundOn(16,24,val);
\r
219 //-------------------------------------------------//
\r
221 SoundOff(0,16,val);
\r
223 //-------------------------------------------------//
\r
225 SoundOff(16,24,val);
\r
227 //-------------------------------------------------//
\r
229 spu.iLeftXAVol=(int16_t)val;
\r
230 if(spu.cddavCallback) spu.cddavCallback(0,(int16_t)val);
\r
233 spu.iRightXAVol=(int16_t)val;
\r
234 if(spu.cddavCallback) spu.cddavCallback(1,(int16_t)val);
\r
236 //-------------------------------------------------//
\r
240 //-------------------------------------------------//
\r
244 //-------------------------------------------------//
\r
248 //-------------------------------------------------//
\r
250 NoiseOn(16,24,val);
\r
252 //-------------------------------------------------//
\r
254 ReverbOn(0,16,val);
\r
256 //-------------------------------------------------//
\r
258 ReverbOn(16,24,val);
\r
260 //-------------------------------------------------//
\r
261 case H_Reverb+0 : goto rvbd;
\r
262 case H_Reverb+2 : goto rvbd;
\r
263 case H_Reverb+4 : spu.rvb->IIR_ALPHA=(short)val; break;
\r
264 case H_Reverb+6 : spu.rvb->ACC_COEF_A=(short)val; break;
\r
265 case H_Reverb+8 : spu.rvb->ACC_COEF_B=(short)val; break;
\r
266 case H_Reverb+10 : spu.rvb->ACC_COEF_C=(short)val; break;
\r
267 case H_Reverb+12 : spu.rvb->ACC_COEF_D=(short)val; break;
\r
268 case H_Reverb+14 : spu.rvb->IIR_COEF=(short)val; break;
\r
269 case H_Reverb+16 : spu.rvb->FB_ALPHA=(short)val; break;
\r
270 case H_Reverb+18 : spu.rvb->FB_X=(short)val; break;
\r
271 case H_Reverb+20 : goto rvbd;
\r
272 case H_Reverb+22 : goto rvbd;
\r
273 case H_Reverb+24 : goto rvbd;
\r
274 case H_Reverb+26 : goto rvbd;
\r
275 case H_Reverb+28 : goto rvbd;
\r
276 case H_Reverb+30 : goto rvbd;
\r
277 case H_Reverb+32 : goto rvbd;
\r
278 case H_Reverb+34 : goto rvbd;
\r
279 case H_Reverb+36 : goto rvbd;
\r
280 case H_Reverb+38 : goto rvbd;
\r
281 case H_Reverb+40 : goto rvbd;
\r
282 case H_Reverb+42 : goto rvbd;
\r
283 case H_Reverb+44 : goto rvbd;
\r
284 case H_Reverb+46 : goto rvbd;
\r
285 case H_Reverb+48 : goto rvbd;
\r
286 case H_Reverb+50 : goto rvbd;
\r
287 case H_Reverb+52 : goto rvbd;
\r
288 case H_Reverb+54 : goto rvbd;
\r
289 case H_Reverb+56 : goto rvbd;
\r
290 case H_Reverb+58 : goto rvbd;
\r
291 case H_Reverb+60 : spu.rvb->IN_COEF_L=(short)val; break;
\r
292 case H_Reverb+62 : spu.rvb->IN_COEF_R=(short)val; break;
\r
297 if (spu.spuCtrl & CTRL_IRQ)
\r
298 schedule_next_irq();
\r
302 spu.rvb->dirty = 1; // recalculate on next update
\r
305 ////////////////////////////////////////////////////////////////////////
\r
306 // READ REGISTER: called by main emu
\r
307 ////////////////////////////////////////////////////////////////////////
\r
309 unsigned short CALLBACK SPUreadRegister(unsigned long reg)
\r
311 const unsigned long r = reg & 0xffe;
\r
313 if(r>=0x0c00 && r<0x0d80)
\r
317 case 12: // get adsr vol
\r
319 const int ch=(r>>4)-0xc0;
\r
320 if(spu.dwNewChannel&(1<<ch)) return 1; // we are started, but not processed? return 1
\r
321 if((spu.dwChannelsAudible&(1<<ch)) && // same here... we haven't decoded one sample yet, so no envelope yet. return 1 as well
\r
322 !spu.s_chan[ch].ADSRX.EnvelopeVol)
\r
324 return (unsigned short)(spu.s_chan[ch].ADSRX.EnvelopeVol>>16);
\r
327 case 14: // get loop address
\r
329 const int ch=(r>>4)-0xc0;
\r
330 return (unsigned short)((spu.s_chan[ch].pLoop-spu.spuMemC)>>3);
\r
334 else if (0x0e00 <= r && r < 0x0e60)
\r
336 int ch = (r >> 2) & 0x1f;
\r
337 int v = spu.s_chan[ch].iVolume[(r >> 1) & 1] << 1;
\r
338 log_unhandled("c%02d r %cvol %04x\n", ch, (r & 2) ? 'r' : 'l', v);
\r
345 return spu.spuCtrl;
\r
348 return (spu.spuStat & ~0x3F) | (spu.spuCtrl & 0x3F);
\r
351 return (unsigned short)(spu.spuAddr>>3);
\r
355 unsigned short s = LE16TOH(*(unsigned short *)(spu.spuMemC + spu.spuAddr));
\r
357 spu.spuAddr &= 0x7fffe;
\r
362 // return IsSoundOn(0,16);
\r
365 // return IsSoundOn(16,24);
\r
369 log_unhandled("r isOn: %08lx\n", reg);
\r
382 log_unhandled("spu r %08lx\n", reg);
\r
386 return spu.regArea[(r-0xc00)>>1];
\r
389 ////////////////////////////////////////////////////////////////////////
\r
390 // SOUND ON register write
\r
391 ////////////////////////////////////////////////////////////////////////
\r
393 static void SoundOn(int start,int end,unsigned short val)
\r
397 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
399 if((val&1) && regAreaGetCh(ch, 6)) // mmm... start has to be set before key on !?!
\r
401 spu.s_chan[ch].bIgnoreLoop = 0;
\r
402 spu.dwNewChannel|=(1<<ch);
\r
407 ////////////////////////////////////////////////////////////////////////
\r
408 // SOUND OFF register write
\r
409 ////////////////////////////////////////////////////////////////////////
\r
411 static void SoundOff(int start,int end,unsigned short val)
\r
414 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
418 spu.s_chan[ch].ADSRX.State = ADSR_RELEASE;
\r
420 // Jungle Book - Rhythm 'n Groove
\r
421 // - turns off buzzing sound (loop hangs)
\r
422 spu.dwNewChannel &= ~(1<<ch);
\r
427 ////////////////////////////////////////////////////////////////////////
\r
428 // FMOD register write
\r
429 ////////////////////////////////////////////////////////////////////////
\r
431 static void FModOn(int start,int end,unsigned short val)
\r
435 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
437 if(val&1) // -> fmod on/off
\r
441 spu.s_chan[ch].bFMod=1; // --> sound channel
\r
442 spu.s_chan[ch-1].bFMod=2; // --> freq channel
\r
447 spu.s_chan[ch].bFMod=0; // --> turn off fmod
\r
448 if(ch>0&&spu.s_chan[ch-1].bFMod==2)
\r
449 spu.s_chan[ch-1].bFMod=0;
\r
454 ////////////////////////////////////////////////////////////////////////
\r
455 // NOISE register write
\r
456 ////////////////////////////////////////////////////////////////////////
\r
458 static void NoiseOn(int start,int end,unsigned short val)
\r
462 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
464 spu.s_chan[ch].bNoise=val&1; // -> noise on/off
\r
468 ////////////////////////////////////////////////////////////////////////
\r
469 // LEFT VOLUME register write
\r
470 ////////////////////////////////////////////////////////////////////////
\r
472 // please note: sweep and phase invert are wrong... but I've never seen
\r
475 static void SetVolumeL(unsigned char ch,short vol) // LEFT VOLUME
\r
477 if(vol&0x8000) // sweep?
\r
479 short sInc=1; // -> sweep up?
\r
480 log_unhandled("ch%d sweepl %04x\n", ch, vol);
\r
481 if(vol&0x2000) sInc=-1; // -> or down?
\r
482 if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this
\r
483 vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64
\r
484 vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!
\r
489 if(vol&0x4000) // -> mmm... phase inverted? have to investigate this
\r
491 vol=0x3fff-(vol&0x3fff);
\r
495 spu.s_chan[ch].iLeftVolume=vol; // store volume
\r
496 //spu.regArea[(0xe00-0xc00)/2 + ch*2 + 0] = vol << 1;
\r
499 ////////////////////////////////////////////////////////////////////////
\r
500 // RIGHT VOLUME register write
\r
501 ////////////////////////////////////////////////////////////////////////
\r
503 static void SetVolumeR(unsigned char ch,short vol) // RIGHT VOLUME
\r
505 if(vol&0x8000) // comments... see above :)
\r
508 log_unhandled("ch%d sweepr %04x\n", ch, vol);
\r
509 if(vol&0x2000) sInc=-1;
\r
510 if(vol&0x1000) vol^=0xffff;
\r
511 vol=((vol&0x7f)+1)/2;
\r
517 if(vol&0x4000) //vol=vol^=0xffff;
\r
518 vol=0x3fff-(vol&0x3fff);
\r
523 spu.s_chan[ch].iRightVolume=vol;
\r
524 //spu.regArea[(0xe00-0xc00)/2 + ch*2 + 1] = vol << 1;
\r
527 ////////////////////////////////////////////////////////////////////////
\r
528 // PITCH register write
\r
529 ////////////////////////////////////////////////////////////////////////
\r
531 static void SetPitch(int ch,unsigned short val) // SET PITCH
\r
534 if(val>0x3fff) NP=0x3fff; // get pitch val
\r
537 spu.s_chan[ch].iRawPitch = NP;
\r
538 spu.s_chan[ch].sinc = NP << 4;
\r
539 spu.s_chan[ch].sinc_inv = 0;
\r
540 spu.s_chan[ch].bNewPitch = 1;
\r
542 // don't mess spu.dwChannelsAudible as adsr runs independently
\r
545 ////////////////////////////////////////////////////////////////////////
\r
546 // REVERB register write
\r
547 ////////////////////////////////////////////////////////////////////////
\r
549 static void ReverbOn(int start,int end,unsigned short val)
\r
553 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
555 spu.s_chan[ch].bReverb=val&1; // -> reverb on/off
\r