149ab747fSPaolo Bonzini /* 249ab747fSPaolo Bonzini * QEMU Proxy for OPL2/3 emulation by MAME team 349ab747fSPaolo Bonzini * 449ab747fSPaolo Bonzini * Copyright (c) 2004-2005 Vassili Karpov (malc) 549ab747fSPaolo Bonzini * 649ab747fSPaolo Bonzini * Permission is hereby granted, free of charge, to any person obtaining a copy 749ab747fSPaolo Bonzini * of this software and associated documentation files (the "Software"), to deal 849ab747fSPaolo Bonzini * in the Software without restriction, including without limitation the rights 949ab747fSPaolo Bonzini * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 1049ab747fSPaolo Bonzini * copies of the Software, and to permit persons to whom the Software is 1149ab747fSPaolo Bonzini * furnished to do so, subject to the following conditions: 1249ab747fSPaolo Bonzini * 1349ab747fSPaolo Bonzini * The above copyright notice and this permission notice shall be included in 1449ab747fSPaolo Bonzini * all copies or substantial portions of the Software. 1549ab747fSPaolo Bonzini * 1649ab747fSPaolo Bonzini * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1749ab747fSPaolo Bonzini * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1849ab747fSPaolo Bonzini * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1949ab747fSPaolo Bonzini * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2049ab747fSPaolo Bonzini * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2149ab747fSPaolo Bonzini * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 2249ab747fSPaolo Bonzini * THE SOFTWARE. 2349ab747fSPaolo Bonzini */ 2449ab747fSPaolo Bonzini 256086a565SPeter Maydell #include "qemu/osdep.h" 26da34e65cSMarkus Armbruster #include "qapi/error.h" 270b8fa32fSMarkus Armbruster #include "qemu/module.h" 288a824e4dSEduardo Habkost #include "hw/audio/soundhw.h" 2949ab747fSPaolo Bonzini #include "audio/audio.h" 3049ab747fSPaolo Bonzini #include "hw/isa/isa.h" 31a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 32db1015e9SEduardo Habkost #include "qom/object.h" 3349ab747fSPaolo Bonzini 3449ab747fSPaolo Bonzini //#define DEBUG 3549ab747fSPaolo Bonzini 3649ab747fSPaolo Bonzini #define ADLIB_KILL_TIMERS 1 3749ab747fSPaolo Bonzini 388c444a19SPaolo Bonzini #define ADLIB_DESC "Yamaha YM3812 (OPL2)" 398c444a19SPaolo Bonzini 4049ab747fSPaolo Bonzini #ifdef DEBUG 4149ab747fSPaolo Bonzini #include "qemu/timer.h" 4249ab747fSPaolo Bonzini #endif 4349ab747fSPaolo Bonzini 4449ab747fSPaolo Bonzini #define dolog(...) AUD_log ("adlib", __VA_ARGS__) 4549ab747fSPaolo Bonzini #ifdef DEBUG 4649ab747fSPaolo Bonzini #define ldebug(...) dolog (__VA_ARGS__) 4749ab747fSPaolo Bonzini #else 4849ab747fSPaolo Bonzini #define ldebug(...) 4949ab747fSPaolo Bonzini #endif 5049ab747fSPaolo Bonzini 5147b43a1fSPaolo Bonzini #include "fmopl.h" 5249ab747fSPaolo Bonzini #define SHIFT 1 5349ab747fSPaolo Bonzini 548c444a19SPaolo Bonzini #define TYPE_ADLIB "adlib" 558063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(AdlibState, ADLIB) 5649ab747fSPaolo Bonzini 57db1015e9SEduardo Habkost struct AdlibState { 588c444a19SPaolo Bonzini ISADevice parent_obj; 598c444a19SPaolo Bonzini 6049ab747fSPaolo Bonzini QEMUSoundCard card; 618c444a19SPaolo Bonzini uint32_t freq; 628c444a19SPaolo Bonzini uint32_t port; 6349ab747fSPaolo Bonzini int ticking[2]; 6449ab747fSPaolo Bonzini int enabled; 6549ab747fSPaolo Bonzini int active; 6649ab747fSPaolo Bonzini int bufpos; 6749ab747fSPaolo Bonzini #ifdef DEBUG 6849ab747fSPaolo Bonzini int64_t exp[2]; 6949ab747fSPaolo Bonzini #endif 7049ab747fSPaolo Bonzini int16_t *mixbuf; 7149ab747fSPaolo Bonzini uint64_t dexp[2]; 7249ab747fSPaolo Bonzini SWVoiceOut *voice; 7349ab747fSPaolo Bonzini int left, pos, samples; 7449ab747fSPaolo Bonzini QEMUAudioTimeStamp ats; 7549ab747fSPaolo Bonzini FM_OPL *opl; 76848696bfSKirill Batuzov PortioList port_list; 77db1015e9SEduardo Habkost }; 7849ab747fSPaolo Bonzini 7949ab747fSPaolo Bonzini static void adlib_stop_opl_timer (AdlibState *s, size_t n) 8049ab747fSPaolo Bonzini { 8149ab747fSPaolo Bonzini OPLTimerOver (s->opl, n); 8249ab747fSPaolo Bonzini s->ticking[n] = 0; 8349ab747fSPaolo Bonzini } 8449ab747fSPaolo Bonzini 8549ab747fSPaolo Bonzini static void adlib_kill_timers (AdlibState *s) 8649ab747fSPaolo Bonzini { 8749ab747fSPaolo Bonzini size_t i; 8849ab747fSPaolo Bonzini 8949ab747fSPaolo Bonzini for (i = 0; i < 2; ++i) { 9049ab747fSPaolo Bonzini if (s->ticking[i]) { 9149ab747fSPaolo Bonzini uint64_t delta; 9249ab747fSPaolo Bonzini 9349ab747fSPaolo Bonzini delta = AUD_get_elapsed_usec_out (s->voice, &s->ats); 9449ab747fSPaolo Bonzini ldebug ( 9549ab747fSPaolo Bonzini "delta = %f dexp = %f expired => %d\n", 9649ab747fSPaolo Bonzini delta / 1000000.0, 9749ab747fSPaolo Bonzini s->dexp[i] / 1000000.0, 9849ab747fSPaolo Bonzini delta >= s->dexp[i] 9949ab747fSPaolo Bonzini ); 10049ab747fSPaolo Bonzini if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) { 10149ab747fSPaolo Bonzini adlib_stop_opl_timer (s, i); 10249ab747fSPaolo Bonzini AUD_init_time_stamp_out (s->voice, &s->ats); 10349ab747fSPaolo Bonzini } 10449ab747fSPaolo Bonzini } 10549ab747fSPaolo Bonzini } 10649ab747fSPaolo Bonzini } 10749ab747fSPaolo Bonzini 1088307c294SNutan Shinde static void adlib_write(void *opaque, uint32_t nport, uint32_t val) 10949ab747fSPaolo Bonzini { 11049ab747fSPaolo Bonzini AdlibState *s = opaque; 11149ab747fSPaolo Bonzini int a = nport & 3; 11249ab747fSPaolo Bonzini 11349ab747fSPaolo Bonzini s->active = 1; 11449ab747fSPaolo Bonzini AUD_set_active_out (s->voice, 1); 11549ab747fSPaolo Bonzini 11649ab747fSPaolo Bonzini adlib_kill_timers (s); 11749ab747fSPaolo Bonzini 11849ab747fSPaolo Bonzini OPLWrite (s->opl, a, val); 11949ab747fSPaolo Bonzini } 12049ab747fSPaolo Bonzini 1218307c294SNutan Shinde static uint32_t adlib_read(void *opaque, uint32_t nport) 12249ab747fSPaolo Bonzini { 12349ab747fSPaolo Bonzini AdlibState *s = opaque; 12449ab747fSPaolo Bonzini int a = nport & 3; 12549ab747fSPaolo Bonzini 12649ab747fSPaolo Bonzini adlib_kill_timers (s); 127b3ac2b94SSimran Singhal return OPLRead (s->opl, a); 12849ab747fSPaolo Bonzini } 12949ab747fSPaolo Bonzini 130c57fbf50SHervé Poussineau static void timer_handler (void *opaque, int c, double interval_Sec) 13149ab747fSPaolo Bonzini { 132639b49efSHervé Poussineau AdlibState *s = opaque; 13349ab747fSPaolo Bonzini unsigned n = c & 1; 13449ab747fSPaolo Bonzini #ifdef DEBUG 13549ab747fSPaolo Bonzini double interval; 13649ab747fSPaolo Bonzini int64_t exp; 13749ab747fSPaolo Bonzini #endif 13849ab747fSPaolo Bonzini 13949ab747fSPaolo Bonzini if (interval_Sec == 0.0) { 14049ab747fSPaolo Bonzini s->ticking[n] = 0; 14149ab747fSPaolo Bonzini return; 14249ab747fSPaolo Bonzini } 14349ab747fSPaolo Bonzini 14449ab747fSPaolo Bonzini s->ticking[n] = 1; 14549ab747fSPaolo Bonzini #ifdef DEBUG 14673bcb24dSRutuja Shah interval = NANOSECONDS_PER_SECOND * interval_Sec; 147bc72ad67SAlex Bligh exp = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + interval; 14849ab747fSPaolo Bonzini s->exp[n] = exp; 14949ab747fSPaolo Bonzini #endif 15049ab747fSPaolo Bonzini 15149ab747fSPaolo Bonzini s->dexp[n] = interval_Sec * 1000000.0; 15249ab747fSPaolo Bonzini AUD_init_time_stamp_out (s->voice, &s->ats); 15349ab747fSPaolo Bonzini } 15449ab747fSPaolo Bonzini 15549ab747fSPaolo Bonzini static int write_audio (AdlibState *s, int samples) 15649ab747fSPaolo Bonzini { 15749ab747fSPaolo Bonzini int net = 0; 15849ab747fSPaolo Bonzini int pos = s->pos; 15949ab747fSPaolo Bonzini 16049ab747fSPaolo Bonzini while (samples) { 16149ab747fSPaolo Bonzini int nbytes, wbytes, wsampl; 16249ab747fSPaolo Bonzini 16349ab747fSPaolo Bonzini nbytes = samples << SHIFT; 16449ab747fSPaolo Bonzini wbytes = AUD_write ( 16549ab747fSPaolo Bonzini s->voice, 16649ab747fSPaolo Bonzini s->mixbuf + (pos << (SHIFT - 1)), 16749ab747fSPaolo Bonzini nbytes 16849ab747fSPaolo Bonzini ); 16949ab747fSPaolo Bonzini 17049ab747fSPaolo Bonzini if (wbytes) { 17149ab747fSPaolo Bonzini wsampl = wbytes >> SHIFT; 17249ab747fSPaolo Bonzini 17349ab747fSPaolo Bonzini samples -= wsampl; 17449ab747fSPaolo Bonzini pos = (pos + wsampl) % s->samples; 17549ab747fSPaolo Bonzini 17649ab747fSPaolo Bonzini net += wsampl; 17749ab747fSPaolo Bonzini } 17849ab747fSPaolo Bonzini else { 17949ab747fSPaolo Bonzini break; 18049ab747fSPaolo Bonzini } 18149ab747fSPaolo Bonzini } 18249ab747fSPaolo Bonzini 18349ab747fSPaolo Bonzini return net; 18449ab747fSPaolo Bonzini } 18549ab747fSPaolo Bonzini 18649ab747fSPaolo Bonzini static void adlib_callback (void *opaque, int free) 18749ab747fSPaolo Bonzini { 18849ab747fSPaolo Bonzini AdlibState *s = opaque; 18979fe9e43SRichard Henderson int samples, to_play, written; 19049ab747fSPaolo Bonzini 19149ab747fSPaolo Bonzini samples = free >> SHIFT; 19249ab747fSPaolo Bonzini if (!(s->active && s->enabled) || !samples) { 19349ab747fSPaolo Bonzini return; 19449ab747fSPaolo Bonzini } 19549ab747fSPaolo Bonzini 19658935915SKővágó, Zoltán to_play = MIN (s->left, samples); 19749ab747fSPaolo Bonzini while (to_play) { 19849ab747fSPaolo Bonzini written = write_audio (s, to_play); 19949ab747fSPaolo Bonzini 20049ab747fSPaolo Bonzini if (written) { 20149ab747fSPaolo Bonzini s->left -= written; 20249ab747fSPaolo Bonzini samples -= written; 20349ab747fSPaolo Bonzini to_play -= written; 20449ab747fSPaolo Bonzini s->pos = (s->pos + written) % s->samples; 20549ab747fSPaolo Bonzini } 20649ab747fSPaolo Bonzini else { 20749ab747fSPaolo Bonzini return; 20849ab747fSPaolo Bonzini } 20949ab747fSPaolo Bonzini } 21049ab747fSPaolo Bonzini 21158935915SKővágó, Zoltán samples = MIN (samples, s->samples - s->pos); 21249ab747fSPaolo Bonzini if (!samples) { 21349ab747fSPaolo Bonzini return; 21449ab747fSPaolo Bonzini } 21549ab747fSPaolo Bonzini 21649ab747fSPaolo Bonzini YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples); 21749ab747fSPaolo Bonzini 21849ab747fSPaolo Bonzini while (samples) { 21949ab747fSPaolo Bonzini written = write_audio (s, samples); 22049ab747fSPaolo Bonzini 22149ab747fSPaolo Bonzini if (written) { 22249ab747fSPaolo Bonzini samples -= written; 22349ab747fSPaolo Bonzini s->pos = (s->pos + written) % s->samples; 22449ab747fSPaolo Bonzini } 22549ab747fSPaolo Bonzini else { 22649ab747fSPaolo Bonzini s->left = samples; 22749ab747fSPaolo Bonzini return; 22849ab747fSPaolo Bonzini } 22949ab747fSPaolo Bonzini } 23049ab747fSPaolo Bonzini } 23149ab747fSPaolo Bonzini 23249ab747fSPaolo Bonzini static void Adlib_fini (AdlibState *s) 23349ab747fSPaolo Bonzini { 23449ab747fSPaolo Bonzini if (s->opl) { 23549ab747fSPaolo Bonzini OPLDestroy (s->opl); 23649ab747fSPaolo Bonzini s->opl = NULL; 23749ab747fSPaolo Bonzini } 23849ab747fSPaolo Bonzini 23949ab747fSPaolo Bonzini g_free(s->mixbuf); 24049ab747fSPaolo Bonzini 24149ab747fSPaolo Bonzini s->active = 0; 24249ab747fSPaolo Bonzini s->enabled = 0; 24349ab747fSPaolo Bonzini AUD_remove_card (&s->card); 24449ab747fSPaolo Bonzini } 24549ab747fSPaolo Bonzini 246a8aec295SJan Kiszka static MemoryRegionPortio adlib_portio_list[] = { 247a8aec295SJan Kiszka { 0, 4, 1, .read = adlib_read, .write = adlib_write, }, 248a8aec295SJan Kiszka { 0, 2, 1, .read = adlib_read, .write = adlib_write, }, 2492b21fb57SHervé Poussineau { 0x388, 4, 1, .read = adlib_read, .write = adlib_write, }, 250a8aec295SJan Kiszka PORTIO_END_OF_LIST(), 251a8aec295SJan Kiszka }; 252a8aec295SJan Kiszka 253db895a1eSAndreas Färber static void adlib_realizefn (DeviceState *dev, Error **errp) 25449ab747fSPaolo Bonzini { 2558c444a19SPaolo Bonzini AdlibState *s = ADLIB(dev); 25649ab747fSPaolo Bonzini struct audsettings as; 25749ab747fSPaolo Bonzini 258*cb94ff5fSMartin Kletzander if (!AUD_register_card ("adlib", &s->card, errp)) { 259*cb94ff5fSMartin Kletzander return; 260*cb94ff5fSMartin Kletzander } 261*cb94ff5fSMartin Kletzander 2628f7e2c2cSJuan Quintela s->opl = OPLCreate (3579545, s->freq); 26349ab747fSPaolo Bonzini if (!s->opl) { 264db895a1eSAndreas Färber error_setg (errp, "OPLCreate %d failed", s->freq); 265db895a1eSAndreas Färber return; 26649ab747fSPaolo Bonzini } 26749ab747fSPaolo Bonzini else { 268639b49efSHervé Poussineau OPLSetTimerHandler(s->opl, timer_handler, s); 26949ab747fSPaolo Bonzini s->enabled = 1; 27049ab747fSPaolo Bonzini } 27149ab747fSPaolo Bonzini 2728c444a19SPaolo Bonzini as.freq = s->freq; 27349ab747fSPaolo Bonzini as.nchannels = SHIFT; 27485bc5852SKővágó, Zoltán as.fmt = AUDIO_FORMAT_S16; 27549ab747fSPaolo Bonzini as.endianness = AUDIO_HOST_ENDIANNESS; 27649ab747fSPaolo Bonzini 27749ab747fSPaolo Bonzini s->voice = AUD_open_out ( 27849ab747fSPaolo Bonzini &s->card, 27949ab747fSPaolo Bonzini s->voice, 28049ab747fSPaolo Bonzini "adlib", 28149ab747fSPaolo Bonzini s, 28249ab747fSPaolo Bonzini adlib_callback, 28349ab747fSPaolo Bonzini &as 28449ab747fSPaolo Bonzini ); 28549ab747fSPaolo Bonzini if (!s->voice) { 28649ab747fSPaolo Bonzini Adlib_fini (s); 287db895a1eSAndreas Färber error_setg (errp, "Initializing audio voice failed"); 288db895a1eSAndreas Färber return; 28949ab747fSPaolo Bonzini } 29049ab747fSPaolo Bonzini 29149ab747fSPaolo Bonzini s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT; 29249ab747fSPaolo Bonzini s->mixbuf = g_malloc0 (s->samples << SHIFT); 29349ab747fSPaolo Bonzini 2947f0ba7bbSPaolo Bonzini adlib_portio_list[0].offset = s->port; 2957f0ba7bbSPaolo Bonzini adlib_portio_list[1].offset = s->port + 8; 296848696bfSKirill Batuzov portio_list_init (&s->port_list, OBJECT(s), adlib_portio_list, s, "adlib"); 297848696bfSKirill Batuzov portio_list_add (&s->port_list, isa_address_space_io(&s->parent_obj), 0); 29849ab747fSPaolo Bonzini } 2998c444a19SPaolo Bonzini 3008c444a19SPaolo Bonzini static Property adlib_properties[] = { 30188e47b9aSKővágó, Zoltán DEFINE_AUDIO_PROPERTIES(AdlibState, card), 302c7bcc85dSPaolo Bonzini DEFINE_PROP_UINT32 ("iobase", AdlibState, port, 0x220), 3038c444a19SPaolo Bonzini DEFINE_PROP_UINT32 ("freq", AdlibState, freq, 44100), 3048c444a19SPaolo Bonzini DEFINE_PROP_END_OF_LIST (), 3058c444a19SPaolo Bonzini }; 3068c444a19SPaolo Bonzini 3078c444a19SPaolo Bonzini static void adlib_class_initfn (ObjectClass *klass, void *data) 3088c444a19SPaolo Bonzini { 3098c444a19SPaolo Bonzini DeviceClass *dc = DEVICE_CLASS (klass); 310db895a1eSAndreas Färber 311db895a1eSAndreas Färber dc->realize = adlib_realizefn; 312125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 3138c444a19SPaolo Bonzini dc->desc = ADLIB_DESC; 3144f67d30bSMarc-André Lureau device_class_set_props(dc, adlib_properties); 3158c444a19SPaolo Bonzini } 3168c444a19SPaolo Bonzini 3178c444a19SPaolo Bonzini static const TypeInfo adlib_info = { 3188c444a19SPaolo Bonzini .name = TYPE_ADLIB, 3198c444a19SPaolo Bonzini .parent = TYPE_ISA_DEVICE, 3208c444a19SPaolo Bonzini .instance_size = sizeof (AdlibState), 3218c444a19SPaolo Bonzini .class_init = adlib_class_initfn, 3228c444a19SPaolo Bonzini }; 3238c444a19SPaolo Bonzini 3248c444a19SPaolo Bonzini static void adlib_register_types (void) 3258c444a19SPaolo Bonzini { 3268c444a19SPaolo Bonzini type_register_static (&adlib_info); 32786388a3bSGerd Hoffmann deprecated_register_soundhw("adlib", ADLIB_DESC, 1, TYPE_ADLIB); 3288c444a19SPaolo Bonzini } 3298c444a19SPaolo Bonzini 3308c444a19SPaolo Bonzini type_init (adlib_register_types) 331