aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2005-10-30 18:58:22 +0000
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2005-10-30 18:58:22 +0000
commit1d14ffa97eacd3cb722271eaf6f093038396eac4 (patch)
tree1aae1f090262c3642cc672971890141050413d26 /hw
parent3b0d4f61c917c4612b561d75b33a11f4da00738b (diff)
downloadqemu-1d14ffa97eacd3cb722271eaf6f093038396eac4.zip
qemu-1d14ffa97eacd3cb722271eaf6f093038396eac4.tar.gz
qemu-1d14ffa97eacd3cb722271eaf6f093038396eac4.tar.bz2
merged 15a_aqemu.patch audio patch (malc)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1584 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'hw')
-rw-r--r--hw/adlib.c285
-rw-r--r--hw/es1370.c1007
-rw-r--r--hw/pc.c6
-rw-r--r--hw/sb16.c266
4 files changed, 1347 insertions, 217 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);
}
diff --git a/hw/es1370.c b/hw/es1370.c
new file mode 100644
index 0000000..0191f5f
--- /dev/null
+++ b/hw/es1370.c
@@ -0,0 +1,1007 @@
+/*
+ * QEMU ES1370 emulation
+ *
+ * Copyright (c) 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
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* #define DEBUG_ES1370 */
+/* #define VERBOSE_ES1370 */
+#define SILENT_ES1370
+
+#include "vl.h"
+
+/* Missing stuff:
+ SCTRL_P[12](END|ST)INC
+ SCTRL_P1SCTRLD
+ SCTRL_P2DACSEN
+ CTRL_DAC_SYNC
+ MIDI
+ non looped mode
+ surely more
+*/
+
+/*
+ Following macros and samplerate array were copied verbatim from
+ Linux kernel 2.4.30: drivers/sound/es1370.c
+
+ Copyright (C) 1998-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch)
+*/
+
+/* Start blatant GPL violation */
+
+#define ES1370_REG_CONTROL 0x00
+#define ES1370_REG_STATUS 0x04
+#define ES1370_REG_UART_DATA 0x08
+#define ES1370_REG_UART_STATUS 0x09
+#define ES1370_REG_UART_CONTROL 0x09
+#define ES1370_REG_UART_TEST 0x0a
+#define ES1370_REG_MEMPAGE 0x0c
+#define ES1370_REG_CODEC 0x10
+#define ES1370_REG_SERIAL_CONTROL 0x20
+#define ES1370_REG_DAC1_SCOUNT 0x24
+#define ES1370_REG_DAC2_SCOUNT 0x28
+#define ES1370_REG_ADC_SCOUNT 0x2c
+
+#define ES1370_REG_DAC1_FRAMEADR 0xc30
+#define ES1370_REG_DAC1_FRAMECNT 0xc34
+#define ES1370_REG_DAC2_FRAMEADR 0xc38
+#define ES1370_REG_DAC2_FRAMECNT 0xc3c
+#define ES1370_REG_ADC_FRAMEADR 0xd30
+#define ES1370_REG_ADC_FRAMECNT 0xd34
+#define ES1370_REG_PHANTOM_FRAMEADR 0xd38
+#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c
+
+static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 };
+
+#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2)
+#define DAC2_DIVTOSR(x) (1411200/((x)+2))
+
+#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */
+#define CTRL_XCTL1 0x40000000 /* electret mic bias */
+#define CTRL_OPEN 0x20000000 /* no function, can be read and written */
+#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */
+#define CTRL_SH_PCLKDIV 16
+#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */
+#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */
+#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */
+#define CTRL_SH_WTSRSEL 12
+#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */
+#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */
+#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 = MPEG */
+#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */
+#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */
+#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */
+#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */
+#define CTRL_ADC_EN 0x00000010 /* enable ADC */
+#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */
+#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably at address 0x200) */
+#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */
+#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */
+
+#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */
+#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in progress */
+#define STAT_CBUSY 0x00000200 /* 1 = codec busy */
+#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */
+#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */
+#define STAT_SH_VC 5
+#define STAT_MCCB 0x00000010 /* CCB int pending */
+#define STAT_UART 0x00000008 /* UART int pending */
+#define STAT_DAC1 0x00000004 /* DAC1 int pending */
+#define STAT_DAC2 0x00000002 /* DAC2 int pending */
+#define STAT_ADC 0x00000001 /* ADC int pending */
+
+#define USTAT_RXINT 0x80 /* UART rx int pending */
+#define USTAT_TXINT 0x04 /* UART tx int pending */
+#define USTAT_TXRDY 0x02 /* UART tx ready */
+#define USTAT_RXRDY 0x01 /* UART rx ready */
+
+#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */
+#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */
+#define UCTRL_ENA_TXINT 0x20 /* enable TX int */
+#define UCTRL_CNTRL 0x03 /* control field */
+#define UCTRL_CNTRL_SWR 0x03 /* software reset command */
+
+#define SCTRL_P2ENDINC 0x00380000 /* */
+#define SCTRL_SH_P2ENDINC 19
+#define SCTRL_P2STINC 0x00070000 /* */
+#define SCTRL_SH_P2STINC 16
+#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */
+#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */
+#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */
+#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */
+#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */
+#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */
+#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */
+#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */
+#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */
+#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */
+#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */
+#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */
+#define SCTRL_R1FMT 0x00000030 /* format mask */
+#define SCTRL_SH_R1FMT 4
+#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */
+#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */
+#define SCTRL_P2FMT 0x0000000c /* format mask */
+#define SCTRL_SH_P2FMT 2
+#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */
+#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */
+#define SCTRL_P1FMT 0x00000003 /* format mask */
+#define SCTRL_SH_P1FMT 0
+
+/* End blatant GPL violation */
+
+#define NB_CHANNELS 3
+#define DAC1_CHANNEL 0
+#define DAC2_CHANNEL 1
+#define ADC_CHANNEL 2
+
+#define IO_READ_PROTO(n) \
+static uint32_t n (void *opaque, uint32_t addr)
+#define IO_WRITE_PROTO(n) \
+static void n (void *opaque, uint32_t addr, uint32_t val)
+
+static void es1370_dac1_callback (void *opaque, int free);
+static void es1370_dac2_callback (void *opaque, int free);
+static void es1370_adc_callback (void *opaque, int avail);
+
+#ifdef DEBUG_ES1370
+
+#define ldebug(...) AUD_log ("es1370", __VA_ARGS__)
+
+static void print_ctl (uint32_t val)
+{
+ char buf[1024];
+
+ buf[0] = '\0';
+#define a(n) if (val & CTRL_##n) strcat (buf, " "#n)
+ a (ADC_STOP);
+ a (XCTL1);
+ a (OPEN);
+ a (MSFMTSEL);
+ a (M_SBB);
+ a (DAC_SYNC);
+ a (CCB_INTRM);
+ a (M_CB);
+ a (XCTL0);
+ a (BREQ);
+ a (DAC1_EN);
+ a (DAC2_EN);
+ a (ADC_EN);
+ a (UART_EN);
+ a (JYSTK_EN);
+ a (CDC_EN);
+ a (SERR_DIS);
+#undef a
+ AUD_log ("es1370", "ctl - PCLKDIV %d(DAC2 freq %d), freq %d,%s\n",
+ (val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV,
+ DAC2_DIVTOSR ((val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV),
+ dac1_samplerate[(val & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL],
+ buf);
+}
+
+static void print_sctl (uint32_t val)
+{
+ static const char *fmt_names[] = {"8M", "8S", "16M", "16S"};
+ char buf[1024];
+
+ buf[0] = '\0';
+
+#define a(n) if (val & SCTRL_##n) strcat (buf, " "#n)
+#define b(n) if (!(val & SCTRL_##n)) strcat (buf, " "#n)
+ b (R1LOOPSEL);
+ b (P2LOOPSEL);
+ b (P1LOOPSEL);
+ a (P2PAUSE);
+ a (P1PAUSE);
+ a (R1INTEN);
+ a (P2INTEN);
+ a (P1INTEN);
+ a (P1SCTRLD);
+ a (P2DACSEN);
+ if (buf[0]) {
+ strcat (buf, "\n ");
+ }
+ else {
+ buf[0] = ' ';
+ buf[1] = '\0';
+ }
+#undef b
+#undef a
+ AUD_log ("es1370",
+ "%s"
+ "p2_end_inc %d, p2_st_inc %d, r1_fmt %s, p2_fmt %s, p1_fmt %s\n",
+ buf,
+ (val & SCTRL_P2ENDINC) >> SCTRL_SH_P2ENDINC,
+ (val & SCTRL_P2STINC) >> SCTRL_SH_P2STINC,
+ fmt_names [(val >> SCTRL_SH_R1FMT) & 3],
+ fmt_names [(val >> SCTRL_SH_P2FMT) & 3],
+ fmt_names [(val >> SCTRL_SH_P1FMT) & 3]
+ );
+}
+#else
+#define ldebug(...)
+#define print_ctl(...)
+#define print_sctl(...)
+#endif
+
+#ifdef VERBOSE_ES1370
+#define dolog(...) AUD_log ("es1370", __VA_ARGS__)
+#else
+#define dolog(...)
+#endif
+
+#ifndef SILENT_ES1370
+#define lwarn(...) AUD_log ("es1370: warning:", __VA_ARGS__)
+#else
+#define lwarn(...)
+#endif
+
+struct chan {
+ uint32_t shift;
+ uint32_t leftover;
+ uint32_t scount;
+ uint32_t frame_addr;
+ uint32_t frame_cnt;
+};
+
+typedef struct ES1370State {
+ PCIDevice *pci_dev;
+
+ struct chan chan[NB_CHANNELS];
+ SWVoiceOut *dac_voice[2];
+ SWVoiceIn *adc_voice;
+
+ uint32_t ctl;
+ uint32_t status;
+ uint32_t mempage;
+ uint32_t codec;
+ uint32_t sctl;
+} ES1370State;
+
+typedef struct PCIES1370State {
+ PCIDevice dev;
+ ES1370State es1370;
+} PCIES1370State;
+
+struct chan_bits {
+ uint32_t ctl_en;
+ uint32_t stat_int;
+ uint32_t sctl_pause;
+ uint32_t sctl_inten;
+ uint32_t sctl_fmt;
+ uint32_t sctl_sh_fmt;
+ uint32_t sctl_loopsel;
+ void (*calc_freq) (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq, uint32_t *new_freq);
+};
+
+static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq, uint32_t *new_freq);
+static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq,
+ uint32_t *new_freq);
+
+static const struct chan_bits es1370_chan_bits[] = {
+ {CTRL_DAC1_EN, STAT_DAC1, SCTRL_P1PAUSE, SCTRL_P1INTEN,
+ SCTRL_P1FMT, SCTRL_SH_P1FMT, SCTRL_P1LOOPSEL,
+ es1370_dac1_calc_freq},
+
+ {CTRL_DAC2_EN, STAT_DAC2, SCTRL_P2PAUSE, SCTRL_P2INTEN,
+ SCTRL_P2FMT, SCTRL_SH_P2FMT, SCTRL_P2LOOPSEL,
+ es1370_dac2_and_adc_calc_freq},
+
+ {CTRL_ADC_EN, STAT_ADC, 0, SCTRL_R1INTEN,
+ SCTRL_R1FMT, SCTRL_SH_R1FMT, SCTRL_R1LOOPSEL,
+ es1370_dac2_and_adc_calc_freq}
+};
+
+static void es1370_update_status (ES1370State *s, uint32_t new_status)
+{
+ uint32_t level = new_status & (STAT_DAC1 | STAT_DAC2 | STAT_ADC);
+
+ if (level) {
+ s->status = new_status | STAT_INTR;
+ }
+ else {
+ s->status = new_status & ~STAT_INTR;
+ }
+ pci_set_irq (s->pci_dev, 0, !!level);
+}
+
+static void es1370_reset (ES1370State *s)
+{
+ size_t i;
+
+ s->ctl = 1;
+ s->status = 0x60;
+ s->mempage = 0;
+ s->codec = 0;
+ s->sctl = 0;
+
+ for (i = 0; i < NB_CHANNELS; ++i) {
+ struct chan *d = &s->chan[i];
+ d->scount = 0;
+ d->leftover = 0;
+ if (i == ADC_CHANNEL) {
+ AUD_close_in (s->adc_voice);
+ s->adc_voice = NULL;
+ }
+ else {
+ AUD_close_out (s->dac_voice[i]);
+ s->dac_voice[i] = NULL;
+ }
+ }
+ pci_set_irq (s->pci_dev, 0, 0);
+}
+
+static void es1370_maybe_lower_irq (ES1370State *s, uint32_t sctl)
+{
+ uint32_t new_status = s->status;
+
+ if (!(sctl & SCTRL_P1INTEN) && (s->sctl & SCTRL_P1INTEN)) {
+ new_status &= ~STAT_DAC1;
+ }
+
+ if (!(sctl & SCTRL_P2INTEN) && (s->sctl & SCTRL_P2INTEN)) {
+ new_status &= ~STAT_DAC2;
+ }
+
+ if (!(sctl & SCTRL_R1INTEN) && (s->sctl & SCTRL_R1INTEN)) {
+ new_status &= ~STAT_ADC;
+ }
+
+ if (new_status != s->status) {
+ es1370_update_status (s, new_status);
+ }
+}
+
+static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq, uint32_t *new_freq)
+
+{
+ *old_freq = dac1_samplerate[(s->ctl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
+ *new_freq = dac1_samplerate[(ctl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
+}
+
+static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq,
+ uint32_t *new_freq)
+
+{
+ uint32_t old_pclkdiv, new_pclkdiv;
+
+ new_pclkdiv = (ctl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV;
+ old_pclkdiv = (s->ctl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV;
+ *new_freq = DAC2_DIVTOSR (new_pclkdiv);
+ *old_freq = DAC2_DIVTOSR (old_pclkdiv);
+}
+
+static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl)
+{
+ size_t i;
+ uint32_t old_freq, new_freq, old_fmt, new_fmt;
+
+ for (i = 0; i < NB_CHANNELS; ++i) {
+ struct chan *d = &s->chan[i];
+ const struct chan_bits *b = &es1370_chan_bits[i];
+
+ new_fmt = (sctl & b->sctl_fmt) >> b->sctl_sh_fmt;
+ old_fmt = (s->sctl & b->sctl_fmt) >> b->sctl_sh_fmt;
+
+ b->calc_freq (s, ctl, &old_freq, &new_freq);
+
+ if ((old_fmt != new_fmt) || (old_freq != new_freq)) {
+ d->shift = (new_fmt & 1) + (new_fmt >> 1);
+ ldebug ("channel %d, freq = %d, nchannels %d, fmt %d, shift %d\n",
+ i,
+ new_freq,
+ 1 << (new_fmt & 1),
+ (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8,
+ d->shift);
+ if (new_freq) {
+ if (i == ADC_CHANNEL) {
+ s->adc_voice =
+ AUD_open_in (
+ s->adc_voice,
+ "es1370.adc",
+ s,
+ es1370_adc_callback,
+ new_freq,
+ 1 << (new_fmt & 1),
+ (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8
+ );
+ }
+ else {
+ s->dac_voice[i] =
+ AUD_open_out (
+ s->dac_voice[i],
+ i ? "es1370.dac2" : "es1370.dac1",
+ s,
+ i ? es1370_dac2_callback : es1370_dac1_callback,
+ new_freq,
+ 1 << (new_fmt & 1),
+ (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8
+ );
+ }
+ }
+ }
+
+ if (((ctl ^ s->ctl) & b->ctl_en)
+ || ((sctl ^ s->sctl) & b->sctl_pause)) {
+ int on = (ctl & b->ctl_en) && !(sctl & b->sctl_pause);
+
+ if (i == ADC_CHANNEL) {
+ AUD_set_active_in (s->adc_voice, on);
+ }
+ else {
+ AUD_set_active_out (s->dac_voice[i], on);
+ }
+ }
+ }
+
+ s->ctl = ctl;
+ s->sctl = sctl;
+}
+
+static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr)
+{
+ addr &= 0xff;
+ if (addr >= 0x30 && addr <= 0x3f)
+ addr |= s->mempage << 8;
+ return addr;
+}
+
+IO_WRITE_PROTO (es1370_writeb)
+{
+ ES1370State *s = opaque;
+ addr = es1370_fixup (s, addr);
+ uint32_t shift, mask;
+
+ switch (addr) {
+ case ES1370_REG_CONTROL:
+ case ES1370_REG_CONTROL + 1:
+ case ES1370_REG_CONTROL + 2:
+ case ES1370_REG_CONTROL + 3:
+ shift = (addr - ES1370_REG_CONTROL) << 3;
+ mask = 0xff << shift;
+ val = (s->ctl & ~mask) | ((val & 0xff) << shift);
+ es1370_update_voices (s, val, s->sctl);
+ print_ctl (val);
+ break;
+ case ES1370_REG_MEMPAGE:
+ s->mempage = val;
+ break;
+ case ES1370_REG_SERIAL_CONTROL:
+ case ES1370_REG_SERIAL_CONTROL + 1:
+ case ES1370_REG_SERIAL_CONTROL + 2:
+ case ES1370_REG_SERIAL_CONTROL + 3:
+ shift = (addr - ES1370_REG_SERIAL_CONTROL) << 3;
+ mask = 0xff << shift;
+ val = (s->sctl & ~mask) | ((val & 0xff) << shift);
+ es1370_maybe_lower_irq (s, val);
+ es1370_update_voices (s, s->ctl, val);
+ print_sctl (val);
+ break;
+ default:
+ lwarn ("writeb %#x <- %#x\n", addr, val);
+ break;
+ }
+}
+
+IO_WRITE_PROTO (es1370_writew)
+{
+ ES1370State *s = opaque;
+ addr = es1370_fixup (s, addr);
+ uint32_t shift, mask;
+ struct chan *d = &s->chan[0];
+
+ switch (addr) {
+ case ES1370_REG_CODEC:
+ dolog ("ignored codec write address %#x, data %#x\n",
+ (val >> 8) & 0xff, val & 0xff);
+ s->codec = val;
+ break;
+
+ case ES1370_REG_CONTROL:
+ case ES1370_REG_CONTROL + 2:
+ shift = (addr != ES1370_REG_CONTROL) << 4;
+ mask = 0xffff << shift;
+ val = (s->ctl & ~mask) | ((val & 0xffff) << shift);
+ es1370_update_voices (s, val, s->sctl);
+ print_ctl (val);
+ break;
+
+ case ES1370_REG_ADC_SCOUNT:
+ d++;
+ case ES1370_REG_DAC2_SCOUNT:
+ d++;
+ case ES1370_REG_DAC1_SCOUNT:
+ d->scount = (d->scount & ~0xffff) | (val & 0xffff);
+ break;
+
+ default:
+ lwarn ("writew %#x <- %#x\n", addr, val);
+ break;
+ }
+}
+
+IO_WRITE_PROTO (es1370_writel)
+{
+ ES1370State *s = opaque;
+ struct chan *d = &s->chan[0];
+
+ addr = es1370_fixup (s, addr);
+
+ switch (addr) {
+ case ES1370_REG_CONTROL:
+ es1370_update_voices (s, val, s->sctl);
+ print_ctl (val);
+ break;
+
+ case ES1370_REG_MEMPAGE:
+ s->mempage = val & 0xf;
+ break;
+
+ case ES1370_REG_SERIAL_CONTROL:
+ es1370_maybe_lower_irq (s, val);
+ es1370_update_voices (s, s->ctl, val);
+ print_sctl (val);
+ break;
+
+ case ES1370_REG_ADC_SCOUNT:
+ d++;
+ case ES1370_REG_DAC2_SCOUNT:
+ d++;
+ case ES1370_REG_DAC1_SCOUNT:
+ d->scount = (val & 0xffff) | (d->scount & ~0xffff);
+ ldebug ("chan %d CURR_SAMP_CT %d, SAMP_CT %d\n",
+ d - &s->chan[0], val >> 16, (val & 0xffff));
+ break;
+
+ case ES1370_REG_ADC_FRAMEADR:
+ d++;
+ case ES1370_REG_DAC2_FRAMEADR:
+ d++;
+ case ES1370_REG_DAC1_FRAMEADR:
+ d->frame_addr = val;
+ ldebug ("chan %d frame address %#x\n", d - &s->chan[0], val);
+ break;
+
+ case ES1370_REG_ADC_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC2_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC1_FRAMECNT:
+ d->frame_cnt = val;
+ d->leftover = 0;
+ ldebug ("chan %d frame count %d, buffer size %d\n",
+ d - &s->chan[0], val >> 16, val & 0xffff);
+ break;
+
+ default:
+ lwarn ("writel %#x <- %#x\n", addr, val);
+ break;
+ }
+}
+
+IO_READ_PROTO (es1370_readb)
+{
+ ES1370State *s = opaque;
+ uint32_t val;
+
+ addr = es1370_fixup (s, addr);
+
+ switch (addr) {
+ case 0x1b: /* Legacy */
+ lwarn ("Attempt to read from legacy register\n");
+ val = 5;
+ break;
+ case ES1370_REG_MEMPAGE:
+ val = s->mempage;
+ break;
+ case ES1370_REG_CONTROL + 0:
+ case ES1370_REG_CONTROL + 1:
+ case ES1370_REG_CONTROL + 2:
+ case ES1370_REG_CONTROL + 3:
+ val = s->ctl >> ((addr - ES1370_REG_CONTROL) << 3);
+ break;
+ case ES1370_REG_STATUS + 0:
+ case ES1370_REG_STATUS + 1:
+ case ES1370_REG_STATUS + 2:
+ case ES1370_REG_STATUS + 3:
+ val = s->status >> ((addr - ES1370_REG_STATUS) << 3);
+ break;
+ default:
+ val = ~0;
+ lwarn ("readb %#x -> %#x\n", addr, val);
+ break;
+ }
+ return val;
+}
+
+IO_READ_PROTO (es1370_readw)
+{
+ ES1370State *s = opaque;
+ struct chan *d = &s->chan[0];
+ uint32_t val;
+
+ addr = es1370_fixup (s, addr);
+
+ switch (addr) {
+ case ES1370_REG_ADC_SCOUNT + 2:
+ d++;
+ case ES1370_REG_DAC2_SCOUNT + 2:
+ d++;
+ case ES1370_REG_DAC1_SCOUNT + 2:
+ val = d->scount >> 16;
+ break;
+
+ default:
+ val = ~0;
+ lwarn ("readw %#x -> %#x\n", addr, val);
+ break;
+ }
+
+ return val;
+}
+
+IO_READ_PROTO (es1370_readl)
+{
+ ES1370State *s = opaque;
+ uint32_t val;
+ struct chan *d = &s->chan[0];
+
+ addr = es1370_fixup (s, addr);
+
+ switch (addr) {
+ case ES1370_REG_CONTROL:
+ val = s->ctl;
+ break;
+ case ES1370_REG_STATUS:
+ val = s->status;
+ break;
+ case ES1370_REG_MEMPAGE:
+ val = s->mempage;
+ break;
+ case ES1370_REG_CODEC:
+ val = s->codec;
+ break;
+ case ES1370_REG_SERIAL_CONTROL:
+ val = s->sctl;
+ break;
+
+ case ES1370_REG_ADC_SCOUNT:
+ d++;
+ case ES1370_REG_DAC2_SCOUNT:
+ d++;
+ case ES1370_REG_DAC1_SCOUNT:
+ val = d->scount;
+#ifdef DEBUG_ES1370
+ {
+ uint32_t curr_count = d->scount >> 16;
+ uint32_t count = d->scount & 0xffff;
+
+ curr_count <<= d->shift;
+ count <<= d->shift;
+ dolog ("read scount curr %d, total %d\n", curr_count, count);
+ }
+#endif
+ break;
+
+ case ES1370_REG_ADC_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC2_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC1_FRAMECNT:
+ val = d->frame_cnt;
+#ifdef DEBUG_ES1370
+ {
+ uint32_t size = ((d->frame_cnt & 0xffff) + 1) << 2;
+ uint32_t curr = ((d->frame_cnt >> 16) + 1) << 2;
+ if (curr > size)
+ dolog ("read framecnt curr %d, size %d %d\n", curr, size,
+ curr > size);
+ }
+#endif
+ break;
+
+ case ES1370_REG_ADC_FRAMEADR:
+ d++;
+ case ES1370_REG_DAC2_FRAMEADR:
+ d++;
+ case ES1370_REG_DAC1_FRAMEADR:
+ val = d->frame_addr;
+ break;
+
+ default:
+ val = ~0U;
+ lwarn ("readl %#x -> %#x\n", addr, val);
+ break;
+ }
+ return val;
+}
+
+
+static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel,
+ int max, int *irq)
+{
+ uint8_t tmpbuf[4096];
+ uint32_t addr = d->frame_addr;
+ int sc = d->scount & 0xffff;
+ int csc = d->scount >> 16;
+ int csc_bytes = (csc + 1) << d->shift;
+ int cnt = d->frame_cnt >> 16;
+ int size = d->frame_cnt & 0xffff;
+ int left = ((size - cnt + 1) << 2) + d->leftover;
+ int transfered = 0;
+ int temp = audio_MIN (max, audio_MIN (left, csc_bytes));
+ int index = d - &s->chan[0];
+
+ addr += (cnt << 2) + d->leftover;
+
+ if (index == ADC_CHANNEL) {
+ while (temp) {
+ int acquired, to_copy;
+
+ to_copy = audio_MIN (temp, sizeof (tmpbuf));
+ acquired = AUD_read (s->adc_voice, tmpbuf, to_copy);
+ if (!acquired)
+ break;
+
+ cpu_physical_memory_write (addr, tmpbuf, acquired);
+
+ temp -= acquired;
+ addr += acquired;
+ transfered += acquired;
+ }
+ }
+ else {
+ SWVoiceOut *voice = s->dac_voice[index];
+
+ while (temp) {
+ int copied, to_copy;
+
+ to_copy = audio_MIN (temp, sizeof (tmpbuf));
+ cpu_physical_memory_read (addr, tmpbuf, to_copy);
+ copied = AUD_write (voice, tmpbuf, to_copy);
+ if (!copied)
+ break;
+ temp -= copied;
+ addr += copied;
+ transfered += copied;
+ }
+ }
+
+ if (csc_bytes == transfered) {
+ *irq = 1;
+ d->scount = sc | (sc << 16);
+ ldebug ("sc = %d, rate = %f\n",
+ (sc + 1) << d->shift,
+ (sc + 1) / (double) 44100);
+ }
+ else {
+ *irq = 0;
+ d->scount = sc | (((csc_bytes - transfered - 1) >> d->shift) << 16);
+ }
+
+ cnt += (transfered + d->leftover) >> 2;
+
+ if (s->sctl & loop_sel) {
+ /* Bah, how stupid is that having a 0 represent true value?
+ i just spent few hours on this shit */
+ lwarn ("whoops non looping mode\n");
+ }
+ else {
+ d->frame_cnt = size;
+
+ if (cnt <= d->frame_cnt)
+ d->frame_cnt |= cnt << 16;
+ }
+
+ d->leftover = (transfered + d->leftover) & 3;
+}
+
+static void es1370_run_channel (ES1370State *s, size_t chan, int free_or_avail)
+{
+ uint32_t new_status = s->status;
+ int max_bytes, irq;
+ struct chan *d = &s->chan[chan];
+ const struct chan_bits *b = &es1370_chan_bits[chan];
+
+ if (!(s->ctl & b->ctl_en) || (s->sctl & b->sctl_pause)) {
+ return;
+ }
+
+ max_bytes = free_or_avail;
+ max_bytes &= ~((1 << d->shift) - 1);
+ if (!max_bytes) {
+ return;
+ }
+
+ es1370_transfer_audio (s, d, b->sctl_loopsel, max_bytes, &irq);
+
+ if (irq) {
+ if (s->sctl & b->sctl_inten) {
+ new_status |= b->stat_int;
+ }
+ }
+
+ if (new_status != s->status) {
+ es1370_update_status (s, new_status);
+ }
+}
+
+static void es1370_dac1_callback (void *opaque, int free)
+{
+ ES1370State *s = opaque;
+
+ es1370_run_channel (s, DAC1_CHANNEL, free);
+}
+
+static void es1370_dac2_callback (void *opaque, int free)
+{
+ ES1370State *s = opaque;
+
+ es1370_run_channel (s, DAC2_CHANNEL, free);
+}
+
+static void es1370_adc_callback (void *opaque, int avail)
+{
+ ES1370State *s = opaque;
+
+ es1370_run_channel (s, ADC_CHANNEL, avail);
+}
+
+static void es1370_map (PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCIES1370State *d = (PCIES1370State *) pci_dev;
+ ES1370State *s = &d->es1370;
+
+ register_ioport_write (addr, 0x40 * 4, 1, es1370_writeb, s);
+ register_ioport_write (addr, 0x40 * 2, 2, es1370_writew, s);
+ register_ioport_write (addr, 0x40, 4, es1370_writel, s);
+
+ register_ioport_read (addr, 0x40 * 4, 1, es1370_readb, s);
+ register_ioport_read (addr, 0x40 * 2, 2, es1370_readw, s);
+ register_ioport_read (addr, 0x40, 4, es1370_readl, s);
+}
+
+static void es1370_save (QEMUFile *f, void *opaque)
+{
+ ES1370State *s = opaque;
+ size_t i;
+
+ for (i = 0; i < NB_CHANNELS; ++i) {
+ struct chan *d = &s->chan[i];
+ qemu_put_be32s (f, &d->shift);
+ qemu_put_be32s (f, &d->leftover);
+ qemu_put_be32s (f, &d->scount);
+ qemu_put_be32s (f, &d->frame_addr);
+ qemu_put_be32s (f, &d->frame_cnt);
+ }
+ qemu_put_be32s (f, &s->ctl);
+ qemu_put_be32s (f, &s->status);
+ qemu_put_be32s (f, &s->mempage);
+ qemu_put_be32s (f, &s->codec);
+ qemu_put_be32s (f, &s->sctl);
+}
+
+static int es1370_load (QEMUFile *f, void *opaque, int version_id)
+{
+ uint32_t ctl, sctl;
+ ES1370State *s = opaque;
+ size_t i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ for (i = 0; i < NB_CHANNELS; ++i) {
+ struct chan *d = &s->chan[i];
+ qemu_get_be32s (f, &d->shift);
+ qemu_get_be32s (f, &d->leftover);
+ qemu_get_be32s (f, &d->scount);
+ qemu_get_be32s (f, &d->frame_addr);
+ qemu_get_be32s (f, &d->frame_cnt);
+ if (i == ADC_CHANNEL) {
+ if (s->adc_voice) {
+ AUD_close_in (s->adc_voice);
+ s->adc_voice = NULL;
+ }
+ }
+ else {
+ if (s->dac_voice[i]) {
+ AUD_close_out (s->dac_voice[i]);
+ s->dac_voice[i] = NULL;
+ }
+ }
+ }
+
+ qemu_get_be32s (f, &ctl);
+ qemu_get_be32s (f, &s->status);
+ qemu_get_be32s (f, &s->mempage);
+ qemu_get_be32s (f, &s->codec);
+ qemu_get_be32s (f, &sctl);
+
+ s->ctl = 0;
+ s->sctl = 0;
+ es1370_update_voices (s, ctl, sctl);
+ return 0;
+}
+
+static void es1370_on_reset (void *opaque)
+{
+ ES1370State *s = opaque;
+ es1370_reset (s);
+}
+
+int es1370_init (PCIBus *bus)
+{
+ PCIES1370State *d;
+ ES1370State *s;
+ uint8_t *c;
+
+ d = (PCIES1370State *) pci_register_device (bus, "ES1370",
+ sizeof (PCIES1370State),
+ -1, NULL, NULL);
+
+ if (!d) {
+ fprintf (stderr, "Failed to register PCI device for ES1370\n");
+ return -1;
+ }
+
+ c = d->dev.config;
+ c[0x00] = 0x74;
+ c[0x01] = 0x12;
+ c[0x02] = 0x00;
+ c[0x03] = 0x50;
+ c[0x07] = 2 << 1;
+ c[0x0a] = 0x01;
+ c[0x0b] = 0x04;
+
+#if 1
+ c[0x2c] = 0x42;
+ c[0x2d] = 0x49;
+ c[0x2e] = 0x4c;
+ c[0x2f] = 0x4c;
+#else
+ c[0x2c] = 0x74;
+ c[0x2d] = 0x12;
+ c[0x2e] = 0x71;
+ c[0x2f] = 0x13;
+ c[0x34] = 0xdc;
+ c[0x3c] = 10;
+ c[0xdc] = 0x00;
+#endif
+
+ c[0x3d] = 1;
+ c[0x3e] = 0x0c;
+ c[0x3f] = 0x80;
+
+ s = &d->es1370;
+ s->pci_dev = &d->dev;
+
+ pci_register_io_region (&d->dev, 0, 256, PCI_ADDRESS_SPACE_IO, es1370_map);
+ register_savevm ("es1370", 0, 1, es1370_save, es1370_load, s);
+ qemu_register_reset (es1370_on_reset, s);
+ es1370_reset (s);
+ return 0;
+}
diff --git a/hw/pc.c b/hw/pc.c
index f0ae1b9..ad3d5e0 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -602,18 +602,18 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device,
if (audio_enabled) {
AUD_init();
-#ifdef USE_SB16
if (sb16_enabled)
SB16_init ();
-#endif
#ifdef CONFIG_ADLIB
if (adlib_enabled)
Adlib_init ();
#endif
-#ifdef USE_GUS
+#ifdef CONFIG_GUS
if (gus_enabled)
GUS_init ();
#endif
+ if (pci_enabled && es1370_enabled)
+ es1370_init (pci_bus);
}
floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table);
diff --git a/hw/sb16.c b/hw/sb16.c
index bca5795..e79d192 100644
--- a/hw/sb16.c
+++ b/hw/sb16.c
@@ -1,8 +1,8 @@
/*
* QEMU Soundblaster 16 emulation
- *
- * Copyright (c) 2003-2004 Vassili Karpov (malc)
- *
+ *
+ * Copyright (c) 2003-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
@@ -99,9 +99,10 @@ typedef struct SB16State {
int dma_running;
int bytes_per_second;
int align;
- SWVoice *voice;
+ int audio_free;
+ SWVoiceOut *voice;
- QEMUTimer *ts, *aux_ts;
+ QEMUTimer *aux_ts;
/* mixer state */
int mixer_nreg;
uint8_t mixer_regs[256];
@@ -110,6 +111,8 @@ typedef struct SB16State {
/* XXX: suppress that and use a context */
static struct SB16State dsp;
+static void SB_audio_callback (void *opaque, int free);
+
static int magic_of_irq (int irq)
{
switch (irq) {
@@ -174,11 +177,11 @@ static void control (SB16State *s, int hold)
if (hold) {
DMA_hold_DREQ (dma);
- AUD_enable (s->voice, 1);
+ AUD_set_active_out (s->voice, 1);
}
else {
DMA_release_DREQ (dma);
- AUD_enable (s->voice, 0);
+ AUD_set_active_out (s->voice, 0);
}
}
@@ -207,8 +210,9 @@ static void dma_cmd8 (SB16State *s, int mask, int dma_len)
s->freq = (1000000 + (tmp / 2)) / tmp;
}
- if (dma_len != -1)
+ if (dma_len != -1) {
s->block_size = dma_len << s->fmt_stereo;
+ }
else {
/* This is apparently the only way to make both Act1/PL
and SecondReality/FC work
@@ -227,17 +231,28 @@ static void dma_cmd8 (SB16State *s, int mask, int dma_len)
s->dma_auto = (mask & DMA8_AUTO) != 0;
s->align = (1 << s->fmt_stereo) - 1;
- if (s->block_size & s->align)
- dolog ("warning: unaligned buffer\n");
+ if (s->block_size & s->align) {
+ dolog ("warning: misaligned block size %d, alignment %d\n",
+ s->block_size, s->align + 1);
+ }
ldebug ("freq %d, stereo %d, sign %d, bits %d, "
"dma %d, auto %d, fifo %d, high %d\n",
s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
s->block_size, s->dma_auto, s->fifo, s->highspeed);
- if (s->freq)
- s->voice = AUD_open (s->voice, "sb16", s->freq,
- 1 << s->fmt_stereo, s->fmt);
+ if (s->freq) {
+ s->audio_free = 0;
+ s->voice = AUD_open_out (
+ s->voice,
+ "sb16",
+ s,
+ SB_audio_callback,
+ s->freq,
+ 1 << s->fmt_stereo,
+ s->fmt
+ );
+ }
control (s, 1);
speaker (s, 1);
@@ -309,12 +324,23 @@ static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
s->bytes_per_second = (s->freq << s->fmt_stereo) << (s->fmt_bits == 16);
s->highspeed = 0;
s->align = (1 << (s->fmt_stereo + (s->fmt_bits == 16))) - 1;
- if (s->block_size & s->align)
- dolog ("warning: unaligned buffer\n");
+ if (s->block_size & s->align) {
+ dolog ("warning: misaligned block size %d, alignment %d\n",
+ s->block_size, s->align + 1);
+ }
- if (s->freq)
- s->voice = AUD_open (s->voice, "sb16", s->freq,
- 1 << s->fmt_stereo, s->fmt);
+ if (s->freq) {
+ s->audio_free = 0;
+ s->voice = AUD_open_out (
+ s->voice,
+ "sb16",
+ s,
+ SB_audio_callback,
+ s->freq,
+ 1 << s->fmt_stereo,
+ s->fmt
+ );
+ }
control (s, 1);
speaker (s, 1);
@@ -323,14 +349,16 @@ static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
static inline void dsp_out_data (SB16State *s, uint8_t val)
{
ldebug ("outdata %#x\n", val);
- if (s->out_data_len < sizeof (s->out_data))
+ if (s->out_data_len < sizeof (s->out_data)) {
s->out_data[s->out_data_len++] = val;
+ }
}
static inline uint8_t dsp_get_data (SB16State *s)
{
- if (s->in_index)
+ if (s->in_index) {
return s->in2_data[--s->in_index];
+ }
else {
dolog ("buffer underflow\n");
return 0;
@@ -356,6 +384,8 @@ static void command (SB16State *s, uint8_t cmd)
s->needed_bytes = 3;
}
else {
+ s->needed_bytes = 0;
+
switch (cmd) {
case 0x03:
dsp_out_data (s, 0x10); /* s->csp_param); */
@@ -403,7 +433,7 @@ static void command (SB16State *s, uint8_t cmd)
goto warn;
case 0x35:
- dolog ("MIDI command(0x35) not implemented\n");
+ dolog ("0x35 - MIDI command not implemented\n");
break;
case 0x40:
@@ -435,6 +465,38 @@ static void command (SB16State *s, uint8_t cmd)
s->needed_bytes = 2;
break;
+ case 0x74:
+ s->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */
+ dolog ("0x75 - DMA DAC, 4-bit ADPCM not implemented\n");
+ break;
+
+ case 0x75: /* DMA DAC, 4-bit ADPCM Reference */
+ s->needed_bytes = 2;
+ dolog ("0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n");
+ break;
+
+ case 0x76: /* DMA DAC, 2.6-bit ADPCM */
+ s->needed_bytes = 2;
+ dolog ("0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n");
+ break;
+
+ case 0x77: /* DMA DAC, 2.6-bit ADPCM Reference */
+ s->needed_bytes = 2;
+ dolog ("0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n");
+ break;
+
+ case 0x7d:
+ dolog ("0x7d - Autio-Initialize DMA DAC, 4-bit ADPCM Reference\n");
+ dolog ("not implemented\n");
+ break;
+
+ case 0x7f:
+ dolog (
+ "0x7d - Autio-Initialize DMA DAC, 2.6-bit ADPCM Reference\n"
+ );
+ dolog ("not implemented\n");
+ break;
+
case 0x80:
s->needed_bytes = 2;
break;
@@ -476,9 +538,9 @@ static void command (SB16State *s, uint8_t cmd)
s->dma_auto = 0;
break;
- case 0xe0:
+ case 0xe0: /* DSP identification */
s->needed_bytes = 1;
- goto warn;
+ break;
case 0xe1:
dsp_out_data (s, s->ver & 0xff);
@@ -503,7 +565,7 @@ static void command (SB16State *s, uint8_t cmd)
case 0xe7:
dolog ("Attempt to probe for ESS (0xe7)?\n");
- return;
+ break;
case 0xe8: /* read test reg */
dsp_out_data (s, s->test_reg);
@@ -529,21 +591,29 @@ static void command (SB16State *s, uint8_t cmd)
goto warn;
default:
- dolog ("unrecognized command %#x\n", cmd);
- return;
+ dolog ("Unrecognized command %#x\n", cmd);
+ break;
}
}
- s->cmd = cmd;
- if (!s->needed_bytes)
+ if (!s->needed_bytes) {
ldebug ("\n");
+ }
+
+ exit:
+ if (!s->needed_bytes) {
+ s->cmd = -1;
+ }
+ else {
+ s->cmd = cmd;
+ }
return;
warn:
dolog ("warning: command %#x,%d is not truly understood yet\n",
cmd, s->needed_bytes);
- s->cmd = cmd;
- return;
+ goto exit;
+
}
static uint16_t dsp_get_lohi (SB16State *s)
@@ -607,8 +677,9 @@ static void complete (SB16State *s)
s->csp_reg83[s->csp_reg83r % 4] = d0;
s->csp_reg83r += 1;
}
- else
+ else {
s->csp_regs[d1] = d0;
+ }
break;
case 0x0f:
@@ -622,8 +693,9 @@ static void complete (SB16State *s)
dsp_out_data (s, s->csp_reg83[s->csp_reg83w % 4]);
s->csp_reg83w += 1;
}
- else
+ else {
dsp_out_data (s, s->csp_regs[d0]);
+ }
break;
case 0x10:
@@ -641,8 +713,9 @@ static void complete (SB16State *s)
break;
case 0x42: /* FT2 sets output freq with this, go figure */
+#if 0
dolog ("cmd 0x42 might not do what it think it should\n");
-
+#endif
case 0x41:
s->freq = dsp_get_hilo (s);
ldebug ("set freq %d\n", s->freq);
@@ -653,6 +726,13 @@ static void complete (SB16State *s)
ldebug ("set dma block len %d\n", s->block_size);
break;
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ /* ADPCM stuff, ignore */
+ break;
+
case 0x80:
{
int freq, samples, bytes;
@@ -662,10 +742,17 @@ static void complete (SB16State *s)
samples = dsp_get_lohi (s) + 1;
bytes = samples << s->fmt_stereo << (s->fmt_bits == 16);
ticks = (bytes * ticks_per_sec) / freq;
- if (ticks < ticks_per_sec / 1024)
+ if (ticks < ticks_per_sec / 1024) {
pic_set_irq (s->irq, 1);
- else
- qemu_mod_timer (s->aux_ts, qemu_get_clock (vm_clock) + ticks);
+ }
+ else {
+ if (s->aux_ts) {
+ qemu_mod_timer (
+ s->aux_ts,
+ qemu_get_clock (vm_clock) + ticks
+ );
+ }
+ }
ldebug ("mix silence %d %d %lld\n", samples, bytes, ticks);
}
break;
@@ -674,7 +761,7 @@ static void complete (SB16State *s)
d0 = dsp_get_data (s);
s->out_data_len = 0;
ldebug ("E0 data = %#x\n", d0);
- dsp_out_data(s, ~d0);
+ dsp_out_data (s, ~d0);
break;
case 0xe2:
@@ -737,6 +824,7 @@ static void reset (SB16State *s)
s->nzero = 0;
s->highspeed = 0;
s->v2x6 = 0;
+ s->cmd = -1;
dsp_out_data(s, 0xaa);
speaker (s, 0);
@@ -761,8 +849,9 @@ static IO_WRITE_PROTO (dsp_write)
pic_set_irq (s->irq, 0);
control (s, 0);
}
- else
+ else {
reset (s);
+ }
}
s->v2x6 = 0;
break;
@@ -845,7 +934,10 @@ static IO_READ_PROTO (dsp_read)
s->last_read_byte = retval;
}
else {
- dolog ("empty output buffer\n");
+ if (s->cmd != -1) {
+ dolog ("empty output buffer for command %#x\n",
+ s->cmd);
+ }
retval = s->last_read_byte;
/* goto error; */
}
@@ -882,13 +974,14 @@ static IO_READ_PROTO (dsp_read)
goto error;
}
- if (!ack)
+ if (!ack) {
ldebug ("read %#x -> %#x\n", nport, retval);
+ }
return retval;
error:
- dolog ("WARNING dsp_read %#x error\n", nport);
+ dolog ("warning: dsp_read %#x error\n", nport);
return 0xff;
}
@@ -933,8 +1026,9 @@ static IO_WRITE_PROTO(mixer_write_datab)
SB16State *s = opaque;
ldebug ("mixer_write [%#x] <- %#x\n", s->mixer_nreg, val);
- if (s->mixer_nreg > sizeof (s->mixer_regs))
+ if (s->mixer_nreg > sizeof (s->mixer_regs)) {
return;
+ }
switch (s->mixer_nreg) {
case 0x00:
@@ -945,8 +1039,9 @@ static IO_WRITE_PROTO(mixer_write_datab)
{
int irq = irq_of_magic (val);
ldebug ("setting irq to %d (val=%#x)\n", irq, val);
- if (irq > 0)
+ if (irq > 0) {
s->irq = irq;
+ }
}
break;
@@ -956,8 +1051,12 @@ static IO_WRITE_PROTO(mixer_write_datab)
dma = lsbindex (val & 0xf);
hdma = lsbindex (val & 0xf0);
- dolog ("attempt to set DMA register 8bit %d, 16bit %d (val=%#x)\n",
- dma, hdma, val);
+ if (dma != s->dma || hdma != s->hdma) {
+ dolog (
+ "attempt to change DMA "
+ "8bit %d(%d), 16bit %d(%d) (val=%#x)\n",
+ dma, s->dma, hdma, s->hdma, val);
+ }
#if 0
s->dma = dma;
s->hdma = hdma;
@@ -971,8 +1070,9 @@ static IO_WRITE_PROTO(mixer_write_datab)
return;
default:
- if (s->mixer_nreg >= 0x80)
- dolog ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val);
+ if (s->mixer_nreg >= 0x80) {
+ ldebug ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val);
+ }
break;
}
@@ -989,10 +1089,14 @@ static IO_READ_PROTO(mixer_read)
{
SB16State *s = opaque;
#ifndef DEBUG_SB16_MOST
- if (s->mixer_nreg != 0x82)
-#endif
+ if (s->mixer_nreg != 0x82) {
+ ldebug ("mixer_read[%#x] -> %#x\n",
+ s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
+ }
+#else
ldebug ("mixer_read[%#x] -> %#x\n",
s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
+#endif
return s->mixer_regs[s->mixer_nreg];
}
@@ -1010,8 +1114,9 @@ static int write_audio (SB16State *s, int nchan, int dma_pos,
int to_copy, copied;
to_copy = audio_MIN (temp, left);
- if (to_copy > sizeof(tmpbuf))
+ if (to_copy > sizeof(tmpbuf)) {
to_copy = sizeof(tmpbuf);
+ }
copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
copied = AUD_write (s->voice, tmpbuf, copied);
@@ -1020,8 +1125,9 @@ static int write_audio (SB16State *s, int nchan, int dma_pos,
dma_pos = (dma_pos + copied) % dma_len;
net += copied;
- if (!copied)
+ if (!copied) {
break;
+ }
}
return net;
@@ -1030,27 +1136,28 @@ static int write_audio (SB16State *s, int nchan, int dma_pos,
static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
{
SB16State *s = opaque;
- int free, rfree, till, copy, written, elapsed;
+ int till, copy, written, free;
if (s->left_till_irq < 0) {
s->left_till_irq = s->block_size;
}
- elapsed = AUD_calc_elapsed (s->voice);
- free = elapsed;/* AUD_get_free (s->voice); */
- rfree = free;
- free = audio_MIN (free, elapsed) & ~s->align;
-
- if ((free <= 0) || !dma_len) {
- return dma_pos;
+ if (s->voice) {
+ free = s->audio_free & ~s->align;
+ if ((free <= 0) || !dma_len) {
+ return dma_pos;
+ }
+ }
+ else {
+ free = dma_len;
}
copy = free;
till = s->left_till_irq;
#ifdef DEBUG_SB16_MOST
- dolog ("pos:%06d free:%d,%d till:%d len:%d\n",
- dma_pos, free, AUD_get_free (s->voice), till, dma_len);
+ dolog ("pos:%06d %d till:%d len:%d\n",
+ dma_pos, free, till, dma_len);
#endif
if (till <= copy) {
@@ -1082,15 +1189,13 @@ static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
s->left_till_irq = s->block_size + s->left_till_irq;
}
- AUD_adjust (s->voice, written);
return dma_pos;
}
-void SB_timer (void *opaque)
+static void SB_audio_callback (void *opaque, int free)
{
SB16State *s = opaque;
- AUD_run ();
- qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
+ s->audio_free = free;
}
static void SB_save (QEMUFile *f, void *opaque)
@@ -1150,8 +1255,9 @@ static int SB_load (QEMUFile *f, void *opaque, int version_id)
{
SB16State *s = opaque;
- if (version_id != 1)
+ if (version_id != 1) {
return -EINVAL;
+ }
qemu_get_be32s (f, &s->irq);
qemu_get_be32s (f, &s->dma);
@@ -1202,14 +1308,23 @@ static int SB_load (QEMUFile *f, void *opaque, int version_id)
qemu_get_buffer (f, s->mixer_regs, 256);
if (s->voice) {
- AUD_close (s->voice);
+ AUD_close_out (s->voice);
s->voice = NULL;
}
if (s->dma_running) {
- if (s->freq)
- s->voice = AUD_open (s->voice, "sb16", s->freq,
- 1 << s->fmt_stereo, s->fmt);
+ if (s->freq) {
+ s->audio_free = 0;
+ s->voice = AUD_open_out (
+ s->voice,
+ "sb16",
+ s,
+ SB_audio_callback,
+ s->freq,
+ 1 << s->fmt_stereo,
+ s->fmt
+ );
+ }
control (s, 1);
speaker (s, s->speaker);
@@ -1224,10 +1339,7 @@ void SB16_init (void)
static const uint8_t dsp_write_ports[] = {0x6, 0xc};
static const uint8_t dsp_read_ports[] = {0x6, 0xa, 0xc, 0xd, 0xe, 0xf};
- s->ts = qemu_new_timer (vm_clock, SB_timer, s);
- if (!s->ts)
- return;
-
+ s->cmd = -1;
s->irq = conf.irq;
s->dma = conf.dma;
s->hdma = conf.hdma;
@@ -1243,8 +1355,9 @@ void SB16_init (void)
reset_mixer (s);
s->aux_ts = qemu_new_timer (vm_clock, aux_timer, s);
- if (!s->aux_ts)
- return;
+ if (!s->aux_ts) {
+ dolog ("Can not create auxiliary timer\n");
+ }
for (i = 0; i < LENOFA (dsp_write_ports); i++) {
register_ioport_write (s->port + dsp_write_ports[i], 1, 1, dsp_write, s);
@@ -1263,6 +1376,5 @@ void SB16_init (void)
DMA_register_channel (s->dma, SB_read_DMA, s);
s->can_write = 1;
- qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
register_savevm ("sb16", 0, 1, SB_save, SB_load, s);
}