Statistics
| Branch: | Revision:

root / hw / audio / gus.c @ db895a1e

History | View | Annotate | Download (8.5 kB)

1
/*
2
 * QEMU Proxy for Gravis Ultrasound GF1 emulation by Tibor "TS" Schütz
3
 *
4
 * Copyright (c) 2002-2005 Vassili Karpov (malc)
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
#include "hw/hw.h"
25
#include "hw/audio/audio.h"
26
#include "audio/audio.h"
27
#include "hw/isa/isa.h"
28
#include "gusemu.h"
29
#include "gustate.h"
30

    
31
#define dolog(...) AUD_log ("audio", __VA_ARGS__)
32
#ifdef DEBUG
33
#define ldebug(...) dolog (__VA_ARGS__)
34
#else
35
#define ldebug(...)
36
#endif
37

    
38
#ifdef HOST_WORDS_BIGENDIAN
39
#define GUS_ENDIANNESS 1
40
#else
41
#define GUS_ENDIANNESS 0
42
#endif
43

    
44
#define IO_READ_PROTO(name) \
45
    static uint32_t name (void *opaque, uint32_t nport)
46
#define IO_WRITE_PROTO(name) \
47
    static void name (void *opaque, uint32_t nport, uint32_t val)
48

    
49
#define TYPE_GUS "gus"
50
#define GUS(obj) OBJECT_CHECK (GUSState, (obj), TYPE_GUS)
51

    
52
typedef struct GUSState {
53
    ISADevice dev;
54
    GUSEmuState emu;
55
    QEMUSoundCard card;
56
    uint32_t freq;
57
    uint32_t port;
58
    int pos, left, shift, irqs;
59
    GUSsample *mixbuf;
60
    uint8_t himem[1024 * 1024 + 32 + 4096];
61
    int samples;
62
    SWVoiceOut *voice;
63
    int64_t last_ticks;
64
    qemu_irq pic;
65
} GUSState;
66

    
67
IO_READ_PROTO (gus_readb)
68
{
69
    GUSState *s = opaque;
70

    
71
    return gus_read (&s->emu, nport, 1);
72
}
73

    
74
IO_READ_PROTO (gus_readw)
75
{
76
    GUSState *s = opaque;
77

    
78
    return gus_read (&s->emu, nport, 2);
79
}
80

    
81
IO_WRITE_PROTO (gus_writeb)
82
{
83
    GUSState *s = opaque;
84

    
85
    gus_write (&s->emu, nport, 1, val);
86
}
87

    
88
IO_WRITE_PROTO (gus_writew)
89
{
90
    GUSState *s = opaque;
91

    
92
    gus_write (&s->emu, nport, 2, val);
93
}
94

    
95
static int write_audio (GUSState *s, int samples)
96
{
97
    int net = 0;
98
    int pos = s->pos;
99

    
100
    while (samples) {
101
        int nbytes, wbytes, wsampl;
102

    
103
        nbytes = samples << s->shift;
104
        wbytes = AUD_write (
105
            s->voice,
106
            s->mixbuf + (pos << (s->shift - 1)),
107
            nbytes
108
            );
109

    
110
        if (wbytes) {
111
            wsampl = wbytes >> s->shift;
112

    
113
            samples -= wsampl;
114
            pos = (pos + wsampl) % s->samples;
115

    
116
            net += wsampl;
117
        }
118
        else {
119
            break;
120
        }
121
    }
122

    
123
    return net;
124
}
125

    
126
static void GUS_callback (void *opaque, int free)
127
{
128
    int samples, to_play, net = 0;
129
    GUSState *s = opaque;
130

    
131
    samples = free >> s->shift;
132
    to_play = audio_MIN (samples, s->left);
133

    
134
    while (to_play) {
135
        int written = write_audio (s, to_play);
136

    
137
        if (!written) {
138
            goto reset;
139
        }
140

    
141
        s->left -= written;
142
        to_play -= written;
143
        samples -= written;
144
        net += written;
145
    }
146

    
147
    samples = audio_MIN (samples, s->samples);
148
    if (samples) {
149
        gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf);
150

    
151
        while (samples) {
152
            int written = write_audio (s, samples);
153
            if (!written) {
154
                break;
155
            }
156
            samples -= written;
157
            net += written;
158
        }
159
    }
160
    s->left = samples;
161

    
162
 reset:
163
    gus_irqgen (&s->emu, muldiv64 (net, 1000000, s->freq));
164
}
165

    
166
int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n)
167
{
168
    GUSState *s = emu->opaque;
169
    /* qemu_irq_lower (s->pic); */
170
    qemu_irq_raise (s->pic);
171
    s->irqs += n;
172
    ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs);
173
    return n;
174
}
175

    
176
void GUS_irqclear (GUSEmuState *emu, int hwirq)
177
{
178
    GUSState *s = emu->opaque;
179
    ldebug ("irqclear %d %d\n", hwirq, s->irqs);
180
    qemu_irq_lower (s->pic);
181
    s->irqs -= 1;
182
#ifdef IRQ_STORM
183
    if (s->irqs > 0) {
184
        qemu_irq_raise (s->pic[hwirq]);
185
    }
186
#endif
187
}
188

    
189
void GUS_dmarequest (GUSEmuState *der)
190
{
191
    /* GUSState *s = (GUSState *) der; */
192
    ldebug ("dma request %d\n", der->gusdma);
193
    DMA_hold_DREQ (der->gusdma);
194
}
195

    
196
static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
197
{
198
    GUSState *s = opaque;
199
    char tmpbuf[4096];
200
    int pos = dma_pos, mode, left = dma_len - dma_pos;
201

    
202
    ldebug ("read DMA %#x %d\n", dma_pos, dma_len);
203
    mode = DMA_get_channel_mode (s->emu.gusdma);
204
    while (left) {
205
        int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf));
