Statistics
| Branch: | Revision:

root / hw / adlib.c @ e57ec016

History | View | Annotate | Download (7.6 kB)

1
/*
2
 * QEMU Proxy for OPL2/3 emulation by MAME team
3
 *
4
 * Copyright (c) 2004-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 <assert.h>
25
#include "hw.h"
26
#include "audiodev.h"
27

    
28
#define ADLIB_KILL_TIMERS 1
29

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

    
37
#ifdef HAS_YMF262
38
#include "ymf262.h"
39
void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
40
#define SHIFT 2
41
#else
42
#include "fmopl.h"
43
#define SHIFT 1
44
#endif
45

    
46
#define IO_READ_PROTO(name) \
47
    uint32_t name (void *opaque, uint32_t nport)
48
#define IO_WRITE_PROTO(name) \
49
    void name (void *opaque, uint32_t nport, uint32_t val)
50

    
51
static struct {
52
    int port;
53
    int freq;
54
} conf = {0x220, 44100};
55

    
56
typedef struct {
57
    QEMUSoundCard card;
58
    int ticking[2];
59
    int enabled;
60
    int active;
61
    int bufpos;
62
#ifdef DEBUG
63
    int64_t exp[2];
64
#endif
65
    int16_t *mixbuf;
66
    uint64_t dexp[2];
67
    SWVoiceOut *voice;
68
    int left, pos, samples;
69
    QEMUAudioTimeStamp ats;
70
#ifndef HAS_YMF262
71
    FM_OPL *opl;
72
#endif
73
} AdlibState;
74

    
75
static AdlibState glob_adlib;
76

    
77
static void adlib_stop_opl_timer (AdlibState *s, size_t n)
78
{
79
#ifdef HAS_YMF262
80
    YMF262TimerOver (0, n);
81
#else
82
    OPLTimerOver (s->opl, n);
83
#endif
84
    s->ticking[n] = 0;
85
}
86

    
87
static void adlib_kill_timers (AdlibState *s)
88
{
89
    size_t i;
90

    
91
    for (i = 0; i < 2; ++i) {
92
        if (s->ticking[i]) {
93
            uint64_t delta;
94

    
95
            delta = AUD_get_elapsed_usec_out (s->voice, &s->ats);
96
            ldebug (
97
                "delta = %f dexp = %f expired => %d\n",
98
                delta / 1000000.0,
99
                s->dexp[i] / 1000000.0,
100
                delta >= s->dexp[i]
101
                );
102
            if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) {
103
                adlib_stop_opl_timer (s, i);
104
                AUD_init_time_stamp_out (s->voice, &s->ats);
105
            }
106
        }
107
    }
108
}
109

    
110
static IO_WRITE_PROTO(adlib_write)
111
{
112
    AdlibState *s = opaque;
113
    int a = nport & 3;
114
    int status;
115

    
116
    s->active = 1;
117
    AUD_set_active_out (s->voice, 1);
118

    
119
    adlib_kill_timers (s);
120

    
121
#ifdef HAS_YMF262
122
    status = YMF262Write (0, a, val);
123
#else
124
    status = OPLWrite (s->opl, a, val);
125
#endif
126
}
127

    
128
static IO_READ_PROTO(adlib_read)
129
{
130
    AdlibState *s = opaque;
131
    uint8_t data;
132
    int a = nport & 3;
133

    
134
    adlib_kill_timers (s);
135

    
136
#ifdef HAS_YMF262
137
    data = YMF262Read (0, a);
138
#else
139
    data = OPLRead (s->opl, a);
140
#endif
141
    return data;
142
}
143

    
144
static void timer_handler (int c, double interval_Sec)
145
{
146
    AdlibState *s = &glob_adlib;
147
    unsigned n = c & 1;
148
#ifdef DEBUG
149
    double interval;
150
    int64_t exp;
151
#endif
152

    
153
    if (interval_Sec == 0.0) {
154
        s->ticking[n] = 0;
155
        return;
156
    }
157

    
158
    s->ticking[n] = 1;
159
#ifdef DEBUG
160
    interval = ticks_per_sec * interval_Sec;
161
    exp = qemu_get_clock (vm_clock) + interval;
162
    s->exp[n] = exp;
163
#endif
164

    
165
    s->dexp[n] = interval_Sec * 1000000.0;
166
    AUD_init_time_stamp_out (s->voice, &s->ats);
167
}
168

    
169
static int write_audio (AdlibState *s, int samples)
170
{
171
    int net = 0;
172
    int pos = s->pos;
173

    
174
    while (samples) {
175
        int nbytes, wbytes, wsampl;
176

    
177
        nbytes = samples << SHIFT;
178
        wbytes = AUD_write (
179
            s->voice,
180
            s->mixbuf + (pos << (SHIFT - 1)),
181
            nbytes
182
            );
183

    
184
        if (wbytes) {
185
            wsampl = wbytes >> SHIFT;
186

    
187
            samples -= wsampl;
188
            pos = (pos + wsampl) % s->samples;
189

    
190
            net += wsampl;
191
        }
192
        else {
193
            break;
194
        }
195
    }
196

    
197
    return net;
198
}
199

    
200
static void adlib_callback (void *opaque, int free)
201
{
202
    AdlibState *s = opaque;
203
    int samples, net = 0, to_play, written;
204

    
205
    samples = free >> SHIFT;
206
    if (!(s->active && s->enabled) || !samples) {
207
        return;
208
    }
209

    
210
    to_play = audio_MIN (s->left, samples);
211
    while (to_play) {
212
        written = write_audio (s, to_play);
213

    
214
        if (written) {
215
            s->left -= written;
216
            samples -= written;
217
            to_play -= written;
218
            s->pos = (s->pos + written) % s->samples;
219
        }
220
        else {
221
            return;
222
        }
223
    }
224

    
225
    samples = audio_MIN (samples, s->samples - s->pos);
226
    if (!samples) {
227
        return;
228
    }
229

    
230
#ifdef HAS_YMF262
231
    YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
232
#else
233
    YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
234
#endif
235

    
236
    while (samples) {
237
        written = write_audio (s, samples);
238

    
239
        if (written) {
240
            net += written;
241
            samples -= written;
242
            s->pos = (s->pos + written) % s->samples;
243
        }
244
        else {
245
            s->left = samples;
246
            return;
247
        }
248
    }
249
}
250

    
251
static void Adlib_fini (AdlibState *s)
252
{
253
#ifdef HAS_YMF262
254
    YMF262Shutdown ();
255
#else
256
    if (s->opl) {
257
        OPLDestroy (s->opl);
258
        s->opl = NULL;
259
    }
260
#endif
261

    
262
    if (s->mixbuf) {
263
        qemu_free (s->mixbuf);
264
    }
265

    
266
    s->active = 0;
267
    s->enabled = 0;
268
    AUD_remove_card (&s->card);
269
}
270

    
271
int Adlib_init (AudioState *audio, qemu_irq *pic)
272
{
273
    AdlibState *s = &glob_adlib;
274
    audsettings_t as;
275

    
276
    if (!audio) {
277
        dolog ("No audio state\n");
278
        return -1;
279
    }
280

    
281
#ifdef HAS_YMF262
282
    if (YMF262Init (1, 14318180, conf.freq)) {
283
        dolog ("YMF262Init %d failed\n", conf.freq);
284
        return -1;
285
    }
286
    else {
287
        YMF262SetTimerHandler (0, timer_handler, 0);
288
        s->enabled = 1;
289
    }
290
#else
291
    s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq);
292
    if (!s->opl) {
293
        dolog ("OPLCreate %d failed\n", conf.freq);
294
        return -1;
295
    }
296
    else {
297
        OPLSetTimerHandler (s->opl, timer_handler, 0);
298
        s->enabled = 1;
299
    }
300
#endif
301

    
302
    as.freq = conf.freq;
303
    as.nchannels = SHIFT;
304
    as.fmt = AUD_FMT_S16;
305
    as.endianness = AUDIO_HOST_ENDIANNESS;
306

    
307
    AUD_register_card (audio, "adlib", &s->card);
308

    
309
    s->voice = AUD_open_out (
310
        &s->card,
311
        s->voice,
312
        "adlib",
313
        s,
314
        adlib_callback,
315
        &as
316
        );
317
    if (!s->voice) {
318
        Adlib_fini (s);
319
        return -1;
320
    }
321

    
322
    s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
323
    s->mixbuf = qemu_mallocz (s->samples << SHIFT);
324

    
325
    if (!s->mixbuf) {
326
        dolog ("Could not allocate mixing buffer, %d samples (each %d bytes)\n",
327
               s->samples, 1 << SHIFT);
328
        Adlib_fini (s);
329
        return -1;
330
    }
331

    
332
    register_ioport_read (0x388, 4, 1, adlib_read, s);
333
    register_ioport_write (0x388, 4, 1, adlib_write, s);
334

    
335
    register_ioport_read (conf.port, 4, 1, adlib_read, s);
336
    register_ioport_write (conf.port, 4, 1, adlib_write, s);
337

    
338
    register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
339
    register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
340

    
341
    return 0;
342
}