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
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=val;
\r
172 //-------------------------------------------------//
\r
174 spu.rvb->VolRight=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
218 //-------------------------------------------------//
\r
220 SoundOn(16,24,val);
\r
222 //-------------------------------------------------//
\r
224 SoundOff(0,16,val);
\r
226 //-------------------------------------------------//
\r
228 SoundOff(16,24,val);
\r
230 //-------------------------------------------------//
\r
232 spu.iLeftXAVol=(int16_t)val;
\r
233 if(spu.cddavCallback) spu.cddavCallback(0,(int16_t)val);
\r
236 spu.iRightXAVol=(int16_t)val;
\r
237 if(spu.cddavCallback) spu.cddavCallback(1,(int16_t)val);
\r
239 //-------------------------------------------------//
\r
243 //-------------------------------------------------//
\r
247 //-------------------------------------------------//
\r
251 //-------------------------------------------------//
\r
253 NoiseOn(16,24,val);
\r
255 //-------------------------------------------------//
\r
257 ReverbOn(0,16,val);
\r
259 //-------------------------------------------------//
\r
261 ReverbOn(16,24,val);
\r
263 //-------------------------------------------------//
\r
264 case H_Reverb+0 : goto rvbd;
\r
265 case H_Reverb+2 : goto rvbd;
\r
266 case H_Reverb+4 : spu.rvb->IIR_ALPHA=(short)val; break;
\r
267 case H_Reverb+6 : spu.rvb->ACC_COEF_A=(short)val; break;
\r
268 case H_Reverb+8 : spu.rvb->ACC_COEF_B=(short)val; break;
\r
269 case H_Reverb+10 : spu.rvb->ACC_COEF_C=(short)val; break;
\r
270 case H_Reverb+12 : spu.rvb->ACC_COEF_D=(short)val; break;
\r
271 case H_Reverb+14 : spu.rvb->IIR_COEF=(short)val; break;
\r
272 case H_Reverb+16 : spu.rvb->FB_ALPHA=(short)val; break;
\r
273 case H_Reverb+18 : spu.rvb->FB_X=(short)val; break;
\r
274 case H_Reverb+20 : goto rvbd;
\r
275 case H_Reverb+22 : goto rvbd;
\r
276 case H_Reverb+24 : goto rvbd;
\r
277 case H_Reverb+26 : goto rvbd;
\r
278 case H_Reverb+28 : goto rvbd;
\r
279 case H_Reverb+30 : goto rvbd;
\r
280 case H_Reverb+32 : goto rvbd;
\r
281 case H_Reverb+34 : goto rvbd;
\r
282 case H_Reverb+36 : goto rvbd;
\r
283 case H_Reverb+38 : goto rvbd;
\r
284 case H_Reverb+40 : goto rvbd;
\r
285 case H_Reverb+42 : goto rvbd;
\r
286 case H_Reverb+44 : goto rvbd;
\r
287 case H_Reverb+46 : goto rvbd;
\r
288 case H_Reverb+48 : goto rvbd;
\r
289 case H_Reverb+50 : goto rvbd;
\r
290 case H_Reverb+52 : goto rvbd;
\r
291 case H_Reverb+54 : goto rvbd;
\r
292 case H_Reverb+56 : goto rvbd;
\r
293 case H_Reverb+58 : goto rvbd;
\r
294 case H_Reverb+60 : spu.rvb->IN_COEF_L=(short)val; break;
\r
295 case H_Reverb+62 : spu.rvb->IN_COEF_R=(short)val; break;
\r
300 if (spu.spuCtrl & CTRL_IRQ)
\r
301 schedule_next_irq();
\r
305 spu.rvb->dirty = 1; // recalculate on next update
\r
308 ////////////////////////////////////////////////////////////////////////
\r
309 // READ REGISTER: called by main emu
\r
310 ////////////////////////////////////////////////////////////////////////
\r
312 unsigned short CALLBACK SPUreadRegister(unsigned long reg)
\r
314 const unsigned long r = reg & 0xffe;
\r
316 if(r>=0x0c00 && r<0x0d80)
\r
320 case 12: // get adsr vol
\r
322 const int ch=(r>>4)-0xc0;
\r
323 if(spu.dwNewChannel&(1<<ch)) return 1; // we are started, but not processed? return 1
\r
324 if((spu.dwChannelsAudible&(1<<ch)) && // same here... we haven't decoded one sample yet, so no envelope yet. return 1 as well
\r
325 !spu.s_chan[ch].ADSRX.EnvelopeVol)
\r
327 return (unsigned short)(spu.s_chan[ch].ADSRX.EnvelopeVol>>16);
\r
330 case 14: // get loop address
\r
332 const int ch=(r>>4)-0xc0;
\r
333 return (unsigned short)((spu.s_chan[ch].pLoop-spu.spuMemC)>>3);
\r
337 else if (0x0e00 <= r && r < 0x0e60)
\r
339 int ch = (r >> 2) & 0x1f;
\r
340 int v = spu.s_chan[ch].iVolume[(r >> 1) & 1] << 1;
\r
341 log_unhandled("c%02d r %cvol %04x\n", ch, (r & 2) ? 'r' : 'l', v);
\r
348 return spu.spuCtrl;
\r
351 return (spu.spuStat & ~0x3F) | (spu.spuCtrl & 0x3F);
\r
354 return (unsigned short)(spu.spuAddr>>3);
\r
356 // this reportedly doesn't work on real hw
\r
359 unsigned short s = LE16TOH(*(unsigned short *)(spu.spuMemC + spu.spuAddr));
\r
361 spu.spuAddr &= 0x7fffe;
\r
362 //check_irq_io(spu.spuAddr);
\r
367 // return IsSoundOn(0,16);
\r
370 // return IsSoundOn(16,24);
\r
374 log_unhandled("r isOn: %08lx\n", reg);
\r
387 log_unhandled("spu r %08lx\n", reg);
\r
391 return spu.regArea[(r-0xc00)>>1];
\r
394 ////////////////////////////////////////////////////////////////////////
\r
395 // SOUND ON register write
\r
396 ////////////////////////////////////////////////////////////////////////
\r
398 static void SoundOn(int start,int end,unsigned short val)
\r
402 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
404 if((val&1) && regAreaGetCh(ch, 6)) // mmm... start has to be set before key on !?!
\r
406 spu.s_chan[ch].bIgnoreLoop = 0;
\r
407 spu.dwNewChannel|=(1<<ch);
\r
412 ////////////////////////////////////////////////////////////////////////
\r
413 // SOUND OFF register write
\r
414 ////////////////////////////////////////////////////////////////////////
\r
416 static void SoundOff(int start,int end,unsigned short val)
\r
419 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
423 spu.s_chan[ch].ADSRX.State = ADSR_RELEASE;
\r
425 // Jungle Book - Rhythm 'n Groove
\r
426 // - turns off buzzing sound (loop hangs)
\r
427 spu.dwNewChannel &= ~(1<<ch);
\r
432 ////////////////////////////////////////////////////////////////////////
\r
433 // FMOD register write
\r
434 ////////////////////////////////////////////////////////////////////////
\r
436 static void FModOn(int start,int end,unsigned short val)
\r
440 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
442 if(val&1) // -> fmod on/off
\r
446 spu.s_chan[ch].bFMod=1; // --> sound channel
\r
447 spu.s_chan[ch-1].bFMod=2; // --> freq channel
\r
452 spu.s_chan[ch].bFMod=0; // --> turn off fmod
\r
453 if(ch>0&&spu.s_chan[ch-1].bFMod==2)
\r
454 spu.s_chan[ch-1].bFMod=0;
\r
459 ////////////////////////////////////////////////////////////////////////
\r
460 // NOISE register write
\r
461 ////////////////////////////////////////////////////////////////////////
\r
463 static void NoiseOn(int start,int end,unsigned short val)
\r
467 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
469 spu.s_chan[ch].bNoise=val&1; // -> noise on/off
\r
473 ////////////////////////////////////////////////////////////////////////
\r
474 // LEFT VOLUME register write
\r
475 ////////////////////////////////////////////////////////////////////////
\r
477 // please note: sweep and phase invert are wrong... but I've never seen
\r
480 static void SetVolumeL(unsigned char ch,short vol) // LEFT VOLUME
\r
482 if(vol&0x8000) // sweep?
\r
484 short sInc=1; // -> sweep up?
\r
485 log_unhandled("ch%d sweepl %04x\n", ch, vol);
\r
486 if(vol&0x2000) sInc=-1; // -> or down?
\r
487 if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this
\r
488 vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64
\r
489 vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!
\r
494 if(vol&0x4000) // -> mmm... phase inverted? have to investigate this
\r
496 vol=0x3fff-(vol&0x3fff);
\r
500 spu.s_chan[ch].iLeftVolume=vol; // store volume
\r
501 //spu.regArea[(0xe00-0xc00)/2 + ch*2 + 0] = vol << 1;
\r
504 ////////////////////////////////////////////////////////////////////////
\r
505 // RIGHT VOLUME register write
\r
506 ////////////////////////////////////////////////////////////////////////
\r
508 static void SetVolumeR(unsigned char ch,short vol) // RIGHT VOLUME
\r
510 if(vol&0x8000) // comments... see above :)
\r
513 log_unhandled("ch%d sweepr %04x\n", ch, vol);
\r
514 if(vol&0x2000) sInc=-1;
\r
515 if(vol&0x1000) vol^=0xffff;
\r
516 vol=((vol&0x7f)+1)/2;
\r
522 if(vol&0x4000) //vol=vol^=0xffff;
\r
523 vol=0x3fff-(vol&0x3fff);
\r
528 spu.s_chan[ch].iRightVolume=vol;
\r
529 //spu.regArea[(0xe00-0xc00)/2 + ch*2 + 1] = vol << 1;
\r
532 ////////////////////////////////////////////////////////////////////////
\r
533 // PITCH register write
\r
534 ////////////////////////////////////////////////////////////////////////
\r
536 static void SetPitch(int ch,unsigned short val) // SET PITCH
\r
539 if(val>0x3fff) NP=0x3fff; // get pitch val
\r
542 spu.s_chan[ch].iRawPitch = NP;
\r
543 spu.s_chan[ch].sinc = NP << 4;
\r
544 spu.s_chan[ch].sinc_inv = 0;
\r
545 spu.s_chan[ch].bNewPitch = 1;
\r
547 // don't mess spu.dwChannelsAudible as adsr runs independently
\r
550 ////////////////////////////////////////////////////////////////////////
\r
551 // REVERB register write
\r
552 ////////////////////////////////////////////////////////////////////////
\r
554 static void ReverbOn(int start,int end,unsigned short val)
\r
558 for(ch=start;ch<end;ch++,val>>=1) // loop channels
\r
560 spu.s_chan[ch].bReverb=val&1; // -> reverb on/off
\r