206
        int copied;
207

    
208
        ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos);
209
        copied = DMA_read_memory (nchan, tmpbuf, pos, to_copy);
210
        gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied);
211
        left -= copied;
212
        pos += copied;
213
    }
214

    
215
    if (0 == ((mode >> 4) & 1)) {
216
        DMA_release_DREQ (s->emu.gusdma);
217
    }
218
    return dma_len;
219
}
220

    
221
static const VMStateDescription vmstate_gus = {
222
    .name = "gus",
223
    .version_id = 2,
224
    .minimum_version_id = 2,
225
    .minimum_version_id_old = 2,
226
    .fields      = (VMStateField []) {
227
        VMSTATE_INT32 (pos, GUSState),
228
        VMSTATE_INT32 (left, GUSState),
229
        VMSTATE_INT32 (shift, GUSState),
230
        VMSTATE_INT32 (irqs, GUSState),
231
        VMSTATE_INT32 (samples, GUSState),
232
        VMSTATE_INT64 (last_ticks, GUSState),
233
        VMSTATE_BUFFER (himem, GUSState),
234
        VMSTATE_END_OF_LIST ()
235
    }
236
};
237

    
238
static const MemoryRegionPortio gus_portio_list1[] = {
239
    {0x000,  1, 1, .write = gus_writeb },
240
    {0x000,  1, 2, .write = gus_writew },
241
    {0x006, 10, 1, .read = gus_readb, .write = gus_writeb },
242
    {0x006, 10, 2, .read = gus_readw, .write = gus_writew },
243
    {0x100,  8, 1, .read = gus_readb, .write = gus_writeb },
244
    {0x100,  8, 2, .read = gus_readw, .write = gus_writew },
245
    PORTIO_END_OF_LIST (),
246
};
247

    
248
static const MemoryRegionPortio gus_portio_list2[] = {
249
    {0, 1, 1, .read = gus_readb },
250
    {0, 1, 2, .read = gus_readw },
251
    PORTIO_END_OF_LIST (),
252
};
253

    
254
static void gus_realizefn (DeviceState *dev, Error **errp)
255
{
256
    ISADevice *d = ISA_DEVICE(dev);
257
    GUSState *s = GUS (dev);
258
    struct audsettings as;
259

    
260
    AUD_register_card ("gus", &s->card);
261

    
262
    as.freq = s->freq;
263
    as.nchannels = 2;
264
    as.fmt = AUD_FMT_S16;
265
    as.endianness = GUS_ENDIANNESS;
266

    
267
    s->voice = AUD_open_out (
268
        &s->card,
269
        NULL,
270
        "gus",
271
        s,
272
        GUS_callback,
273
        &as
274
        );
275

    
276
    if (!s->voice) {
277
        AUD_remove_card (&s->card);
278
        error_setg(errp, "No voice");
279
        return;
280
    }
281

    
282
    s->shift = 2;
283
    s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift;
284
    s->mixbuf = g_malloc0 (s->samples << s->shift);
285

    
286
    isa_register_portio_list (d, s->port, gus_portio_list1, s, "gus");
287
    isa_register_portio_list (d, (s->port + 0x100) & 0xf00,
288
                              gus_portio_list2, s, "gus");
289

    
290
    DMA_register_channel (s->emu.gusdma, GUS_read_DMA, s);
291
    s->emu.himemaddr = s->himem;
292
    s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32;
293
    s->emu.opaque = s;
294
    isa_init_irq (d, &s->pic, s->emu.gusirq);
295

    
296
    AUD_set_active_out (s->voice, 1);
297
}
298

    
299
static int GUS_init (ISABus *bus)
300
{
301
    isa_create_simple (bus, TYPE_GUS);
302
    return 0;
303
}
304

    
305
static Property gus_properties[] = {
306
    DEFINE_PROP_UINT32 ("freq",    GUSState, freq,        44100),
307
    DEFINE_PROP_HEX32  ("iobase",  GUSState, port,        0x240),
308
    DEFINE_PROP_UINT32 ("irq",     GUSState, emu.gusirq,  7),
309
    DEFINE_PROP_UINT32 ("dma",     GUSState, emu.gusdma,  3),
310
    DEFINE_PROP_END_OF_LIST (),
311
};
312

    
313
static void gus_class_initfn (ObjectClass *klass, void *data)
314
{
315
    DeviceClass *dc = DEVICE_CLASS (klass);
316

    
317
    dc->realize = gus_realizefn;
318
    dc->desc = "Gravis Ultrasound GF1";
319
    dc->vmsd = &vmstate_gus;
320
    dc->props = gus_properties;
321
}
322

    
323
static const TypeInfo gus_info = {
324
    .name          = TYPE_GUS,
325
    .parent        = TYPE_ISA_DEVICE,
326
    .instance_size = sizeof (GUSState),
327
    .class_init    = gus_class_initfn,
328
};
329

    
330
static void gus_register_types (void)
331
{
332
    type_register_static (&gus_info);
333
    isa_register_soundhw("gus", "Gravis Ultrasound GF1", GUS_init);
334
}
335

    
336
type_init (gus_register_types)