/*************************************************************************** registers.c - description ------------------- begin : Wed May 15 2002 copyright : (C) 2002 by Pete Bernert email : BlackDove@addcom.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. See also the license.txt file for * * additional informations. * * * ***************************************************************************/ #include "stdafx.h" #define _IN_REGISTERS #include "externals.h" #include "registers.h" #include "spu_config.h" static void SoundOn(int start,int end,unsigned short val); static void SoundOff(int start,int end,unsigned short val); static void FModOn(int start,int end,unsigned short val); static void NoiseOn(int start,int end,unsigned short val); static void SetVolumeL(unsigned char ch,short vol); static void SetVolumeR(unsigned char ch,short vol); static void SetPitch(int ch,unsigned short val); static void ReverbOn(int start,int end,unsigned short val); //////////////////////////////////////////////////////////////////////// // WRITE REGISTERS: called by main emu //////////////////////////////////////////////////////////////////////// static const uint32_t ignore_dupe[8] = { // ch 0-15 c40 c80 cc0 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, // ch 16-24 d40 control reverb 0x7f7f7f7f, 0x7f7f7f7f, 0xff05ff0f, 0xffffffff }; void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val, unsigned int cycles) { int r = reg & 0xfff; int rofs = (r - 0xc00) >> 1; int changed = spu.regArea[rofs] != val; spu.regArea[rofs] = val; if (!changed && (ignore_dupe[rofs >> 5] & (1 << (rofs & 0x1f)))) return; // zero keyon/keyoff? if (val == 0 && (r & 0xff8) == 0xd88) return; do_samples_if_needed(cycles, 0); if(r>=0x0c00 && r<0x0d80) // some channel info? { int ch=(r>>4)-0xc0; // calc channel switch(r&0x0f) { //------------------------------------------------// r volume case 0: SetVolumeL((unsigned char)ch,val); break; //------------------------------------------------// l volume case 2: SetVolumeR((unsigned char)ch,val); break; //------------------------------------------------// pitch case 4: SetPitch(ch,val); goto upd_irq; //------------------------------------------------// start case 6: // taken from regArea later break; //------------------------------------------------// level with pre-calcs case 8: { const unsigned long lval=val; //---------------------------------------------// spu.s_chan[ch].ADSRX.AttackModeExp=(lval&0x8000)?1:0; spu.s_chan[ch].ADSRX.AttackRate=(lval>>8) & 0x007f; spu.s_chan[ch].ADSRX.DecayRate=(lval>>4) & 0x000f; spu.s_chan[ch].ADSRX.SustainLevel=lval & 0x000f; //---------------------------------------------// } break; //------------------------------------------------// adsr times with pre-calcs case 10: { const unsigned long lval=val; //----------------------------------------------// spu.s_chan[ch].ADSRX.SustainModeExp = (lval&0x8000)?1:0; spu.s_chan[ch].ADSRX.SustainIncrease= (lval&0x4000)?0:1; spu.s_chan[ch].ADSRX.SustainRate = (lval>>6) & 0x007f; spu.s_chan[ch].ADSRX.ReleaseModeExp = (lval&0x0020)?1:0; spu.s_chan[ch].ADSRX.ReleaseRate = lval & 0x001f; //----------------------------------------------// } break; //------------------------------------------------// adsr volume... mmm have to investigate this case 12: break; //------------------------------------------------// case 14: // loop? spu.s_chan[ch].pLoop=spu.spuMemC+((val&~1)<<3); goto upd_irq; //------------------------------------------------// } return; } switch(r) { //-------------------------------------------------// case H_SPUaddr: spu.spuAddr = (unsigned long) val<<3; break; //-------------------------------------------------// case H_SPUdata: *(unsigned short *)(spu.spuMemC + spu.spuAddr) = val; spu.spuAddr += 2; spu.spuAddr &= 0x7fffe; break; //-------------------------------------------------// case H_SPUctrl: if (!(spu.spuCtrl & CTRL_IRQ)) { spu.spuStat&=~STAT_IRQ; if (val & CTRL_IRQ) schedule_next_irq(); } spu.spuCtrl=val; break; //-------------------------------------------------// case H_SPUstat: spu.spuStat=val&0xf800; break; //-------------------------------------------------// case H_SPUReverbAddr: goto rvbd; //-------------------------------------------------// case H_SPUirqAddr: spu.pSpuIrq=spu.spuMemC+(((unsigned long) val<<3)&~0xf); goto upd_irq; //-------------------------------------------------// case H_SPUrvolL: spu.rvb->VolLeft=val; break; //-------------------------------------------------// case H_SPUrvolR: spu.rvb->VolRight=val; break; //-------------------------------------------------// /* case H_ExtLeft: //auxprintf("EL %d\n",val); break; //-------------------------------------------------// case H_ExtRight: //auxprintf("ER %d\n",val); break; //-------------------------------------------------// case H_SPUmvolL: //auxprintf("ML %d\n",val); break; //-------------------------------------------------// case H_SPUmvolR: //auxprintf("MR %d\n",val); break; //-------------------------------------------------// case H_SPUMute1: //auxprintf("M0 %04x\n",val); break; //-------------------------------------------------// case H_SPUMute2: //auxprintf("M1 %04x\n",val); break; */ //-------------------------------------------------// case H_SPUon1: SoundOn(0,16,val); break; //-------------------------------------------------// case H_SPUon2: SoundOn(16,24,val); break; //-------------------------------------------------// case H_SPUoff1: SoundOff(0,16,val); break; //-------------------------------------------------// case H_SPUoff2: SoundOff(16,24,val); break; //-------------------------------------------------// case H_CDLeft: spu.iLeftXAVol=val & 0x7fff; if(spu.cddavCallback) spu.cddavCallback(0,val); break; case H_CDRight: spu.iRightXAVol=val & 0x7fff; if(spu.cddavCallback) spu.cddavCallback(1,val); break; //-------------------------------------------------// case H_FMod1: FModOn(0,16,val); break; //-------------------------------------------------// case H_FMod2: FModOn(16,24,val); break; //-------------------------------------------------// case H_Noise1: NoiseOn(0,16,val); break; //-------------------------------------------------// case H_Noise2: NoiseOn(16,24,val); break; //-------------------------------------------------// case H_RVBon1: ReverbOn(0,16,val); break; //-------------------------------------------------// case H_RVBon2: ReverbOn(16,24,val); break; //-------------------------------------------------// case H_Reverb+0 : goto rvbd; case H_Reverb+2 : goto rvbd; case H_Reverb+4 : spu.rvb->IIR_ALPHA=(short)val; break; case H_Reverb+6 : spu.rvb->ACC_COEF_A=(short)val; break; case H_Reverb+8 : spu.rvb->ACC_COEF_B=(short)val; break; case H_Reverb+10 : spu.rvb->ACC_COEF_C=(short)val; break; case H_Reverb+12 : spu.rvb->ACC_COEF_D=(short)val; break; case H_Reverb+14 : spu.rvb->IIR_COEF=(short)val; break; case H_Reverb+16 : spu.rvb->FB_ALPHA=(short)val; break; case H_Reverb+18 : spu.rvb->FB_X=(short)val; break; case H_Reverb+20 : goto rvbd; case H_Reverb+22 : goto rvbd; case H_Reverb+24 : goto rvbd; case H_Reverb+26 : goto rvbd; case H_Reverb+28 : goto rvbd; case H_Reverb+30 : goto rvbd; case H_Reverb+32 : goto rvbd; case H_Reverb+34 : goto rvbd; case H_Reverb+36 : goto rvbd; case H_Reverb+38 : goto rvbd; case H_Reverb+40 : goto rvbd; case H_Reverb+42 : goto rvbd; case H_Reverb+44 : goto rvbd; case H_Reverb+46 : goto rvbd; case H_Reverb+48 : goto rvbd; case H_Reverb+50 : goto rvbd; case H_Reverb+52 : goto rvbd; case H_Reverb+54 : goto rvbd; case H_Reverb+56 : goto rvbd; case H_Reverb+58 : goto rvbd; case H_Reverb+60 : spu.rvb->IN_COEF_L=(short)val; break; case H_Reverb+62 : spu.rvb->IN_COEF_R=(short)val; break; } return; upd_irq: if (spu.spuCtrl & CTRL_IRQ) schedule_next_irq(); return; rvbd: spu.rvb->dirty = 1; // recalculate on next update } //////////////////////////////////////////////////////////////////////// // READ REGISTER: called by main emu //////////////////////////////////////////////////////////////////////// unsigned short CALLBACK SPUreadRegister(unsigned long reg) { const unsigned long r=reg&0xfff; if(r>=0x0c00 && r<0x0d80) { switch(r&0x0f) { case 12: // get adsr vol { const int ch=(r>>4)-0xc0; if(spu.dwNewChannel&(1<>16); } case 14: // get loop address { const int ch=(r>>4)-0xc0; return (unsigned short)((spu.s_chan[ch].pLoop-spu.spuMemC)>>3); } } } switch(r) { case H_SPUctrl: return spu.spuCtrl; case H_SPUstat: return spu.spuStat; case H_SPUaddr: return (unsigned short)(spu.spuAddr>>3); case H_SPUdata: { unsigned short s = *(unsigned short *)(spu.spuMemC + spu.spuAddr); spu.spuAddr += 2; spu.spuAddr &= 0x7fffe; return s; } //case H_SPUIsOn1: // return IsSoundOn(0,16); //case H_SPUIsOn2: // return IsSoundOn(16,24); } return spu.regArea[(r-0xc00)>>1]; } //////////////////////////////////////////////////////////////////////// // SOUND ON register write //////////////////////////////////////////////////////////////////////// static void SoundOn(int start,int end,unsigned short val) { int ch; for(ch=start;ch>=1) // loop channels { if((val&1) && regAreaGet(ch,6)) // mmm... start has to be set before key on !?! { spu.s_chan[ch].pCurr=spu.spuMemC+((regAreaGet(ch,6)&~1)<<3); // must be block aligned if (spu_config.idiablofix == 0) spu.s_chan[ch].pLoop=spu.spuMemC+((regAreaGet(ch,14)&~1)<<3); spu.dwNewChannel|=(1<>=1) // loop channels { if(val&1) { spu.s_chan[ch].ADSRX.State = ADSR_RELEASE; // Jungle Book - Rhythm 'n Groove // - turns off buzzing sound (loop hangs) spu.dwNewChannel &= ~(1<>=1) // loop channels { if(val&1) // -> fmod on/off { if(ch>0) { spu.s_chan[ch].bFMod=1; // --> sound channel spu.s_chan[ch-1].bFMod=2; // --> freq channel } } else { spu.s_chan[ch].bFMod=0; // --> turn off fmod if(ch>0&&spu.s_chan[ch-1].bFMod==2) spu.s_chan[ch-1].bFMod=0; } } } //////////////////////////////////////////////////////////////////////// // NOISE register write //////////////////////////////////////////////////////////////////////// static void NoiseOn(int start,int end,unsigned short val) { int ch; for(ch=start;ch>=1) // loop channels { spu.s_chan[ch].bNoise=val&1; // -> noise on/off } } //////////////////////////////////////////////////////////////////////// // LEFT VOLUME register write //////////////////////////////////////////////////////////////////////// // please note: sweep and phase invert are wrong... but I've never seen // them used static void SetVolumeL(unsigned char ch,short vol) // LEFT VOLUME { if(vol&0x8000) // sweep? { short sInc=1; // -> sweep up? if(vol&0x2000) sInc=-1; // -> or down? if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64 vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half! vol*=128; } else // no sweep: { if(vol&0x4000) // -> mmm... phase inverted? have to investigate this //vol^=0xffff; vol=0x3fff-(vol&0x3fff); } vol&=0x3fff; spu.s_chan[ch].iLeftVolume=vol; // store volume } //////////////////////////////////////////////////////////////////////// // RIGHT VOLUME register write //////////////////////////////////////////////////////////////////////// static void SetVolumeR(unsigned char ch,short vol) // RIGHT VOLUME { if(vol&0x8000) // comments... see above :) { short sInc=1; if(vol&0x2000) sInc=-1; if(vol&0x1000) vol^=0xffff; vol=((vol&0x7f)+1)/2; vol+=vol/(2*sInc); vol*=128; } else { if(vol&0x4000) //vol=vol^=0xffff; vol=0x3fff-(vol&0x3fff); } vol&=0x3fff; spu.s_chan[ch].iRightVolume=vol; } //////////////////////////////////////////////////////////////////////// // PITCH register write //////////////////////////////////////////////////////////////////////// static void SetPitch(int ch,unsigned short val) // SET PITCH { int NP; if(val>0x3fff) NP=0x3fff; // get pitch val else NP=val; spu.s_chan[ch].iRawPitch=NP; spu.s_chan[ch].sinc=(NP<<4)|8; spu.s_chan[ch].sinc_inv=0; if (spu_config.iUseInterpolation == 1) spu.SB[ch * SB_SIZE + 32] = 1; // -> freq change in simple interpolation mode: set flag } //////////////////////////////////////////////////////////////////////// // REVERB register write //////////////////////////////////////////////////////////////////////// static void ReverbOn(int start,int end,unsigned short val) { int ch; for(ch=start;ch>=1) // loop channels { spu.s_chan[ch].bReverb=val&1; // -> reverb on/off } }