root / hw / gus.c @ d0c22f49
History | View | Annotate | Download (8.2 kB)
1 | 423d65f4 | balrog | /*
|
---|---|---|---|
2 | 423d65f4 | balrog | * QEMU Proxy for Gravis Ultrasound GF1 emulation by Tibor "TS" Schütz
|
3 | 423d65f4 | balrog | *
|
4 | 423d65f4 | balrog | * Copyright (c) 2002-2005 Vassili Karpov (malc)
|
5 | 423d65f4 | balrog | *
|
6 | 423d65f4 | balrog | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 | 423d65f4 | balrog | * of this software and associated documentation files (the "Software"), to deal
|
8 | 423d65f4 | balrog | * in the Software without restriction, including without limitation the rights
|
9 | 423d65f4 | balrog | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 | 423d65f4 | balrog | * copies of the Software, and to permit persons to whom the Software is
|
11 | 423d65f4 | balrog | * furnished to do so, subject to the following conditions:
|
12 | 423d65f4 | balrog | *
|
13 | 423d65f4 | balrog | * The above copyright notice and this permission notice shall be included in
|
14 | 423d65f4 | balrog | * all copies or substantial portions of the Software.
|
15 | 423d65f4 | balrog | *
|
16 | 423d65f4 | balrog | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 | 423d65f4 | balrog | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 | 423d65f4 | balrog | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19 | 423d65f4 | balrog | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 | 423d65f4 | balrog | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 | 423d65f4 | balrog | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22 | 423d65f4 | balrog | * THE SOFTWARE.
|
23 | 423d65f4 | balrog | */
|
24 | 423d65f4 | balrog | #include "hw.h" |
25 | 423d65f4 | balrog | #include "audiodev.h" |
26 | 423d65f4 | balrog | #include "audio/audio.h" |
27 | 423d65f4 | balrog | #include "isa.h" |
28 | 423d65f4 | balrog | #include "gusemu.h" |
29 | 423d65f4 | balrog | #include "gustate.h" |
30 | 423d65f4 | balrog | |
31 | 423d65f4 | balrog | #define dolog(...) AUD_log ("audio", __VA_ARGS__) |
32 | 423d65f4 | balrog | #ifdef DEBUG
|
33 | 423d65f4 | balrog | #define ldebug(...) dolog (__VA_ARGS__)
|
34 | 423d65f4 | balrog | #else
|
35 | 423d65f4 | balrog | #define ldebug(...)
|
36 | 423d65f4 | balrog | #endif
|
37 | 423d65f4 | balrog | |
38 | 423d65f4 | balrog | #ifdef WORDS_BIGENDIAN
|
39 | 423d65f4 | balrog | #define GUS_ENDIANNESS 1 |
40 | 423d65f4 | balrog | #else
|
41 | 423d65f4 | balrog | #define GUS_ENDIANNESS 0 |
42 | 423d65f4 | balrog | #endif
|
43 | 423d65f4 | balrog | |
44 | 423d65f4 | balrog | #define IO_READ_PROTO(name) \
|
45 | 423d65f4 | balrog | uint32_t name (void *opaque, uint32_t nport)
|
46 | 423d65f4 | balrog | #define IO_WRITE_PROTO(name) \
|
47 | 423d65f4 | balrog | void name (void *opaque, uint32_t nport, uint32_t val) |
48 | 423d65f4 | balrog | |
49 | 423d65f4 | balrog | static struct { |
50 | 423d65f4 | balrog | int port;
|
51 | 423d65f4 | balrog | int irq;
|
52 | 423d65f4 | balrog | int dma;
|
53 | 423d65f4 | balrog | int freq;
|
54 | 423d65f4 | balrog | } conf = {0x240, 7, 3, 44100}; |
55 | 423d65f4 | balrog | |
56 | 423d65f4 | balrog | typedef struct GUSState { |
57 | 423d65f4 | balrog | GUSEmuState emu; |
58 | 423d65f4 | balrog | QEMUSoundCard card; |
59 | 423d65f4 | balrog | int freq;
|
60 | 423d65f4 | balrog | int pos, left, shift, irqs;
|
61 | 731ba0ce | malc | GUSsample *mixbuf; |
62 | 423d65f4 | balrog | uint8_t himem[1024 * 1024 + 32 + 4096]; |
63 | 423d65f4 | balrog | int samples;
|
64 | 423d65f4 | balrog | SWVoiceOut *voice; |
65 | 423d65f4 | balrog | int64_t last_ticks; |
66 | 423d65f4 | balrog | qemu_irq *pic; |
67 | 423d65f4 | balrog | } GUSState; |
68 | 423d65f4 | balrog | |
69 | 423d65f4 | balrog | IO_READ_PROTO (gus_readb) |
70 | 423d65f4 | balrog | { |
71 | 423d65f4 | balrog | GUSState *s = opaque; |
72 | 423d65f4 | balrog | |
73 | 423d65f4 | balrog | return gus_read (&s->emu, nport, 1); |
74 | 423d65f4 | balrog | } |
75 | 423d65f4 | balrog | |
76 | 423d65f4 | balrog | IO_READ_PROTO (gus_readw) |
77 | 423d65f4 | balrog | { |
78 | 423d65f4 | balrog | GUSState *s = opaque; |
79 | 423d65f4 | balrog | |
80 | 423d65f4 | balrog | return gus_read (&s->emu, nport, 2); |
81 | 423d65f4 | balrog | } |
82 | 423d65f4 | balrog | |
83 | 423d65f4 | balrog | IO_WRITE_PROTO (gus_writeb) |
84 | 423d65f4 | balrog | { |
85 | 423d65f4 | balrog | GUSState *s = opaque; |
86 | 423d65f4 | balrog | |
87 | 423d65f4 | balrog | gus_write (&s->emu, nport, 1, val);
|
88 | 423d65f4 | balrog | } |
89 | 423d65f4 | balrog | |
90 | 423d65f4 | balrog | IO_WRITE_PROTO (gus_writew) |
91 | 423d65f4 | balrog | { |
92 | 423d65f4 | balrog | GUSState *s = opaque; |
93 | 423d65f4 | balrog | |
94 | 423d65f4 | balrog | gus_write (&s->emu, nport, 2, val);
|
95 | 423d65f4 | balrog | } |
96 | 423d65f4 | balrog | |
97 | 423d65f4 | balrog | static int write_audio (GUSState *s, int samples) |
98 | 423d65f4 | balrog | { |
99 | 423d65f4 | balrog | int net = 0; |
100 | 423d65f4 | balrog | int pos = s->pos;
|
101 | 423d65f4 | balrog | |
102 | 423d65f4 | balrog | while (samples) {
|
103 | 423d65f4 | balrog | int nbytes, wbytes, wsampl;
|
104 | 423d65f4 | balrog | |
105 | 423d65f4 | balrog | nbytes = samples << s->shift; |
106 | 423d65f4 | balrog | wbytes = AUD_write ( |
107 | 423d65f4 | balrog | s->voice, |
108 | 423d65f4 | balrog | s->mixbuf + (pos << (s->shift - 1)),
|
109 | 423d65f4 | balrog | nbytes |
110 | 423d65f4 | balrog | ); |
111 | 423d65f4 | balrog | |
112 | 423d65f4 | balrog | if (wbytes) {
|
113 | 423d65f4 | balrog | wsampl = wbytes >> s->shift; |
114 | 423d65f4 | balrog | |
115 | 423d65f4 | balrog | samples -= wsampl; |
116 | 423d65f4 | balrog | pos = (pos + wsampl) % s->samples; |
117 | 423d65f4 | balrog | |
118 | 423d65f4 | balrog | net += wsampl; |
119 | 423d65f4 | balrog | } |
120 | 423d65f4 | balrog | else {
|
121 | 423d65f4 | balrog | break;
|
122 | 423d65f4 | balrog | } |
123 | 423d65f4 | balrog | } |
124 | 423d65f4 | balrog | |
125 | 423d65f4 | balrog | return net;
|
126 | 423d65f4 | balrog | } |
127 | 423d65f4 | balrog | |
128 | 423d65f4 | balrog | static void GUS_callback (void *opaque, int free) |
129 | 423d65f4 | balrog | { |
130 | 423d65f4 | balrog | int samples, to_play, net = 0; |
131 | 423d65f4 | balrog | GUSState *s = opaque; |
132 | 423d65f4 | balrog | |
133 | 423d65f4 | balrog | samples = free >> s->shift; |
134 | 423d65f4 | balrog | to_play = audio_MIN (samples, s->left); |
135 | 423d65f4 | balrog | |
136 | 423d65f4 | balrog | while (to_play) {
|
137 | 423d65f4 | balrog | int written = write_audio (s, to_play);
|
138 | 423d65f4 | balrog | |
139 | 423d65f4 | balrog | if (!written) {
|
140 | 423d65f4 | balrog | goto reset;
|
141 | 423d65f4 | balrog | } |
142 | 423d65f4 | balrog | |
143 | 423d65f4 | balrog | s->left -= written; |
144 | 423d65f4 | balrog | to_play -= written; |
145 | 423d65f4 | balrog | samples -= written; |
146 | 423d65f4 | balrog | net += written; |
147 | 423d65f4 | balrog | } |
148 | 423d65f4 | balrog | |
149 | 423d65f4 | balrog | samples = audio_MIN (samples, s->samples); |
150 | 423d65f4 | balrog | if (samples) {
|
151 | 423d65f4 | balrog | gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf); |
152 | 423d65f4 | balrog | |
153 | 423d65f4 | balrog | while (samples) {
|
154 | 423d65f4 | balrog | int written = write_audio (s, samples);
|
155 | 423d65f4 | balrog | if (!written) {
|
156 | 423d65f4 | balrog | break;
|
157 | 423d65f4 | balrog | } |
158 | 423d65f4 | balrog | samples -= written; |
159 | 423d65f4 | balrog | net += written; |
160 | 423d65f4 | balrog | } |
161 | 423d65f4 | balrog | } |
162 | 423d65f4 | balrog | s->left = samples; |
163 | 423d65f4 | balrog | |
164 | 423d65f4 | balrog | reset:
|
165 | 423d65f4 | balrog | gus_irqgen (&s->emu, (double) (net * 1000000) / s->freq); |
166 | 423d65f4 | balrog | } |
167 | 423d65f4 | balrog | |
168 | 423d65f4 | balrog | int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n) |
169 | 423d65f4 | balrog | { |
170 | 423d65f4 | balrog | GUSState *s = emu->opaque; |
171 | 423d65f4 | balrog | /* qemu_irq_lower (s->pic[hwirq]); */
|
172 | 423d65f4 | balrog | qemu_irq_raise (s->pic[hwirq]); |
173 | 423d65f4 | balrog | s->irqs += n; |
174 | 423d65f4 | balrog | ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs);
|
175 | 423d65f4 | balrog | return n;
|
176 | 423d65f4 | balrog | } |
177 | 423d65f4 | balrog | |
178 | 423d65f4 | balrog | void GUS_irqclear (GUSEmuState *emu, int hwirq) |
179 | 423d65f4 | balrog | { |
180 | 423d65f4 | balrog | GUSState *s = emu->opaque; |
181 | 423d65f4 | balrog | ldebug ("irqclear %d %d\n", hwirq, s->irqs);
|
182 | 423d65f4 | balrog | qemu_irq_lower (s->pic[hwirq]); |
183 | 423d65f4 | balrog | s->irqs -= 1;
|
184 | 423d65f4 | balrog | #ifdef IRQ_STORM
|
185 | 423d65f4 | balrog | if (s->irqs > 0) { |
186 | 423d65f4 | balrog | qemu_irq_raise (s->pic[hwirq]); |
187 | 423d65f4 | balrog | } |
188 | 423d65f4 | balrog | #endif
|
189 | 423d65f4 | balrog | } |
190 | 423d65f4 | balrog | |
191 | 423d65f4 | balrog | void GUS_dmarequest (GUSEmuState *der)
|
192 | 423d65f4 | balrog | { |
193 | 423d65f4 | balrog | /* GUSState *s = (GUSState *) der; */
|
194 | 423d65f4 | balrog | ldebug ("dma request %d\n", der->gusdma);
|
195 | 423d65f4 | balrog | DMA_hold_DREQ (der->gusdma); |
196 | 423d65f4 | balrog | } |
197 | 423d65f4 | balrog | |
198 | 423d65f4 | balrog | int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) |
199 | 423d65f4 | balrog | { |
200 | 423d65f4 | balrog | GUSState *s = opaque; |
201 | 731ba0ce | malc | char tmpbuf[4096]; |
202 | 423d65f4 | balrog | int pos = dma_pos, mode, left = dma_len - dma_pos;
|
203 | 423d65f4 | balrog | |
204 | 423d65f4 | balrog | ldebug ("read DMA %#x %d\n", dma_pos, dma_len);
|
205 | 423d65f4 | balrog | mode = DMA_get_channel_mode (s->emu.gusdma); |
206 | 423d65f4 | balrog | while (left) {
|
207 | 423d65f4 | balrog | int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf)); |
208 | 423d65f4 | balrog | int copied;
|
209 | 423d65f4 | balrog | |
210 | 423d65f4 | balrog | ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos);
|
211 | 423d65f4 | balrog | copied = DMA_read_memory (nchan, tmpbuf, pos, to_copy); |
212 | 423d65f4 | balrog | gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied); |
213 | 423d65f4 | balrog | left -= copied; |
214 | 423d65f4 | balrog | pos += copied; |
215 | 423d65f4 | balrog | } |
216 | 423d65f4 | balrog | |
217 | 423d65f4 | balrog | if (0 == ((mode >> 4) & 1)) { |
218 | 423d65f4 | balrog | DMA_release_DREQ (s->emu.gusdma); |
219 | 423d65f4 | balrog | } |
220 | 423d65f4 | balrog | return dma_len;
|
221 | 423d65f4 | balrog | } |
222 | 423d65f4 | balrog | |
223 | 5b5ef0db | malc | static void GUS_save (QEMUFile *f, void *opaque) |
224 | 5b5ef0db | malc | { |
225 | 5b5ef0db | malc | GUSState *s = opaque; |
226 | 5b5ef0db | malc | |
227 | db8baf14 | malc | qemu_put_be32 (f, s->pos); |
228 | db8baf14 | malc | qemu_put_be32 (f, s->left); |
229 | db8baf14 | malc | qemu_put_be32 (f, s->shift); |
230 | db8baf14 | malc | qemu_put_be32 (f, s->irqs); |
231 | db8baf14 | malc | qemu_put_be32 (f, s->samples); |
232 | db8baf14 | malc | qemu_put_be64 (f, s->last_ticks); |
233 | 5b5ef0db | malc | qemu_put_buffer (f, s->himem, sizeof (s->himem));
|
234 | 5b5ef0db | malc | } |
235 | 5b5ef0db | malc | |
236 | 5b5ef0db | malc | static int GUS_load (QEMUFile *f, void *opaque, int version_id) |
237 | 5b5ef0db | malc | { |
238 | 5b5ef0db | malc | GUSState *s = opaque; |
239 | 5b5ef0db | malc | |
240 | 8210169a | malc | if (version_id != 2) |
241 | 5b5ef0db | malc | return -EINVAL;
|
242 | 5b5ef0db | malc | |
243 | db8baf14 | malc | s->pos = qemu_get_be32 (f); |
244 | db8baf14 | malc | s->left = qemu_get_be32 (f); |
245 | db8baf14 | malc | s->shift = qemu_get_be32 (f); |
246 | db8baf14 | malc | s->irqs = qemu_get_be32 (f); |
247 | db8baf14 | malc | s->samples = qemu_get_be32 (f); |
248 | db8baf14 | malc | s->last_ticks = qemu_get_be64 (f); |
249 | 5b5ef0db | malc | qemu_get_buffer (f, s->himem, sizeof (s->himem));
|
250 | 5b5ef0db | malc | return 0; |
251 | 5b5ef0db | malc | } |
252 | 5b5ef0db | malc | |
253 | 423d65f4 | balrog | int GUS_init (AudioState *audio, qemu_irq *pic)
|
254 | 423d65f4 | balrog | { |
255 | 423d65f4 | balrog | GUSState *s; |
256 | 423d65f4 | balrog | audsettings_t as; |
257 | 423d65f4 | balrog | |
258 | 423d65f4 | balrog | if (!audio) {
|
259 | 423d65f4 | balrog | dolog ("No audio state\n");
|
260 | 423d65f4 | balrog | return -1; |
261 | 423d65f4 | balrog | } |
262 | 423d65f4 | balrog | |
263 | 423d65f4 | balrog | s = qemu_mallocz (sizeof (*s));
|
264 | 423d65f4 | balrog | if (!s) {
|
265 | 423d65f4 | balrog | dolog ("Could not allocate memory for GUS (%zu bytes)\n",
|
266 | 423d65f4 | balrog | sizeof (*s));
|
267 | 423d65f4 | balrog | return -1; |
268 | 423d65f4 | balrog | } |
269 | 423d65f4 | balrog | |
270 | 423d65f4 | balrog | AUD_register_card (audio, "gus", &s->card);
|
271 | 423d65f4 | balrog | |
272 | 423d65f4 | balrog | as.freq = conf.freq; |
273 | 423d65f4 | balrog | as.nchannels = 2;
|
274 | 423d65f4 | balrog | as.fmt = AUD_FMT_S16; |
275 | 423d65f4 | balrog | as.endianness = GUS_ENDIANNESS; |
276 | 423d65f4 | balrog | |
277 | 423d65f4 | balrog | s->voice = AUD_open_out ( |
278 | 423d65f4 | balrog | &s->card, |
279 | 423d65f4 | balrog | NULL,
|
280 | 423d65f4 | balrog | "gus",
|
281 | 423d65f4 | balrog | s, |
282 | 423d65f4 | balrog | GUS_callback, |
283 | 423d65f4 | balrog | &as |
284 | 423d65f4 | balrog | ); |
285 | 423d65f4 | balrog | |
286 | 423d65f4 | balrog | if (!s->voice) {
|
287 | 423d65f4 | balrog | AUD_remove_card (&s->card); |
288 | 423d65f4 | balrog | qemu_free (s); |
289 | 423d65f4 | balrog | return -1; |
290 | 423d65f4 | balrog | } |
291 | 423d65f4 | balrog | |
292 | 423d65f4 | balrog | s->shift = 2;
|
293 | 423d65f4 | balrog | s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift; |
294 | 423d65f4 | balrog | s->mixbuf = qemu_mallocz (s->samples << s->shift); |
295 | 423d65f4 | balrog | if (!s->mixbuf) {
|
296 | 423d65f4 | balrog | AUD_close_out (&s->card, s->voice); |
297 | 423d65f4 | balrog | AUD_remove_card (&s->card); |
298 | 423d65f4 | balrog | qemu_free (s); |
299 | 423d65f4 | balrog | return -1; |
300 | 423d65f4 | balrog | } |
301 | 423d65f4 | balrog | |
302 | 423d65f4 | balrog | register_ioport_write (conf.port, 1, 1, gus_writeb, s); |
303 | 423d65f4 | balrog | register_ioport_write (conf.port, 1, 2, gus_writew, s); |
304 | 423d65f4 | balrog | |
305 | 423d65f4 | balrog | register_ioport_read ((conf.port + 0x100) & 0xf00, 1, 1, gus_readb, s); |
306 | 423d65f4 | balrog | register_ioport_read ((conf.port + 0x100) & 0xf00, 1, 2, gus_readw, s); |
307 | 423d65f4 | balrog | |
308 | 423d65f4 | balrog | register_ioport_write (conf.port + 6, 10, 1, gus_writeb, s); |
309 | 423d65f4 | balrog | register_ioport_write (conf.port + 6, 10, 2, gus_writew, s); |
310 | 423d65f4 | balrog | register_ioport_read (conf.port + 6, 10, 1, gus_readb, s); |
311 | 423d65f4 | balrog | register_ioport_read (conf.port + 6, 10, 2, gus_readw, s); |
312 | 423d65f4 | balrog | |
313 | 423d65f4 | balrog | |
314 | 423d65f4 | balrog | register_ioport_write (conf.port + 0x100, 8, 1, gus_writeb, s); |
315 | 423d65f4 | balrog | register_ioport_write (conf.port + 0x100, 8, 2, gus_writew, s); |
316 | 423d65f4 | balrog | register_ioport_read (conf.port + 0x100, 8, 1, gus_readb, s); |
317 | 423d65f4 | balrog | register_ioport_read (conf.port + 0x100, 8, 2, gus_readw, s); |
318 | 423d65f4 | balrog | |
319 | 423d65f4 | balrog | DMA_register_channel (conf.dma, GUS_read_DMA, s); |
320 | 423d65f4 | balrog | s->emu.gusirq = conf.irq; |
321 | 423d65f4 | balrog | s->emu.gusdma = conf.dma; |
322 | 423d65f4 | balrog | s->emu.himemaddr = s->himem; |
323 | 423d65f4 | balrog | s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32; |
324 | 423d65f4 | balrog | s->emu.opaque = s; |
325 | 423d65f4 | balrog | s->freq = conf.freq; |
326 | 423d65f4 | balrog | s->pic = pic; |
327 | 423d65f4 | balrog | |
328 | 423d65f4 | balrog | AUD_set_active_out (s->voice, 1);
|
329 | 5b5ef0db | malc | |
330 | 8210169a | malc | register_savevm ("gus", 0, 2, GUS_save, GUS_load, s); |
331 | 423d65f4 | balrog | return 0; |
332 | 423d65f4 | balrog | } |