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 2549ab747fSPaolo Bonzini #include "hw/hw.h" 2649ab747fSPaolo Bonzini #include "hw/audio/audio.h" 2749ab747fSPaolo Bonzini #include "audio/audio.h" 2849ab747fSPaolo Bonzini #include "hw/isa/isa.h" 2949ab747fSPaolo Bonzini 3049ab747fSPaolo Bonzini //#define DEBUG 3149ab747fSPaolo Bonzini 3249ab747fSPaolo Bonzini #define ADLIB_KILL_TIMERS 1 3349ab747fSPaolo Bonzini 3449ab747fSPaolo Bonzini #ifdef DEBUG 3549ab747fSPaolo Bonzini #include "qemu/timer.h" 3649ab747fSPaolo Bonzini #endif 3749ab747fSPaolo Bonzini 3849ab747fSPaolo Bonzini #define dolog(...) AUD_log ("adlib", __VA_ARGS__) 3949ab747fSPaolo Bonzini #ifdef DEBUG 4049ab747fSPaolo Bonzini #define ldebug(...) dolog (__VA_ARGS__) 4149ab747fSPaolo Bonzini #else 4249ab747fSPaolo Bonzini #define ldebug(...) 4349ab747fSPaolo Bonzini #endif 4449ab747fSPaolo Bonzini 4549ab747fSPaolo Bonzini #ifdef HAS_YMF262 4649ab747fSPaolo Bonzini #include "ymf262.h" 4749ab747fSPaolo Bonzini void YMF262UpdateOneQEMU (int which, INT16 *dst, int length); 4849ab747fSPaolo Bonzini #define SHIFT 2 4949ab747fSPaolo Bonzini #else 50*47b43a1fSPaolo Bonzini #include "fmopl.h" 5149ab747fSPaolo Bonzini #define SHIFT 1 5249ab747fSPaolo Bonzini #endif 5349ab747fSPaolo Bonzini 5449ab747fSPaolo Bonzini #define IO_READ_PROTO(name) \ 5549ab747fSPaolo Bonzini uint32_t name (void *opaque, uint32_t nport) 5649ab747fSPaolo Bonzini #define IO_WRITE_PROTO(name) \ 5749ab747fSPaolo Bonzini void name (void *opaque, uint32_t nport, uint32_t val) 5849ab747fSPaolo Bonzini 5949ab747fSPaolo Bonzini static struct { 6049ab747fSPaolo Bonzini int port; 6149ab747fSPaolo Bonzini int freq; 6249ab747fSPaolo Bonzini } conf = {0x220, 44100}; 6349ab747fSPaolo Bonzini 6449ab747fSPaolo Bonzini typedef struct { 6549ab747fSPaolo Bonzini QEMUSoundCard card; 6649ab747fSPaolo Bonzini int ticking[2]; 6749ab747fSPaolo Bonzini int enabled; 6849ab747fSPaolo Bonzini int active; 6949ab747fSPaolo Bonzini int bufpos; 7049ab747fSPaolo Bonzini #ifdef DEBUG 7149ab747fSPaolo Bonzini int64_t exp[2]; 7249ab747fSPaolo Bonzini #endif 7349ab747fSPaolo Bonzini int16_t *mixbuf; 7449ab747fSPaolo Bonzini uint64_t dexp[2]; 7549ab747fSPaolo Bonzini SWVoiceOut *voice; 7649ab747fSPaolo Bonzini int left, pos, samples; 7749ab747fSPaolo Bonzini QEMUAudioTimeStamp ats; 7849ab747fSPaolo Bonzini #ifndef HAS_YMF262 7949ab747fSPaolo Bonzini FM_OPL *opl; 8049ab747fSPaolo Bonzini #endif 8149ab747fSPaolo Bonzini } AdlibState; 8249ab747fSPaolo Bonzini 8349ab747fSPaolo Bonzini static AdlibState glob_adlib; 8449ab747fSPaolo Bonzini 8549ab747fSPaolo Bonzini static void adlib_stop_opl_timer (AdlibState *s, size_t n) 8649ab747fSPaolo Bonzini { 8749ab747fSPaolo Bonzini #ifdef HAS_YMF262 8849ab747fSPaolo Bonzini YMF262TimerOver (0, n); 8949ab747fSPaolo Bonzini #else 9049ab747fSPaolo Bonzini OPLTimerOver (s->opl, n); 9149ab747fSPaolo Bonzini #endif 9249ab747fSPaolo Bonzini s->ticking[n] = 0; 9349ab747fSPaolo Bonzini } 9449ab747fSPaolo Bonzini 9549ab747fSPaolo Bonzini static void adlib_kill_timers (AdlibState *s) 9649ab747fSPaolo Bonzini { 9749ab747fSPaolo Bonzini size_t i; 9849ab747fSPaolo Bonzini 9949ab747fSPaolo Bonzini for (i = 0; i < 2; ++i) { 10049ab747fSPaolo Bonzini if (s->ticking[i]) { 10149ab747fSPaolo Bonzini uint64_t delta; 10249ab747fSPaolo Bonzini 10349ab747fSPaolo Bonzini delta = AUD_get_elapsed_usec_out (s->voice, &s->ats); 10449ab747fSPaolo Bonzini ldebug ( 10549ab747fSPaolo Bonzini "delta = %f dexp = %f expired => %d\n", 10649ab747fSPaolo Bonzini delta / 1000000.0, 10749ab747fSPaolo Bonzini s->dexp[i] / 1000000.0, 10849ab747fSPaolo Bonzini delta >= s->dexp[i] 10949ab747fSPaolo Bonzini ); 11049ab747fSPaolo Bonzini if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) { 11149ab747fSPaolo Bonzini adlib_stop_opl_timer (s, i); 11249ab747fSPaolo Bonzini AUD_init_time_stamp_out (s->voice, &s->ats); 11349ab747fSPaolo Bonzini } 11449ab747fSPaolo Bonzini } 11549ab747fSPaolo Bonzini } 11649ab747fSPaolo Bonzini } 11749ab747fSPaolo Bonzini 11849ab747fSPaolo Bonzini static IO_WRITE_PROTO (adlib_write) 11949ab747fSPaolo Bonzini { 12049ab747fSPaolo Bonzini AdlibState *s = opaque; 12149ab747fSPaolo Bonzini int a = nport & 3; 12249ab747fSPaolo Bonzini 12349ab747fSPaolo Bonzini s->active = 1; 12449ab747fSPaolo Bonzini AUD_set_active_out (s->voice, 1); 12549ab747fSPaolo Bonzini 12649ab747fSPaolo Bonzini adlib_kill_timers (s); 12749ab747fSPaolo Bonzini 12849ab747fSPaolo Bonzini #ifdef HAS_YMF262 12949ab747fSPaolo Bonzini YMF262Write (0, a, val); 13049ab747fSPaolo Bonzini #else 13149ab747fSPaolo Bonzini OPLWrite (s->opl, a, val); 13249ab747fSPaolo Bonzini #endif 13349ab747fSPaolo Bonzini } 13449ab747fSPaolo Bonzini 13549ab747fSPaolo Bonzini static IO_READ_PROTO (adlib_read) 13649ab747fSPaolo Bonzini { 13749ab747fSPaolo Bonzini AdlibState *s = opaque; 13849ab747fSPaolo Bonzini uint8_t data; 13949ab747fSPaolo Bonzini int a = nport & 3; 14049ab747fSPaolo Bonzini 14149ab747fSPaolo Bonzini adlib_kill_timers (s); 14249ab747fSPaolo Bonzini 14349ab747fSPaolo Bonzini #ifdef HAS_YMF262 14449ab747fSPaolo Bonzini data = YMF262Read (0, a); 14549ab747fSPaolo Bonzini #else 14649ab747fSPaolo Bonzini data = OPLRead (s->opl, a); 14749ab747fSPaolo Bonzini #endif 14849ab747fSPaolo Bonzini return data; 14949ab747fSPaolo Bonzini } 15049ab747fSPaolo Bonzini 15149ab747fSPaolo Bonzini static void timer_handler (int c, double interval_Sec) 15249ab747fSPaolo Bonzini { 15349ab747fSPaolo Bonzini AdlibState *s = &glob_adlib; 15449ab747fSPaolo Bonzini unsigned n = c & 1; 15549ab747fSPaolo Bonzini #ifdef DEBUG 15649ab747fSPaolo Bonzini double interval; 15749ab747fSPaolo Bonzini int64_t exp; 15849ab747fSPaolo Bonzini #endif 15949ab747fSPaolo Bonzini 16049ab747fSPaolo Bonzini if (interval_Sec == 0.0) { 16149ab747fSPaolo Bonzini s->ticking[n] = 0; 16249ab747fSPaolo Bonzini return; 16349ab747fSPaolo Bonzini } 16449ab747fSPaolo Bonzini 16549ab747fSPaolo Bonzini s->ticking[n] = 1; 16649ab747fSPaolo Bonzini #ifdef DEBUG 16749ab747fSPaolo Bonzini interval = get_ticks_per_sec () * interval_Sec; 16849ab747fSPaolo Bonzini exp = qemu_get_clock_ns (vm_clock) + interval; 16949ab747fSPaolo Bonzini s->exp[n] = exp; 17049ab747fSPaolo Bonzini #endif 17149ab747fSPaolo Bonzini 17249ab747fSPaolo Bonzini s->dexp[n] = interval_Sec * 1000000.0; 17349ab747fSPaolo Bonzini AUD_init_time_stamp_out (s->voice, &s->ats); 17449ab747fSPaolo Bonzini } 17549ab747fSPaolo Bonzini 17649ab747fSPaolo Bonzini static int write_audio (AdlibState *s, int samples) 17749ab747fSPaolo Bonzini { 17849ab747fSPaolo Bonzini int net = 0; 17949ab747fSPaolo Bonzini int pos = s->pos; 18049ab747fSPaolo Bonzini 18149ab747fSPaolo Bonzini while (samples) { 18249ab747fSPaolo Bonzini int nbytes, wbytes, wsampl; 18349ab747fSPaolo Bonzini 18449ab747fSPaolo Bonzini nbytes = samples << SHIFT; 18549ab747fSPaolo Bonzini wbytes = AUD_write ( 18649ab747fSPaolo Bonzini s->voice, 18749ab747fSPaolo Bonzini s->mixbuf + (pos << (SHIFT - 1)), 18849ab747fSPaolo Bonzini nbytes 18949ab747fSPaolo Bonzini ); 19049ab747fSPaolo Bonzini 19149ab747fSPaolo Bonzini if (wbytes) { 19249ab747fSPaolo Bonzini wsampl = wbytes >> SHIFT; 19349ab747fSPaolo Bonzini 19449ab747fSPaolo Bonzini samples -= wsampl; 19549ab747fSPaolo Bonzini pos = (pos + wsampl) % s->samples; 19649ab747fSPaolo Bonzini 19749ab747fSPaolo Bonzini net += wsampl; 19849ab747fSPaolo Bonzini } 19949ab747fSPaolo Bonzini else { 20049ab747fSPaolo Bonzini break; 20149ab747fSPaolo Bonzini } 20249ab747fSPaolo Bonzini } 20349ab747fSPaolo Bonzini 20449ab747fSPaolo Bonzini return net; 20549ab747fSPaolo Bonzini } 20649ab747fSPaolo Bonzini 20749ab747fSPaolo Bonzini static void adlib_callback (void *opaque, int free) 20849ab747fSPaolo Bonzini { 20949ab747fSPaolo Bonzini AdlibState *s = opaque; 21049ab747fSPaolo Bonzini int samples, net = 0, to_play, written; 21149ab747fSPaolo Bonzini 21249ab747fSPaolo Bonzini samples = free >> SHIFT; 21349ab747fSPaolo Bonzini if (!(s->active && s->enabled) || !samples) { 21449ab747fSPaolo Bonzini return; 21549ab747fSPaolo Bonzini } 21649ab747fSPaolo Bonzini 21749ab747fSPaolo Bonzini to_play = audio_MIN (s->left, samples); 21849ab747fSPaolo Bonzini while (to_play) { 21949ab747fSPaolo Bonzini written = write_audio (s, to_play); 22049ab747fSPaolo Bonzini 22149ab747fSPaolo Bonzini if (written) { 22249ab747fSPaolo Bonzini s->left -= written; 22349ab747fSPaolo Bonzini samples -= written; 22449ab747fSPaolo Bonzini to_play -= written; 22549ab747fSPaolo Bonzini s->pos = (s->pos + written) % s->samples; 22649ab747fSPaolo Bonzini } 22749ab747fSPaolo Bonzini else { 22849ab747fSPaolo Bonzini return; 22949ab747fSPaolo Bonzini } 23049ab747fSPaolo Bonzini } 23149ab747fSPaolo Bonzini 23249ab747fSPaolo Bonzini samples = audio_MIN (samples, s->samples - s->pos); 23349ab747fSPaolo Bonzini if (!samples) { 23449ab747fSPaolo Bonzini return; 23549ab747fSPaolo Bonzini } 23649ab747fSPaolo Bonzini 23749ab747fSPaolo Bonzini #ifdef HAS_YMF262 23849ab747fSPaolo Bonzini YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples); 23949ab747fSPaolo Bonzini #else 24049ab747fSPaolo Bonzini YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples); 24149ab747fSPaolo Bonzini #endif 24249ab747fSPaolo Bonzini 24349ab747fSPaolo Bonzini while (samples) { 24449ab747fSPaolo Bonzini written = write_audio (s, samples); 24549ab747fSPaolo Bonzini 24649ab747fSPaolo Bonzini if (written) { 24749ab747fSPaolo Bonzini net += written; 24849ab747fSPaolo Bonzini samples -= written; 24949ab747fSPaolo Bonzini s->pos = (s->pos + written) % s->samples; 25049ab747fSPaolo Bonzini } 25149ab747fSPaolo Bonzini else { 25249ab747fSPaolo Bonzini s->left = samples; 25349ab747fSPaolo Bonzini return; 25449ab747fSPaolo Bonzini } 25549ab747fSPaolo Bonzini } 25649ab747fSPaolo Bonzini } 25749ab747fSPaolo Bonzini 25849ab747fSPaolo Bonzini static void Adlib_fini (AdlibState *s) 25949ab747fSPaolo Bonzini { 26049ab747fSPaolo Bonzini #ifdef HAS_YMF262 26149ab747fSPaolo Bonzini YMF262Shutdown (); 26249ab747fSPaolo Bonzini #else 26349ab747fSPaolo Bonzini if (s->opl) { 26449ab747fSPaolo Bonzini OPLDestroy (s->opl); 26549ab747fSPaolo Bonzini s->opl = NULL; 26649ab747fSPaolo Bonzini } 26749ab747fSPaolo Bonzini #endif 26849ab747fSPaolo Bonzini 26949ab747fSPaolo Bonzini if (s->mixbuf) { 27049ab747fSPaolo Bonzini g_free (s->mixbuf); 27149ab747fSPaolo Bonzini } 27249ab747fSPaolo Bonzini 27349ab747fSPaolo Bonzini s->active = 0; 27449ab747fSPaolo Bonzini s->enabled = 0; 27549ab747fSPaolo Bonzini AUD_remove_card (&s->card); 27649ab747fSPaolo Bonzini } 27749ab747fSPaolo Bonzini 27849ab747fSPaolo Bonzini int Adlib_init (ISABus *bus) 27949ab747fSPaolo Bonzini { 28049ab747fSPaolo Bonzini AdlibState *s = &glob_adlib; 28149ab747fSPaolo Bonzini struct audsettings as; 28249ab747fSPaolo Bonzini 28349ab747fSPaolo Bonzini #ifdef HAS_YMF262 28449ab747fSPaolo Bonzini if (YMF262Init (1, 14318180, conf.freq)) { 28549ab747fSPaolo Bonzini dolog ("YMF262Init %d failed\n", conf.freq); 28649ab747fSPaolo Bonzini return -1; 28749ab747fSPaolo Bonzini } 28849ab747fSPaolo Bonzini else { 28949ab747fSPaolo Bonzini YMF262SetTimerHandler (0, timer_handler, 0); 29049ab747fSPaolo Bonzini s->enabled = 1; 29149ab747fSPaolo Bonzini } 29249ab747fSPaolo Bonzini #else 29349ab747fSPaolo Bonzini s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq); 29449ab747fSPaolo Bonzini if (!s->opl) { 29549ab747fSPaolo Bonzini dolog ("OPLCreate %d failed\n", conf.freq); 29649ab747fSPaolo Bonzini return -1; 29749ab747fSPaolo Bonzini } 29849ab747fSPaolo Bonzini else { 29949ab747fSPaolo Bonzini OPLSetTimerHandler (s->opl, timer_handler, 0); 30049ab747fSPaolo Bonzini s->enabled = 1; 30149ab747fSPaolo Bonzini } 30249ab747fSPaolo Bonzini #endif 30349ab747fSPaolo Bonzini 30449ab747fSPaolo Bonzini as.freq = conf.freq; 30549ab747fSPaolo Bonzini as.nchannels = SHIFT; 30649ab747fSPaolo Bonzini as.fmt = AUD_FMT_S16; 30749ab747fSPaolo Bonzini as.endianness = AUDIO_HOST_ENDIANNESS; 30849ab747fSPaolo Bonzini 30949ab747fSPaolo Bonzini AUD_register_card ("adlib", &s->card); 31049ab747fSPaolo Bonzini 31149ab747fSPaolo Bonzini s->voice = AUD_open_out ( 31249ab747fSPaolo Bonzini &s->card, 31349ab747fSPaolo Bonzini s->voice, 31449ab747fSPaolo Bonzini "adlib", 31549ab747fSPaolo Bonzini s, 31649ab747fSPaolo Bonzini adlib_callback, 31749ab747fSPaolo Bonzini &as 31849ab747fSPaolo Bonzini ); 31949ab747fSPaolo Bonzini if (!s->voice) { 32049ab747fSPaolo Bonzini Adlib_fini (s); 32149ab747fSPaolo Bonzini return -1; 32249ab747fSPaolo Bonzini } 32349ab747fSPaolo Bonzini 32449ab747fSPaolo Bonzini s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT; 32549ab747fSPaolo Bonzini s->mixbuf = g_malloc0 (s->samples << SHIFT); 32649ab747fSPaolo Bonzini 32749ab747fSPaolo Bonzini register_ioport_read (0x388, 4, 1, adlib_read, s); 32849ab747fSPaolo Bonzini register_ioport_write (0x388, 4, 1, adlib_write, s); 32949ab747fSPaolo Bonzini 33049ab747fSPaolo Bonzini register_ioport_read (conf.port, 4, 1, adlib_read, s); 33149ab747fSPaolo Bonzini register_ioport_write (conf.port, 4, 1, adlib_write, s); 33249ab747fSPaolo Bonzini 33349ab747fSPaolo Bonzini register_ioport_read (conf.port + 8, 2, 1, adlib_read, s); 33449ab747fSPaolo Bonzini register_ioport_write (conf.port + 8, 2, 1, adlib_write, s); 33549ab747fSPaolo Bonzini 33649ab747fSPaolo Bonzini return 0; 33749ab747fSPaolo Bonzini } 338