This repository has been archived on 2025-05-24. You can view files and clone it, but cannot push or open issues or pull requests.
Files
VARCem/src/sound/snd_audiopci.c

1358 lines
38 KiB
C

/*
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Implementation of the AudioPCI sound device.
*
* Version: @(#)snd_audiopci.c 1.0.6 2018/03/25
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* Sarah Walker, <tommowalker@tommowalker.co.uk>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
* Copyright 2008-2018 Sarah Walker.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#ifdef _WIN32
# define _USE_MATH_DEFINES
#endif
#include <math.h>
#define HAVE_STDARG_H
#include "../emu.h"
#include "../device.h"
#include "../io.h"
#include "../nmi.h"
#include "../mem.h"
#include "../pci.h"
#include "../timer.h"
#include "sound.h"
#include "snd_audiopci.h"
#define N 16
#define ES1371_NCoef 91
static float low_fir_es1371_coef[ES1371_NCoef];
typedef struct {
uint8_t pci_command, pci_serr;
uint32_t base_addr;
uint8_t int_line;
uint16_t pmcsr;
uint32_t int_ctrl;
uint32_t int_status;
uint32_t legacy_ctrl;
int mem_page;
uint32_t si_cr;
uint32_t sr_cir;
uint16_t sr_ram[128];
uint8_t uart_ctrl;
uint8_t uart_status;
uint16_t codec_regs[64];
uint32_t codec_ctrl;
struct {
uint32_t addr, addr_latch;
uint16_t count, size;
uint16_t samp_ct, curr_samp_ct;
int64_t time, latch;
uint32_t vf, ac;
int16_t buffer_l[64], buffer_r[64];
int buffer_pos, buffer_pos_end;
int filtered_l[32], filtered_r[32];
int f_pos;
int16_t out_l, out_r;
int32_t vol_l, vol_r;
} dac[2], adc;
int64_t dac_latch, dac_time;
int master_vol_l, master_vol_r;
int card;
int pos;
int16_t buffer[SOUNDBUFLEN * 2];
} es1371_t;
#define LEGACY_SB_ADDR (1<<29)
#define LEGACY_SSCAPE_ADDR_SHIFT 27
#define LEGACY_CODEC_ADDR_SHIFT 25
#define LEGACY_FORCE_IRQ (1<<24)
#define LEGACY_CAPTURE_SLAVE_DMA (1<<23)
#define LEGACY_CAPTURE_SLAVE_PIC (1<<22)
#define LEGACY_CAPTURE_MASTER_DMA (1<<21)
#define LEGACY_CAPTURE_MASTER_PIC (1<<20)
#define LEGACY_CAPTURE_ADLIB (1<<19)
#define LEGACY_CAPTURE_SB (1<<18)
#define LEGACY_CAPTURE_CODEC (1<<17)
#define LEGACY_CAPTURE_SSCAPE (1<<16)
#define LEGACY_EVENT_SSCAPE (0<<8)
#define LEGACY_EVENT_CODEC (1<<8)
#define LEGACY_EVENT_SB (2<<8)
#define LEGACY_EVENT_ADLIB (3<<8)
#define LEGACY_EVENT_MASTER_PIC (4<<8)
#define LEGACY_EVENT_MASTER_DMA (5<<8)
#define LEGACY_EVENT_SLAVE_PIC (6<<8)
#define LEGACY_EVENT_SLAVE_DMA (7<<8)
#define LEGACY_EVENT_MASK (7<<8)
#define LEGACY_EVENT_ADDR_SHIFT 3
#define LEGACY_EVENT_ADDR_MASK (0x1f<<3)
#define LEGACY_EVENT_TYPE_RW (1<<2)
#define LEGACY_INT (1<<0)
#define SRC_RAM_WE (1<<24)
#define CODEC_READ (1<<23)
#define CODEC_READY (1<<31)
#define INT_DAC1_EN (1<<6)
#define INT_DAC2_EN (1<<5)
#define SI_P2_INTR_EN (1<<9)
#define SI_P1_INTR_EN (1<<8)
#define INT_STATUS_INTR (1<<31)
#define INT_STATUS_DAC1 (1<<2)
#define INT_STATUS_DAC2 (1<<1)
#define FORMAT_MONO_8 0
#define FORMAT_STEREO_8 1
#define FORMAT_MONO_16 2
#define FORMAT_STEREO_16 3
const int32_t codec_attn[]= {
25,32,41,51,65,82,103,130,164,206,260,327,412,519,653,
822,1036,1304,1641,2067,2602,3276,4125,5192,6537,8230,10362,13044,
16422,20674,26027,32767
};
static void es1371_fetch(es1371_t *es1371, int dac_nr);
static void update_legacy(es1371_t *es1371);
#ifdef ENABLE_AUDIOPCI_LOG
int audiopci_do_log = ENABLE_AUDIOPCI_LOG;
#endif
static void
audiopci_log(const char *fmt, ...)
{
#ifdef ENABLE_AUDIOPCI_LOG
va_list ap;
if (audiopci_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
#endif
}
static void es1371_update_irqs(es1371_t *es1371)
{
int irq = 0;
if ((es1371->int_status & INT_STATUS_DAC1) && (es1371->si_cr & SI_P1_INTR_EN))
irq = 1;
if ((es1371->int_status & INT_STATUS_DAC2) && (es1371->si_cr & SI_P2_INTR_EN))
irq = 1;
if (irq)
es1371->int_status |= INT_STATUS_INTR;
else
es1371->int_status &= ~INT_STATUS_INTR;
if (es1371->legacy_ctrl & LEGACY_FORCE_IRQ)
irq = 1;
if (irq)
{
pci_set_irq(es1371->card, PCI_INTA);
// audiopci_log("Raise IRQ\n");
}
else
{
pci_clear_irq(es1371->card, PCI_INTA);
// audiopci_log("Drop IRQ\n");
}
}
static uint8_t es1371_inb(uint16_t port, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
uint8_t ret = 0;
switch (port & 0x3f)
{
case 0x00:
ret = es1371->int_ctrl & 0xff;
break;
case 0x01:
ret = (es1371->int_ctrl >> 8) & 0xff;
break;
case 0x02:
ret = (es1371->int_ctrl >> 16) & 0xff;
break;
case 0x03:
ret = (es1371->int_ctrl >> 24) & 0xff;
break;
case 0x04:
ret = es1371->int_status & 0xff;
break;
case 0x05:
ret = (es1371->int_status >> 8) & 0xff;
break;
case 0x06:
ret = (es1371->int_status >> 16) & 0xff;
break;
case 0x07:
ret = (es1371->int_status >> 24) & 0xff;
break;
case 0x09:
ret = es1371->uart_status;
break;
case 0x0c:
ret = es1371->mem_page;
break;
case 0x1a:
ret = es1371->legacy_ctrl >> 16;
break;
case 0x1b:
ret = es1371->legacy_ctrl >> 24;
break;
case 0x20:
ret = es1371->si_cr & 0xff;
break;
case 0x21:
ret = es1371->si_cr >> 8;
break;
case 0x22:
ret = (es1371->si_cr >> 16) | 0x80;
break;
case 0x23:
ret = 0xff;
break;
default:
audiopci_log("Bad es1371_inb: port=%04x\n", port);
}
// audiopci_log("es1371_inb: port=%04x ret=%02x\n", port, ret);
// output = 3;
return ret;
}
static uint16_t es1371_inw(uint16_t port, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
uint16_t ret = 0;
switch (port & 0x3e)
{
case 0x00:
ret = es1371->int_ctrl & 0xffff;
break;
case 0x02:
ret = (es1371->int_ctrl >> 16) & 0xffff;
break;
case 0x18:
ret = es1371->legacy_ctrl & 0xffff;
// audiopci_log("Read legacy ctrl %04x\n", ret);
break;
case 0x26:
ret = es1371->dac[0].curr_samp_ct;
break;
case 0x2a:
ret = es1371->dac[1].curr_samp_ct;
break;
case 0x36:
switch (es1371->mem_page)
{
case 0xc:
ret = es1371->dac[0].count;
break;
default:
audiopci_log("Bad es1371_inw: mem_page=%x port=%04x\n", es1371->mem_page, port);
}
break;
case 0x3e:
switch (es1371->mem_page)
{
case 0xc:
ret = es1371->dac[1].count;
break;
default:
audiopci_log("Bad es1371_inw: mem_page=%x port=%04x\n", es1371->mem_page, port);
}
break;
default:
audiopci_log("Bad es1371_inw: port=%04x\n", port);
}
// audiopci_log("es1371_inw: port=%04x ret=%04x %04x:%08x\n", port, ret, CS,cpu_state.pc);
return ret;
}
static uint32_t es1371_inl(uint16_t port, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
uint32_t ret = 0;
switch (port & 0x3c)
{
case 0x00:
ret = es1371->int_ctrl;
break;
case 0x04:
ret = es1371->int_status;
break;
case 0x10:
ret = es1371->sr_cir & ~0xffff;
ret |= es1371->sr_ram[es1371->sr_cir >> 25];
break;
case 0x14:
ret = es1371->codec_ctrl & 0x00ff0000;
ret |= es1371->codec_regs[(es1371->codec_ctrl >> 16) & 0x3f];
ret |= CODEC_READY;
break;
case 0x34:
switch (es1371->mem_page)
{
case 0xc:
ret = es1371->dac[0].size | (es1371->dac[0].count << 16);
break;
case 0xd:
ret = es1371->adc.size | (es1371->adc.count << 16);
break;
default:
audiopci_log("Bad es1371_inl: mem_page=%x port=%04x\n", es1371->mem_page, port);
}
break;
case 0x3c:
switch (es1371->mem_page)
{
case 0xc:
ret = es1371->dac[1].size | (es1371->dac[1].count << 16);
break;
default:
audiopci_log("Bad es1371_inl: mem_page=%x port=%04x\n", es1371->mem_page, port);
}
break;
default:
audiopci_log("Bad es1371_inl: port=%04x\n", port);
}
// audiopci_log("es1371_inl: port=%04x ret=%08x %08x\n", port, ret, cpu_state.pc);
return ret;
}
static void es1371_outb(uint16_t port, uint8_t val, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
// audiopci_log("es1371_outb: port=%04x val=%02x %04x:%08x\n", port, val, cs, cpu_state.pc);
switch (port & 0x3f)
{
case 0x00:
if (!(es1371->int_ctrl & INT_DAC1_EN) && (val & INT_DAC1_EN))
{
es1371->dac[0].addr = es1371->dac[0].addr_latch;
es1371->dac[0].buffer_pos = 0;
es1371->dac[0].buffer_pos_end = 0;
es1371_fetch(es1371, 0);
}
if (!(es1371->int_ctrl & INT_DAC2_EN) && (val & INT_DAC2_EN))
{
es1371->dac[1].addr = es1371->dac[1].addr_latch;
es1371->dac[1].buffer_pos = 0;
es1371->dac[1].buffer_pos_end = 0;
es1371_fetch(es1371, 1);
}
es1371->int_ctrl = (es1371->int_ctrl & 0xffffff00) | val;
break;
case 0x01:
es1371->int_ctrl = (es1371->int_ctrl & 0xffff00ff) | (val << 8);
break;
case 0x02:
es1371->int_ctrl = (es1371->int_ctrl & 0xff00ffff) | (val << 16);
break;
case 0x03:
es1371->int_ctrl = (es1371->int_ctrl & 0x00ffffff) | (val << 24);
break;
case 0x09:
es1371->uart_ctrl = val;
break;
case 0x0c:
es1371->mem_page = val & 0xf;
break;
case 0x18:
es1371->legacy_ctrl |= LEGACY_INT;
nmi = 0;
break;
case 0x1a:
es1371->legacy_ctrl = (es1371->legacy_ctrl & 0xff00ffff) | (val << 16);
update_legacy(es1371);
break;
case 0x1b:
es1371->legacy_ctrl = (es1371->legacy_ctrl & 0x00ffffff) | (val << 24);
es1371_update_irqs(es1371);
// output = 3;
update_legacy(es1371);
break;
case 0x20:
es1371->si_cr = (es1371->si_cr & 0xffff00) | val;
break;
case 0x21:
es1371->si_cr = (es1371->si_cr & 0xff00ff) | (val << 8);
if (!(es1371->si_cr & SI_P1_INTR_EN))
es1371->int_status &= ~INT_STATUS_DAC1;
if (!(es1371->si_cr & SI_P2_INTR_EN))
es1371->int_status &= ~INT_STATUS_DAC2;
es1371_update_irqs(es1371);
break;
case 0x22:
es1371->si_cr = (es1371->si_cr & 0x00ffff) | (val << 16);
break;
default:
audiopci_log("Bad es1371_outb: port=%04x val=%02x\n", port, val);
}
}
static void es1371_outw(uint16_t port, uint16_t val, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
// audiopci_log("es1371_outw: port=%04x val=%04x\n", port, val);
switch (port & 0x3f)
{
case 0x0c:
es1371->mem_page = val & 0xf;
break;
case 0x24:
es1371->dac[0].samp_ct = val;
break;
case 0x28:
es1371->dac[1].samp_ct = val;
break;
default:
audiopci_log("Bad es1371_outw: port=%04x val=%04x\n", port, val);
}
}
static void es1371_outl(uint16_t port, uint32_t val, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
// audiopci_log("es1371_outl: port=%04x val=%08x %04x:%08x\n", port, val, CS, cpu_state.pc);
switch (port & 0x3f)
{
case 0x04:
break;
case 0x0c:
es1371->mem_page = val & 0xf;
break;
case 0x10:
es1371->sr_cir = val;
if (es1371->sr_cir & SRC_RAM_WE)
{
// audiopci_log("Write SR RAM %02x %04x\n", es1371->sr_cir >> 25, val & 0xffff);
es1371->sr_ram[es1371->sr_cir >> 25] = val & 0xffff;
switch (es1371->sr_cir >> 25)
{
case 0x71:
es1371->dac[0].vf = (es1371->dac[0].vf & ~0x1f8000) | ((val & 0xfc00) << 5);
es1371->dac[0].ac = (es1371->dac[0].ac & ~0x7f8000) | ((val & 0x00ff) << 15);
es1371->dac[0].f_pos = 0;
break;
case 0x72:
es1371->dac[0].ac = (es1371->dac[0].ac & ~0x7fff) | (val & 0x7fff);
break;
case 0x73:
es1371->dac[0].vf = (es1371->dac[0].vf & ~0x7fff) | (val & 0x7fff);
break;
case 0x75:
es1371->dac[1].vf = (es1371->dac[1].vf & ~0x1f8000) | ((val & 0xfc00) << 5);
es1371->dac[1].ac = (es1371->dac[1].ac & ~0x7f8000) | ((val & 0x00ff) << 15);
es1371->dac[1].f_pos = 0;
break;
case 0x76:
es1371->dac[1].ac = (es1371->dac[1].ac & ~0x7fff) | (val & 0x7fff);
break;
case 0x77:
es1371->dac[1].vf = (es1371->dac[1].vf & ~0x7fff) | (val & 0x7fff);
break;
case 0x7c:
es1371->dac[0].vol_l = (int32_t)(int16_t)(val & 0xffff);
break;
case 0x7d:
es1371->dac[0].vol_r = (int32_t)(int16_t)(val & 0xffff);
break;
case 0x7e:
es1371->dac[1].vol_l = (int32_t)(int16_t)(val & 0xffff);
break;
case 0x7f:
es1371->dac[1].vol_r = (int32_t)(int16_t)(val & 0xffff);
break;
}
}
break;
case 0x14:
es1371->codec_ctrl = val;
if (!(val & CODEC_READ))
{
// audiopci_log("Write codec %02x %04x\n", (val >> 16) & 0x3f, val & 0xffff);
es1371->codec_regs[(val >> 16) & 0x3f] = val & 0xffff;
switch ((val >> 16) & 0x3f)
{
case 0x02: /*Master volume*/
if (val & 0x8000)
es1371->master_vol_l = es1371->master_vol_r = 0;
else
{
if (val & 0x2000)
es1371->master_vol_l = codec_attn[0];
else
es1371->master_vol_l = codec_attn[0x1f - ((val >> 8) & 0x1f)];
if (val & 0x20)
es1371->master_vol_r = codec_attn[0];
else
es1371->master_vol_r = codec_attn[0x1f - (val & 0x1f)];
}
break;
case 0x12: /*CD volume*/
if (val & 0x8000)
sound_set_cd_volume(0, 0);
else
sound_set_cd_volume(codec_attn[0x1f - ((val >> 8) & 0x1f)] * 2, codec_attn[0x1f - (val & 0x1f)] * 2);
break;
}
}
break;
case 0x24:
es1371->dac[0].samp_ct = val & 0xffff;
break;
case 0x28:
es1371->dac[1].samp_ct = val & 0xffff;
break;
case 0x30:
switch (es1371->mem_page)
{
case 0x0: case 0x1: case 0x2: case 0x3:
case 0x4: case 0x5: case 0x6: case 0x7:
case 0x8: case 0x9: case 0xa: case 0xb:
break;
case 0xc:
es1371->dac[0].addr_latch = val;
// audiopci_log("DAC1 addr %08x\n", val);
break;
default:
audiopci_log("Bad es1371_outl: mem_page=%x port=%04x val=%08x\n", es1371->mem_page, port, val);
}
break;
case 0x34:
switch (es1371->mem_page)
{
case 0x0: case 0x1: case 0x2: case 0x3:
case 0x4: case 0x5: case 0x6: case 0x7:
case 0x8: case 0x9: case 0xa: case 0xb:
break;
case 0xc:
es1371->dac[0].size = val & 0xffff;
es1371->dac[0].count = val >> 16;
if (es1371->dac[0].count)
es1371->dac[0].count -= 4;
break;
case 0xd:
es1371->adc.size = val & 0xffff;
es1371->adc.count = val >> 16;
break;
default:
audiopci_log("Bad es1371_outl: mem_page=%x port=%04x val=%08x\n", es1371->mem_page, port, val);
}
break;
case 0x38:
switch (es1371->mem_page)
{
case 0x0: case 0x1: case 0x2: case 0x3:
case 0x4: case 0x5: case 0x6: case 0x7:
case 0x8: case 0x9: case 0xa: case 0xb:
break;
case 0xc:
es1371->dac[1].addr_latch = val;
break;
case 0xd:
break;
default:
audiopci_log("Bad es1371_outl: mem_page=%x port=%04x val=%08x\n", es1371->mem_page, port, val);
}
break;
case 0x3c:
switch (es1371->mem_page)
{
case 0x0: case 0x1: case 0x2: case 0x3:
case 0x4: case 0x5: case 0x6: case 0x7:
case 0x8: case 0x9: case 0xa: case 0xb:
break;
case 0xc:
es1371->dac[1].size = val & 0xffff;
es1371->dac[1].count = val >> 16;
break;
default:
audiopci_log("Bad es1371_outl: mem_page=%x port=%04x val=%08x\n", es1371->mem_page, port, val);
}
break;
default:
audiopci_log("Bad es1371_outl: port=%04x val=%08x\n", port, val);
}
}
static void capture_event(es1371_t *es1371, int type, int rw, uint16_t port)
{
es1371->legacy_ctrl &= ~(LEGACY_EVENT_MASK | LEGACY_EVENT_ADDR_MASK);
es1371->legacy_ctrl |= type;
if (rw)
es1371->legacy_ctrl |= LEGACY_EVENT_TYPE_RW;
else
es1371->legacy_ctrl &= ~LEGACY_EVENT_TYPE_RW;
es1371->legacy_ctrl |= ((port << LEGACY_EVENT_ADDR_SHIFT) & LEGACY_EVENT_ADDR_MASK);
es1371->legacy_ctrl &= ~LEGACY_INT;
nmi = 1;
// audiopci_log("Event! %s %04x\n", rw ? "write" : "read", port);
}
static void capture_write_sscape(uint16_t port, uint8_t val, void *p)
{
capture_event(p, LEGACY_EVENT_SSCAPE, 1, port);
}
static void capture_write_codec(uint16_t port, uint8_t val, void *p)
{
capture_event(p, LEGACY_EVENT_CODEC, 1, port);
}
static void capture_write_sb(uint16_t port, uint8_t val, void *p)
{
capture_event(p, LEGACY_EVENT_SB, 1, port);
}
static void capture_write_adlib(uint16_t port, uint8_t val, void *p)
{
capture_event(p, LEGACY_EVENT_ADLIB, 1, port);
}
static void capture_write_master_pic(uint16_t port, uint8_t val, void *p)
{
capture_event(p, LEGACY_EVENT_MASTER_PIC, 1, port);
}
static void capture_write_master_dma(uint16_t port, uint8_t val, void *p)
{
capture_event(p, LEGACY_EVENT_MASTER_DMA, 1, port);
}
static void capture_write_slave_pic(uint16_t port, uint8_t val, void *p)
{
capture_event(p, LEGACY_EVENT_SLAVE_PIC, 1, port);
}
static void capture_write_slave_dma(uint16_t port, uint8_t val, void *p)
{
capture_event(p, LEGACY_EVENT_SLAVE_DMA, 1, port);
}
static uint8_t capture_read_sscape(uint16_t port, void *p)
{
capture_event(p, LEGACY_EVENT_SSCAPE, 0, port);
return 0xff;
}
static uint8_t capture_read_codec(uint16_t port, void *p)
{
capture_event(p, LEGACY_EVENT_CODEC, 0, port);
return 0xff;
}
static uint8_t capture_read_sb(uint16_t port, void *p)
{
capture_event(p, LEGACY_EVENT_SB, 0, port);
return 0xff;
}
static uint8_t capture_read_adlib(uint16_t port, void *p)
{
capture_event(p, LEGACY_EVENT_ADLIB, 0, port);
return 0xff;
}
static uint8_t capture_read_master_pic(uint16_t port, void *p)
{
capture_event(p, LEGACY_EVENT_MASTER_PIC, 0, port);
return 0xff;
}
static uint8_t capture_read_master_dma(uint16_t port, void *p)
{
capture_event(p, LEGACY_EVENT_MASTER_DMA, 0, port);
return 0xff;
}
static uint8_t capture_read_slave_pic(uint16_t port, void *p)
{
capture_event(p, LEGACY_EVENT_SLAVE_PIC, 0, port);
return 0xff;
}
static uint8_t capture_read_slave_dma(uint16_t port, void *p)
{
capture_event(p, LEGACY_EVENT_SLAVE_DMA, 0, port);
return 0xff;
}
static void update_legacy(es1371_t *es1371)
{
io_removehandler(0x0320, 0x0008, capture_read_sscape,NULL,NULL, capture_write_sscape,NULL,NULL, es1371);
io_removehandler(0x0330, 0x0008, capture_read_sscape,NULL,NULL, capture_write_sscape,NULL,NULL, es1371);
io_removehandler(0x0340, 0x0008, capture_read_sscape,NULL,NULL, capture_write_sscape,NULL,NULL, es1371);
io_removehandler(0x0350, 0x0008, capture_read_sscape,NULL,NULL, capture_write_sscape,NULL,NULL, es1371);
io_removehandler(0x5300, 0x0080, capture_read_codec,NULL,NULL, capture_write_codec,NULL,NULL, es1371);
io_removehandler(0xe800, 0x0080, capture_read_codec,NULL,NULL, capture_write_codec,NULL,NULL, es1371);
io_removehandler(0xf400, 0x0080, capture_read_codec,NULL,NULL, capture_write_codec,NULL,NULL, es1371);
io_removehandler(0x0220, 0x0010, capture_read_sb,NULL,NULL, capture_write_sb,NULL,NULL, es1371);
io_removehandler(0x0240, 0x0010, capture_read_sb,NULL,NULL, capture_write_sb,NULL,NULL, es1371);
io_removehandler(0x0388, 0x0004, capture_read_adlib,NULL,NULL, capture_write_adlib,NULL,NULL, es1371);
io_removehandler(0x0020, 0x0002, capture_read_master_pic,NULL,NULL, capture_write_master_pic,NULL,NULL, es1371);
io_removehandler(0x0000, 0x0010, capture_read_master_dma,NULL,NULL, capture_write_master_dma,NULL,NULL, es1371);
io_removehandler(0x00a0, 0x0002, capture_read_slave_pic,NULL,NULL, capture_write_slave_pic,NULL,NULL, es1371);
io_removehandler(0x00c0, 0x0020, capture_read_slave_dma,NULL,NULL, capture_write_slave_dma,NULL,NULL, es1371);
if (es1371->legacy_ctrl & LEGACY_CAPTURE_SSCAPE)
{
switch ((es1371->legacy_ctrl >> LEGACY_SSCAPE_ADDR_SHIFT) & 3)
{
case 0: io_sethandler(0x0320, 0x0008, capture_read_sscape,NULL,NULL, capture_write_sscape,NULL,NULL, es1371); break;
case 1: io_sethandler(0x0330, 0x0008, capture_read_sscape,NULL,NULL, capture_write_sscape,NULL,NULL, es1371); break;
case 2: io_sethandler(0x0340, 0x0008, capture_read_sscape,NULL,NULL, capture_write_sscape,NULL,NULL, es1371); break;
case 3: io_sethandler(0x0350, 0x0008, capture_read_sscape,NULL,NULL, capture_write_sscape,NULL,NULL, es1371); break;
}
}
if (es1371->legacy_ctrl & LEGACY_CAPTURE_CODEC)
{
switch ((es1371->legacy_ctrl >> LEGACY_CODEC_ADDR_SHIFT) & 3)
{
case 0: io_sethandler(0x5300, 0x0080, capture_read_codec,NULL,NULL, capture_write_codec,NULL,NULL, es1371); break;
case 2: io_sethandler(0xe800, 0x0080, capture_read_codec,NULL,NULL, capture_write_codec,NULL,NULL, es1371); break;
case 3: io_sethandler(0xf400, 0x0080, capture_read_codec,NULL,NULL, capture_write_codec,NULL,NULL, es1371); break;
}
}
if (es1371->legacy_ctrl & LEGACY_CAPTURE_SB)
{
if (!(es1371->legacy_ctrl & LEGACY_SB_ADDR))
io_sethandler(0x0220, 0x0010, capture_read_sb,NULL,NULL, capture_write_sb,NULL,NULL, es1371);
else
io_sethandler(0x0240, 0x0010, capture_read_sb,NULL,NULL, capture_write_sb,NULL,NULL, es1371);
}
if (es1371->legacy_ctrl & LEGACY_CAPTURE_ADLIB)
io_sethandler(0x0388, 0x0004, capture_read_adlib,NULL,NULL, capture_write_adlib,NULL,NULL, es1371);
if (es1371->legacy_ctrl & LEGACY_CAPTURE_MASTER_PIC)
io_sethandler(0x0020, 0x0002, capture_read_master_pic,NULL,NULL, capture_write_master_pic,NULL,NULL, es1371);
if (es1371->legacy_ctrl & LEGACY_CAPTURE_MASTER_DMA)
io_sethandler(0x0000, 0x0010, capture_read_master_dma,NULL,NULL, capture_write_master_dma,NULL,NULL, es1371);
if (es1371->legacy_ctrl & LEGACY_CAPTURE_SLAVE_PIC)
io_sethandler(0x00a0, 0x0002, capture_read_slave_pic,NULL,NULL, capture_write_slave_pic,NULL,NULL, es1371);
if (es1371->legacy_ctrl & LEGACY_CAPTURE_SLAVE_DMA)
io_sethandler(0x00c0, 0x0020, capture_read_slave_dma,NULL,NULL, capture_write_slave_dma,NULL,NULL, es1371);
}
static uint8_t es1371_pci_read(int func, int addr, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
if (func)
return 0;
//audiopci_log("ES1371 PCI read %08X PC=%08x\n", addr, cpu_state.pc);
switch (addr)
{
case 0x00: return 0x74; /*Ensoniq*/
case 0x01: return 0x12;
case 0x02: return 0x71; /*ES1371*/
case 0x03: return 0x13;
case 0x04: return es1371->pci_command;
case 0x05: return es1371->pci_serr;
case 0x06: return 0x10; /*Supports ACPI*/
case 0x07: return 0;
case 0x08: return 2; /*Revision ID*/
case 0x09: return 0x00; /*Multimedia audio device*/
case 0x0a: return 0x01;
case 0x0b: return 0x04;
case 0x10: return 0x01 | (es1371->base_addr & 0xc0); /*memBaseAddr*/
case 0x11: return es1371->base_addr >> 8;
case 0x12: return es1371->base_addr >> 16;
case 0x13: return es1371->base_addr >> 24;
case 0x2c: return 0x74; /*Subsystem vendor ID*/
case 0x2d: return 0x12;
case 0x2e: return 0x71;
case 0x2f: return 0x13;
case 0x34: return 0xdc; /*Capabilites pointer*/
case 0x3c: return es1371->int_line;
case 0x3d: return 0x01; /*INTA*/
case 0x3e: return 0xc; /*Minimum grant*/
case 0x3f: return 0x80; /*Maximum latency*/
case 0xdc: return 0x01; /*Capabilities identifier*/
case 0xdd: return 0x00; /*Next item pointer*/
case 0xde: return 0x31; /*Power management capabilities*/
case 0xdf: return 0x6c;
case 0xe0: return es1371->pmcsr & 0xff;
case 0xe1: return es1371->pmcsr >> 8;
}
return 0;
}
static void es1371_pci_write(int func, int addr, uint8_t val, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
if (func)
return;
// audiopci_log("ES1371 PCI write %04X %02X PC=%08x\n", addr, val, cpu_state.pc);
switch (addr)
{
case 0x04:
if (es1371->pci_command & PCI_COMMAND_IO)
io_removehandler(es1371->base_addr, 0x0040, es1371_inb, es1371_inw, es1371_inl, es1371_outb, es1371_outw, es1371_outl, es1371);
es1371->pci_command = val & 0x05;
if (es1371->pci_command & PCI_COMMAND_IO)
io_sethandler(es1371->base_addr, 0x0040, es1371_inb, es1371_inw, es1371_inl, es1371_outb, es1371_outw, es1371_outl, es1371);
break;
case 0x05:
es1371->pci_serr = val & 1;
break;
case 0x10:
if (es1371->pci_command & PCI_COMMAND_IO)
io_removehandler(es1371->base_addr, 0x0040, es1371_inb, es1371_inw, es1371_inl, es1371_outb, es1371_outw, es1371_outl, es1371);
es1371->base_addr = (es1371->base_addr & 0xffffff00) | (val & 0xc0);
if (es1371->pci_command & PCI_COMMAND_IO)
io_sethandler(es1371->base_addr, 0x0040, es1371_inb, es1371_inw, es1371_inl, es1371_outb, es1371_outw, es1371_outl, es1371);
break;
case 0x11:
if (es1371->pci_command & PCI_COMMAND_IO)
io_removehandler(es1371->base_addr, 0x0040, es1371_inb, es1371_inw, es1371_inl, es1371_outb, es1371_outw, es1371_outl, es1371);
es1371->base_addr = (es1371->base_addr & 0xffff00c0) | (val << 8);
if (es1371->pci_command & PCI_COMMAND_IO)
io_sethandler(es1371->base_addr, 0x0040, es1371_inb, es1371_inw, es1371_inl, es1371_outb, es1371_outw, es1371_outl, es1371);
break;
case 0x12:
es1371->base_addr = (es1371->base_addr & 0xff00ffc0) | (val << 16);
break;
case 0x13:
es1371->base_addr = (es1371->base_addr & 0x00ffffc0) | (val << 24);
break;
case 0x3c:
es1371->int_line = val;
break;
case 0xe0:
es1371->pmcsr = (es1371->pmcsr & 0xff00) | (val & 0x03);
break;
case 0xe1:
es1371->pmcsr = (es1371->pmcsr & 0x00ff) | ((val & 0x01) << 8);
break;
}
// audiopci_log("es1371->base_addr %08x\n", es1371->base_addr);
}
static void es1371_fetch(es1371_t *es1371, int dac_nr)
{
int format = dac_nr ? ((es1371->si_cr >> 2) & 3) : (es1371->si_cr & 3);
int pos = es1371->dac[dac_nr].buffer_pos & 63;
int c;
//audiopci_log("Fetch format=%i %08x %08x %08x %08x %08x\n", format, es1371->dac[dac_nr].count, es1371->dac[dac_nr].size, es1371->dac[dac_nr].curr_samp_ct,es1371->dac[dac_nr].samp_ct, es1371->dac[dac_nr].addr);
switch (format)
{
case FORMAT_MONO_8:
for (c = 0; c < 32; c += 4)
{
es1371->dac[dac_nr].buffer_l[(pos+c) & 63] = es1371->dac[dac_nr].buffer_r[(pos+c) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr) ^ 0x80) << 8;
es1371->dac[dac_nr].buffer_l[(pos+c+1) & 63] = es1371->dac[dac_nr].buffer_r[(pos+c+1) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr+1) ^ 0x80) << 8;
es1371->dac[dac_nr].buffer_l[(pos+c+2) & 63] = es1371->dac[dac_nr].buffer_r[(pos+c+2) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr+2) ^ 0x80) << 8;
es1371->dac[dac_nr].buffer_l[(pos+c+3) & 63] = es1371->dac[dac_nr].buffer_r[(pos+c+3) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr+3) ^ 0x80) << 8;
es1371->dac[dac_nr].addr += 4;
es1371->dac[dac_nr].buffer_pos_end += 4;
es1371->dac[dac_nr].count++;
if (es1371->dac[dac_nr].count > es1371->dac[dac_nr].size)
{
es1371->dac[dac_nr].count = 0;
es1371->dac[dac_nr].addr = es1371->dac[dac_nr].addr_latch;
break;
}
}
break;
case FORMAT_STEREO_8:
for (c = 0; c < 16; c += 2)
{
es1371->dac[dac_nr].buffer_l[(pos+c) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr) ^ 0x80) << 8;
es1371->dac[dac_nr].buffer_r[(pos+c) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr + 1) ^ 0x80) << 8;
es1371->dac[dac_nr].buffer_l[(pos+c+1) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr + 2) ^ 0x80) << 8;
es1371->dac[dac_nr].buffer_r[(pos+c+1) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr + 3) ^ 0x80) << 8;
es1371->dac[dac_nr].addr += 4;
es1371->dac[dac_nr].buffer_pos_end += 2;
es1371->dac[dac_nr].count++;
if (es1371->dac[dac_nr].count > es1371->dac[dac_nr].size)
{
es1371->dac[dac_nr].count = 0;
es1371->dac[dac_nr].addr = es1371->dac[dac_nr].addr_latch;
break;
}
}
break;
case FORMAT_MONO_16:
for (c = 0; c < 16; c += 2)
{
es1371->dac[dac_nr].buffer_l[(pos+c) & 63] = es1371->dac[dac_nr].buffer_r[(pos+c) & 63] = mem_readw_phys(es1371->dac[dac_nr].addr);
es1371->dac[dac_nr].buffer_l[(pos+c+1) & 63] = es1371->dac[dac_nr].buffer_r[(pos+c+1) & 63] = mem_readw_phys(es1371->dac[dac_nr].addr + 2);
es1371->dac[dac_nr].addr += 4;
es1371->dac[dac_nr].buffer_pos_end += 2;
es1371->dac[dac_nr].count++;
if (es1371->dac[dac_nr].count > es1371->dac[dac_nr].size)
{
es1371->dac[dac_nr].count = 0;
es1371->dac[dac_nr].addr = es1371->dac[dac_nr].addr_latch;
break;
}
}
break;
case FORMAT_STEREO_16:
for (c = 0; c < 4; c++)
{
es1371->dac[dac_nr].buffer_l[(pos+c) & 63] = mem_readw_phys(es1371->dac[dac_nr].addr);
es1371->dac[dac_nr].buffer_r[(pos+c) & 63] = mem_readw_phys(es1371->dac[dac_nr].addr + 2);
// audiopci_log("Fetch %02x %08x %04x %04x\n", (pos+c) & 63, es1371->dac[dac_nr].addr, es1371->dac[dac_nr].buffer_l[(pos+c) & 63], es1371->dac[dac_nr].buffer_r[(pos+c) & 63]);
es1371->dac[dac_nr].addr += 4;
es1371->dac[dac_nr].buffer_pos_end++;
es1371->dac[dac_nr].count++;
if (es1371->dac[dac_nr].count > es1371->dac[dac_nr].size)
{
es1371->dac[dac_nr].count = 0;
es1371->dac[dac_nr].addr = es1371->dac[dac_nr].addr_latch;
break;
}
}
break;
}
}
static inline float low_fir_es1371(int dac_nr, int i, float NewSample)
{
static float x[2][2][128]; //input samples
static int x_pos[2] = {0, 0};
float out = 0.0;
int read_pos;
int n_coef;
int pos = x_pos[dac_nr];
x[dac_nr][i][pos] = NewSample;
/*Since only 1/16th of input samples are non-zero, only filter those that
are valid.*/
read_pos = (pos + 15) & (127 & ~15);
n_coef = (16 - pos) & 15;
while (n_coef < ES1371_NCoef)
{
out += low_fir_es1371_coef[n_coef] * x[dac_nr][i][read_pos];
read_pos = (read_pos + 16) & (127 & ~15);
n_coef += 16;
}
if (i == 1)
{
x_pos[dac_nr] = (x_pos[dac_nr] + 1) & 127;
if (x_pos[dac_nr] > 127)
x_pos[dac_nr] = 0;
}
return out;
}
static void es1371_next_sample_filtered(es1371_t *es1371, int dac_nr, int out_idx)
{
int out_l, out_r;
int c;
if ((es1371->dac[dac_nr].buffer_pos - es1371->dac[dac_nr].buffer_pos_end) >= 0)
{
es1371_fetch(es1371, dac_nr);
}
out_l = es1371->dac[dac_nr].buffer_l[es1371->dac[dac_nr].buffer_pos & 63];
out_r = es1371->dac[dac_nr].buffer_r[es1371->dac[dac_nr].buffer_pos & 63];
es1371->dac[dac_nr].filtered_l[out_idx] = (int)low_fir_es1371(dac_nr, 0, (float)out_l);
es1371->dac[dac_nr].filtered_r[out_idx] = (int)low_fir_es1371(dac_nr, 1, (float)out_r);
for (c = 1; c < 16; c++)
{
es1371->dac[dac_nr].filtered_l[out_idx+c] = (int)low_fir_es1371(dac_nr, 0, 0);
es1371->dac[dac_nr].filtered_r[out_idx+c] = (int)low_fir_es1371(dac_nr, 1, 0);
}
// audiopci_log("Use %02x %04x %04x\n", es1371->dac[dac_nr].buffer_pos & 63, es1371->dac[dac_nr].out_l, es1371->dac[dac_nr].out_r);
es1371->dac[dac_nr].buffer_pos++;
// audiopci_log("Next sample %08x %08x %08x\n", es1371->dac[dac_nr].buffer_pos, es1371->dac[dac_nr].buffer_pos_end, es1371->dac[dac_nr].curr_samp_ct);
}
//static FILE *es1371_f;//,*es1371_f2;
static void es1371_update(es1371_t *es1371)
{
int32_t l, r;
l = (es1371->dac[0].out_l * es1371->dac[0].vol_l) >> 12;
l += ((es1371->dac[1].out_l * es1371->dac[1].vol_l) >> 12);
r = (es1371->dac[0].out_r * es1371->dac[0].vol_r) >> 12;
r += ((es1371->dac[1].out_r * es1371->dac[1].vol_r) >> 12);
l >>= 1;
r >>= 1;
l = (l * es1371->master_vol_l) >> 15;
r = (r * es1371->master_vol_r) >> 15;
if (l < -32768)
l = -32768;
else if (l > 32767)
l = 32767;
if (r < -32768)
r = -32768;
else if (r > 32767)
r = 32767;
for (; es1371->pos < sound_pos_global; es1371->pos++)
{
es1371->buffer[es1371->pos*2] = l;
es1371->buffer[es1371->pos*2 + 1] = r;
}
}
static void es1371_poll(void *p)
{
es1371_t *es1371 = (es1371_t *)p;
es1371->dac[1].time += es1371->dac[1].latch;
es1371_update(es1371);
if (es1371->int_ctrl & INT_DAC1_EN)
{
int frac = es1371->dac[0].ac & 0x7fff;
int idx = es1371->dac[0].ac >> 15;
int samp1_l = es1371->dac[0].filtered_l[idx];
int samp1_r = es1371->dac[0].filtered_r[idx];
int samp2_l = es1371->dac[0].filtered_l[(idx + 1) & 31];
int samp2_r = es1371->dac[0].filtered_r[(idx + 1) & 31];
es1371->dac[0].out_l = ((samp1_l * (0x8000 - frac)) + (samp2_l * frac)) >> 15;
es1371->dac[0].out_r = ((samp1_r * (0x8000 - frac)) + (samp2_r * frac)) >> 15;
// audiopci_log("1Samp %i %i %08x\n", es1371->dac[0].curr_samp_ct, es1371->dac[0].samp_ct, es1371->dac[0].ac);
es1371->dac[0].ac += es1371->dac[0].vf;
es1371->dac[0].ac &= ((32 << 15) - 1);
if ((es1371->dac[0].ac >> (15+4)) != es1371->dac[0].f_pos)
{
es1371_next_sample_filtered(es1371, 0, es1371->dac[0].f_pos ? 16 : 0);
es1371->dac[0].f_pos = (es1371->dac[0].f_pos + 1) & 1;
es1371->dac[0].curr_samp_ct++;
if (es1371->dac[0].curr_samp_ct == es1371->dac[0].samp_ct)
{
// audiopci_log("DAC1 IRQ\n");
es1371->int_status |= INT_STATUS_DAC1;
es1371_update_irqs(es1371);
}
if (es1371->dac[0].curr_samp_ct > es1371->dac[0].samp_ct)
{
es1371->dac[0].curr_samp_ct = 0;
}
}
}
if (es1371->int_ctrl & INT_DAC2_EN)
{
int frac = es1371->dac[1].ac & 0x7fff;
int idx = es1371->dac[1].ac >> 15;
int samp1_l = es1371->dac[1].filtered_l[idx];
int samp1_r = es1371->dac[1].filtered_r[idx];
int samp2_l = es1371->dac[1].filtered_l[(idx + 1) & 31];
int samp2_r = es1371->dac[1].filtered_r[(idx + 1) & 31];
es1371->dac[1].out_l = ((samp1_l * (0x8000 - frac)) + (samp2_l * frac)) >> 15;
es1371->dac[1].out_r = ((samp1_r * (0x8000 - frac)) + (samp2_r * frac)) >> 15;
// audiopci_log("2Samp %i %i %08x\n", es1371->dac[1].curr_samp_ct, es1371->dac[1].samp_ct, es1371->dac[1].ac);
es1371->dac[1].ac += es1371->dac[1].vf;
es1371->dac[1].ac &= ((32 << 15) - 1);
if ((es1371->dac[1].ac >> (15+4)) != es1371->dac[1].f_pos)
{
es1371_next_sample_filtered(es1371, 1, es1371->dac[1].f_pos ? 16 : 0);
es1371->dac[1].f_pos = (es1371->dac[1].f_pos + 1) & 1;
es1371->dac[1].curr_samp_ct++;
if (es1371->dac[1].curr_samp_ct > es1371->dac[1].samp_ct)
{
// es1371->dac[1].curr_samp_ct = 0;
// audiopci_log("DAC2 IRQ\n");
es1371->int_status |= INT_STATUS_DAC2;
es1371_update_irqs(es1371);
}
if (es1371->dac[1].curr_samp_ct > es1371->dac[1].samp_ct)
es1371->dac[1].curr_samp_ct = 0;
}
}
}
static void es1371_get_buffer(int32_t *buffer, int len, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
int c;
es1371_update(es1371);
for (c = 0; c < len * 2; c++)
buffer[c] += (es1371->buffer[c] / 2);
es1371->pos = 0;
}
static inline double sinc(double x)
{
return sin(M_PI * x) / (M_PI * x);
}
static void generate_es1371_filter()
{
/*Cutoff frequency = 1 / 32*/
float fC = 1.0 / 32.0;
float gain;
int n;
for (n = 0; n < ES1371_NCoef; n++)
{
/*Blackman window*/
double w = 0.42 - (0.5 * cos((2.0*n*M_PI)/(double)(ES1371_NCoef-1))) + (0.08 * cos((4.0*n*M_PI)/(double)(ES1371_NCoef-1)));
/*Sinc filter*/
double h = sinc(2.0 * fC * ((double)n - ((double)(ES1371_NCoef-1) / 2.0)));
/*Create windowed-sinc filter*/
low_fir_es1371_coef[n] = w * h;
}
low_fir_es1371_coef[(ES1371_NCoef - 1) / 2] = 1.0;
gain = 0.0;
for (n = 0; n < ES1371_NCoef; n++)
gain += low_fir_es1371_coef[n] / (float)N;
/*Normalise filter, to produce unity gain*/
for (n = 0; n < ES1371_NCoef; n++)
low_fir_es1371_coef[n] /= gain;
}
static void *es1371_init(const device_t *info)
{
es1371_t *es1371 = malloc(sizeof(es1371_t));
memset(es1371, 0, sizeof(es1371_t));
sound_add_handler(es1371_get_buffer, es1371);
es1371->card = pci_add_card(PCI_ADD_NORMAL, es1371_pci_read, es1371_pci_write, es1371);
timer_add(es1371_poll, &es1371->dac[1].time, TIMER_ALWAYS_ENABLED, es1371);
generate_es1371_filter();
return es1371;
}
static void es1371_close(void *p)
{
es1371_t *es1371 = (es1371_t *)p;
free(es1371);
}
static void es1371_speed_changed(void *p)
{
es1371_t *es1371 = (es1371_t *)p;
es1371->dac[1].latch = (int)((double)TIMER_USEC * (1000000.0 / 48000.0));
}
void es1371_add_status_info_dac(es1371_t *es1371, char *s, int max_len, int dac_nr)
{
int ena = dac_nr ? INT_DAC2_EN : INT_DAC1_EN;
char *dac_name = dac_nr ? "DAC2 (Wave)" : "DAC1 (MIDI)";
char temps[128];
if (es1371->int_ctrl & ena)
{
int format = dac_nr ? ((es1371->si_cr >> 2) & 3) : (es1371->si_cr & 3);
double freq = 48000.0 * ((double)es1371->dac[dac_nr].vf / (32768.0 * 16.0));
switch (format)
{
case FORMAT_MONO_8:
snprintf(temps, 128, "%s format : 8-bit mono\n", dac_name);
break;
case FORMAT_STEREO_8:
snprintf(temps, 128, "%s format : 8-bit stereo\n", dac_name);
break;
case FORMAT_MONO_16:
snprintf(temps, 128, "%s format : 16-bit mono\n", dac_name);
break;
case FORMAT_STEREO_16:
snprintf(temps, 128, "%s format : 16-bit stereo\n", dac_name);
break;
}
strncat(s, temps, max_len);
max_len -= strlen(temps);
snprintf(temps, 128, "Playback frequency : %i Hz\n", (int)freq);
strncat(s, temps, max_len);
}
else
{
snprintf(temps, max_len, "%s stopped\n", dac_name);
strncat(s, temps, max_len);
}
}
void es1371_add_status_info(char *s, int max_len, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
es1371_add_status_info_dac(es1371, s, max_len, 0);
es1371_add_status_info_dac(es1371, s, max_len, 1);
}
const device_t es1371_device =
{
"Ensoniq AudioPCI (ES1371)",
DEVICE_PCI,
0,
es1371_init,
es1371_close,
NULL,
NULL,
es1371_speed_changed,
NULL,
es1371_add_status_info,
NULL
};