balrog | 423d65f | 2008-01-14 22:09:11 +0000 | [diff] [blame] | 1 | /* |
| 2 | * GUSEMU32 - mixing engine (similar to Interwave GF1 compatibility) |
| 3 | * |
| 4 | * Copyright (C) 2000-2007 Tibor "TS" Schütz |
| 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to deal |
| 8 | * in the Software without restriction, including without limitation the rights |
| 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 10 | * copies of the Software, and to permit persons to whom the Software is |
| 11 | * furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice and this permission notice shall be included in |
| 14 | * all copies or substantial portions of the Software. |
| 15 | * |
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 22 | * THE SOFTWARE. |
| 23 | */ |
| 24 | |
| 25 | #include "gusemu.h" |
| 26 | #include "gustate.h" |
| 27 | |
| 28 | #define GUSregb(position) (* (gusptr+(position))) |
| 29 | #define GUSregw(position) (*(GUSword *) (gusptr+(position))) |
| 30 | #define GUSregd(position) (*(GUSdword *)(gusptr+(position))) |
| 31 | |
| 32 | #define GUSvoice(position) (*(GUSword *)(voiceptr+(position))) |
| 33 | |
| 34 | /* samples are always 16bit stereo (4 bytes each, first right then left interleaved) */ |
| 35 | void gus_mixvoices(GUSEmuState * state, unsigned int playback_freq, unsigned int numsamples, |
malc | 731ba0c | 2008-06-08 01:42:47 +0000 | [diff] [blame] | 36 | GUSsample *bufferpos) |
balrog | 423d65f | 2008-01-14 22:09:11 +0000 | [diff] [blame] | 37 | { |
| 38 | /* note that byte registers are stored in the upper half of each voice register! */ |
| 39 | GUSbyte *gusptr; |
| 40 | int Voice; |
| 41 | GUSword *voiceptr; |
| 42 | |
| 43 | unsigned int count; |
| 44 | for (count = 0; count < numsamples * 2; count++) |
| 45 | *(bufferpos + count) = 0; /* clear */ |
| 46 | |
| 47 | gusptr = state->gusdatapos; |
| 48 | voiceptr = (GUSword *) gusptr; |
| 49 | if (!(GUSregb(GUS4cReset) & 0x01)) /* reset flag active? */ |
| 50 | return; |
| 51 | |
| 52 | for (Voice = 0; Voice <= (GUSregb(NumVoices) & 31); Voice++) |
| 53 | { |
| 54 | if (GUSvoice(wVSRControl) & 0x200) |
| 55 | GUSvoice(wVSRControl) |= 0x100; /* voice stop request */ |
| 56 | if (GUSvoice(wVSRVolRampControl) & 0x200) |
| 57 | GUSvoice(wVSRVolRampControl) |= 0x100; /* Volume ramp stop request */ |
| 58 | if (!(GUSvoice(wVSRControl) & GUSvoice(wVSRVolRampControl) & 0x100)) /* neither voice nor volume calculation active - save some time here ;) */ |
| 59 | { |
| 60 | unsigned int sample; |
| 61 | |
| 62 | unsigned int LoopStart = (GUSvoice(wVSRLoopStartHi) << 16) | GUSvoice(wVSRLoopStartLo); /* 23.9 format */ |
| 63 | unsigned int LoopEnd = (GUSvoice(wVSRLoopEndHi) << 16) | GUSvoice(wVSRLoopEndLo); /* 23.9 format */ |
| 64 | unsigned int CurrPos = (GUSvoice(wVSRCurrPosHi) << 16) | GUSvoice(wVSRCurrPosLo); /* 23.9 format */ |
| 65 | int VoiceIncrement = ((((unsigned long) GUSvoice(wVSRFreq) * 44100) / playback_freq) * (14 >> 1)) / |
| 66 | ((GUSregb(NumVoices) & 31) + 1); /* 6.10 increment/frame to 23.9 increment/sample */ |
| 67 | |
| 68 | int PanningPos = (GUSvoice(wVSRPanning) >> 8) & 0xf; |
| 69 | |
| 70 | unsigned int Volume32 = 32 * GUSvoice(wVSRCurrVol); /* 32 times larger than original gus for maintaining precision while ramping */ |
| 71 | unsigned int StartVol32 = (GUSvoice(wVSRVolRampStartVol) & 0xff00) * 32; |
| 72 | unsigned int EndVol32 = (GUSvoice(wVSRVolRampEndVol) & 0xff00) * 32; |
| 73 | int VolumeIncrement32 = (32 * 16 * (GUSvoice(wVSRVolRampRate) & 0x3f00) >> 8) >> ((((GUSvoice(wVSRVolRampRate) & 0xc000) >> 8) >> 6) * 3); /* including 1/8/64/512 volume speed divisor */ |
| 74 | VolumeIncrement32 = (((VolumeIncrement32 * 44100 / 2) / playback_freq) * 14) / ((GUSregb(NumVoices) & 31) + 1); /* adjust ramping speed to playback speed */ |
| 75 | |
| 76 | if (GUSvoice(wVSRControl) & 0x4000) |
| 77 | VoiceIncrement = -VoiceIncrement; /* reverse playback */ |
| 78 | if (GUSvoice(wVSRVolRampControl) & 0x4000) |
| 79 | VolumeIncrement32 = -VolumeIncrement32; /* reverse ramping */ |
| 80 | |
| 81 | for (sample = 0; sample < numsamples; sample++) |
| 82 | { |
| 83 | int sample1, sample2, Volume; |
| 84 | if (GUSvoice(wVSRControl) & 0x400) /* 16bit */ |
| 85 | { |
| 86 | int offset = ((CurrPos >> 9) & 0xc0000) + (((CurrPos >> 9) & 0x1ffff) << 1); |
| 87 | GUSchar *adr; |
| 88 | adr = (GUSchar *) state->himemaddr + offset; |
| 89 | sample1 = (*adr & 0xff) + (*(adr + 1) * 256); |
| 90 | sample2 = (*(adr + 2) & 0xff) + (*(adr + 2 + 1) * 256); |
| 91 | } |
| 92 | else /* 8bit */ |
| 93 | { |
| 94 | int offset = (CurrPos >> 9) & 0xfffff; |
| 95 | GUSchar *adr; |
| 96 | adr = (GUSchar *) state->himemaddr + offset; |
| 97 | sample1 = (*adr) * 256; |
| 98 | sample2 = (*(adr + 1)) * 256; |
| 99 | } |
| 100 | |
| 101 | Volume = ((((Volume32 >> (4 + 5)) & 0xff) + 256) << (Volume32 >> ((4 + 8) + 5))) / 512; /* semi-logarithmic volume, +5 due to additional precision */ |
| 102 | sample1 = (((sample1 * Volume) >> 16) * (512 - (CurrPos % 512))) / 512; |
| 103 | sample2 = (((sample2 * Volume) >> 16) * (CurrPos % 512)) / 512; |
| 104 | sample1 += sample2; |
| 105 | |
| 106 | if (!(GUSvoice(wVSRVolRampControl) & 0x100)) |
| 107 | { |
| 108 | Volume32 += VolumeIncrement32; |
| 109 | if ((GUSvoice(wVSRVolRampControl) & 0x4000) ? (Volume32 <= StartVol32) : (Volume32 >= EndVol32)) /* ramp up boundary cross */ |
| 110 | { |
| 111 | if (GUSvoice(wVSRVolRampControl) & 0x2000) |
| 112 | GUSvoice(wVSRVolRampControl) |= 0x8000; /* volramp IRQ enabled? -> IRQ wait flag */ |
| 113 | if (GUSvoice(wVSRVolRampControl) & 0x800) /* loop enabled */ |
| 114 | { |
| 115 | if (GUSvoice(wVSRVolRampControl) & 0x1000) /* bidir. loop */ |
| 116 | { |
| 117 | GUSvoice(wVSRVolRampControl) ^= 0x4000; /* toggle dir */ |
| 118 | VolumeIncrement32 = -VolumeIncrement32; |
| 119 | } |
| 120 | else |
| 121 | Volume32 = (GUSvoice(wVSRVolRampControl) & 0x4000) ? EndVol32 : StartVol32; /* unidir. loop ramp */ |
| 122 | } |
| 123 | else |
| 124 | { |
| 125 | GUSvoice(wVSRVolRampControl) |= 0x100; |
| 126 | Volume32 = |
| 127 | (GUSvoice(wVSRVolRampControl) & 0x4000) ? StartVol32 : EndVol32; |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | if ((GUSvoice(wVSRVolRampControl) & 0xa000) == 0xa000) /* volramp IRQ set and enabled? */ |
| 132 | { |
| 133 | GUSregd(voicevolrampirq) |= 1 << Voice; /* set irq slot */ |
| 134 | } |
| 135 | else |
| 136 | { |
| 137 | GUSregd(voicevolrampirq) &= (~(1 << Voice)); /* clear irq slot */ |
| 138 | GUSvoice(wVSRVolRampControl) &= 0x7f00; |
| 139 | } |
| 140 | |
| 141 | if (!(GUSvoice(wVSRControl) & 0x100)) |
| 142 | { |
| 143 | CurrPos += VoiceIncrement; |
| 144 | if ((GUSvoice(wVSRControl) & 0x4000) ? (CurrPos <= LoopStart) : (CurrPos >= LoopEnd)) /* playback boundary cross */ |
| 145 | { |
| 146 | if (GUSvoice(wVSRControl) & 0x2000) |
| 147 | GUSvoice(wVSRControl) |= 0x8000; /* voice IRQ enabled -> IRQ wait flag */ |
| 148 | if (GUSvoice(wVSRControl) & 0x800) /* loop enabled */ |
| 149 | { |
| 150 | if (GUSvoice(wVSRControl) & 0x1000) /* pingpong loop */ |
| 151 | { |
| 152 | GUSvoice(wVSRControl) ^= 0x4000; /* toggle dir */ |
| 153 | VoiceIncrement = -VoiceIncrement; |
| 154 | } |
| 155 | else |
| 156 | CurrPos = (GUSvoice(wVSRControl) & 0x4000) ? LoopEnd : LoopStart; /* unidir. loop */ |
| 157 | } |
| 158 | else if (!(GUSvoice(wVSRVolRampControl) & 0x400)) |
| 159 | GUSvoice(wVSRControl) |= 0x100; /* loop disabled, rollover check */ |
| 160 | } |
| 161 | } |
| 162 | if ((GUSvoice(wVSRControl) & 0xa000) == 0xa000) /* wavetable IRQ set and enabled? */ |
| 163 | { |
| 164 | GUSregd(voicewavetableirq) |= 1 << Voice; /* set irq slot */ |
| 165 | } |
| 166 | else |
| 167 | { |
| 168 | GUSregd(voicewavetableirq) &= (~(1 << Voice)); /* clear irq slot */ |
| 169 | GUSvoice(wVSRControl) &= 0x7f00; |
| 170 | } |
| 171 | |
| 172 | /* mix samples into buffer */ |
malc | 731ba0c | 2008-06-08 01:42:47 +0000 | [diff] [blame] | 173 | *(bufferpos + 2 * sample) += (GUSsample) ((sample1 * PanningPos) >> 4); /* right */ |
| 174 | *(bufferpos + 2 * sample + 1) += (GUSsample) ((sample1 * (15 - PanningPos)) >> 4); /* left */ |
balrog | 423d65f | 2008-01-14 22:09:11 +0000 | [diff] [blame] | 175 | } |
| 176 | /* write back voice and volume */ |
| 177 | GUSvoice(wVSRCurrVol) = Volume32 / 32; |
| 178 | GUSvoice(wVSRCurrPosHi) = CurrPos >> 16; |
| 179 | GUSvoice(wVSRCurrPosLo) = CurrPos & 0xffff; |
| 180 | } |
| 181 | voiceptr += 16; /* next voice */ |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | void gus_irqgen(GUSEmuState * state, unsigned int elapsed_time) |
| 186 | /* time given in microseconds */ |
| 187 | { |
| 188 | int requestedIRQs = 0; |
| 189 | GUSbyte *gusptr; |
| 190 | gusptr = state->gusdatapos; |
| 191 | if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */ |
| 192 | { |
| 193 | unsigned int timer1fraction = state->timer1fraction; |
| 194 | int newtimerirqs; |
| 195 | newtimerirqs = (elapsed_time + timer1fraction) / (80 * (256 - GUSregb(GUS46Counter1))); |
| 196 | state->timer1fraction = (elapsed_time + timer1fraction) % (80 * (256 - GUSregb(GUS46Counter1))); |
| 197 | if (newtimerirqs) |
| 198 | { |
| 199 | if (!(GUSregb(TimerDataReg2x9) & 0x40)) |
| 200 | GUSregb(TimerStatus2x8) |= 0xc0; /* maskable bits */ |
| 201 | if (GUSregb(GUS45TimerCtrl) & 4) /* timer1 irq enable */ |
| 202 | { |
| 203 | GUSregb(TimerStatus2x8) |= 4; /* nonmaskable bit */ |
| 204 | GUSregb(IRQStatReg2x6) |= 4; /* timer 1 irq pending */ |
| 205 | GUSregw(TimerIRQs) += newtimerirqs; |
| 206 | requestedIRQs += newtimerirqs; |
| 207 | } |
| 208 | } |
| 209 | } |
| 210 | if (GUSregb(TimerDataReg2x9) & 2) /* start timer 2 (320us decrement rate) */ |
| 211 | { |
| 212 | unsigned int timer2fraction = state->timer2fraction; |
| 213 | int newtimerirqs; |
| 214 | newtimerirqs = (elapsed_time + timer2fraction) / (320 * (256 - GUSregb(GUS47Counter2))); |
| 215 | state->timer2fraction = (elapsed_time + timer2fraction) % (320 * (256 - GUSregb(GUS47Counter2))); |
| 216 | if (newtimerirqs) |
| 217 | { |
| 218 | if (!(GUSregb(TimerDataReg2x9) & 0x20)) |
| 219 | GUSregb(TimerStatus2x8) |= 0xa0; /* maskable bits */ |
| 220 | if (GUSregb(GUS45TimerCtrl) & 8) /* timer2 irq enable */ |
| 221 | { |
| 222 | GUSregb(TimerStatus2x8) |= 2; /* nonmaskable bit */ |
| 223 | GUSregb(IRQStatReg2x6) |= 8; /* timer 2 irq pending */ |
| 224 | GUSregw(TimerIRQs) += newtimerirqs; |
| 225 | requestedIRQs += newtimerirqs; |
| 226 | } |
| 227 | } |
| 228 | } |
| 229 | if (GUSregb(GUS4cReset) & 0x4) /* synth IRQ enable */ |
| 230 | { |
| 231 | if (GUSregd(voicewavetableirq)) |
| 232 | GUSregb(IRQStatReg2x6) |= 0x20; |
| 233 | if (GUSregd(voicevolrampirq)) |
| 234 | GUSregb(IRQStatReg2x6) |= 0x40; |
| 235 | } |
| 236 | if ((!requestedIRQs) && GUSregb(IRQStatReg2x6)) |
| 237 | requestedIRQs++; |
| 238 | if (GUSregb(IRQStatReg2x6)) |
| 239 | GUSregw(BusyTimerIRQs) = GUS_irqrequest(state, state->gusirq, requestedIRQs); |
| 240 | } |