aboutsummaryrefslogtreecommitdiff
path: root/hw/adlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/adlib.c')
-rw-r--r--hw/adlib.c285
1 files changed, 148 insertions, 137 deletions
diff --git a/hw/adlib.c b/hw/adlib.c
index 939a7ed..70de4ff 100644
--- a/hw/adlib.c
+++ b/hw/adlib.c
@@ -1,8 +1,8 @@
/*
- * QEMU Adlib emulation
- *
- * Copyright (c) 2004 Vassili Karpov (malc)
- *
+ * QEMU Proxy for OPL2/3 emulation by MAME team
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
@@ -21,8 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include <assert.h>
#include "vl.h"
+#define ADLIB_KILL_TIMERS 1
+
#define dolog(...) AUD_log ("adlib", __VA_ARGS__)
#ifdef DEBUG
#define ldebug(...) dolog (__VA_ARGS__)
@@ -30,23 +33,15 @@
#define ldebug(...)
#endif
-#ifdef USE_YMF262
-#define HAS_YMF262 1
+#ifdef HAS_YMF262
#include "ymf262.h"
-void YMF262UpdateOneQEMU(int which, INT16 *dst, int length);
+void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
#define SHIFT 2
#else
#include "fmopl.h"
#define SHIFT 1
#endif
-#ifdef _WIN32
-#include <windows.h>
-#define small_delay() Sleep (1)
-#else
-#define small_delay() usleep (1)
-#endif
-
#define IO_READ_PROTO(name) \
uint32_t name (void *opaque, uint32_t nport)
#define IO_WRITE_PROTO(name) \
@@ -58,35 +53,70 @@ static struct {
} conf = {0x220, 44100};
typedef struct {
+ int ticking[2];
int enabled;
int active;
- int cparam;
- int64_t ticks;
int bufpos;
+#ifdef DEBUG
+ int64_t exp[2];
+#endif
int16_t *mixbuf;
- double interval;
- QEMUTimer *ts, *opl_ts;
- SWVoice *voice;
- int left, pos, samples, bytes_per_second, old_free;
- int refcount;
-#ifndef USE_YMF262
+ uint64_t dexp[2];
+ SWVoiceOut *voice;
+ int left, pos, samples;
+ QEMUAudioTimeStamp ats;
+#ifndef HAS_YMF262
FM_OPL *opl;
#endif
} AdlibState;
static AdlibState adlib;
+static void adlib_stop_opl_timer (AdlibState *s, size_t n)
+{
+#ifdef HAS_YMF262
+ YMF262TimerOver (0, n);
+#else
+ OPLTimerOver (s->opl, n);
+#endif
+ s->ticking[n] = 0;
+}
+
+static void adlib_kill_timers (AdlibState *s)
+{
+ size_t i;
+
+ for (i = 0; i < 2; ++i) {
+ if (s->ticking[i]) {
+ uint64_t delta;
+
+ delta = AUD_time_stamp_get_elapsed_usec_out (s->voice, &s->ats);
+ ldebug (
+ "delta = %f dexp = %f expired => %d\n",
+ delta / 1000000.0,
+ s->dexp[i] / 1000000.0,
+ delta >= s->dexp[i]
+ );
+ if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) {
+ adlib_stop_opl_timer (s, i);
+ AUD_init_time_stamp_out (s->voice, &s->ats);
+ }
+ }
+ }
+}
+
static IO_WRITE_PROTO(adlib_write)
{
AdlibState *s = opaque;
int a = nport & 3;
int status;
- s->ticks = qemu_get_clock (vm_clock);
s->active = 1;
- AUD_enable (s->voice, 1);
+ AUD_set_active_out (s->voice, 1);
-#ifdef USE_YMF262
+ adlib_kill_timers (s);
+
+#ifdef HAS_YMF262
status = YMF262Write (0, a, val);
#else
status = OPLWrite (s->opl, a, val);
@@ -99,8 +129,9 @@ static IO_READ_PROTO(adlib_read)
uint8_t data;
int a = nport & 3;
-#ifdef USE_YMF262
- (void) s;
+ adlib_kill_timers (s);
+
+#ifdef HAS_YMF262
data = YMF262Read (0, a);
#else
data = OPLRead (s->opl, a);
@@ -108,119 +139,115 @@ static IO_READ_PROTO(adlib_read)
return data;
}
-static void OPL_timer (void *opaque)
+static void timer_handler (int c, double interval_Sec)
{
- AdlibState *s = opaque;
-#ifdef USE_YMF262
- YMF262TimerOver (s->cparam >> 1, s->cparam & 1);
-#else
- OPLTimerOver (s->opl, s->cparam);
+ AdlibState *s = &adlib;
+ unsigned n = c & 1;
+#ifdef DEBUG
+ double interval;
#endif
- qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval);
-}
-static void YMF262TimerHandler (int c, double interval_Sec)
-{
- AdlibState *s = &adlib;
if (interval_Sec == 0.0) {
- qemu_del_timer (s->opl_ts);
+ s->ticking[n] = 0;
return;
}
- s->cparam = c;
- s->interval = ticks_per_sec * interval_Sec;
- qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval);
- small_delay ();
+
+ s->ticking[n] = 1;
+#ifdef DEBUG
+ interval = ticks_per_sec * interval_Sec;
+ exp = qemu_get_clock (vm_clock) + interval;
+ s->exp[n] = exp;
+#endif
+
+ s->dexp[n] = interval_Sec * 1000000.0;
+ AUD_init_time_stamp_out (s->voice, &s->ats);
}
static int write_audio (AdlibState *s, int samples)
{
int net = 0;
- int ss = samples;
+ int pos = s->pos;
+
while (samples) {
- int nbytes = samples << SHIFT;
- int wbytes = AUD_write (s->voice,
- s->mixbuf + (s->pos << (SHIFT - 1)),
- nbytes);
- int wsampl = wbytes >> SHIFT;
- samples -= wsampl;
- s->pos = (s->pos + wsampl) % s->samples;
- net += wsampl;
- if (!wbytes)
+ int nbytes, wbytes, wsampl;
+
+ nbytes = samples << SHIFT;
+ wbytes = AUD_write (
+ s->voice,
+ s->mixbuf + (pos << (SHIFT - 1)),
+ nbytes
+ );
+
+ if (wbytes) {
+ wsampl = wbytes >> SHIFT;
+
+ samples -= wsampl;
+ pos = (pos + wsampl) % s->samples;
+
+ net += wsampl;
+ }
+ else {
break;
+ }
}
- if (net > ss) {
- dolog ("WARNING: net > ss\n");
- }
+
return net;
}
-static void timer (void *opaque)
+static void adlib_callback (void *opaque, int free)
{
AdlibState *s = opaque;
- int elapsed, samples, net = 0;
-
- if (s->refcount)
- dolog ("refcount=%d\n", s->refcount);
+ int samples, net = 0, to_play, written;
- s->refcount += 1;
- if (!(s->active && s->enabled))
- goto reset;
-
- AUD_run ();
-
- while (s->left) {
- int written = write_audio (s, s->left);
- net += written;
- if (!written)
- goto reset2;
- s->left -= written;
+ samples = free >> SHIFT;
+ if (!(s->active && s->enabled) || !samples) {
+ return;
}
- s->pos = 0;
-
- elapsed = AUD_calc_elapsed (s->voice);
- if (!elapsed)
- goto reset2;
- /* elapsed = AUD_get_free (s->voice); */
- samples = elapsed >> SHIFT;
- if (!samples)
- goto reset2;
+ to_play = audio_MIN (s->left, samples);
+ while (to_play) {
+ written = write_audio (s, to_play);
+
+ if (written) {
+ s->left -= written;
+ samples -= written;
+ to_play -= written;
+ s->pos = (s->pos + written) % s->samples;
+ }
+ else {
+ return;
+ }
+ }
samples = audio_MIN (samples, s->samples - s->pos);
- if (s->left)
- dolog ("left=%d samples=%d elapsed=%d free=%d\n",
- s->left, samples, elapsed, AUD_get_free (s->voice));
-
- if (!samples)
- goto reset2;
+ if (!samples) {
+ return;
+ }
-#ifdef USE_YMF262
+#ifdef HAS_YMF262
YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
#else
YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
#endif
while (samples) {
- int written = write_audio (s, samples);
- net += written;
- if (!written)
- break;
- samples -= written;
+ written = write_audio (s, samples);
+
+ if (written) {
+ net += written;
+ samples -= written;
+ s->pos = (s->pos + written) % s->samples;
+ }
+ else {
+ s->left = samples;
+ return;
+ }
}
- if (!samples)
- s->pos = 0;
- s->left = samples;
-
-reset2:
- AUD_adjust (s->voice, net << SHIFT);
-reset:
- qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + ticks_per_sec / 1024);
- s->refcount -= 1;
}
static void Adlib_fini (AdlibState *s)
{
-#ifdef USE_YMF262
+#ifdef HAS_YMF262
YMF262Shutdown ();
#else
if (s->opl) {
@@ -229,15 +256,9 @@ static void Adlib_fini (AdlibState *s)
}
#endif
- if (s->opl_ts)
- qemu_free_timer (s->opl_ts);
-
- if (s->ts)
- qemu_free_timer (s->ts);
-
-#define maybe_free(p) if (p) qemu_free (p)
- maybe_free (s->mixbuf);
-#undef maybe_free
+ if (s->mixbuf) {
+ qemu_free (s->mixbuf);
+ }
s->active = 0;
s->enabled = 0;
@@ -247,15 +268,13 @@ void Adlib_init (void)
{
AdlibState *s = &adlib;
- memset (s, 0, sizeof (*s));
-
-#ifdef USE_YMF262
+#ifdef HAS_YMF262
if (YMF262Init (1, 14318180, conf.freq)) {
dolog ("YMF262Init %d failed\n", conf.freq);
return;
}
else {
- YMF262SetTimerHandler (0, YMF262TimerHandler, 0);
+ YMF262SetTimerHandler (0, timer_handler, 0);
s->enabled = 1;
}
#else
@@ -265,33 +284,26 @@ void Adlib_init (void)
return;
}
else {
- OPLSetTimerHandler (s->opl, YMF262TimerHandler, 0);
+ OPLSetTimerHandler (s->opl, timer_handler, 0);
s->enabled = 1;
}
#endif
- s->opl_ts = qemu_new_timer (vm_clock, OPL_timer, s);
- if (!s->opl_ts) {
- dolog ("Can not get timer for adlib emulation\n");
- Adlib_fini (s);
- return;
- }
-
- s->ts = qemu_new_timer (vm_clock, timer, s);
- if (!s->opl_ts) {
- dolog ("Can not get timer for adlib emulation\n");
- Adlib_fini (s);
- return;
- }
-
- s->voice = AUD_open (s->voice, "adlib", conf.freq, SHIFT, AUD_FMT_S16);
+ s->voice = AUD_open_out (
+ s->voice,
+ "adlib",
+ s,
+ adlib_callback,
+ conf.freq,
+ SHIFT,
+ AUD_FMT_S16
+ );
if (!s->voice) {
Adlib_fini (s);
return;
}
- s->bytes_per_second = conf.freq << SHIFT;
- s->samples = AUD_get_buffer_size (s->voice) >> SHIFT;
+ s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
s->mixbuf = qemu_mallocz (s->samples << SHIFT);
if (!s->mixbuf) {
@@ -300,6 +312,7 @@ void Adlib_init (void)
Adlib_fini (s);
return;
}
+
register_ioport_read (0x388, 4, 1, adlib_read, s);
register_ioport_write (0x388, 4, 1, adlib_write, s);
@@ -308,6 +321,4 @@ void Adlib_init (void)
register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
-
- qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
}