qemu/hw/adlib.c
<<
>>
Prefs
   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
  25#include "hw.h"
  26#include "audiodev.h"
  27#include "audio/audio.h"
  28#include "isa.h"
  29
  30//#define DEBUG
  31
  32#define ADLIB_KILL_TIMERS 1
  33
  34#ifdef DEBUG
  35#include "qemu-timer.h"
  36#endif
  37
  38#define dolog(...) AUD_log ("adlib", __VA_ARGS__)
  39#ifdef DEBUG
  40#define ldebug(...) dolog (__VA_ARGS__)
  41#else
  42#define ldebug(...)
  43#endif
  44
  45#ifdef HAS_YMF262
  46#include "ymf262.h"
  47void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
  48#define SHIFT 2
  49#else
  50#include "fmopl.h"
  51#define SHIFT 1
  52#endif
  53
  54#define IO_READ_PROTO(name) \
  55    uint32_t name (void *opaque, uint32_t nport)
  56#define IO_WRITE_PROTO(name) \
  57    void name (void *opaque, uint32_t nport, uint32_t val)
  58
  59static struct {
  60    int port;
  61    int freq;
  62} conf = {0x220, 44100};
  63
  64typedef struct {
  65    QEMUSoundCard card;
  66    int ticking[2];
  67    int enabled;
  68    int active;
  69    int bufpos;
  70#ifdef DEBUG
  71    int64_t exp[2];
  72#endif
  73    int16_t *mixbuf;
  74    uint64_t dexp[2];
  75    SWVoiceOut *voice;
  76    int left, pos, samples;
  77    QEMUAudioTimeStamp ats;
  78#ifndef HAS_YMF262
  79    FM_OPL *opl;
  80#endif
  81} AdlibState;
  82
  83static AdlibState glob_adlib;
  84
  85static void adlib_stop_opl_timer (AdlibState *s, size_t n)
  86{
  87#ifdef HAS_YMF262
  88    YMF262TimerOver (0, n);
  89#else
  90    OPLTimerOver (s->opl, n);
  91#endif
  92    s->ticking[n] = 0;
  93}
  94
  95static void adlib_kill_timers (AdlibState *s)
  96{
  97    size_t i;
  98
  99    for (i = 0; i < 2; ++i) {
 100        if (s->ticking[i]) {
 101            uint64_t delta;
 102
 103            delta = AUD_get_elapsed_usec_out (s->voice, &s->ats);
 104            ldebug (
 105                "delta = %f dexp = %f expired => %d\n",
 106                delta / 1000000.0,
 107                s->dexp[i] / 1000000.0,
 108                delta >= s->dexp[i]
 109                );
 110            if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) {
 111                adlib_stop_opl_timer (s, i);
 112                AUD_init_time_stamp_out (s->voice, &s->ats);
 113            }
 114        }
 115    }
 116}
 117
 118static IO_WRITE_PROTO (adlib_write)
 119{
 120    AdlibState *s = opaque;
 121    int a = nport & 3;
 122
 123    s->active = 1;
 124    AUD_set_active_out (s->voice, 1);
 125
 126    adlib_kill_timers (s);
 127
 128#ifdef HAS_YMF262
 129    YMF262Write (0, a, val);
 130#else
 131    OPLWrite (s->opl, a, val);
 132#endif
 133}
 134
 135static IO_READ_PROTO (adlib_read)
 136{
 137    AdlibState *s = opaque;
 138    uint8_t data;
 139    int a = nport & 3;
 140
 141    adlib_kill_timers (s);
 142
 143#ifdef HAS_YMF262
 144    data = YMF262Read (0, a);
 145#else
 146    data = OPLRead (s->opl, a);
 147#endif
 148    return data;
 149}
 150
 151static void timer_handler (int c, double interval_Sec)
 152{
 153    AdlibState *s = &glob_adlib;
 154    unsigned n = c & 1;
 155#ifdef DEBUG
 156    double interval;
 157    int64_t exp;
 158#endif
 159
 160    if (interval_Sec == 0.0) {
 161        s->ticking[n] = 0;
 162        return;
 163    }
 164
 165    s->ticking[n] = 1;
 166#ifdef DEBUG
 167    interval = get_ticks_per_sec () * interval_Sec;
 168    exp = qemu_get_clock_ns (vm_clock) + interval;
 169    s->exp[n] = exp;
 170#endif
 171
 172    s->dexp[n] = interval_Sec * 1000000.0;
 173    AUD_init_time_stamp_out (s->voice, &s->ats);
 174}
 175
 176static int write_audio (AdlibState *s, int samples)
 177{
 178    int net = 0;
 179    int pos = s->pos;
 180
 181    while (samples) {
 182        int nbytes, wbytes, wsampl;
 183
 184        nbytes = samples << SHIFT;
 185        wbytes = AUD_write (
 186            s->voice,
 187            s->mixbuf + (pos << (SHIFT - 1)),
 188            nbytes
 189            );
 190
 191        if (wbytes) {
 192            wsampl = wbytes >> SHIFT;
 193
 194            samples -= wsampl;
 195            pos = (pos + wsampl) % s->samples;
 196
 197            net += wsampl;
 198        }
 199        else {
 200            break;
 201        }
 202    }
 203
 204    return net;
 205}
 206
 207static void adlib_callback (void *opaque, int free)
 208{
 209    AdlibState *s = opaque;
 210    int samples, net = 0, to_play, written;
 211
 212    samples = free >> SHIFT;
 213    if (!(s->active && s->enabled) || !samples) {
 214        return;
 215    }
 216
 217    to_play = audio_MIN (s->left, samples);
 218    while (to_play) {
 219        written = write_audio (s, to_play);
 220
 221        if (written) {
 222            s->left -= written;
 223            samples -= written;
 224            to_play -= written;
 225            s->pos = (s->pos + written) % s->samples;
 226        }
 227        else {
 228            return;
 229        }
 230    }
 231
 232    samples = audio_MIN (samples, s->samples - s->pos);
 233    if (!samples) {
 234        return;
 235    }
 236
 237#ifdef HAS_YMF262
 238    YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
 239#else
 240    YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
 241#endif
 242
 243    while (samples) {
 244        written = write_audio (s, samples);
 245
 246        if (written) {
 247            net += written;
 248            samples -= written;
 249            s->pos = (s->pos + written) % s->samples;
 250        }
 251        else {
 252            s->left = samples;
 253            return;
 254        }
 255    }
 256}
 257
 258static void Adlib_fini (AdlibState *s)
 259{
 260#ifdef HAS_YMF262
 261    YMF262Shutdown ();
 262#else
 263    if (s->opl) {
 264        OPLDestroy (s->opl);
 265        s->opl = NULL;
 266    }
 267#endif
 268
 269    if (s->mixbuf) {
 270        g_free (s->mixbuf);
 271    }
 272
 273    s->active = 0;
 274    s->enabled = 0;
 275    AUD_remove_card (&s->card);
 276}
 277
 278int Adlib_init (ISABus *bus)
 279{
 280    AdlibState *s = &glob_adlib;
 281    struct audsettings as;
 282
 283#ifdef HAS_YMF262
 284    if (YMF262Init (1, 14318180, conf.freq)) {
 285        dolog ("YMF262Init %d failed\n", conf.freq);
 286        return -1;
 287    }
 288    else {
 289        YMF262SetTimerHandler (0, timer_handler, 0);
 290        s->enabled = 1;
 291    }
 292#else
 293    s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq);
 294    if (!s->opl) {
 295        dolog ("OPLCreate %d failed\n", conf.freq);
 296        return -1;
 297    }
 298    else {
 299        OPLSetTimerHandler (s->opl, timer_handler, 0);
 300        s->enabled = 1;
 301    }
 302#endif
 303
 304    as.freq = conf.freq;
 305    as.nchannels = SHIFT;
 306    as.fmt = AUD_FMT_S16;
 307    as.endianness = AUDIO_HOST_ENDIANNESS;
 308
 309    AUD_register_card ("adlib", &s->card);
 310
 311    s->voice = AUD_open_out (
 312        &s->card,
 313        s->voice,
 314        "adlib",
 315        s,
 316        adlib_callback,
 317        &as
 318        );
 319    if (!s->voice) {
 320        Adlib_fini (s);
 321        return -1;
 322    }
 323
 324    s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
 325    s->mixbuf = g_malloc0 (s->samples << SHIFT);
 326
 327    register_ioport_read (0x388, 4, 1, adlib_read, s);
 328    register_ioport_write (0x388, 4, 1, adlib_write, s);
 329
 330    register_ioport_read (conf.port, 4, 1, adlib_read, s);
 331    register_ioport_write (conf.port, 4, 1, adlib_write, s);
 332
 333    register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
 334    register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
 335
 336    return 0;
 337}
 338