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
256086a565SPeter Maydell #include "qemu/osdep.h"
2647b43a1fSPaolo Bonzini #include "gusemu.h"
2747b43a1fSPaolo Bonzini #include "gustate.h"
28423d65f4Sbalrog
29423d65f4Sbalrog #define GUSregb(position) (* (gusptr+(position)))
304bf7792aSJuan Quintela #define GUSregw(position) (*(uint16_t *) (gusptr+(position)))
31*58680345SAllan Peramaki #define GUSregd(position) (*(uint32_t *)(gusptr + (position)))
32423d65f4Sbalrog
334bf7792aSJuan Quintela #define GUSvoice(position) (*(uint16_t *)(voiceptr+(position)))
34423d65f4Sbalrog
35423d65f4Sbalrog /* samples are always 16bit stereo (4 bytes each, first right then left interleaved) */
gus_mixvoices(GUSEmuState * state,unsigned int playback_freq,unsigned int numsamples,int16_t * bufferpos)36423d65f4Sbalrog void gus_mixvoices(GUSEmuState * state, unsigned int playback_freq, unsigned int numsamples,
37135f5ae1SJuan Quintela int16_t *bufferpos)
38423d65f4Sbalrog {
39423d65f4Sbalrog /* note that byte registers are stored in the upper half of each voice register! */
400af81c56SJuan Quintela uint8_t *gusptr;
41423d65f4Sbalrog int Voice;
424bf7792aSJuan Quintela uint16_t *voiceptr;
43423d65f4Sbalrog
44423d65f4Sbalrog unsigned int count;
45423d65f4Sbalrog for (count = 0; count < numsamples * 2; count++)
46423d65f4Sbalrog *(bufferpos + count) = 0; /* clear */
47423d65f4Sbalrog
48423d65f4Sbalrog gusptr = state->gusdatapos;
494bf7792aSJuan Quintela voiceptr = (uint16_t *) 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);
88222e0356SJuan Quintela int8_t *adr;
89222e0356SJuan Quintela adr = (int8_t *) 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;
96222e0356SJuan Quintela int8_t *adr;
97222e0356SJuan Quintela adr = (int8_t *) 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 */
174135f5ae1SJuan Quintela *(bufferpos + 2 * sample) += (int16_t) ((sample1 * PanningPos) >> 4); /* right */
175135f5ae1SJuan Quintela *(bufferpos + 2 * sample + 1) += (int16_t) ((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
gus_irqgen(GUSEmuState * state,unsigned int elapsed_time)186423d65f4Sbalrog void gus_irqgen(GUSEmuState * state, unsigned int elapsed_time)
187423d65f4Sbalrog /* time given in microseconds */
188423d65f4Sbalrog {
189423d65f4Sbalrog int requestedIRQs = 0;
1900af81c56SJuan Quintela uint8_t *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