1*49ab747fSPaolo Bonzini /* 2*49ab747fSPaolo Bonzini * QEMU Proxy for OPL2/3 emulation by MAME team 3*49ab747fSPaolo Bonzini * 4*49ab747fSPaolo Bonzini * Copyright (c) 2004-2005 Vassili Karpov (malc) 5*49ab747fSPaolo Bonzini * 6*49ab747fSPaolo Bonzini * Permission is hereby granted, free of charge, to any person obtaining a copy 7*49ab747fSPaolo Bonzini * of this software and associated documentation files (the "Software"), to deal 8*49ab747fSPaolo Bonzini * in the Software without restriction, including without limitation the rights 9*49ab747fSPaolo Bonzini * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10*49ab747fSPaolo Bonzini * copies of the Software, and to permit persons to whom the Software is 11*49ab747fSPaolo Bonzini * furnished to do so, subject to the following conditions: 12*49ab747fSPaolo Bonzini * 13*49ab747fSPaolo Bonzini * The above copyright notice and this permission notice shall be included in 14*49ab747fSPaolo Bonzini * all copies or substantial portions of the Software. 15*49ab747fSPaolo Bonzini * 16*49ab747fSPaolo Bonzini * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17*49ab747fSPaolo Bonzini * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18*49ab747fSPaolo Bonzini * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19*49ab747fSPaolo Bonzini * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20*49ab747fSPaolo Bonzini * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21*49ab747fSPaolo Bonzini * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22*49ab747fSPaolo Bonzini * THE SOFTWARE. 23*49ab747fSPaolo Bonzini */ 24*49ab747fSPaolo Bonzini 25*49ab747fSPaolo Bonzini #include "hw/hw.h" 26*49ab747fSPaolo Bonzini #include "hw/audio/audio.h" 27*49ab747fSPaolo Bonzini #include "audio/audio.h" 28*49ab747fSPaolo Bonzini #include "hw/isa/isa.h" 29*49ab747fSPaolo Bonzini 30*49ab747fSPaolo Bonzini //#define DEBUG 31*49ab747fSPaolo Bonzini 32*49ab747fSPaolo Bonzini #define ADLIB_KILL_TIMERS 1 33*49ab747fSPaolo Bonzini 34*49ab747fSPaolo Bonzini #ifdef DEBUG 35*49ab747fSPaolo Bonzini #include "qemu/timer.h" 36*49ab747fSPaolo Bonzini #endif 37*49ab747fSPaolo Bonzini 38*49ab747fSPaolo Bonzini #define dolog(...) AUD_log ("adlib", __VA_ARGS__) 39*49ab747fSPaolo Bonzini #ifdef DEBUG 40*49ab747fSPaolo Bonzini #define ldebug(...) dolog (__VA_ARGS__) 41*49ab747fSPaolo Bonzini #else 42*49ab747fSPaolo Bonzini #define ldebug(...) 43*49ab747fSPaolo Bonzini #endif 44*49ab747fSPaolo Bonzini 45*49ab747fSPaolo Bonzini #ifdef HAS_YMF262 46*49ab747fSPaolo Bonzini #include "ymf262.h" 47*49ab747fSPaolo Bonzini void YMF262UpdateOneQEMU (int which, INT16 *dst, int length); 48*49ab747fSPaolo Bonzini #define SHIFT 2 49*49ab747fSPaolo Bonzini #else 50*49ab747fSPaolo Bonzini #include "hw/fmopl.h" 51*49ab747fSPaolo Bonzini #define SHIFT 1 52*49ab747fSPaolo Bonzini #endif 53*49ab747fSPaolo Bonzini 54*49ab747fSPaolo Bonzini #define IO_READ_PROTO(name) \ 55*49ab747fSPaolo Bonzini uint32_t name (void *opaque, uint32_t nport) 56*49ab747fSPaolo Bonzini #define IO_WRITE_PROTO(name) \ 57*49ab747fSPaolo Bonzini void name (void *opaque, uint32_t nport, uint32_t val) 58*49ab747fSPaolo Bonzini 59*49ab747fSPaolo Bonzini static struct { 60*49ab747fSPaolo Bonzini int port; 61*49ab747fSPaolo Bonzini int freq; 62*49ab747fSPaolo Bonzini } conf = {0x220, 44100}; 63*49ab747fSPaolo Bonzini 64*49ab747fSPaolo Bonzini typedef struct { 65*49ab747fSPaolo Bonzini QEMUSoundCard card; 66*49ab747fSPaolo Bonzini int ticking[2]; 67*49ab747fSPaolo Bonzini int enabled; 68*49ab747fSPaolo Bonzini int active; 69*49ab747fSPaolo Bonzini int bufpos; 70*49ab747fSPaolo Bonzini #ifdef DEBUG 71*49ab747fSPaolo Bonzini int64_t exp[2]; 72*49ab747fSPaolo Bonzini #endif 73*49ab747fSPaolo Bonzini int16_t *mixbuf; 74*49ab747fSPaolo Bonzini uint64_t dexp[2]; 75*49ab747fSPaolo Bonzini SWVoiceOut *voice; 76*49ab747fSPaolo Bonzini int left, pos, samples; 77*49ab747fSPaolo Bonzini QEMUAudioTimeStamp ats; 78*49ab747fSPaolo Bonzini #ifndef HAS_YMF262 79*49ab747fSPaolo Bonzini FM_OPL *opl; 80*49ab747fSPaolo Bonzini #endif 81*49ab747fSPaolo Bonzini } AdlibState; 82*49ab747fSPaolo Bonzini 83*49ab747fSPaolo Bonzini static AdlibState glob_adlib; 84*49ab747fSPaolo Bonzini 85*49ab747fSPaolo Bonzini static void adlib_stop_opl_timer (AdlibState *s, size_t n) 86*49ab747fSPaolo Bonzini { 87*49ab747fSPaolo Bonzini #ifdef HAS_YMF262 88*49ab747fSPaolo Bonzini YMF262TimerOver (0, n); 89*49ab747fSPaolo Bonzini #else 90*49ab747fSPaolo Bonzini OPLTimerOver (s->opl, n); 91*49ab747fSPaolo Bonzini #endif 92*49ab747fSPaolo Bonzini s->ticking[n] = 0; 93*49ab747fSPaolo Bonzini } 94*49ab747fSPaolo Bonzini 95*49ab747fSPaolo Bonzini static void adlib_kill_timers (AdlibState *s) 96*49ab747fSPaolo Bonzini { 97*49ab747fSPaolo Bonzini size_t i; 98*49ab747fSPaolo Bonzini 99*49ab747fSPaolo Bonzini for (i = 0; i < 2; ++i) { 100*49ab747fSPaolo Bonzini if (s->ticking[i]) { 101*49ab747fSPaolo Bonzini uint64_t delta; 102*49ab747fSPaolo Bonzini 103*49ab747fSPaolo Bonzini delta = AUD_get_elapsed_usec_out (s->voice, &s->ats); 104*49ab747fSPaolo Bonzini ldebug ( 105*49ab747fSPaolo Bonzini "delta = %f dexp = %f expired => %d\n", 106*49ab747fSPaolo Bonzini delta / 1000000.0, 107*49ab747fSPaolo Bonzini s->dexp[i] / 1000000.0, 108*49ab747fSPaolo Bonzini delta >= s->dexp[i] 109*49ab747fSPaolo Bonzini ); 110*49ab747fSPaolo Bonzini if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) { 111*49ab747fSPaolo Bonzini adlib_stop_opl_timer (s, i); 112*49ab747fSPaolo Bonzini AUD_init_time_stamp_out (s->voice, &s->ats); 113*49ab747fSPaolo Bonzini } 114*49ab747fSPaolo Bonzini } 115*49ab747fSPaolo Bonzini } 116*49ab747fSPaolo Bonzini } 117*49ab747fSPaolo Bonzini 118*49ab747fSPaolo Bonzini static IO_WRITE_PROTO (adlib_write) 119*49ab747fSPaolo Bonzini { 120*49ab747fSPaolo Bonzini AdlibState *s = opaque; 121*49ab747fSPaolo Bonzini int a = nport & 3; 122*49ab747fSPaolo Bonzini 123*49ab747fSPaolo Bonzini s->active = 1; 124*49ab747fSPaolo Bonzini AUD_set_active_out (s->voice, 1); 125*49ab747fSPaolo Bonzini 126*49ab747fSPaolo Bonzini adlib_kill_timers (s); 127*49ab747fSPaolo Bonzini 128*49ab747fSPaolo Bonzini #ifdef HAS_YMF262 129*49ab747fSPaolo Bonzini YMF262Write (0, a, val); 130*49ab747fSPaolo Bonzini #else 131*49ab747fSPaolo Bonzini OPLWrite (s->opl, a, val); 132*49ab747fSPaolo Bonzini #endif 133*49ab747fSPaolo Bonzini } 134*49ab747fSPaolo Bonzini 135*49ab747fSPaolo Bonzini static IO_READ_PROTO (adlib_read) 136*49ab747fSPaolo Bonzini { 137*49ab747fSPaolo Bonzini AdlibState *s = opaque; 138*49ab747fSPaolo Bonzini uint8_t data; 139*49ab747fSPaolo Bonzini int a = nport & 3; 140*49ab747fSPaolo Bonzini 141*49ab747fSPaolo Bonzini adlib_kill_timers (s); 142*49ab747fSPaolo Bonzini 143*49ab747fSPaolo Bonzini #ifdef HAS_YMF262 144*49ab747fSPaolo Bonzini data = YMF262Read (0, a); 145*49ab747fSPaolo Bonzini #else 146*49ab747fSPaolo Bonzini data = OPLRead (s->opl, a); 147*49ab747fSPaolo Bonzini #endif 148*49ab747fSPaolo Bonzini return data; 149*49ab747fSPaolo Bonzini } 150*49ab747fSPaolo Bonzini 151*49ab747fSPaolo Bonzini static void timer_handler (int c, double interval_Sec) 152*49ab747fSPaolo Bonzini { 153*49ab747fSPaolo Bonzini AdlibState *s = &glob_adlib; 154*49ab747fSPaolo Bonzini unsigned n = c & 1; 155*49ab747fSPaolo Bonzini #ifdef DEBUG 156*49ab747fSPaolo Bonzini double interval; 157*49ab747fSPaolo Bonzini int64_t exp; 158*49ab747fSPaolo Bonzini #endif 159*49ab747fSPaolo Bonzini 160*49ab747fSPaolo Bonzini if (interval_Sec == 0.0) { 161*49ab747fSPaolo Bonzini s->ticking[n] = 0; 162*49ab747fSPaolo Bonzini return; 163*49ab747fSPaolo Bonzini } 164*49ab747fSPaolo Bonzini 165*49ab747fSPaolo Bonzini s->ticking[n] = 1; 166*49ab747fSPaolo Bonzini #ifdef DEBUG 167*49ab747fSPaolo Bonzini interval = get_ticks_per_sec () * interval_Sec; 168*49ab747fSPaolo Bonzini exp = qemu_get_clock_ns (vm_clock) + interval; 169*49ab747fSPaolo Bonzini s->exp[n] = exp; 170*49ab747fSPaolo Bonzini #endif 171*49ab747fSPaolo Bonzini 172*49ab747fSPaolo Bonzini s->dexp[n] = interval_Sec * 1000000.0; 173*49ab747fSPaolo Bonzini AUD_init_time_stamp_out (s->voice, &s->ats); 174*49ab747fSPaolo Bonzini } 175*49ab747fSPaolo Bonzini 176*49ab747fSPaolo Bonzini static int write_audio (AdlibState *s, int samples) 177*49ab747fSPaolo Bonzini { 178*49ab747fSPaolo Bonzini int net = 0; 179*49ab747fSPaolo Bonzini int pos = s->pos; 180*49ab747fSPaolo Bonzini 181*49ab747fSPaolo Bonzini while (samples) { 182*49ab747fSPaolo Bonzini int nbytes, wbytes, wsampl; 183*49ab747fSPaolo Bonzini 184*49ab747fSPaolo Bonzini nbytes = samples << SHIFT; 185*49ab747fSPaolo Bonzini wbytes = AUD_write ( 186*49ab747fSPaolo Bonzini s->voice, 187*49ab747fSPaolo Bonzini s->mixbuf + (pos << (SHIFT - 1)), 188*49ab747fSPaolo Bonzini nbytes 189*49ab747fSPaolo Bonzini ); 190*49ab747fSPaolo Bonzini 191*49ab747fSPaolo Bonzini if (wbytes) { 192*49ab747fSPaolo Bonzini wsampl = wbytes >> SHIFT; 193*49ab747fSPaolo Bonzini 194*49ab747fSPaolo Bonzini samples -= wsampl; 195*49ab747fSPaolo Bonzini pos = (pos + wsampl) % s->samples; 196*49ab747fSPaolo Bonzini 197*49ab747fSPaolo Bonzini net += wsampl; 198*49ab747fSPaolo Bonzini } 199*49ab747fSPaolo Bonzini else { 200*49ab747fSPaolo Bonzini break; 201*49ab747fSPaolo Bonzini } 202*49ab747fSPaolo Bonzini } 203*49ab747fSPaolo Bonzini 204*49ab747fSPaolo Bonzini return net; 205*49ab747fSPaolo Bonzini } 206*49ab747fSPaolo Bonzini 207*49ab747fSPaolo Bonzini static void adlib_callback (void *opaque, int free) 208*49ab747fSPaolo Bonzini { 209*49ab747fSPaolo Bonzini AdlibState *s = opaque; 210*49ab747fSPaolo Bonzini int samples, net = 0, to_play, written; 211*49ab747fSPaolo Bonzini 212*49ab747fSPaolo Bonzini samples = free >> SHIFT; 213*49ab747fSPaolo Bonzini if (!(s->active && s->enabled) || !samples) { 214*49ab747fSPaolo Bonzini return; 215*49ab747fSPaolo Bonzini } 216*49ab747fSPaolo Bonzini 217*49ab747fSPaolo Bonzini to_play = audio_MIN (s->left, samples); 218*49ab747fSPaolo Bonzini while (to_play) { 219*49ab747fSPaolo Bonzini written = write_audio (s, to_play); 220*49ab747fSPaolo Bonzini 221*49ab747fSPaolo Bonzini if (written) { 222*49ab747fSPaolo Bonzini s->left -= written; 223*49ab747fSPaolo Bonzini samples -= written; 224*49ab747fSPaolo Bonzini to_play -= written; 225*49ab747fSPaolo Bonzini s->pos = (s->pos + written) % s->samples; 226*49ab747fSPaolo Bonzini } 227*49ab747fSPaolo Bonzini else { 228*49ab747fSPaolo Bonzini return; 229*49ab747fSPaolo Bonzini } 230*49ab747fSPaolo Bonzini } 231*49ab747fSPaolo Bonzini 232*49ab747fSPaolo Bonzini samples = audio_MIN (samples, s->samples - s->pos); 233*49ab747fSPaolo Bonzini if (!samples) { 234*49ab747fSPaolo Bonzini return; 235*49ab747fSPaolo Bonzini } 236*49ab747fSPaolo Bonzini 237*49ab747fSPaolo Bonzini #ifdef HAS_YMF262 238*49ab747fSPaolo Bonzini YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples); 239*49ab747fSPaolo Bonzini #else 240*49ab747fSPaolo Bonzini YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples); 241*49ab747fSPaolo Bonzini #endif 242*49ab747fSPaolo Bonzini 243*49ab747fSPaolo Bonzini while (samples) { 244*49ab747fSPaolo Bonzini written = write_audio (s, samples); 245*49ab747fSPaolo Bonzini 246*49ab747fSPaolo Bonzini if (written) { 247*49ab747fSPaolo Bonzini net += written; 248*49ab747fSPaolo Bonzini samples -= written; 249*49ab747fSPaolo Bonzini s->pos = (s->pos + written) % s->samples; 250*49ab747fSPaolo Bonzini } 251*49ab747fSPaolo Bonzini else { 252*49ab747fSPaolo Bonzini s->left = samples; 253*49ab747fSPaolo Bonzini return; 254*49ab747fSPaolo Bonzini } 255*49ab747fSPaolo Bonzini } 256*49ab747fSPaolo Bonzini } 257*49ab747fSPaolo Bonzini 258*49ab747fSPaolo Bonzini static void Adlib_fini (AdlibState *s) 259*49ab747fSPaolo Bonzini { 260*49ab747fSPaolo Bonzini #ifdef HAS_YMF262 261*49ab747fSPaolo Bonzini YMF262Shutdown (); 262*49ab747fSPaolo Bonzini #else 263*49ab747fSPaolo Bonzini if (s->opl) { 264*49ab747fSPaolo Bonzini OPLDestroy (s->opl); 265*49ab747fSPaolo Bonzini s->opl = NULL; 266*49ab747fSPaolo Bonzini } 267*49ab747fSPaolo Bonzini #endif 268*49ab747fSPaolo Bonzini 269*49ab747fSPaolo Bonzini if (s->mixbuf) { 270*49ab747fSPaolo Bonzini g_free (s->mixbuf); 271*49ab747fSPaolo Bonzini } 272*49ab747fSPaolo Bonzini 273*49ab747fSPaolo Bonzini s->active = 0; 274*49ab747fSPaolo Bonzini s->enabled = 0; 275*49ab747fSPaolo Bonzini AUD_remove_card (&s->card); 276*49ab747fSPaolo Bonzini } 277*49ab747fSPaolo Bonzini 278*49ab747fSPaolo Bonzini int Adlib_init (ISABus *bus) 279*49ab747fSPaolo Bonzini { 280*49ab747fSPaolo Bonzini AdlibState *s = &glob_adlib; 281*49ab747fSPaolo Bonzini struct audsettings as; 282*49ab747fSPaolo Bonzini 283*49ab747fSPaolo Bonzini #ifdef HAS_YMF262 284*49ab747fSPaolo Bonzini if (YMF262Init (1, 14318180, conf.freq)) { 285*49ab747fSPaolo Bonzini dolog ("YMF262Init %d failed\n", conf.freq); 286*49ab747fSPaolo Bonzini return -1; 287*49ab747fSPaolo Bonzini } 288*49ab747fSPaolo Bonzini else { 289*49ab747fSPaolo Bonzini YMF262SetTimerHandler (0, timer_handler, 0); 290*49ab747fSPaolo Bonzini s->enabled = 1; 291*49ab747fSPaolo Bonzini } 292*49ab747fSPaolo Bonzini #else 293*49ab747fSPaolo Bonzini s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq); 294*49ab747fSPaolo Bonzini if (!s->opl) { 295*49ab747fSPaolo Bonzini dolog ("OPLCreate %d failed\n", conf.freq); 296*49ab747fSPaolo Bonzini return -1; 297*49ab747fSPaolo Bonzini } 298*49ab747fSPaolo Bonzini else { 299*49ab747fSPaolo Bonzini OPLSetTimerHandler (s->opl, timer_handler, 0); 300*49ab747fSPaolo Bonzini s->enabled = 1; 301*49ab747fSPaolo Bonzini } 302*49ab747fSPaolo Bonzini #endif 303*49ab747fSPaolo Bonzini 304*49ab747fSPaolo Bonzini as.freq = conf.freq; 305*49ab747fSPaolo Bonzini as.nchannels = SHIFT; 306*49ab747fSPaolo Bonzini as.fmt = AUD_FMT_S16; 307*49ab747fSPaolo Bonzini as.endianness = AUDIO_HOST_ENDIANNESS; 308*49ab747fSPaolo Bonzini 309*49ab747fSPaolo Bonzini AUD_register_card ("adlib", &s->card); 310*49ab747fSPaolo Bonzini 311*49ab747fSPaolo Bonzini s->voice = AUD_open_out ( 312*49ab747fSPaolo Bonzini &s->card, 313*49ab747fSPaolo Bonzini s->voice, 314*49ab747fSPaolo Bonzini "adlib", 315*49ab747fSPaolo Bonzini s, 316*49ab747fSPaolo Bonzini adlib_callback, 317*49ab747fSPaolo Bonzini &as 318*49ab747fSPaolo Bonzini ); 319*49ab747fSPaolo Bonzini if (!s->voice) { 320*49ab747fSPaolo Bonzini Adlib_fini (s); 321*49ab747fSPaolo Bonzini return -1; 322*49ab747fSPaolo Bonzini } 323*49ab747fSPaolo Bonzini 324*49ab747fSPaolo Bonzini s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT; 325*49ab747fSPaolo Bonzini s->mixbuf = g_malloc0 (s->samples << SHIFT); 326*49ab747fSPaolo Bonzini 327*49ab747fSPaolo Bonzini register_ioport_read (0x388, 4, 1, adlib_read, s); 328*49ab747fSPaolo Bonzini register_ioport_write (0x388, 4, 1, adlib_write, s); 329*49ab747fSPaolo Bonzini 330*49ab747fSPaolo Bonzini register_ioport_read (conf.port, 4, 1, adlib_read, s); 331*49ab747fSPaolo Bonzini register_ioport_write (conf.port, 4, 1, adlib_write, s); 332*49ab747fSPaolo Bonzini 333*49ab747fSPaolo Bonzini register_ioport_read (conf.port + 8, 2, 1, adlib_read, s); 334*49ab747fSPaolo Bonzini register_ioport_write (conf.port + 8, 2, 1, adlib_write, s); 335*49ab747fSPaolo Bonzini 336*49ab747fSPaolo Bonzini return 0; 337*49ab747fSPaolo Bonzini } 338