1423d65f4Sbalrog /* 2423d65f4Sbalrog * GUSEMU32 - mixing engine (similar to Interwave GF1 compatibility) 3423d65f4Sbalrog * 4423d65f4Sbalrog * Copyright (C) 2000-2007 Tibor "TS" Schütz 5423d65f4Sbalrog * 6423d65f4Sbalrog * Permission is hereby granted, free of charge, to any person obtaining a copy 7423d65f4Sbalrog * of this software and associated documentation files (the "Software"), to deal 8423d65f4Sbalrog * in the Software without restriction, including without limitation the rights 9423d65f4Sbalrog * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10423d65f4Sbalrog * copies of the Software, and to permit persons to whom the Software is 11423d65f4Sbalrog * furnished to do so, subject to the following conditions: 12423d65f4Sbalrog * 13423d65f4Sbalrog * The above copyright notice and this permission notice shall be included in 14423d65f4Sbalrog * all copies or substantial portions of the Software. 15423d65f4Sbalrog * 16423d65f4Sbalrog * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17423d65f4Sbalrog * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18423d65f4Sbalrog * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19423d65f4Sbalrog * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20423d65f4Sbalrog * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21423d65f4Sbalrog * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22423d65f4Sbalrog * THE SOFTWARE. 23423d65f4Sbalrog */ 24423d65f4Sbalrog 25*6086a565SPeter Maydell #include "qemu/osdep.h" 2647b43a1fSPaolo Bonzini #include "gusemu.h" 2747b43a1fSPaolo Bonzini #include "gustate.h" 28423d65f4Sbalrog 29423d65f4Sbalrog #define GUSregb(position) (* (gusptr+(position))) 30423d65f4Sbalrog #define GUSregw(position) (*(GUSword *) (gusptr+(position))) 31423d65f4Sbalrog #define GUSregd(position) (*(GUSdword *)(gusptr+(position))) 32423d65f4Sbalrog 33423d65f4Sbalrog #define GUSvoice(position) (*(GUSword *)(voiceptr+(position))) 34423d65f4Sbalrog 35423d65f4Sbalrog /* samples are always 16bit stereo (4 bytes each, first right then left interleaved) */ 36423d65f4Sbalrog void gus_mixvoices(GUSEmuState * state, unsigned int playback_freq, unsigned int numsamples, 37731ba0ceSmalc GUSsample *bufferpos) 38423d65f4Sbalrog { 39423d65f4Sbalrog /* note that byte registers are stored in the upper half of each voice register! */ 40423d65f4Sbalrog GUSbyte *gusptr; 41423d65f4Sbalrog int Voice; 42423d65f4Sbalrog GUSword *voiceptr; 43423d65f4Sbalrog 44423d65f4Sbalrog unsigned int count; 45423d65f4Sbalrog for (count = 0; count < numsamples * 2; count++) 46423d65f4Sbalrog *(bufferpos + count) = 0; /* clear */ 47423d65f4Sbalrog 48423d65f4Sbalrog gusptr = state->gusdatapos; 49423d65f4Sbalrog voiceptr = (GUSword *) gusptr; 50423d65f4Sbalrog if (!(GUSregb(GUS4cReset) & 0x01)) /* reset flag active? */ 51423d65f4Sbalrog return; 52423d65f4Sbalrog 53423d65f4Sbalrog for (Voice = 0; Voice <= (GUSregb(NumVoices) & 31); Voice++) 54423d65f4Sbalrog { 55423d65f4Sbalrog if (GUSvoice(wVSRControl) & 0x200) 56423d65f4Sbalrog GUSvoice(wVSRControl) |= 0x100; /* voice stop request */ 57423d65f4Sbalrog if (GUSvoice(wVSRVolRampControl) & 0x200) 58423d65f4Sbalrog GUSvoice(wVSRVolRampControl) |= 0x100; /* Volume ramp stop request */ 59423d65f4Sbalrog if (!(GUSvoice(wVSRControl) & GUSvoice(wVSRVolRampControl) & 0x100)) /* neither voice nor volume calculation active - save some time here ;) */ 60423d65f4Sbalrog { 61423d65f4Sbalrog unsigned int sample; 62423d65f4Sbalrog 63423d65f4Sbalrog unsigned int LoopStart = (GUSvoice(wVSRLoopStartHi) << 16) | GUSvoice(wVSRLoopStartLo); /* 23.9 format */ 64423d65f4Sbalrog unsigned int LoopEnd = (GUSvoice(wVSRLoopEndHi) << 16) | GUSvoice(wVSRLoopEndLo); /* 23.9 format */ 65423d65f4Sbalrog unsigned int CurrPos = (GUSvoice(wVSRCurrPosHi) << 16) | GUSvoice(wVSRCurrPosLo); /* 23.9 format */ 66423d65f4Sbalrog int VoiceIncrement = ((((unsigned long) GUSvoice(wVSRFreq) * 44100) / playback_freq) * (14 >> 1)) / 67423d65f4Sbalrog ((GUSregb(NumVoices) & 31) + 1); /* 6.10 increment/frame to 23.9 increment/sample */ 68423d65f4Sbalrog 69423d65f4Sbalrog int PanningPos = (GUSvoice(wVSRPanning) >> 8) & 0xf; 70423d65f4Sbalrog 71423d65f4Sbalrog unsigned int Volume32 = 32 * GUSvoice(wVSRCurrVol); /* 32 times larger than original gus for maintaining precision while ramping */ 72423d65f4Sbalrog unsigned int StartVol32 = (GUSvoice(wVSRVolRampStartVol) & 0xff00) * 32; 73423d65f4Sbalrog unsigned int EndVol32 = (GUSvoice(wVSRVolRampEndVol) & 0xff00) * 32; 74423d65f4Sbalrog int VolumeIncrement32 = (32 * 16 * (GUSvoice(wVSRVolRampRate) & 0x3f00) >> 8) >> ((((GUSvoice(wVSRVolRampRate) & 0xc000) >> 8) >> 6) * 3); /* including 1/8/64/512 volume speed divisor */ 75423d65f4Sbalrog VolumeIncrement32 = (((VolumeIncrement32 * 44100 / 2) / playback_freq) * 14) / ((GUSregb(NumVoices) & 31) + 1); /* adjust ramping speed to playback speed */ 76423d65f4Sbalrog 77423d65f4Sbalrog if (GUSvoice(wVSRControl) & 0x4000) 78423d65f4Sbalrog VoiceIncrement = -VoiceIncrement; /* reverse playback */ 79423d65f4Sbalrog if (GUSvoice(wVSRVolRampControl) & 0x4000) 80423d65f4Sbalrog VolumeIncrement32 = -VolumeIncrement32; /* reverse ramping */ 81423d65f4Sbalrog 82423d65f4Sbalrog for (sample = 0; sample < numsamples; sample++) 83423d65f4Sbalrog { 84423d65f4Sbalrog int sample1, sample2, Volume; 85423d65f4Sbalrog if (GUSvoice(wVSRControl) & 0x400) /* 16bit */ 86423d65f4Sbalrog { 87423d65f4Sbalrog int offset = ((CurrPos >> 9) & 0xc0000) + (((CurrPos >> 9) & 0x1ffff) << 1); 88423d65f4Sbalrog GUSchar *adr; 89423d65f4Sbalrog adr = (GUSchar *) state->himemaddr + offset; 90423d65f4Sbalrog sample1 = (*adr & 0xff) + (*(adr + 1) * 256); 91423d65f4Sbalrog sample2 = (*(adr + 2) & 0xff) + (*(adr + 2 + 1) * 256); 92423d65f4Sbalrog } 93423d65f4Sbalrog else /* 8bit */ 94423d65f4Sbalrog { 95423d65f4Sbalrog int offset = (CurrPos >> 9) & 0xfffff; 96423d65f4Sbalrog GUSchar *adr; 97423d65f4Sbalrog adr = (GUSchar *) state->himemaddr + offset; 98423d65f4Sbalrog sample1 = (*adr) * 256; 99423d65f4Sbalrog sample2 = (*(adr + 1)) * 256; 100423d65f4Sbalrog } 101423d65f4Sbalrog 102423d65f4Sbalrog Volume = ((((Volume32 >> (4 + 5)) & 0xff) + 256) << (Volume32 >> ((4 + 8) + 5))) / 512; /* semi-logarithmic volume, +5 due to additional precision */ 103423d65f4Sbalrog sample1 = (((sample1 * Volume) >> 16) * (512 - (CurrPos % 512))) / 512; 104423d65f4Sbalrog sample2 = (((sample2 * Volume) >> 16) * (CurrPos % 512)) / 512; 105423d65f4Sbalrog sample1 += sample2; 106423d65f4Sbalrog 107423d65f4Sbalrog if (!(GUSvoice(wVSRVolRampControl) & 0x100)) 108423d65f4Sbalrog { 109423d65f4Sbalrog Volume32 += VolumeIncrement32; 110423d65f4Sbalrog if ((GUSvoice(wVSRVolRampControl) & 0x4000) ? (Volume32 <= StartVol32) : (Volume32 >= EndVol32)) /* ramp up boundary cross */ 111423d65f4Sbalrog { 112423d65f4Sbalrog if (GUSvoice(wVSRVolRampControl) & 0x2000) 113423d65f4Sbalrog GUSvoice(wVSRVolRampControl) |= 0x8000; /* volramp IRQ enabled? -> IRQ wait flag */ 114423d65f4Sbalrog if (GUSvoice(wVSRVolRampControl) & 0x800) /* loop enabled */ 115423d65f4Sbalrog { 116423d65f4Sbalrog if (GUSvoice(wVSRVolRampControl) & 0x1000) /* bidir. loop */ 117423d65f4Sbalrog { 118423d65f4Sbalrog GUSvoice(wVSRVolRampControl) ^= 0x4000; /* toggle dir */ 119423d65f4Sbalrog VolumeIncrement32 = -VolumeIncrement32; 120423d65f4Sbalrog } 121423d65f4Sbalrog else 122423d65f4Sbalrog Volume32 = (GUSvoice(wVSRVolRampControl) & 0x4000) ? EndVol32 : StartVol32; /* unidir. loop ramp */ 123423d65f4Sbalrog } 124423d65f4Sbalrog else 125423d65f4Sbalrog { 126423d65f4Sbalrog GUSvoice(wVSRVolRampControl) |= 0x100; 127423d65f4Sbalrog Volume32 = 128423d65f4Sbalrog (GUSvoice(wVSRVolRampControl) & 0x4000) ? StartVol32 : EndVol32; 129423d65f4Sbalrog } 130423d65f4Sbalrog } 131423d65f4Sbalrog } 132423d65f4Sbalrog if ((GUSvoice(wVSRVolRampControl) & 0xa000) == 0xa000) /* volramp IRQ set and enabled? */ 133423d65f4Sbalrog { 134423d65f4Sbalrog GUSregd(voicevolrampirq) |= 1 << Voice; /* set irq slot */ 135423d65f4Sbalrog } 136423d65f4Sbalrog else 137423d65f4Sbalrog { 138423d65f4Sbalrog GUSregd(voicevolrampirq) &= (~(1 << Voice)); /* clear irq slot */ 139423d65f4Sbalrog GUSvoice(wVSRVolRampControl) &= 0x7f00; 140423d65f4Sbalrog } 141423d65f4Sbalrog 142423d65f4Sbalrog if (!(GUSvoice(wVSRControl) & 0x100)) 143423d65f4Sbalrog { 144423d65f4Sbalrog CurrPos += VoiceIncrement; 145423d65f4Sbalrog if ((GUSvoice(wVSRControl) & 0x4000) ? (CurrPos <= LoopStart) : (CurrPos >= LoopEnd)) /* playback boundary cross */ 146423d65f4Sbalrog { 147423d65f4Sbalrog if (GUSvoice(wVSRControl) & 0x2000) 148423d65f4Sbalrog GUSvoice(wVSRControl) |= 0x8000; /* voice IRQ enabled -> IRQ wait flag */ 149423d65f4Sbalrog if (GUSvoice(wVSRControl) & 0x800) /* loop enabled */ 150423d65f4Sbalrog { 151423d65f4Sbalrog if (GUSvoice(wVSRControl) & 0x1000) /* pingpong loop */ 152423d65f4Sbalrog { 153423d65f4Sbalrog GUSvoice(wVSRControl) ^= 0x4000; /* toggle dir */ 154423d65f4Sbalrog VoiceIncrement = -VoiceIncrement; 155423d65f4Sbalrog } 156423d65f4Sbalrog else 157423d65f4Sbalrog CurrPos = (GUSvoice(wVSRControl) & 0x4000) ? LoopEnd : LoopStart; /* unidir. loop */ 158423d65f4Sbalrog } 159423d65f4Sbalrog else if (!(GUSvoice(wVSRVolRampControl) & 0x400)) 160423d65f4Sbalrog GUSvoice(wVSRControl) |= 0x100; /* loop disabled, rollover check */ 161423d65f4Sbalrog } 162423d65f4Sbalrog } 163423d65f4Sbalrog if ((GUSvoice(wVSRControl) & 0xa000) == 0xa000) /* wavetable IRQ set and enabled? */ 164423d65f4Sbalrog { 165423d65f4Sbalrog GUSregd(voicewavetableirq) |= 1 << Voice; /* set irq slot */ 166423d65f4Sbalrog } 167423d65f4Sbalrog else 168423d65f4Sbalrog { 169423d65f4Sbalrog GUSregd(voicewavetableirq) &= (~(1 << Voice)); /* clear irq slot */ 170423d65f4Sbalrog GUSvoice(wVSRControl) &= 0x7f00; 171423d65f4Sbalrog } 172423d65f4Sbalrog 173423d65f4Sbalrog /* mix samples into buffer */ 174731ba0ceSmalc *(bufferpos + 2 * sample) += (GUSsample) ((sample1 * PanningPos) >> 4); /* right */ 175731ba0ceSmalc *(bufferpos + 2 * sample + 1) += (GUSsample) ((sample1 * (15 - PanningPos)) >> 4); /* left */ 176423d65f4Sbalrog } 177423d65f4Sbalrog /* write back voice and volume */ 178423d65f4Sbalrog GUSvoice(wVSRCurrVol) = Volume32 / 32; 179423d65f4Sbalrog GUSvoice(wVSRCurrPosHi) = CurrPos >> 16; 180423d65f4Sbalrog GUSvoice(wVSRCurrPosLo) = CurrPos & 0xffff; 181423d65f4Sbalrog } 182423d65f4Sbalrog voiceptr += 16; /* next voice */ 183423d65f4Sbalrog } 184423d65f4Sbalrog } 185423d65f4Sbalrog 186423d65f4Sbalrog void gus_irqgen(GUSEmuState * state, unsigned int elapsed_time) 187423d65f4Sbalrog /* time given in microseconds */ 188423d65f4Sbalrog { 189423d65f4Sbalrog int requestedIRQs = 0; 190423d65f4Sbalrog GUSbyte *gusptr; 191423d65f4Sbalrog gusptr = state->gusdatapos; 192423d65f4Sbalrog if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */ 193423d65f4Sbalrog { 194423d65f4Sbalrog unsigned int timer1fraction = state->timer1fraction; 195423d65f4Sbalrog int newtimerirqs; 196423d65f4Sbalrog newtimerirqs = (elapsed_time + timer1fraction) / (80 * (256 - GUSregb(GUS46Counter1))); 197423d65f4Sbalrog state->timer1fraction = (elapsed_time + timer1fraction) % (80 * (256 - GUSregb(GUS46Counter1))); 198423d65f4Sbalrog if (newtimerirqs) 199423d65f4Sbalrog { 200423d65f4Sbalrog if (!(GUSregb(TimerDataReg2x9) & 0x40)) 201423d65f4Sbalrog GUSregb(TimerStatus2x8) |= 0xc0; /* maskable bits */ 202423d65f4Sbalrog if (GUSregb(GUS45TimerCtrl) & 4) /* timer1 irq enable */ 203423d65f4Sbalrog { 204423d65f4Sbalrog GUSregb(TimerStatus2x8) |= 4; /* nonmaskable bit */ 205423d65f4Sbalrog GUSregb(IRQStatReg2x6) |= 4; /* timer 1 irq pending */ 206423d65f4Sbalrog GUSregw(TimerIRQs) += newtimerirqs; 207423d65f4Sbalrog requestedIRQs += newtimerirqs; 208423d65f4Sbalrog } 209423d65f4Sbalrog } 210423d65f4Sbalrog } 211423d65f4Sbalrog if (GUSregb(TimerDataReg2x9) & 2) /* start timer 2 (320us decrement rate) */ 212423d65f4Sbalrog { 213423d65f4Sbalrog unsigned int timer2fraction = state->timer2fraction; 214423d65f4Sbalrog int newtimerirqs; 215423d65f4Sbalrog newtimerirqs = (elapsed_time + timer2fraction) / (320 * (256 - GUSregb(GUS47Counter2))); 216423d65f4Sbalrog state->timer2fraction = (elapsed_time + timer2fraction) % (320 * (256 - GUSregb(GUS47Counter2))); 217423d65f4Sbalrog if (newtimerirqs) 218423d65f4Sbalrog { 219423d65f4Sbalrog if (!(GUSregb(TimerDataReg2x9) & 0x20)) 220423d65f4Sbalrog GUSregb(TimerStatus2x8) |= 0xa0; /* maskable bits */ 221423d65f4Sbalrog if (GUSregb(GUS45TimerCtrl) & 8) /* timer2 irq enable */ 222423d65f4Sbalrog { 223423d65f4Sbalrog GUSregb(TimerStatus2x8) |= 2; /* nonmaskable bit */ 224423d65f4Sbalrog GUSregb(IRQStatReg2x6) |= 8; /* timer 2 irq pending */ 225423d65f4Sbalrog GUSregw(TimerIRQs) += newtimerirqs; 226423d65f4Sbalrog requestedIRQs += newtimerirqs; 227423d65f4Sbalrog } 228423d65f4Sbalrog } 229423d65f4Sbalrog } 230423d65f4Sbalrog if (GUSregb(GUS4cReset) & 0x4) /* synth IRQ enable */ 231423d65f4Sbalrog { 232423d65f4Sbalrog if (GUSregd(voicewavetableirq)) 233423d65f4Sbalrog GUSregb(IRQStatReg2x6) |= 0x20; 234423d65f4Sbalrog if (GUSregd(voicevolrampirq)) 235423d65f4Sbalrog GUSregb(IRQStatReg2x6) |= 0x40; 236423d65f4Sbalrog } 237423d65f4Sbalrog if ((!requestedIRQs) && GUSregb(IRQStatReg2x6)) 238423d65f4Sbalrog requestedIRQs++; 239423d65f4Sbalrog if (GUSregb(IRQStatReg2x6)) 240423d65f4Sbalrog GUSregw(BusyTimerIRQs) = GUS_irqrequest(state, state->gusirq, requestedIRQs); 241423d65f4Sbalrog } 242