root / hw / gus.c @ 21eea4b3
History | View | Annotate | Download (8.4 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 | e2542fe2 | Juan Quintela | #ifdef HOST_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 | 371bc573 | malc | static uint32_t name (void *opaque, uint32_t nport) |
46 | 423d65f4 | balrog | #define IO_WRITE_PROTO(name) \
|
47 | 371bc573 | malc | static void name (void *opaque, uint32_t nport, uint32_t val) |
48 | 423d65f4 | balrog | |
49 | 423d65f4 | balrog | typedef struct GUSState { |
50 | 9df34396 | Gerd Hoffmann | ISADevice dev; |
51 | 423d65f4 | balrog | GUSEmuState emu; |
52 | 423d65f4 | balrog | QEMUSoundCard card; |
53 | 9df34396 | Gerd Hoffmann | uint32_t freq; |
54 | 9df34396 | Gerd Hoffmann | uint32_t port; |
55 | 423d65f4 | balrog | int pos, left, shift, irqs;
|
56 | 731ba0ce | malc | GUSsample *mixbuf; |
57 | 423d65f4 | balrog | uint8_t himem[1024 * 1024 + 32 + 4096]; |
58 | 423d65f4 | balrog | int samples;
|
59 | 423d65f4 | balrog | SWVoiceOut *voice; |
60 | 423d65f4 | balrog | int64_t last_ticks; |
61 | 9df34396 | Gerd Hoffmann | qemu_irq pic; |
62 | 423d65f4 | balrog | } GUSState; |
63 | 423d65f4 | balrog | |
64 | 423d65f4 | balrog | IO_READ_PROTO (gus_readb) |
65 | 423d65f4 | balrog | { |
66 | 423d65f4 | balrog | GUSState *s = opaque; |
67 | 423d65f4 | balrog | |
68 | 423d65f4 | balrog | return gus_read (&s->emu, nport, 1); |
69 | 423d65f4 | balrog | } |
70 | 423d65f4 | balrog | |
71 | 423d65f4 | balrog | IO_READ_PROTO (gus_readw) |
72 | 423d65f4 | balrog | { |
73 | 423d65f4 | balrog | GUSState *s = opaque; |
74 | 423d65f4 | balrog | |
75 | 423d65f4 | balrog | return gus_read (&s->emu, nport, 2); |
76 | 423d65f4 | balrog | } |
77 | 423d65f4 | balrog | |
78 | 423d65f4 | balrog | IO_WRITE_PROTO (gus_writeb) |
79 | 423d65f4 | balrog | { |
80 | 423d65f4 | balrog | GUSState *s = opaque; |
81 | 423d65f4 | balrog | |
82 | 423d65f4 | balrog | gus_write (&s->emu, nport, 1, val);
|
83 | 423d65f4 | balrog | } |
84 | 423d65f4 | balrog | |
85 | 423d65f4 | balrog | IO_WRITE_PROTO (gus_writew) |
86 | 423d65f4 | balrog | { |
87 | 423d65f4 | balrog | GUSState *s = opaque; |
88 | 423d65f4 | balrog | |
89 | 423d65f4 | balrog | gus_write (&s->emu, nport, 2, val);
|
90 | 423d65f4 | balrog | } |
91 | 423d65f4 | balrog | |
92 | 423d65f4 | balrog | static int write_audio (GUSState *s, int samples) |
93 | 423d65f4 | balrog | { |
94 | 423d65f4 | balrog | int net = 0; |
95 | 423d65f4 | balrog | int pos = s->pos;
|
96 | 423d65f4 | balrog | |
97 | 423d65f4 | balrog | while (samples) {
|
98 | 423d65f4 | balrog | int nbytes, wbytes, wsampl;
|
99 | 423d65f4 | balrog | |
100 | 423d65f4 | balrog | nbytes = samples << s->shift; |
101 | 423d65f4 | balrog | wbytes = AUD_write ( |
102 | 423d65f4 | balrog | s->voice, |
103 | 423d65f4 | balrog | s->mixbuf + (pos << (s->shift - 1)),
|
104 | 423d65f4 | balrog | nbytes |
105 | 423d65f4 | balrog | ); |
106 | 423d65f4 | balrog | |
107 | 423d65f4 | balrog | if (wbytes) {
|
108 | 423d65f4 | balrog | wsampl = wbytes >> s->shift; |
109 | 423d65f4 | balrog | |
110 | 423d65f4 | balrog | samples -= wsampl; |
111 | 423d65f4 | balrog | pos = (pos + wsampl) % s->samples; |
112 | 423d65f4 | balrog | |
113 | 423d65f4 | balrog | net += wsampl; |
114 | 423d65f4 | balrog | } |
115 | 423d65f4 | balrog | else {
|
116 | 423d65f4 | balrog | break;
|
117 | 423d65f4 | balrog | } |
118 | 423d65f4 | balrog | } |
119 | 423d65f4 | balrog | |
120 | 423d65f4 | balrog | return net;
|
121 | 423d65f4 | balrog | } |
122 | 423d65f4 | balrog | |
123 | 423d65f4 | balrog | static void GUS_callback (void *opaque, int free) |
124 | 423d65f4 | balrog | { |
125 | 423d65f4 | balrog | int samples, to_play, net = 0; |
126 | 423d65f4 | balrog | GUSState *s = opaque; |
127 | 423d65f4 | balrog | |
128 | 423d65f4 | balrog | samples = free >> s->shift; |
129 | 423d65f4 | balrog | to_play = audio_MIN (samples, s->left); |
130 | 423d65f4 | balrog | |
131 | 423d65f4 | balrog | while (to_play) {
|
132 | 423d65f4 | balrog | int written = write_audio (s, to_play);
|
133 | 423d65f4 | balrog | |
134 | 423d65f4 | balrog | if (!written) {
|
135 | 423d65f4 | balrog | goto reset;
|
136 | 423d65f4 | balrog | } |
137 | 423d65f4 | balrog | |
138 | 423d65f4 | balrog | s->left -= written; |
139 | 423d65f4 | balrog | to_play -= written; |
140 | 423d65f4 | balrog | samples -= written; |
141 | 423d65f4 | balrog | net += written; |
142 | 423d65f4 | balrog | } |
143 | 423d65f4 | balrog | |
144 | 423d65f4 | balrog | samples = audio_MIN (samples, s->samples); |
145 | 423d65f4 | balrog | if (samples) {
|
146 | 423d65f4 | balrog | gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf); |
147 | 423d65f4 | balrog | |
148 | 423d65f4 | balrog | while (samples) {
|
149 | 423d65f4 | balrog | int written = write_audio (s, samples);
|
150 | 423d65f4 | balrog | if (!written) {
|
151 | 423d65f4 | balrog | break;
|
152 | 423d65f4 | balrog | } |
153 | 423d65f4 | balrog | samples -= written; |
154 | 423d65f4 | balrog | net += written; |
155 | 423d65f4 | balrog | } |
156 | 423d65f4 | balrog | } |
157 | 423d65f4 | balrog | s->left = samples; |
158 | 423d65f4 | balrog | |
159 | 4f4cc0ef | malc | reset:
|
160 | 4f4cc0ef | malc | gus_irqgen (&s->emu, muldiv64 (net, 1000000, s->freq));
|
161 | 423d65f4 | balrog | } |
162 | 423d65f4 | balrog | |
163 | 423d65f4 | balrog | int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n) |
164 | 423d65f4 | balrog | { |
165 | 423d65f4 | balrog | GUSState *s = emu->opaque; |
166 | 9df34396 | Gerd Hoffmann | /* qemu_irq_lower (s->pic); */
|
167 | 9df34396 | Gerd Hoffmann | qemu_irq_raise (s->pic); |
168 | 423d65f4 | balrog | s->irqs += n; |
169 | 423d65f4 | balrog | ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs);
|
170 | 423d65f4 | balrog | return n;
|
171 | 423d65f4 | balrog | } |
172 | 423d65f4 | balrog | |
173 | 423d65f4 | balrog | void GUS_irqclear (GUSEmuState *emu, int hwirq) |
174 | 423d65f4 | balrog | { |
175 | 423d65f4 | balrog | GUSState *s = emu->opaque; |
176 | 423d65f4 | balrog | ldebug ("irqclear %d %d\n", hwirq, s->irqs);
|
177 | 9df34396 | Gerd Hoffmann | qemu_irq_lower (s->pic); |
178 | 423d65f4 | balrog | s->irqs -= 1;
|
179 | 423d65f4 | balrog | #ifdef IRQ_STORM
|
180 | 423d65f4 | balrog | if (s->irqs > 0) { |
181 | 423d65f4 | balrog | qemu_irq_raise (s->pic[hwirq]); |
182 | 423d65f4 | balrog | } |
183 | 423d65f4 | balrog | #endif
|
184 | 423d65f4 | balrog | } |
185 | 423d65f4 | balrog | |
186 | 423d65f4 | balrog | void GUS_dmarequest (GUSEmuState *der)
|
187 | 423d65f4 | balrog | { |
188 | 423d65f4 | balrog | /* GUSState *s = (GUSState *) der; */
|
189 | 423d65f4 | balrog | ldebug ("dma request %d\n", der->gusdma);
|
190 | 423d65f4 | balrog | DMA_hold_DREQ (der->gusdma); |
191 | 423d65f4 | balrog | } |
192 | 423d65f4 | balrog | |
193 | 371bc573 | malc | static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) |
194 | 423d65f4 | balrog | { |
195 | 423d65f4 | balrog | GUSState *s = opaque; |
196 | 731ba0ce | malc | char tmpbuf[4096]; |
197 | 423d65f4 | balrog | int pos = dma_pos, mode, left = dma_len - dma_pos;
|
198 | 423d65f4 | balrog | |
199 | 423d65f4 | balrog | ldebug ("read DMA %#x %d\n", dma_pos, dma_len);
|
200 | 423d65f4 | balrog | mode = DMA_get_channel_mode (s->emu.gusdma); |
201 | 423d65f4 | balrog | while (left) {
|
202 | 423d65f4 | balrog | int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf)); |
203 | 423d65f4 | balrog | int copied;
|
204 | 423d65f4 | balrog | |
205 | 423d65f4 | balrog | ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos);
|
206 | 423d65f4 | balrog | copied = DMA_read_memory (nchan, tmpbuf, pos, to_copy); |
207 | 423d65f4 | balrog | gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied); |
208 | 423d65f4 | balrog | left -= copied; |
209 | 423d65f4 | balrog | pos += copied; |
210 | 423d65f4 | balrog | } |
211 | 423d65f4 | balrog | |
212 | 423d65f4 | balrog | if (0 == ((mode >> 4) & 1)) { |
213 | 423d65f4 | balrog | DMA_release_DREQ (s->emu.gusdma); |
214 | 423d65f4 | balrog | } |
215 | 423d65f4 | balrog | return dma_len;
|
216 | 423d65f4 | balrog | } |
217 | 423d65f4 | balrog | |
218 | 5b5ef0db | malc | static void GUS_save (QEMUFile *f, void *opaque) |
219 | 5b5ef0db | malc | { |
220 | 5b5ef0db | malc | GUSState *s = opaque; |
221 | 5b5ef0db | malc | |
222 | db8baf14 | malc | qemu_put_be32 (f, s->pos); |
223 | db8baf14 | malc | qemu_put_be32 (f, s->left); |
224 | db8baf14 | malc | qemu_put_be32 (f, s->shift); |
225 | db8baf14 | malc | qemu_put_be32 (f, s->irqs); |
226 | db8baf14 | malc | qemu_put_be32 (f, s->samples); |
227 | db8baf14 | malc | qemu_put_be64 (f, s->last_ticks); |
228 | 5b5ef0db | malc | qemu_put_buffer (f, s->himem, sizeof (s->himem));
|
229 | 5b5ef0db | malc | } |
230 | 5b5ef0db | malc | |
231 | 5b5ef0db | malc | static int GUS_load (QEMUFile *f, void *opaque, int version_id) |
232 | 5b5ef0db | malc | { |
233 | 5b5ef0db | malc | GUSState *s = opaque; |
234 | 5b5ef0db | malc | |
235 | 8210169a | malc | if (version_id != 2) |
236 | 5b5ef0db | malc | return -EINVAL;
|
237 | 5b5ef0db | malc | |
238 | db8baf14 | malc | s->pos = qemu_get_be32 (f); |
239 | db8baf14 | malc | s->left = qemu_get_be32 (f); |
240 | db8baf14 | malc | s->shift = qemu_get_be32 (f); |
241 | db8baf14 | malc | s->irqs = qemu_get_be32 (f); |
242 | db8baf14 | malc | s->samples = qemu_get_be32 (f); |
243 | db8baf14 | malc | s->last_ticks = qemu_get_be64 (f); |
244 | 5b5ef0db | malc | qemu_get_buffer (f, s->himem, sizeof (s->himem));
|
245 | 5b5ef0db | malc | return 0; |
246 | 5b5ef0db | malc | } |
247 | 5b5ef0db | malc | |
248 | 9df34396 | Gerd Hoffmann | static int gus_initfn (ISADevice *dev) |
249 | 423d65f4 | balrog | { |
250 | 9df34396 | Gerd Hoffmann | GUSState *s = DO_UPCAST(GUSState, dev, dev); |
251 | 1ea879e5 | malc | struct audsettings as;
|
252 | 423d65f4 | balrog | |
253 | 1a7dafce | malc | AUD_register_card ("gus", &s->card);
|
254 | 423d65f4 | balrog | |
255 | 9df34396 | Gerd Hoffmann | as.freq = s->freq; |
256 | 423d65f4 | balrog | as.nchannels = 2;
|
257 | 423d65f4 | balrog | as.fmt = AUD_FMT_S16; |
258 | 423d65f4 | balrog | as.endianness = GUS_ENDIANNESS; |
259 | 423d65f4 | balrog | |
260 | 423d65f4 | balrog | s->voice = AUD_open_out ( |
261 | 423d65f4 | balrog | &s->card, |
262 | 423d65f4 | balrog | NULL,
|
263 | 423d65f4 | balrog | "gus",
|
264 | 423d65f4 | balrog | s, |
265 | 423d65f4 | balrog | GUS_callback, |
266 | 423d65f4 | balrog | &as |
267 | 423d65f4 | balrog | ); |
268 | 423d65f4 | balrog | |
269 | 423d65f4 | balrog | if (!s->voice) {
|
270 | 423d65f4 | balrog | AUD_remove_card (&s->card); |
271 | 423d65f4 | balrog | return -1; |
272 | 423d65f4 | balrog | } |
273 | 423d65f4 | balrog | |
274 | 423d65f4 | balrog | s->shift = 2;
|
275 | 423d65f4 | balrog | s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift; |
276 | 423d65f4 | balrog | s->mixbuf = qemu_mallocz (s->samples << s->shift); |
277 | 423d65f4 | balrog | |
278 | 9df34396 | Gerd Hoffmann | register_ioport_write (s->port, 1, 1, gus_writeb, s); |
279 | 9df34396 | Gerd Hoffmann | register_ioport_write (s->port, 1, 2, gus_writew, s); |
280 | 423d65f4 | balrog | |
281 | 9df34396 | Gerd Hoffmann | register_ioport_read ((s->port + 0x100) & 0xf00, 1, 1, gus_readb, s); |
282 | 9df34396 | Gerd Hoffmann | register_ioport_read ((s->port + 0x100) & 0xf00, 1, 2, gus_readw, s); |
283 | 423d65f4 | balrog | |
284 | 9df34396 | Gerd Hoffmann | register_ioport_write (s->port + 6, 10, 1, gus_writeb, s); |
285 | 9df34396 | Gerd Hoffmann | register_ioport_write (s->port + 6, 10, 2, gus_writew, s); |
286 | 9df34396 | Gerd Hoffmann | register_ioport_read (s->port + 6, 10, 1, gus_readb, s); |
287 | 9df34396 | Gerd Hoffmann | register_ioport_read (s->port + 6, 10, 2, gus_readw, s); |
288 | 423d65f4 | balrog | |
289 | 423d65f4 | balrog | |
290 | 9df34396 | Gerd Hoffmann | register_ioport_write (s->port + 0x100, 8, 1, gus_writeb, s); |
291 | 9df34396 | Gerd Hoffmann | register_ioport_write (s->port + 0x100, 8, 2, gus_writew, s); |
292 | 9df34396 | Gerd Hoffmann | register_ioport_read (s->port + 0x100, 8, 1, gus_readb, s); |
293 | 9df34396 | Gerd Hoffmann | register_ioport_read (s->port + 0x100, 8, 2, gus_readw, s); |
294 | 423d65f4 | balrog | |
295 | a5e8e46b | malc | DMA_register_channel (s->emu.gusdma, GUS_read_DMA, s); |
296 | 423d65f4 | balrog | s->emu.himemaddr = s->himem; |
297 | 423d65f4 | balrog | s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32; |
298 | 423d65f4 | balrog | s->emu.opaque = s; |
299 | a5e8e46b | malc | isa_init_irq (dev, &s->pic, s->emu.gusirq); |
300 | 423d65f4 | balrog | |
301 | 423d65f4 | balrog | AUD_set_active_out (s->voice, 1);
|
302 | 5b5ef0db | malc | |
303 | 8210169a | malc | register_savevm ("gus", 0, 2, GUS_save, GUS_load, s); |
304 | 423d65f4 | balrog | return 0; |
305 | 423d65f4 | balrog | } |
306 | 9df34396 | Gerd Hoffmann | |
307 | 9df34396 | Gerd Hoffmann | int GUS_init (qemu_irq *pic)
|
308 | 9df34396 | Gerd Hoffmann | { |
309 | a5e8e46b | malc | isa_create_simple ("gus");
|
310 | 9df34396 | Gerd Hoffmann | return 0; |
311 | 9df34396 | Gerd Hoffmann | } |
312 | 9df34396 | Gerd Hoffmann | |
313 | 9df34396 | Gerd Hoffmann | static ISADeviceInfo gus_info = {
|
314 | 9df34396 | Gerd Hoffmann | .qdev.name = "gus",
|
315 | a5e8e46b | malc | .qdev.desc = "Gravis Ultrasound GF1",
|
316 | 9df34396 | Gerd Hoffmann | .qdev.size = sizeof (GUSState),
|
317 | 9df34396 | Gerd Hoffmann | .init = gus_initfn, |
318 | 9df34396 | Gerd Hoffmann | .qdev.props = (Property[]) { |
319 | 9df34396 | Gerd Hoffmann | DEFINE_PROP_UINT32 ("freq", GUSState, freq, 44100), |
320 | 9df34396 | Gerd Hoffmann | DEFINE_PROP_HEX32 ("iobase", GUSState, port, 0x240), |
321 | 9df34396 | Gerd Hoffmann | DEFINE_PROP_UINT32 ("irq", GUSState, emu.gusirq, 7), |
322 | 9df34396 | Gerd Hoffmann | DEFINE_PROP_UINT32 ("dma", GUSState, emu.gusdma, 3), |
323 | 9df34396 | Gerd Hoffmann | DEFINE_PROP_END_OF_LIST (), |
324 | 9df34396 | Gerd Hoffmann | }, |
325 | 9df34396 | Gerd Hoffmann | }; |
326 | 9df34396 | Gerd Hoffmann | |
327 | a5e8e46b | malc | static void gus_register (void) |
328 | 9df34396 | Gerd Hoffmann | { |
329 | a5e8e46b | malc | isa_qdev_register (&gus_info); |
330 | 9df34396 | Gerd Hoffmann | } |
331 | a5e8e46b | malc | device_init (gus_register) |