diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/adlib.c | 285 | ||||
-rw-r--r-- | hw/es1370.c | 1007 | ||||
-rw-r--r-- | hw/pc.c | 6 | ||||
-rw-r--r-- | hw/sb16.c | 266 |
4 files changed, 1347 insertions, 217 deletions
@@ -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; +} @@ -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); @@ -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); } |