From 219129f97ec9262655711eef0aefcad6b510972b Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sun, 6 Mar 2022 19:59:14 -0300 Subject: [PATCH] CMI8x38: Implement Sound Blaster emulation --- .ci/Jenkinsfile | 2 +- src/dma.c | 2 +- src/include/86box/dma.h | 1 + src/include/86box/snd_sb_dsp.h | 17 +- src/io.c | 6 +- src/sound/snd_cmi8x38.c | 465 +++++++++++++++++++++++++++------ src/sound/snd_sb_dsp.c | 150 +++++++---- 7 files changed, 502 insertions(+), 141 deletions(-) diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile index d1713bb64..00532dc58 100644 --- a/.ci/Jenkinsfile +++ b/.ci/Jenkinsfile @@ -25,7 +25,7 @@ def osArchs = [ ] def osFlags = [ - 'Windows': '', + 'Windows': '-D QT=ON', 'Linux': '-D QT=ON' ] diff --git a/src/dma.c b/src/dma.c index 876c34356..a995660a5 100644 --- a/src/dma.c +++ b/src/dma.c @@ -35,11 +35,11 @@ dma_t dma[8]; uint8_t dma_e; +uint8_t dma_m; static uint8_t dmaregs[3][16]; static int dma_wp[2]; -static uint8_t dma_m; static uint8_t dma_stat; static uint8_t dma_stat_rq; static uint8_t dma_stat_rq_pc; diff --git a/src/include/86box/dma.h b/src/include/86box/dma.h index fc6a0b388..585d77e95 100644 --- a/src/include/86box/dma.h +++ b/src/include/86box/dma.h @@ -66,6 +66,7 @@ typedef struct { extern dma_t dma[8]; extern uint8_t dma_e; +extern uint8_t dma_m; extern void dma_init(void); diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index eff16373d..aeceb62f1 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -20,6 +20,11 @@ typedef struct sb_dsp_t { int sb_16_length, sb_16_format, sb_16_autoinit, sb_16_pause, sb_16_enable, sb_16_autolen, sb_16_output; int sb_16_dmanum; int sb_pausetime; + int (*dma_readb)(void *priv), + (*dma_readw)(void *priv), + (*dma_writeb)(void *priv, uint8_t val), + (*dma_writew)(void *priv, uint16_t val); + void *dma_priv; uint8_t sb_read_data[256]; int sb_read_wp, sb_read_rp; @@ -36,6 +41,8 @@ typedef struct sb_dsp_t { int midi_in_timestamp; int sb_irqnum; + void (*irq_update)(void *priv, int set), + *irq_priv; uint8_t sbe2; int sbe2count; @@ -53,7 +60,7 @@ typedef struct sb_dsp_t { int sbdacpos; - int sbleftright; + int sbleftright, sbleftright_default; int sbreset; uint8_t sbreaddat; @@ -123,4 +130,12 @@ void sb_dsp_set_stereo(sb_dsp_t *dsp, int stereo); void sb_dsp_update(sb_dsp_t *dsp); void sb_update_mask(sb_dsp_t *dsp, int irqm8, int irqm16, int irqm401); +void sb_dsp_irq_attach(sb_dsp_t *dsp, void (*irq_update)(void *priv, int set), void *priv); +void sb_dsp_dma_attach(sb_dsp_t *dsp, + int (*dma_readb)(void *priv), + int (*dma_readw)(void *priv), + int (*dma_writeb)(void *priv, uint8_t val), + int (*dma_writew)(void *priv, uint16_t val), + void *priv); + #endif /* SOUND_SND_SB_DSP_H */ diff --git a/src/io.c b/src/io.c index 9f9df62a4..268305e1a 100644 --- a/src/io.c +++ b/src/io.c @@ -673,7 +673,7 @@ io_trap_remap(void *handle, int enable, uint16_t addr, uint16_t size) trap->base, trap->base + trap->size - 1, trap->enable, addr, addr + size - 1, enable); /* Remove old I/O mapping. */ - if (trap->enable && trap->base && trap->size) { + if (trap->enable && trap->size) { io_removehandler(trap->base, trap->size, io_trap_readb, io_trap_readw, io_trap_readl, io_trap_writeb, io_trap_writew, io_trap_writel, @@ -686,8 +686,8 @@ io_trap_remap(void *handle, int enable, uint16_t addr, uint16_t size) trap->size = size; /* Add new I/O mapping. */ - if (trap->enable && trap->base && trap->size) { - io_sethandler(trap->base, trap->size, + if (trap->enable && trap->size) { + io_sethandler(trap->base, trap->size, io_trap_readb, io_trap_readw, io_trap_readl, io_trap_writeb, io_trap_writew, io_trap_writel, trap); diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index 2be5c6bfc..f8623eb96 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -26,10 +26,13 @@ #include <86box/mem.h> #include <86box/pic.h> #include <86box/timer.h> +#include <86box/dma.h> #include <86box/pci.h> #include <86box/sound.h> #include <86box/snd_sb.h> +#include <86box/snd_sb_dsp.h> #include <86box/gameport.h> +#include <86box/nmi.h> #include <86box/ui.h> @@ -43,6 +46,14 @@ enum { CMEDIA_CMI8738_6CH = 0x080011 /* chip version 055 with 6-channel output */ }; +enum { + TRAP_DMA = 0, + TRAP_PIC, + TRAP_OPL, + TRAP_MPU, + TRAP_MAX +}; + typedef struct { uint8_t id, reg, always_run, playback_enabled, channels; struct _cmi8x38_ *dev; @@ -63,17 +74,21 @@ typedef struct { typedef struct _cmi8x38_ { uint32_t type; uint16_t io_base, sb_base, opl_base, mpu_base; - uint8_t pci_regs[256], io_regs[256], mixer_ext_regs[16]; - int slot; + uint8_t pci_regs[256], io_regs[256]; + int slot, sb_irq; sb_t *sb; - void *gameport; + void *gameport, *io_traps[TRAP_MAX]; + cmi8x38_dma_t dma[2]; + uint16_t tdma_base_addr, tdma_base_count; + uint8_t prev_mask; + int tdma_last_8, tdma_last_16, tdma_mask; int master_vol_l, master_vol_r, cd_vol_l, cd_vol_r; } cmi8x38_t; -#define ENABLE_CMI8X38_LOG 1 + #ifdef ENABLE_CMI8X38_LOG int cmi8x38_do_log = ENABLE_CMI8X38_LOG; @@ -104,7 +119,7 @@ static void cmi8x38_update_irqs(cmi8x38_t *dev) { /* Calculate and use the INTR flag. */ - if (*((uint32_t *) &dev->io_regs[0x10]) & 0x0401c003) { + if ((*((uint32_t *) &dev->io_regs[0x10]) & 0x0401c003) || dev->sb_irq) { dev->io_regs[0x13] |= 0x80; pci_set_irq(dev->slot, PCI_INTA); cmi8x38_log("CMI8x38: Raising IRQ\n"); @@ -135,6 +150,290 @@ cmi8x38_mpu_irq_pending(void *priv) } +static void +cmi8x38_sb_irq_update(void *priv, int set) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + dev->sb_irq = set; + cmi8x38_update_irqs(dev); +} + + +static int +cmi8x38_sb_dma_post(cmi8x38_t *dev, uint16_t *addr, uint16_t *count, int channel) +{ + /* Increment address and decrement count. */ + *addr += 1; + *count -= 1; + + /* Handle end of DMA. */ + if (*count == 0xffff) { + if (dma[channel].mode & 0x10) { /* auto-init */ + /* Restart TDMA. */ + *addr = dev->tdma_base_addr; + *count = dev->tdma_base_count; + cmi8x38_log("CMI8x38: Restarting TDMA on DMA %d with addr %08X count %04X\n", channel, (dma[channel].ab & 0xffff0000) | *addr, *count); + } else { + /* Mask TDMA. */ + dev->tdma_mask |= 1 << channel; + } + return DMA_OVER; + } + return 0; +} + + +static int +cmi8x38_sb_dma_readb(void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + /* Stop if the DMA channel is invalid or if TDMA is masked. */ + int channel = dev->sb->dsp.sb_8_dmanum; + if ((channel < 0) || (dev->tdma_mask & (1 << channel))) + return DMA_NODATA; + + /* Get 16-bit address and count registers. */ + uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], + *count = (uint16_t *) &dev->io_regs[0x1e]; + + /* Read data. */ + int ret = mem_readb_phys((dma[channel].ab & 0xffff0000) | *addr); + + /* Handle address, count and end. */ + ret |= cmi8x38_sb_dma_post(dev, addr, count, channel); + + return ret; +} + + +static int +cmi8x38_sb_dma_readw(void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + /* Stop if the DMA channel is invalid or if TDMA is masked. */ + int channel = dev->sb->dsp.sb_16_dmanum; + if ((channel < 0) || (dev->tdma_mask & (1 << channel))) + return DMA_NODATA; + + /* Get 16-bit address and count registers. */ + uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], + *count = (uint16_t *) &dev->io_regs[0x1e]; + + /* Read data. */ + int ret = mem_readw_phys((dma[channel].ab & 0xfffe0000) | ((*addr) << 1)); + + /* Handle address, count and end. */ + ret |= cmi8x38_sb_dma_post(dev, addr, count, channel); + + return ret; +} + + +static int +cmi8x38_sb_dma_writeb(void *priv, uint8_t val) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + /* Stop if the DMA channel is invalid or if TDMA is masked. */ + int channel = dev->sb->dsp.sb_8_dmanum; + if ((channel < 0) || (dev->tdma_mask & (1 << channel))) + return 1; + + /* Get 16-bit address and count registers. */ + uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], + *count = (uint16_t *) &dev->io_regs[0x1e]; + + /* Write data. */ + mem_writeb_phys((dma[channel].ab & 0xffff0000) | *addr, val); + + /* Handle address, count and end. */ + cmi8x38_sb_dma_post(dev, addr, count, channel); + + return 0; +} + + +static int +cmi8x38_sb_dma_writew(void *priv, uint16_t val) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + /* Stop if the DMA channel is invalid or if TDMA is masked. */ + int channel = dev->sb->dsp.sb_16_dmanum; + if ((channel < 0) || (dev->tdma_mask & (1 << channel))) + return 1; + + /* Get 16-bit address and count registers. */ + uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], + *count = (uint16_t *) &dev->io_regs[0x1e]; + + /* Write data. */ + mem_writew_phys((dma[channel].ab & 0xfffe0000) | ((*addr) << 1), val); + + /* Handle address, count and end. */ + cmi8x38_sb_dma_post(dev, addr, count, channel); + + return 0; +} + + +static void +cmi8x38_dma_write(uint16_t addr, uint8_t val, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + /* Keep track of the last DMA channel written to. */ + uint8_t channel; + if (addr < 0x08) { + channel = addr >> 1; + dev->tdma_last_8 = channel; + } else { + channel = 4 | ((addr >> 2) & 3); + dev->tdma_last_16 = channel; + } + + /* Stop if not autodetecting. See note on cmi8x38_write(0x27). */ + if (!(dev->io_regs[0x27] & 0x01)) + return; + + /* Write TDMA registers if this is a TDMA channel. */ + if ((channel == dev->sb->dsp.sb_8_dmanum) || (channel == dev->sb->dsp.sb_16_dmanum)) { + /* Write base address and count. */ + uint16_t *addr = (uint16_t *) &dev->io_regs[0x1c], + *count = (uint16_t *) &dev->io_regs[0x1e]; + *addr = dev->tdma_base_addr = dma[channel].ab >> !!(channel & 4); + *count = dev->tdma_base_count = dma[channel].cb; + cmi8x38_log("CMI8x38: Starting TDMA on DMA %d with addr %08X count %04X\n", channel, (dma[channel].ab & 0xffff0000) | *addr, *count); + + /* Set high channel flag. */ + if (channel & 4) + dev->io_regs[0x10] |= 0x20; + else + dev->io_regs[0x10] &= ~0x20; + } +} + +static void +cmi8x38_dma_mask_write(uint16_t addr, uint8_t val, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + + /* Stop if not autodetecting. See note on cmi8x38_write(0x27). */ + if (!(dev->io_regs[0x27] & 0x01)) + return; + + /* Unmask TDMA on DMA unmasking edge. */ + if ((dev->sb->dsp.sb_8_dmanum >= 0) && (dev->prev_mask & (1 << dev->sb->dsp.sb_8_dmanum)) && !(dma_m & (1 << dev->sb->dsp.sb_8_dmanum))) + dev->tdma_mask &= ~(1 << dev->sb->dsp.sb_8_dmanum); + else if ((dev->sb->dsp.sb_16_dmanum >= 0) && (dev->prev_mask & (1 << dev->sb->dsp.sb_16_dmanum)) && !(dma_m & (1 << dev->sb->dsp.sb_16_dmanum))) + dev->tdma_mask &= ~(1 << dev->sb->dsp.sb_16_dmanum); + dev->prev_mask = dma_m; +} + + +static void +cmi8338_io_trap(int size, uint16_t addr, uint8_t write, uint8_t val, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + +#ifdef ENABLE_CMI8X38_LOG + if (write) + cmi8x38_log("CMI8x38: cmi8338_io_trap(%04X, %02X)\n", addr, val); + else + cmi8x38_log("CMI8x38: cmi8338_io_trap(%04X)\n", addr); +#endif + + /* Weird offsets, it's best to just treat the register as a big dword. */ + uint32_t *lcs = (uint32_t *) &dev->io_regs[0x14]; + *lcs &= ~0x0003dff0; + *lcs |= (addr & 0x0f) << 14; + if (write) + *lcs |= 0x1000 | (val << 4); + + /* Raise NMI. */ + nmi = 1; +} + + +static uint8_t +cmi8x38_sb_mixer_read(uint16_t addr, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; + uint8_t ret = sb_ct1745_mixer_read(addr, dev->sb); + + if (addr & 1) { + if ((mixer->index == 0x0e) || (mixer->index >= 0xf0)) + ret = mixer->regs[mixer->index]; + cmi8x38_log("CMI8x38: sb_mixer_read(1, %02X) = %02X\n", mixer->index, ret); + } else { + cmi8x38_log("CMI8x38: sb_mixer_read(0) = %02X\n", ret); + } + + return ret; +} + + +static void +cmi8x38_sb_mixer_write(uint16_t addr, uint8_t val, void *priv) +{ + cmi8x38_t *dev = (cmi8x38_t *) priv; + sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; + + /* Our clone mixer has a few differences. */ + if (addr & 1) { + cmi8x38_log("CMI8x38: sb_mixer_write(1, %02X, %02X)\n", mixer->index, val); + + switch (mixer->index) { + /* Reset interleaved stereo flag for SBPro mode. */ + case 0x00: + mixer->regs[0x0e] = 0x00; + break; + + /* No dynamic IRQ and DMA assignment. */ + case 0x80: case 0x81: + return; + + /* Some extended registers beyond those accepted by the CT1745. */ + case 0xf0: + if (dev->type == CMEDIA_CMI8338) + val &= 0xfe; + mixer->regs[mixer->index] = val; + return; + + case 0xf8 ... 0xff: + if (dev->type == CMEDIA_CMI8338) + mixer->regs[mixer->index] = val; + /* fall-through */ + + case 0xf1 ... 0xf7: + return; + } + + sb_ct1745_mixer_write(addr, val, dev->sb); + + /* No [3F:47] controls. */ + mixer->input_gain_L = 0; + mixer->input_gain_R = 0; + mixer->output_gain_L = (double) 1.0; + mixer->output_gain_R = (double) 1.0; + mixer->bass_l = 8; + mixer->bass_r = 8; + mixer->treble_l = 8; + mixer->treble_r = 8; + + /* Check interleaved stereo flag for SBPro mode. */ + if ((mixer->index == 0x00) || (mixer->index == 0x0e)) + sb_dsp_set_stereo(&dev->sb->dsp, mixer->regs[0x0e] & 2); + } else { + cmi8x38_log("CMI8x38: sb_mixer_write(0, %02X)\n", val); + sb_ct1745_mixer_write(addr, val, dev->sb); + } +} + + static void cmi8x38_remap_sb(cmi8x38_t *dev) { @@ -143,21 +442,19 @@ cmi8x38_remap_sb(cmi8x38_t *dev) opl3_write, NULL, NULL, &dev->sb->opl); io_removehandler(dev->sb_base + 8, 0x0002, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &dev->sb->opl); - io_removehandler(dev->sb_base + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, - sb_ct1745_mixer_write, NULL, NULL, dev->sb); + io_removehandler(dev->sb_base + 4, 0x0002, cmi8x38_sb_mixer_read, NULL, NULL, + cmi8x38_sb_mixer_write, NULL, NULL, dev); sb_dsp_setaddr(&dev->sb->dsp, 0); } - if (dev->io_regs[0x04] & 0x08) { - dev->sb_base = 0x220; - if (dev->type == CMEDIA_CMI8338) - dev->sb_base += (dev->io_regs[0x17] & 0x80) >> 2; - else - dev->sb_base += (dev->io_regs[0x17] & 0x0c) << 3; - } else { + dev->sb_base = 0x220; + if (dev->type == CMEDIA_CMI8338) + dev->sb_base += (dev->io_regs[0x17] & 0x80) >> 2; + else + dev->sb_base += (dev->io_regs[0x17] & 0x0c) << 3; + if (!(dev->io_regs[0x04] & 0x08)) dev->sb_base = 0; - } cmi8x38_log("CMI8x38: remap_sb(%04X)\n", dev->sb_base); if (dev->sb_base) { @@ -165,8 +462,8 @@ cmi8x38_remap_sb(cmi8x38_t *dev) opl3_write, NULL, NULL, &dev->sb->opl); io_sethandler(dev->sb_base + 8, 0x0002, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &dev->sb->opl); - io_sethandler(dev->sb_base + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, - sb_ct1745_mixer_write, NULL, NULL, dev->sb); + io_sethandler(dev->sb_base + 4, 0x0002, cmi8x38_sb_mixer_read, NULL, NULL, + cmi8x38_sb_mixer_write, NULL, NULL, dev); sb_dsp_setaddr(&dev->sb->dsp, dev->sb_base); } @@ -181,19 +478,16 @@ cmi8x38_remap_opl(cmi8x38_t *dev) opl3_write, NULL, NULL, &dev->sb->opl); } - if (dev->io_regs[0x1a] & 0x08) { - if (dev->type == CMEDIA_CMI8338) - dev->opl_base = 0x388; - else - dev->opl_base = opl_ports_cmi8738[dev->io_regs[0x17] & 0x03]; - } else { + dev->opl_base = (dev->type == CMEDIA_CMI8338) ? 0x388 : opl_ports_cmi8738[dev->io_regs[0x17] & 0x03]; + io_trap_remap(dev->io_traps[TRAP_OPL], dev->io_regs[0x16] & 0x80, dev->opl_base, 4); + if (!(dev->io_regs[0x1a] & 0x08)) dev->opl_base = 0; - } + cmi8x38_log("CMI8x38: remap_opl(%04X)\n", dev->opl_base); if (dev->opl_base) { io_sethandler(dev->opl_base, 0x0004, opl3_read, NULL, NULL, - opl3_write, NULL, NULL, &dev->sb->opl); + opl3_write, NULL, NULL, &dev->sb->opl); } } @@ -204,13 +498,13 @@ cmi8x38_remap_mpu(cmi8x38_t *dev) if (dev->mpu_base) mpu401_change_addr(dev->sb->mpu, 0); - if (dev->io_regs[0x04] & 0x04) { - /* The CMI8338 datasheet's port range of [300:330] is - inaccurate. Drivers expect [330:300] like CMI8738. */ - dev->mpu_base = 0x330 - ((dev->io_regs[0x17] & 0x60) >> 1); - } else { + /* The CMI8338 datasheet's port range of [300:330] is + inaccurate. Drivers expect [330:300] like CMI8738. */ + dev->mpu_base = 0x330 - ((dev->io_regs[0x17] & 0x60) >> 1); + io_trap_remap(dev->io_traps[TRAP_MPU], dev->io_regs[0x16] & 0x20, dev->mpu_base, 2); + if (!(dev->io_regs[0x04] & 0x04)) dev->mpu_base = 0; - } + cmi8x38_log("CMI8x38: remap_mpu(%04X)\n", dev->mpu_base); if (dev->mpu_base) @@ -219,9 +513,9 @@ cmi8x38_remap_mpu(cmi8x38_t *dev) static void -cmi8x38_start_playback(cmi8x38_t *dev, uint8_t val) +cmi8x38_start_playback(cmi8x38_t *dev) { - uint8_t i; + uint8_t i, val = dev->io_regs[0x00]; i = !(val & 0x01); if (!dev->dma[0].playback_enabled && i) @@ -243,16 +537,8 @@ cmi8x38_read(uint16_t addr, void *priv) uint8_t ret; switch (addr) { - case 0x22: - sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; - if (mixer->index >= 0xf0) - ret = dev->mixer_ext_regs[mixer->index & 0x0f]; - else - ret = sb_ct1745_mixer_read(1, dev->sb); - break; - - case 0x23: - ret = sb_ct1745_mixer_read(0, dev->sb); + case 0x22: case 0x23: + ret = cmi8x38_sb_mixer_read(addr ^ 1, dev); break; case 0x40 ... 0x4f: @@ -327,7 +613,8 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) dev->dma[1].always_run = val & 0x02; /* Start playback if requested. */ - cmi8x38_start_playback(dev, val); + dev->io_regs[addr] = val; + cmi8x38_start_playback(dev); break; case 0x02: @@ -354,7 +641,7 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) /* Start playback along with DMA channels. */ if (val & 0x03) - cmi8x38_start_playback(dev, dev->io_regs[0x00]); + cmi8x38_start_playback(dev); break; case 0x04: @@ -425,8 +712,14 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) break; case 0x16: - if (dev->type == CMEDIA_CMI8338) + if (dev->type == CMEDIA_CMI8338) { val &= 0xa0; + + /* Enable or disable I/O traps. */ + dev->io_regs[addr] = val; + cmi8x38_remap_opl(dev); + cmi8x38_remap_mpu(dev); + } break; case 0x17: @@ -438,6 +731,10 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) pci_set_irq(dev->slot, PCI_INTA); else if ((dev->io_regs[0x17] & 0x10) && !(val & 0x10)) pci_clear_irq(dev->slot, PCI_INTA); + + /* Enable or disable I/O traps. */ + io_trap_remap(dev->io_traps[TRAP_DMA], val & 0x02, 0x0000, 16); + io_trap_remap(dev->io_traps[TRAP_PIC], val & 0x01, 0x0020, 2); } /* Remap the legacy devices. */ @@ -484,43 +781,13 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) val &= 0xf7; else val &= 0x07; + + /* Enable or disable SBPro channel swapping. */ + dev->sb->dsp.sbleftright_default = !!(val & 0x02); break; - case 0x22: - sb_ct1745_mixer_t *mixer = &dev->sb->mixer_sb16; - switch (mixer->index) { - case 0xf0: - if (dev->type == CMEDIA_CMI8338) - val &= 0xfe; - dev->mixer_ext_regs[dev->sb->mixer_sb16.index & 0x0f] = val; - break; - - case 0xf8 ... 0xff: - if (dev->type == CMEDIA_CMI8338) - dev->mixer_ext_regs[dev->sb->mixer_sb16.index & 0x0f] = val; - /* fall-through */ - - case 0xf1 ... 0xf7: - break; - - default: - sb_ct1745_mixer_write(1, val, dev->sb); - - /* Our clone mixer lacks the [3F:47] controls. */ - mixer->input_gain_L = 0; - mixer->input_gain_R = 0; - mixer->output_gain_L = (double) 1.0; - mixer->output_gain_R = (double) 1.0; - mixer->bass_l = 8; - mixer->bass_r = 8; - mixer->treble_l = 8; - mixer->treble_r = 8; - break; - } - return; - - case 0x23: - sb_ct1745_mixer_write(0, val, dev->sb); + case 0x22: case 0x23: + cmi8x38_sb_mixer_write(addr ^ 1, val, dev); return; case 0x24: @@ -533,6 +800,16 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) val &= 0x03; else val &= 0x27; + + if (val & 0x01) { + /* Latch last DMA channels that had address/count registers written to. + Nobody knows how this "autodetection" works, but the CMI8338 TSR + disables it before and reenables it after copying the TDMA base/addr + to the 8237 registers corresponding to the 8-bit SB DMA channel. */ + dev->sb->dsp.sb_8_dmanum = dev->tdma_last_8; + if (!(dev->io_regs[0x21] & 0x01)) + dev->sb->dsp.sb_16_dmanum = dev->tdma_last_16; + } break; case 0x40 ... 0x4f: @@ -751,9 +1028,11 @@ cmi8x38_poll(void *priv) int16_t *out_l, *out_r, *out_ol, *out_or; /* o = opposite */ /* Schedule next run if playback is enabled. */ +#if 0 /* temporary */ if (dev->io_regs[0x00] & (1 << dma->id)) dma->playback_enabled = 0; else +#endif timer_advance_u64(&dma->poll_timer, dma->timer_latch); /* Update audio buffer. */ @@ -1000,6 +1279,10 @@ cmi8x38_reset(void *priv) memset(dev->dma[i].fifo, 0, sizeof(dev->dma[i].fifo)); } + /* Reset legacy DMA channel. */ + dev->tdma_last_8 = dev->tdma_last_16 = dev->sb->dsp.sb_8_dmanum = dev->sb->dsp.sb_16_dmanum = -1; + dev->tdma_mask = 0; + /* Reset Sound Blaster 16 mixer. */ sb_ct1745_mixer_reset(dev->sb); } @@ -1023,8 +1306,15 @@ cmi8x38_init(const device_t *info) dev->sb->opl_enabled = 1; /* let snd_sb.c handle the OPL3 */ dev->sb->mixer_sb16.output_filter = 0; /* no output filtering */ - /* Initialize MPU-401 interrupt handler. */ + /* Initialize legacy interrupt and DMA handlers. */ mpu401_irq_attach(dev->sb->mpu, cmi8x38_mpu_irq_update, cmi8x38_mpu_irq_pending, dev); + sb_dsp_irq_attach(&dev->sb->dsp, cmi8x38_sb_irq_update, dev); + sb_dsp_dma_attach(&dev->sb->dsp, cmi8x38_sb_dma_readb, cmi8x38_sb_dma_readw, cmi8x38_sb_dma_writeb, cmi8x38_sb_dma_writew, dev); + dev->sb->dsp.sb_type = SBPRO; + io_sethandler(0x00, 8, NULL, NULL, NULL, cmi8x38_dma_write, NULL, NULL, dev); + io_sethandler(0xc0, 16, NULL, NULL, NULL, cmi8x38_dma_write, NULL, NULL, dev); + io_sethandler(0x08, 8, NULL, NULL, NULL, cmi8x38_dma_mask_write, NULL, NULL, dev); + io_sethandler(0xd0, 16, NULL, NULL, NULL, cmi8x38_dma_mask_write, NULL, NULL, dev); /* Initialize DMA channels. */ for (int i = 0; i < (sizeof(dev->dma) / sizeof(dev->dma[0])); i++) { @@ -1044,6 +1334,14 @@ cmi8x38_init(const device_t *info) /* Initialize game port. */ dev->gameport = gameport_add(&gameport_pnp_device); + /* Initialize I/O traps. */ + if (dev->type == CMEDIA_CMI8338) { + dev->io_traps[TRAP_DMA] = io_trap_add(cmi8338_io_trap, dev); + dev->io_traps[TRAP_PIC] = io_trap_add(cmi8338_io_trap, dev); + dev->io_traps[TRAP_OPL] = io_trap_add(cmi8338_io_trap, dev); + dev->io_traps[TRAP_MPU] = io_trap_add(cmi8338_io_trap, dev); + } + /* Add PCI card. */ dev->slot = pci_add_card((info->local & (1 << 13)) ? PCI_ADD_SOUND : PCI_ADD_NORMAL, cmi8x38_pci_read, cmi8x38_pci_write, dev); @@ -1061,6 +1359,9 @@ cmi8x38_close(void *priv) cmi8x38_log("CMI8x38: close()\n"); + for (int i = 0; i < TRAP_MAX; i++) + io_trap_remove(dev->io_traps[i]); + free(dev); } diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 59f384e2a..3101f706a 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -169,6 +169,16 @@ recalc_sb16_filter(int c, int playback_freq) low_fir_sb16_coef[c][n] /= gain; } +static void +sb_irq_update_pic(void *priv, int set) +{ + sb_dsp_t *dsp = (sb_dsp_t *) priv; + if (set) + picint(1 << dsp->sb_irqnum); + else + picintc(1 << dsp->sb_irqnum); +} + void sb_update_mask(sb_dsp_t *dsp, int irqm8, int irqm16, int irqm401) { @@ -185,7 +195,7 @@ sb_update_mask(sb_dsp_t *dsp, int irqm8, int irqm16, int irqm401) dsp->sb_irqm401 = irqm401; if (clear) - picintc(1 << dsp->sb_irqnum); + dsp->irq_update(dsp->irq_priv, 0); } void @@ -210,9 +220,9 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) } if (set && !masked) - picint(1 << dsp->sb_irqnum); + dsp->irq_update(dsp->irq_priv, 1); else if (!set) - picintc(1 << dsp->sb_irqnum); + dsp->irq_update(dsp->irq_priv, 0); } void @@ -281,7 +291,7 @@ sb_dsp_reset(sb_dsp_t *dsp) dsp->record_pos_read = 0; dsp->record_pos_write = SB_DSP_REC_SAFEFTY_MARGIN; - picintc(1 << dsp->sb_irqnum); + dsp->irq_update(dsp->irq_priv, 0); dsp->asp_data_len = 0; } @@ -350,7 +360,7 @@ sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) dsp->sb_8_output = 1; if (!timer_is_enabled(&dsp->output_timer)) timer_set_delay_u64(&dsp->output_timer, dsp->sblatcho); - dsp->sbleftright = 0; + dsp->sbleftright = dsp->sbleftright_default; dsp->sbdacpos = 0; } else { dsp->sb_16_length = len; @@ -397,29 +407,31 @@ sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) } int -sb_8_read_dma(sb_dsp_t *dsp) +sb_8_read_dma(void *priv) { + sb_dsp_t *dsp = (sb_dsp_t *) priv; return dma_channel_read(dsp->sb_8_dmanum); } -void -sb_8_write_dma(sb_dsp_t *dsp, uint8_t val) +int +sb_8_write_dma(void *priv, uint8_t val) { - dma_channel_write(dsp->sb_8_dmanum, val); + sb_dsp_t *dsp = (sb_dsp_t *) priv; + return dma_channel_write(dsp->sb_8_dmanum, val) == DMA_NODATA; } int -sb_16_read_dma(sb_dsp_t *dsp) +sb_16_read_dma(void *priv) { + sb_dsp_t *dsp = (sb_dsp_t *) priv; return dma_channel_read(dsp->sb_16_dmanum); } int -sb_16_write_dma(sb_dsp_t *dsp, uint16_t val) +sb_16_write_dma(void *priv, uint16_t val) { - int ret = dma_channel_write(dsp->sb_16_dmanum, val); - - return (ret == DMA_NODATA); + sb_dsp_t *dsp = (sb_dsp_t *) priv; + return dma_channel_write(dsp->sb_16_dmanum, val) == DMA_NODATA; } void @@ -469,12 +481,12 @@ sb_exec_command(sb_dsp_t *dsp) sb_start_dma(dsp, 1, 0, 0, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); break; case 0x17: /* 2-bit ADPCM output with reference */ - dsp->sbref = sb_8_read_dma(dsp); + dsp->sbref = dsp->dma_readb(dsp->dma_priv); dsp->sbstep = 0; /* Fall through */ case 0x16: /* 2-bit ADPCM output */ sb_start_dma(dsp, 1, 0, ADPCM_2, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; if (dsp->sb_command == 0x17) dsp->sb_8_length--; @@ -486,7 +498,7 @@ sb_exec_command(sb_dsp_t *dsp) case 0x1F: /* 2-bit ADPCM autoinit output */ if (dsp->sb_type >= SB15) { sb_start_dma(dsp, 1, 1, ADPCM_2, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } break; @@ -581,23 +593,23 @@ sb_exec_command(sb_dsp_t *dsp) dsp->sb_8_autolen = dsp->sb_data[0] + (dsp->sb_data[1] << 8); break; case 0x75: /* 4-bit ADPCM output with reference */ - dsp->sbref = sb_8_read_dma(dsp); + dsp->sbref = dsp->dma_readb(dsp->dma_priv); dsp->sbstep = 0; /* Fall through */ case 0x74: /* 4-bit ADPCM output */ sb_start_dma(dsp, 1, 0, ADPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; if (dsp->sb_command == 0x75) dsp->sb_8_length--; break; case 0x77: /* 2.6-bit ADPCM output with reference */ - dsp->sbref = sb_8_read_dma(dsp); + dsp->sbref = dsp->dma_readb(dsp->dma_priv); dsp->sbstep = 0; /* Fall through */ case 0x76: /* 2.6-bit ADPCM output */ sb_start_dma(dsp, 1, 0, ADPCM_26, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; if (dsp->sb_command == 0x77) dsp->sb_8_length--; @@ -605,14 +617,14 @@ sb_exec_command(sb_dsp_t *dsp) case 0x7D: /* 4-bit ADPCM autoinit output */ if (dsp->sb_type >= SB15) { sb_start_dma(dsp, 1, 1, ADPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } break; case 0x7F: /* 2.6-bit ADPCM autoinit output */ if (dsp->sb_type >= SB15) { sb_start_dma(dsp, 1, 1, ADPCM_26, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } break; @@ -757,7 +769,7 @@ sb_exec_command(sb_dsp_t *dsp) } dsp->sbe2 += sbe2dat[dsp->sbe2count & 3][8]; dsp->sbe2count++; - sb_8_write_dma(dsp, dsp->sbe2); + dsp->dma_writeb(dsp->dma_priv, dsp->sbe2); break; case 0xE3: /* DSP copyright */ if (dsp->sb_type >= SB16) { @@ -1016,7 +1028,7 @@ sb_read(uint16_t a, void *priv) } break; case 0xE: /* Read data ready */ - picintc(1 << dsp->sb_irqnum); + dsp->irq_update(dsp->irq_priv, 0); dsp->sb_irq8 = dsp->sb_irq16 = 0; /* Only bit 7 is defined but aztech diagnostics fail if the others are set. Keep the original behavior to not interfere with what's already working. */ if (IS_AZTECH(dsp)) { @@ -1030,7 +1042,7 @@ sb_read(uint16_t a, void *priv) case 0xF: /* 16-bit ack */ dsp->sb_irq16 = 0; if (!dsp->sb_irq8) - picintc(1 << dsp->sb_irqnum); + dsp->irq_update(dsp->irq_priv, 0); sb_dsp_log("SB 16-bit ACK read 0xFF\n"); ret = 0xff; break; @@ -1108,6 +1120,16 @@ sb_dsp_init(sb_dsp_t *dsp, int type, int subtype, void *parent) dsp->sb_16_dmanum = 5; dsp->mpu = NULL; + dsp->sbleftright_default = 0; + + dsp->irq_update = sb_irq_update_pic; + dsp->irq_priv = dsp; + dsp->dma_readb = sb_8_read_dma; + dsp->dma_readw = sb_16_read_dma; + dsp->dma_writeb = sb_8_write_dma; + dsp->dma_writew = sb_16_write_dma; + dsp->dma_priv = dsp; + sb_doreset(dsp); timer_add(&dsp->output_timer, pollsb, dsp, 0); @@ -1149,6 +1171,28 @@ sb_dsp_set_stereo(sb_dsp_t *dsp, int stereo) dsp->stereo = stereo; } +void +sb_dsp_irq_attach(sb_dsp_t *dsp, void (*irq_update)(void *priv, int set), void *priv) +{ + dsp->irq_update = irq_update; + dsp->irq_priv = priv; +} + +void +sb_dsp_dma_attach(sb_dsp_t *dsp, + int (*dma_readb)(void *priv), + int (*dma_readw)(void *priv), + int (*dma_writeb)(void *priv, uint8_t val), + int (*dma_writew)(void *priv, uint16_t val), + void *priv) +{ + dsp->dma_readb = dma_readb; + dsp->dma_readw = dma_readw; + dsp->dma_writeb = dma_writeb; + dsp->dma_writew = dma_writew; + dsp->dma_priv = priv; +} + void pollsb(void *p) { @@ -1162,7 +1206,7 @@ pollsb(void *p) switch (dsp->sb_8_format) { case 0x00: /* Mono unsigned */ - data[0] = sb_8_read_dma(dsp); + data[0] = dsp->dma_readb(dsp->dma_priv); /* Needed to prevent clicking in Worms, which programs the DSP to auto-init DMA but programs the DMA controller to single cycle */ if (data[0] == DMA_NODATA) @@ -1181,7 +1225,7 @@ pollsb(void *p) dsp->sb_8_length--; break; case 0x10: /* Mono signed */ - data[0] = sb_8_read_dma(dsp); + data[0] = dsp->dma_readb(dsp->dma_priv); if (data[0] == DMA_NODATA) break; dsp->sbdat = data[0] << 8; @@ -1198,8 +1242,8 @@ pollsb(void *p) dsp->sb_8_length--; break; case 0x20: /* Stereo unsigned */ - data[0] = sb_8_read_dma(dsp); - data[1] = sb_8_read_dma(dsp); + data[0] = dsp->dma_readb(dsp->dma_priv); + data[1] = dsp->dma_readb(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; dsp->sbdatl = (data[0] ^ 0x80) << 8; @@ -1207,8 +1251,8 @@ pollsb(void *p) dsp->sb_8_length -= 2; break; case 0x30: /* Stereo signed */ - data[0] = sb_8_read_dma(dsp); - data[1] = sb_8_read_dma(dsp); + data[0] = dsp->dma_readb(dsp->dma_priv); + data[1] = dsp->dma_readb(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; dsp->sbdatl = data[0] << 8; @@ -1241,7 +1285,7 @@ pollsb(void *p) if (dsp->sbdacpos >= 2) { dsp->sbdacpos = 0; - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } @@ -1284,7 +1328,7 @@ pollsb(void *p) dsp->sbdacpos++; if (dsp->sbdacpos >= 3) { dsp->sbdacpos = 0; - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } @@ -1321,7 +1365,7 @@ pollsb(void *p) dsp->sbdacpos++; if (dsp->sbdacpos >= 4) { dsp->sbdacpos = 0; - dsp->sbdat2 = sb_8_read_dma(dsp); + dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); } if ((dsp->sb_type >= SBPRO) && (dsp->sb_type < SB16) && dsp->stereo) { @@ -1352,22 +1396,22 @@ pollsb(void *p) switch (dsp->sb_16_format) { case 0x00: /* Mono unsigned */ - data[0] = sb_16_read_dma(dsp); + data[0] = dsp->dma_readw(dsp->dma_priv); if (data[0] == DMA_NODATA) break; dsp->sbdatl = dsp->sbdatr = data[0] ^ 0x8000; dsp->sb_16_length--; break; case 0x10: /* Mono signed */ - data[0] = sb_16_read_dma(dsp); + data[0] = dsp->dma_readw(dsp->dma_priv); if (data[0] == DMA_NODATA) break; dsp->sbdatl = dsp->sbdatr = data[0]; dsp->sb_16_length--; break; case 0x20: /* Stereo unsigned */ - data[0] = sb_16_read_dma(dsp); - data[1] = sb_16_read_dma(dsp); + data[0] = dsp->dma_readw(dsp->dma_priv); + data[1] = dsp->dma_readw(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; dsp->sbdatl = data[0] ^ 0x8000; @@ -1375,8 +1419,8 @@ pollsb(void *p) dsp->sb_16_length -= 2; break; case 0x30: /* Stereo signed */ - data[0] = sb_16_read_dma(dsp); - data[1] = sb_16_read_dma(dsp); + data[0] = dsp->dma_readw(dsp->dma_priv); + data[1] = dsp->dma_readw(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; dsp->sbdatl = data[0]; @@ -1418,27 +1462,27 @@ sb_poll_i(void *p) if (dsp->sb_8_enable && !dsp->sb_8_pause && dsp->sb_pausetime < 0 && !dsp->sb_8_output) { switch (dsp->sb_8_format) { case 0x00: /* Mono unsigned As the manual says, only the left channel is recorded */ - sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80); + dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80); dsp->sb_8_length--; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x10: /* Mono signed As the manual says, only the left channel is recorded */ - sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read] >> 8)); + dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8)); dsp->sb_8_length--; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x20: /* Stereo unsigned */ - sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80); - sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read + 1] >> 8) ^ 0x80); + dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80); + dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read + 1] >> 8) ^ 0x80); dsp->sb_8_length -= 2; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x30: /* Stereo signed */ - sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read] >> 8)); - sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read + 1] >> 8)); + dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8)); + dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read + 1] >> 8)); dsp->sb_8_length -= 2; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; @@ -1459,31 +1503,31 @@ sb_poll_i(void *p) if (dsp->sb_16_enable && !dsp->sb_16_pause && (dsp->sb_pausetime < 0LL) && !dsp->sb_16_output) { switch (dsp->sb_16_format) { case 0x00: /* Unsigned mono. As the manual says, only the left channel is recorded */ - if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read] ^ 0x8000)) + if (dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read] ^ 0x8000)) return; dsp->sb_16_length--; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x10: /* Signed mono. As the manual says, only the left channel is recorded */ - if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read])) + if (dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read])) return; dsp->sb_16_length--; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x20: /* Unsigned stereo */ - if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read] ^ 0x8000)) + if (dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read] ^ 0x8000)) return; - sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read + 1] ^ 0x8000); + dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read + 1] ^ 0x8000); dsp->sb_16_length -= 2; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x30: /* Signed stereo */ - if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read])) + if (dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read])) return; - sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read + 1]); + dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read + 1]); dsp->sb_16_length -= 2; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF;