Merge pull request #1580 from richardg867/feature/savquest

Audio MOAC II: AC97 edition
This commit is contained in:
Miran Grča
2021-08-04 08:58:16 +02:00
committed by GitHub
31 changed files with 2079 additions and 477 deletions

View File

@@ -49,6 +49,11 @@
#include <86box/chipset.h>
#include <86box/sio.h>
#include <86box/hwm.h>
#include <86box/gameport.h>
#include <86box/sound.h>
#include <86box/snd_ac97.h>
#include <86box/snd_sb.h>
#include <86box/nmi.h>
/* Most revision numbers (PCI-ISA bridge or otherwise) were lifted from PCI device
listings on forums, as VIA's datasheets are not very helpful regarding those. */
@@ -66,17 +71,20 @@ typedef struct
uint32_t local;
uint8_t max_func;
uint8_t pci_isa_regs[256];
uint8_t ide_regs[256];
uint8_t usb_regs[2][256];
uint8_t power_regs[256];
uint8_t ac97_regs[2][256];
uint8_t pci_isa_regs[256],
ide_regs[256],
usb_regs[2][256],
power_regs[256],
ac97_regs[2][256], fmnmi_regs[4];
sff8038i_t *bm[2];
nvr_t *nvr;
int nvr_enabled, slot;
smbus_piix4_t *smbus;
usb_t *usb[2];
acpi_t *acpi;
void *gameport, *ac97;
sb_t *sb;
uint16_t midigame_base, sb_base, fmnmi_base;
} pipc_t;
@@ -100,6 +108,13 @@ pipc_log(const char *fmt, ...)
#endif
static void pipc_sgd_handlers(pipc_t *dev, uint8_t modem);
static void pipc_codec_handlers(pipc_t *dev, uint8_t modem);
static void pipc_sb_handlers(pipc_t *dev, uint8_t modem);
static uint8_t pipc_read(int func, int addr, void *priv);
static void pipc_write(int func, int addr, uint8_t val, void *priv);
static void
pipc_reset_hard(void *priv)
{
@@ -332,19 +347,30 @@ pipc_reset_hard(void *priv)
}
dev->ac97_regs[i][0x10] = 0x01;
dev->ac97_regs[i][(dev->local >= VIA_PIPC_8231) ? 0x1c : 0x14] = 0x01;
if ((i == 0) && (dev->local >= VIA_PIPC_8231)) {
dev->ac97_regs[i][0x18] = 0x31;
dev->ac97_regs[i][0x19] = 0x03;
if (i == 0) {
dev->ac97_regs[i][0x14] = 0x01;
dev->ac97_regs[i][0x18] = 0x01;
}
dev->ac97_regs[i][0x1c] = 0x01;
dev->ac97_regs[i][0x3d] = 0x03;
if (i == 0)
dev->ac97_regs[i][0x40] = 0x01;
dev->ac97_regs[i][0x43] = 0x1c;
dev->ac97_regs[i][0x48] = 0x01;
dev->ac97_regs[i][0x4b] = 0x02;
pipc_sgd_handlers(dev, i);
pipc_codec_handlers(dev, i);
pipc_sb_handlers(dev, i);
}
}
if (dev->gameport)
gameport_remap(dev->gameport, 0x200);
pci_set_irq_routing(PCI_INTA, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTB, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTC, PCI_IRQ_DISABLED);
@@ -431,6 +457,167 @@ pipc_bus_master_handlers(pipc_t *dev)
}
static void
pipc_sgd_handlers(pipc_t *dev, uint8_t modem)
{
if (!dev->ac97)
return;
if (modem)
ac97_via_remap_modem_sgd(dev->ac97, dev->ac97_regs[1][0x11] << 8, dev->ac97_regs[1][0x04] & PCI_COMMAND_IO);
else
ac97_via_remap_audio_sgd(dev->ac97, dev->ac97_regs[0][0x11] << 8, dev->ac97_regs[0][0x04] & PCI_COMMAND_IO);
}
static void
pipc_codec_handlers(pipc_t *dev, uint8_t modem)
{
if (!dev->ac97)
return;
if (modem)
ac97_via_remap_modem_codec(dev->ac97, dev->ac97_regs[1][0x1d] << 8, dev->ac97_regs[1][0x04] & PCI_COMMAND_IO);
else
ac97_via_remap_audio_codec(dev->ac97, dev->ac97_regs[0][0x1d] << 8, dev->ac97_regs[0][0x04] & PCI_COMMAND_IO);
}
static uint8_t
pipc_fmnmi_read(uint16_t addr, void *priv)
{
pipc_t *dev = (pipc_t *) priv;
uint8_t ret = dev->fmnmi_regs[addr & 0x03];
pipc_log("PIPC: fmnmi_read(%02X) = %02X\n", addr & 0x03, ret);
#ifdef VIA_PIPC_FM_EMULATION
/* Clear NMI/SMI if enabled. */
if (dev->ac97_regs[0][0x48] & 0x01) {
if (dev->ac97_regs[0][0x48] & 0x04)
smi_line = 0;
else
nmi = 0;
}
#endif
return ret;
}
static void
pipc_fmnmi_handlers(pipc_t *dev, uint8_t modem)
{
if (!dev->ac97 || modem)
return;
if (dev->fmnmi_base)
io_removehandler(dev->fmnmi_base, 4, pipc_fmnmi_read, NULL, NULL, NULL, NULL, NULL, dev);
dev->fmnmi_base = (dev->ac97_regs[0][0x15] << 8) | (dev->ac97_regs[0][0x14] & 0xfc);
if (dev->fmnmi_base && (dev->ac97_regs[0][0x04] & PCI_COMMAND_IO))
io_sethandler(dev->fmnmi_base, 4, pipc_fmnmi_read, NULL, NULL, NULL, NULL, NULL, dev);
}
static uint8_t
pipc_fm_read(uint16_t addr, void *priv)
{
#ifdef VIA_PIPC_FM_EMULATION
uint8_t ret = 0x00;
#else
pipc_t *dev = (pipc_t *) priv;
uint8_t ret = opl3_read(addr, &dev->sb->opl);
#endif
pipc_log("PIPC: fm_read(%02X) = %02X\n", addr & 0x03, ret);
return ret;
}
static void
pipc_fm_write(uint16_t addr, uint8_t val, void *priv)
{
pipc_t *dev = (pipc_t *) priv;
pipc_log("PIPC: fm_write(%02X, %02X)\n", addr & 0x03, val);
#ifdef VIA_PIPC_FM_EMULATION
/* Real 686B only updates the bank ID register when writing to the
index port, and only fires NMI/SMI when writing to the data port. */
if (!(addr & 0x01)) {
dev->fmnmi_regs[0x00] = (addr & 0x02) ? 0x02 : 0x01;
dev->fmnmi_regs[0x01] = val;
} else {
dev->fmnmi_regs[0x02] = val;
/* Fire NMI/SMI if enabled. */
if (dev->ac97_regs[0][0x48] & 0x01) {
if (dev->ac97_regs[0][0x48] & 0x04)
smi_line = 1;
else
nmi = 1;
}
}
#else
opl3_write(addr, val, &dev->sb->opl);
#endif
}
static void
pipc_sb_handlers(pipc_t *dev, uint8_t modem)
{
if (!dev->ac97 || modem)
return;
sb_dsp_setaddr(&dev->sb->dsp, 0);
if (dev->sb_base) {
io_removehandler(dev->sb_base, 4, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &dev->sb->opl);
io_removehandler(dev->sb_base + 8, 2, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &dev->sb->opl);
io_removehandler(dev->sb_base + 4, 2, sb_ct1345_mixer_read, NULL, NULL, sb_ct1345_mixer_write, NULL, NULL, dev->sb);
}
mpu401_change_addr(dev->sb->mpu, 0);
mpu401_setirq(dev->sb->mpu, 0);
io_removehandler(0x388, 4, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &dev->sb->opl);
if (dev->ac97_regs[0][0x42] & 0x01) {
dev->sb_base = 0x220 + (0x20 * (dev->ac97_regs[0][0x43] & 0x03));
sb_dsp_setaddr(&dev->sb->dsp, dev->sb_base);
if (dev->ac97_regs[0][0x42] & 0x04) {
io_sethandler(dev->sb_base, 4, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &dev->sb->opl);
io_sethandler(dev->sb_base + 8, 2, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &dev->sb->opl);
}
io_sethandler(dev->sb_base + 4, 2, sb_ct1345_mixer_read, NULL, NULL, sb_ct1345_mixer_write, NULL, NULL, dev->sb);
uint8_t irq = 5 + (2 * ((dev->ac97_regs[0][0x43] >> 6) & 0x03));
sb_dsp_setirq(&dev->sb->dsp, (irq == 11) ? 10 : irq);
sb_dsp_setdma8(&dev->sb->dsp, (dev->ac97_regs[0][0x43] >> 4) & 0x03);
}
if (dev->ac97_regs[0][0x42] & 0x02) {
/* BAR 2 is a mess. The MPU and game port remapping registers that VIA claims to be there don't
seem to actually exist on a real 686B. Remapping the MPU to BAR 2 itself does work, though. */
if (dev->ac97_regs[0][0x42] & 0x80)
mpu401_change_addr(dev->sb->mpu, (dev->ac97_regs[0][0x19] << 8) | (dev->ac97_regs[0][0x18] & 0xfc));
else
mpu401_change_addr(dev->sb->mpu, 0x300 | ((dev->ac97_regs[0][0x43] << 2) & 0x30));
if (!(dev->ac97_regs[0][0x42] & 0x40))
mpu401_setirq(dev->sb->mpu, dev->sb->dsp.sb_irqnum);
}
if (dev->ac97_regs[0][0x42] & 0x04) {
io_sethandler(0x388, 4, pipc_fm_read, NULL, NULL, pipc_fm_write, NULL, NULL, dev);
}
}
static uint8_t
pipc_read(int func, int addr, void *priv)
{
@@ -490,8 +677,12 @@ pipc_read(int func, int addr, void *priv)
ret |= 0x10;
}
}
else if ((func <= (pm_func + 2)) && !(dev->pci_isa_regs[0x85] & ((func == (pm_func + 1)) ? 0x04 : 0x08))) /* AC97 / MC97 */
ret = dev->ac97_regs[func - pm_func - 1][addr];
else if ((func <= (pm_func + 2)) && !(dev->pci_isa_regs[0x85] & ((func == (pm_func + 1)) ? 0x04 : 0x08))) { /* AC97 / MC97 */
if (addr == 0x40)
ret = ac97_via_read_status(dev->ac97, func - pm_func - 1);
else
ret = dev->ac97_regs[func - pm_func - 1][addr];
}
pipc_log("PIPC: read(%d, %02X) = %02X\n", func, addr, ret);
@@ -531,7 +722,7 @@ pipc_write(int func, int addr, uint8_t val, void *priv)
pipc_log("PIPC: write(%d, %02X, %02X)\n", func, addr, val);
if (func == 0) { /* PCI-ISA bridge */
/* Read-only addresses */
/* Read-only addresses. */
if ((addr < 4) || (addr == 5) || ((addr >= 8) && (addr < 0x40)) || (addr == 0x49) || (addr == 0x4b) ||
(addr == 0x53) || ((addr >= 0x5d) && (addr < 0x5f)) || (addr >= 0x90))
return;
@@ -698,7 +889,7 @@ pipc_write(int func, int addr, uint8_t val, void *priv)
break;
}
} else if (func == 1) { /* IDE */
/* Read-only addresses and disable bit */
/* Read-only addresses. */
if ((addr < 4) || (addr == 5) || (addr == 8) || ((addr >= 0xa) && (addr < 0x0d)) ||
((addr >= 0x0e) && (addr < 0x10)) || ((addr >= 0x12) && (addr < 0x13)) ||
((addr >= 0x16) && (addr < 0x17)) || ((addr >= 0x1a) && (addr < 0x1b)) ||
@@ -707,12 +898,16 @@ pipc_write(int func, int addr, uint8_t val, void *priv)
((addr >= 0x62) && (addr < 0x68)) || ((addr >= 0x6a) && (addr < 0x70)) ||
(addr == 0x72) || (addr == 0x73) || (addr == 0x76) || (addr == 0x77) ||
(addr == 0x7a) || (addr == 0x7b) || (addr == 0x7e) || (addr == 0x7f) ||
((addr >= 0x84) && (addr < 0x88)) || (addr >= 0x8c) || (dev->pci_isa_regs[0x48] & 0x02))
((addr >= 0x84) && (addr < 0x88)) || (addr >= 0x8c))
return;
if ((dev->local <= VIA_PIPC_586B) && ((addr == 0x54) || (addr >= 0x70)))
return;
/* Check disable bit. */
if (dev->pci_isa_regs[0x48] & 0x02)
return;
switch (addr) {
case 0x04:
dev->ide_regs[0x04] = val & 0x85;
@@ -856,18 +1051,18 @@ pipc_write(int func, int addr, uint8_t val, void *priv)
break;
}
} else if (func < pm_func) { /* USB */
/* Read-only addresses */
/* Read-only addresses. */
if ((addr < 4) || (addr == 5) || (addr == 6) || ((addr >= 8) && (addr < 0xd)) ||
((addr >= 0xe) && (addr < 0x20)) || ((addr >= 0x22) && (addr < 0x3c)) ||
((addr >= 0x3e) && (addr < 0x40)) || ((addr >= 0x42) && (addr < 0x44)) ||
((addr >= 0x46) && (addr < 0x84)) || ((addr >= 0x85) && (addr < 0xc0)) || (addr >= 0xc2))
return;
/* Check disable bits for both controllers */
if ((func == 2) ? (dev->pci_isa_regs[0x48] & 0x04) : (dev->pci_isa_regs[0x85] & 0x10))
if ((dev->local <= VIA_PIPC_596B) && (addr == 0x84))
return;
if ((dev->local <= VIA_PIPC_596B) && (addr == 0x84))
/* Check disable bits for both controllers. */
if ((func == 2) ? (dev->pci_isa_regs[0x48] & 0x04) : (dev->pci_isa_regs[0x85] & 0x10))
return;
switch (addr) {
@@ -970,22 +1165,92 @@ pipc_write(int func, int addr, uint8_t val, void *priv)
break;
}
} else if (func <= pm_func + 2) { /* AC97 / MC97 */
/* Read-only addresses */
if ((addr < 0x4) || ((addr >= 0x6) && (addr < 0xd)) || ((addr >= 0xe) && (addr < 0x10)) || ((addr >= 0x1c) && (addr < 0x2c)) ||
/* Read-only addresses. */
if ((addr < 0x4) || ((addr >= 0x6) && (addr < 0x9)) || ((addr >= 0xc) && (addr < 0x11)) || (addr == 0x16) ||
(addr == 0x17) || (addr == 0x1a) || (addr == 0x1b) || ((addr >= 0x1e) && (addr < 0x2c)) ||
((addr >= 0x30) && (addr < 0x34)) || ((addr >= 0x35) && (addr < 0x3c)) || ((addr >= 0x3d) && (addr < 0x41)) ||
((addr >= 0x45) && (addr < 0x4a)) || (addr >= 0x4c))
return;
/* Also check disable bits for both controllers */
if ((func == (pm_func + 1)) && ((addr == 0x44) || (dev->pci_isa_regs[0x85] & 0x04)))
/* Small shortcut. */
func = func - pm_func - 1;
/* Check disable bits and specific read-only addresses for both controllers. */
if ((func == 0) && (((addr >= 0x09) && (addr < 0xc)) || (addr == 0x44) || (dev->pci_isa_regs[0x85] & 0x04)))
return;
if ((func == (pm_func + 2)) && ((addr == 0x4a) || (addr == 0x4b) || (dev->pci_isa_regs[0x85] & 0x08)))
if ((func == 1) && ((addr == 0x14) || (addr == 0x15) || (addr == 0x18) || (addr == 0x19) || (addr == 0x42) ||
(addr == 0x43) || (addr == 0x48) || (addr == 0x4a) || (addr == 0x4b) || (dev->pci_isa_regs[0x85] & 0x08)))
return;
switch (addr) {
case 0x04:
dev->ac97_regs[func][addr] = val;
pipc_sgd_handlers(dev, func);
pipc_codec_handlers(dev, func);
pipc_fmnmi_handlers(dev, func);
break;
case 0x09: case 0x0a: case 0x0b:
if (dev->ac97_regs[func][0x44] & 0x20)
dev->ac97_regs[func][addr] = val;
break;
case 0x10: case 0x11:
dev->ac97_regs[func][addr] = val;
pipc_sgd_handlers(dev, func);
break;
case 0x14: case 0x15:
if (addr == 0x14)
val = (val & 0xfc) | 1;
dev->ac97_regs[func][addr] = val;
pipc_fmnmi_handlers(dev, func);
break;
case 0x18: case 0x19:
if (addr == 0x18)
val = (val & 0xfc) | 1;
dev->ac97_regs[func][addr] = val;
pipc_sb_handlers(dev, func);
break;
case 0x1c: case 0x1d:
dev->ac97_regs[func][addr] = val;
pipc_codec_handlers(dev, func);
break;
case 0x2c: case 0x2d: case 0x2e: case 0x2f:
if ((func == 0) && (dev->ac97_regs[func][0x42] & 0x20))
dev->ac97_regs[func][addr] = val;
break;
case 0x41:
dev->ac97_regs[func][addr] = val;
ac97_via_write_control(dev->ac97, func, val);
break;
case 0x42: case 0x4a: case 0x4b:
dev->ac97_regs[0][addr] = dev->ac97_regs[1][addr] = val;
gameport_remap(dev->gameport, (dev->ac97_regs[0][0x42] & 0x08) ? ((dev->ac97_regs[0][0x4b] << 8) | (dev->ac97_regs[0][0x4a] & 0xf8)) : 0);
if (addr == 0x42)
pipc_sb_handlers(dev, func);
break;
case 0x43:
dev->ac97_regs[0][addr] = dev->ac97_regs[1][addr] = val;
break;
case 0x44:
dev->ac97_regs[0][addr] = dev->ac97_regs[1][addr] = val & 0xf0;
break;
case 0x45: case 0x48:
dev->ac97_regs[0][addr] = dev->ac97_regs[1][addr] = val & 0x0f;
break;
default:
dev->ac97_regs[func - pm_func - 1][addr] = val;
dev->ac97_regs[func][addr] = val;
break;
}
}
@@ -1059,9 +1324,21 @@ pipc_init(const device_t *info)
dev->acpi = device_add(&acpi_via_device);
dev->usb[0] = device_add_inst(&usb_device, 1);
if (dev->local >= VIA_PIPC_686A)
if (dev->local >= VIA_PIPC_686A) {
dev->usb[1] = device_add_inst(&usb_device, 2);
dev->ac97 = device_add(&ac97_via_device);
ac97_via_set_slot(dev->ac97, dev->slot, PCI_INTC);
dev->sb = device_add(&sb_pro_compat_device);
#ifndef VIA_PIPC_FM_EMULATION
dev->sb->opl_enabled = 1;
#endif
sound_add_handler(sb_get_buffer_sbpro, dev->sb);
dev->gameport = gameport_add(&gameport_sio_device);
}
pipc_reset_hard(dev);
device_add(&port_92_pci_device);

View File

@@ -691,7 +691,12 @@ load_machine(void)
cpu_f = NULL;
p = config_get_string(cat, "cpu_family", NULL);
if (p) {
cpu_f = cpu_get_family(p);
if (! strcmp(p, "enh_am486dx2")) /* migrate modified names */
cpu_f = cpu_get_family("am486dx2_slenh");
else if (! strcmp(p, "enh_am486dx4"))
cpu_f = cpu_get_family("am486dx4_slenh");
else
cpu_f = cpu_get_family(p);
if (cpu_f && !cpu_family_is_eligible(cpu_f, machine)) /* only honor eligible families */
cpu_f = NULL;

View File

@@ -316,8 +316,7 @@ const cpu_family_t cpu_families[] = {
{"33", CPU_i486SX, fpus_486sx, 33333333, 1, 5000, 0x422, 0, 0, CPU_SUPPORTS_DYNAREC, 6, 6,3,3, 4},
{"", 0}
}
},
{
}, {
.package = CPU_PKG_SOCKET1,
.manufacturer = "Intel",
.name = "i486SX (SL-Enhanced)",
@@ -327,8 +326,7 @@ const cpu_family_t cpu_families[] = {
{"33", CPU_i486SX_SLENH, fpus_486sx, 33333333, 1, 5000, 0x42a, 0x42a, 0, CPU_SUPPORTS_DYNAREC, 6, 6,3,3, 4},
{"", 0}
}
},
{
}, {
.package = CPU_PKG_SOCKET1,
.manufacturer = "Intel",
.name = "i486SX2",
@@ -349,8 +347,7 @@ const cpu_family_t cpu_families[] = {
{"50", CPU_i486DX, fpus_internal, 50000000, 1, 5000, 0x411, 0, 0, CPU_SUPPORTS_DYNAREC, 8, 8,4,4, 6},
{"", 0}
}
},
{
}, {
.package = CPU_PKG_SOCKET1,
.manufacturer = "Intel",
.name = "i486DX (SL-Enhanced)",
@@ -371,8 +368,7 @@ const cpu_family_t cpu_families[] = {
{"66", CPU_i486DX, fpus_internal, 66666666, 2, 5000, 0x433, 0, 0, CPU_SUPPORTS_DYNAREC, 12,12,6,6, 8},
{"", 0}
}
},
{
}, {
.package = CPU_PKG_SOCKET1,
.manufacturer = "Intel",
.name = "i486DX2 (SL-Enhanced)",
@@ -383,7 +379,7 @@ const cpu_family_t cpu_families[] = {
{"66", CPU_i486DX_SLENH, fpus_internal, 66666666, 2, 5000, 0x435, 0x435, 0, CPU_SUPPORTS_DYNAREC, 12,12,6,6, 8},
{"", 0}
}
}, {
}, {
.package = CPU_PKG_SOCKET3_PC330,
.manufacturer = "Intel",
.name = "i486DX2",
@@ -454,8 +450,7 @@ const cpu_family_t cpu_families[] = {
{"80", CPU_Am486DX, fpus_internal, 80000000, 2, 5000, 0x432, 0, 0, CPU_SUPPORTS_DYNAREC, 14,14, 6, 6, 10},
{"", 0}
}
},
{
}, {
.package = CPU_PKG_SOCKET1,
.manufacturer = "AMD",
.name = "Am486DXL",
@@ -493,7 +488,7 @@ const cpu_family_t cpu_families[] = {
.package = CPU_PKG_SOCKET3,
.manufacturer = "AMD",
.name = "Am486DX2 (Enhanced)",
.internal_name = "enh_am486dx2",
.internal_name = "am486dx2_slenh",
.cpus = (const CPU[]) {
{"66", CPU_ENH_Am486DX, fpus_internal, 66666666, 2, 5000, 0x435, 0x435, 0, CPU_SUPPORTS_DYNAREC, 12,12, 6, 6, 8},
{"80", CPU_ENH_Am486DX, fpus_internal, 80000000, 2, 5000, 0x435, 0x435, 0, CPU_SUPPORTS_DYNAREC, 14,14, 6, 6, 10},
@@ -503,7 +498,7 @@ const cpu_family_t cpu_families[] = {
.package = CPU_PKG_SOCKET3,
.manufacturer = "AMD",
.name = "Am486DX4 (Enhanced)",
.internal_name = "enh_am486dx4",
.internal_name = "am486dx4_slenh",
.cpus = (const CPU[]) {
{"75", CPU_ENH_Am486DX, fpus_internal, 75000000, 3.0, 5000, 0x482, 0x482, 0, CPU_SUPPORTS_DYNAREC, 12,12, 9, 9, 9},
{"100", CPU_ENH_Am486DX, fpus_internal, 100000000, 3.0, 5000, 0x482, 0x482, 0, CPU_SUPPORTS_DYNAREC, 15,15, 9, 9, 12},
@@ -1736,7 +1731,7 @@ const cpu_legacy_machine_t cpu_legacy_table[] = {
{"dtk", cputables_8088},
{"genxt", cputables_8088},
{"jukopc", cputables_8088},
{"open_xt", cputables_8088},
{"openxt", cputables_8088},
{"pxxt", cputables_8088},
{"europc", cputables_europc},
{"tandy", cputables_europc},
@@ -1793,7 +1788,7 @@ const cpu_legacy_machine_t cpu_legacy_table[] = {
{"shuttle386sx", cputables_i386SX_Am386SX_486SLC},
{"dtk386", cputables_i386SX_Am386SX_486SLC},
{"awardsx", cputables_i386SX_Am386SX_486SLC},
{"cbm_sl386sx25", cputables_i386SX_Am386SX_486SLC},
{"cmdsl386sx25", cputables_i386SX_Am386SX_486SLC},
{"kmxc02", cputables_i386SX_Am386SX_486SLC},
{"megapc", cputables_i386SX_Am386SX_486SLC},
{"ibmps2_m55sx", cputables_i386SX_Am386SX_486SLC_IBM486SLC},
@@ -1804,8 +1799,6 @@ const cpu_legacy_machine_t cpu_legacy_table[] = {
{"asus386", cputables_i386DX_Am386DX_486DLC},
{"ustechnologies386", cputables_i386DX_Am386DX_486DLC},
{"award386dx", cputables_i386DX_Am386DX_486DLC},
{"ami386dx", cputables_i386DX_Am386DX_486DLC},
{"mr386dx", cputables_i386DX_Am386DX_486DLC},
{"ibmps2_m70_type3", cputables_i386DX_Am386DX_486DLC_IBM486BL},
{"ibmps2_m80", cputables_i386DX_Am386DX_486DLC_IBM486BL},
{"pb410a", cputables_i486_Am486_Cx486},
@@ -1923,7 +1916,6 @@ const cpu_legacy_machine_t cpu_legacy_table[] = {
{"tsunamiatx", cputables_PentiumII_Celeron_Cyrix3},
{"p6sba", cputables_PentiumII_Celeron_Cyrix3},
{"ergox365", cputables_PentiumII_Celeron_Cyrix3},
{"fw6400gx_s1", cputables_PentiumII_Celeron_Cyrix3},
{"ficka6130", cputables_PentiumII_Celeron_Cyrix3},
{"6gxu", cputables_Xeon},
{"fw6400gx", cputables_Xeon},
@@ -1937,7 +1929,7 @@ const cpu_legacy_machine_t cpu_legacy_table[] = {
{"63a", cputables_Celeron_Cyrix3},
{"apas3", cputables_Celeron_Cyrix3},
{"wcf681", cputables_Celeron_Cyrix3},
{"6via85x", cputables_Celeron_Cyrix3},
{"6via90ap", cputables_Celeron_Cyrix3},
{"p6bap", cputables_Celeron_Cyrix3},
{"603tcf", cputables_Celeron_Cyrix3},
{"vpc2007", cputables_PentiumIID_Celeron},

View File

@@ -394,6 +394,8 @@ device_get_name(const device_t *d, int bus, char *name)
sbus = "PCI";
else if (d->flags & DEVICE_AGP)
sbus = "AGP";
else if (d->flags & DEVICE_AC97)
sbus = "AMR";
if (sbus != NULL) {
/* First concatenate [<Bus>] before the device's name. */
@@ -666,6 +668,8 @@ device_is_valid(const device_t *device, int mflags)
if ((device->flags & DEVICE_PS2) && !(mflags & MACHINE_BUS_PS2)) return(0);
if ((device->flags & DEVICE_AC97) && !(mflags & MACHINE_BUS_AC97)) return(0);
return(1);
}

View File

@@ -1280,13 +1280,11 @@ ics9xxx_get(uint8_t model)
dev->name = "ICS9xxx-xx Clock Generator";
dev->local = model;
dev->flags = DEVICE_ISA;
#ifdef ENABLE_ICS9xxx_DETECT
if (model == ICS9xxx_xx) {
dev->flags = DEVICE_PCI;
if (model == ICS9xxx_xx)
dev->reset = ics9xxx_detect_reset;
} else
#endif
dev->flags = DEVICE_ISA;
dev->init = ics9xxx_init;
dev->close = ics9xxx_close;

View File

@@ -50,6 +50,7 @@ enum {
HASP_TYPE_SAVQUEST = 0
};
typedef struct {
const uint8_t *password, *prodinfo;
const uint8_t password_size, prodinfo_size;
@@ -266,7 +267,7 @@ hasp_read_status(void *priv)
static void *
hasp_init(void *lpt, int type)
{
hasp_t *dev = (hasp_t *) malloc(sizeof(hasp_t));
hasp_t *dev = malloc(sizeof(hasp_t));
memset(dev, 0, sizeof(hasp_t));
hasp_log("HASP: init(%d)\n", type);

View File

@@ -29,18 +29,17 @@
#define CLAMP(a, min, max) (((a) < (min)) ? (min) : (((a) > (max)) ? (max) : (a)))
#define VT82C686_RPM_TO_REG(r, d) ((r) ? CLAMP(1350000 / (r * d), 1, 255) : 0)
/* Temperature/voltage formulas and factors derived from Linux's via686a.c driver */
/* Temperature/voltage formulas and factors derived from Linux's via686a.c driver. */
#define VT82C686_TEMP_TO_REG(t) (-1.160370e-10*(t*t*t*t*t*t) + 3.193693e-08*(t*t*t*t*t) - 1.464447e-06*(t*t*t*t) - 2.525453e-04*(t*t*t) + 1.424593e-02*(t*t) + 2.148941e+00*t + 7.275808e+01)
#define VT82C686_VOLTAGE_TO_REG(v, f) CLAMP((((v) * (2.628 / (f))) - 120.5) / 25, 0, 255)
typedef struct {
hwm_values_t *values;
device_t *lm75[2];
uint8_t enable;
uint16_t io_base;
uint8_t regs[80];
uint8_t regs[128];
} vt82c686_t;
@@ -59,6 +58,11 @@ vt82c686_read(uint16_t addr, void *priv)
addr -= dev->io_base;
switch (addr) {
case 0x00 ... 0x0f: case 0x50 ... 0x7f: /* undefined registers */
/* Real 686B returns the contents of 0x40. */
ret = dev->regs[0x40];
break;
case 0x1f: case 0x20: case 0x21: /* temperatures */
ret = VT82C686_TEMP_TO_REG(dev->values->temperatures[(addr == 0x1f) ? 2 : (addr & 1)]);
break;
@@ -84,14 +88,26 @@ static void
vt82c686_write(uint16_t port, uint8_t val, void *priv)
{
vt82c686_t *dev = (vt82c686_t *) priv;
uint8_t reg = port - dev->io_base;
uint8_t reg = port & 0x7f;
if ((reg == 0x41) || (reg == 0x42) || (reg == 0x45) || (reg == 0x46) || (reg == 0x48) || (reg == 0x4a) || (reg >= 0x4c))
return;
switch (reg) {
case 0x00 ... 0x0f:
case 0x3f: case 0x41: case 0x42: case 0x4a:
case 0x4c ... 0x7f:
/* Read-only registers. */
return;
if ((reg == 0x40) && (val & 0x80)) {
val &= 0x7f;
vt82c686_reset(dev, 1);
case 0x40:
/* Reset if requested. */
if (val & 0x80) {
vt82c686_reset(dev, 1);
return;
}
break;
case 0x48:
val &= 0x7f;
break;
}
dev->regs[reg] = val;
@@ -106,7 +122,7 @@ vt82c686_hwm_write(uint8_t addr, uint8_t val, void *priv)
vt82c686_t *dev = (vt82c686_t *) priv;
if (dev->io_base)
io_removehandler(dev->io_base, 0x0050,
io_removehandler(dev->io_base, 128,
vt82c686_read, NULL, NULL, vt82c686_write, NULL, NULL, dev);
switch (addr) {
@@ -126,7 +142,7 @@ vt82c686_hwm_write(uint8_t addr, uint8_t val, void *priv)
}
if (dev->enable && dev->io_base)
io_sethandler(dev->io_base, 0x0050,
io_sethandler(dev->io_base, 128,
vt82c686_read, NULL, NULL, vt82c686_write, NULL, NULL, dev);
}
@@ -134,8 +150,10 @@ vt82c686_hwm_write(uint8_t addr, uint8_t val, void *priv)
static void
vt82c686_reset(vt82c686_t *dev, uint8_t initialization)
{
memset(dev->regs, 0, 80);
memset(dev->regs, 0, sizeof(dev->regs));
dev->regs[0x17] = 0x80;
dev->regs[0x3f] = 0xa2;
dev->regs[0x40] = 0x08;
dev->regs[0x47] = 0x50;
dev->regs[0x4b] = 0x15;

View File

@@ -124,13 +124,13 @@ smbus_piix4_write(uint16_t addr, uint8_t val, void *priv)
if (val & 0x40) { /* dispatch command if START is set */
timer_bytes++; /* address */
smbus_addr = (dev->addr >> 1);
smbus_addr = dev->addr >> 1;
read = dev->addr & 0x01;
cmd = (dev->ctl >> 2) & 0xf;
smbus_piix4_log("SMBus PIIX4: addr=%02X read=%d protocol=%X cmd=%02X data0=%02X data1=%02X\n", smbus_addr, read, cmd, dev->cmd, dev->data0, dev->data1);
/* Raise DEV_ERR if no device is at this address, or if the device returned NAK when starting the transfer. */
/* Raise DEV_ERR if no device is at this address, or if the device returned NAK. */
if (!i2c_start(i2c_smbus, smbus_addr, read)) {
dev->next_stat = 0x04;
break;

View File

@@ -2765,7 +2765,7 @@ ide_board_init(int board, int irq, int base_main, int side_main, int type)
}
static void
void
ide_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv)
{
if (ld)
@@ -2796,12 +2796,23 @@ ide_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv)
static void *
ide_ter_init(const device_t *info)
{
int irq = device_get_config_int("irq");
if (irq == -1) {
ide_board_init(2, -1, 0, 0, info->local);
isapnp_add_card(ide_ter_pnp_rom, sizeof(ide_ter_pnp_rom), ide_pnp_config_changed, NULL, NULL, NULL, (void *) 2);
} else
ide_board_init(2, irq, 0x168, 0x36e, info->local);
/* Don't claim this channel again if it was already claimed. */
if (ide_boards[2])
return(NULL);
int irq;
if (info->local)
irq = -2;
else
irq = device_get_config_int("irq");
if (irq < 0) {
ide_board_init(2, -1, 0, 0, 0);
if (irq == -1)
isapnp_add_card(ide_ter_pnp_rom, sizeof(ide_ter_pnp_rom), ide_pnp_config_changed, NULL, NULL, NULL, (void *) 2);
} else {
ide_board_init(2, irq, 0x168, 0x36e, 0);
}
return(ide_boards[2]);
}
@@ -2818,12 +2829,23 @@ ide_ter_close(void *priv)
static void *
ide_qua_init(const device_t *info)
{
int irq = device_get_config_int("irq");
if (irq == -1) {
ide_board_init(3, -1, 0, 0, info->local);
isapnp_add_card(ide_qua_pnp_rom, sizeof(ide_qua_pnp_rom), ide_pnp_config_changed, NULL, NULL, NULL, (void *) 3);
} else
ide_board_init(3, irq, 0x1e8, 0x3ee, info->local);
/* Don't claim this channel again if it was already claimed. */
if (ide_boards[3])
return(NULL);
int irq;
if (info->local)
irq = -2;
else
irq = device_get_config_int("irq");
if (irq < 0) {
ide_board_init(3, -1, 0, 0, 0);
if (irq == -1)
isapnp_add_card(ide_qua_pnp_rom, sizeof(ide_qua_pnp_rom), ide_pnp_config_changed, NULL, NULL, NULL, (void *) 3);
} else {
ide_board_init(3, irq, 0x1e8, 0x3ee, 0);
}
return(ide_boards[3]);
}
@@ -3103,6 +3125,15 @@ const device_t ide_ter_device = {
ide_ter_config
};
const device_t ide_ter_pnp_device = {
"Tertiary IDE Controller (Plug and Play only)",
DEVICE_AT,
1,
ide_ter_init, ide_ter_close, NULL,
{ NULL }, NULL, NULL,
NULL
};
const device_t ide_qua_device = {
"Quaternary IDE Controller",
DEVICE_AT,
@@ -3111,3 +3142,12 @@ const device_t ide_qua_device = {
{ NULL }, NULL, NULL,
ide_qua_config
};
const device_t ide_qua_pnp_device = {
"Quaternary IDE Controller (Plug and Play only)",
DEVICE_AT,
1,
ide_qua_init, ide_qua_close, NULL,
{ NULL }, NULL, NULL,
ide_qua_config
};

View File

@@ -324,7 +324,7 @@ gameport_remap(void *priv, uint16_t address)
if (dev->addr) {
/* Add this port to the active ports list. */
if ( !active_gameports || ((dev->addr & 0xfff8) == 0x200)) {
if (!active_gameports || ((dev->addr & 0xfff8) == 0x200)) {
/* No ports have been added yet, or port within 200-207h: add to top. */
dev->next = active_gameports;
active_gameports = dev;

View File

@@ -65,7 +65,8 @@ enum {
DEVICE_EISA = 0x100, /* requires the EISA bus */
DEVICE_VLB = 0x200, /* requires the PCI bus */
DEVICE_PCI = 0x400, /* requires the VLB bus */
DEVICE_AGP = 0x800 /* requires the AGP bus */
DEVICE_AGP = 0x800, /* requires the AGP bus */
DEVICE_AC97 = 0x1000 /* requires the AC'97 bus */
};

View File

@@ -60,7 +60,9 @@ extern const device_t ide_cmd640_pci_single_channel_device; /* CMD PCI-640B PCI
extern const device_t ide_opti611_vlb_device; /* OPTi 82c611/611A VLB */
extern const device_t ide_ter_device;
extern const device_t ide_ter_pnp_device;
extern const device_t ide_qua_device;
extern const device_t ide_qua_pnp_device;
extern const device_t xta_wdxt150_device; /* xta_wdxt150 */
extern const device_t xta_hd20_device; /* EuroPC internal */

View File

@@ -136,6 +136,9 @@ extern void ide_sec_enable(void);
extern void ide_sec_disable(void);
extern void ide_board_set_force_ata3(int board, int force_ata3);
#ifdef EMU_ISAPNP_H
extern void ide_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv);
#endif
extern double ide_atapi_get_period(uint8_t channel);
#ifdef SCSI_DEVICE_H

View File

@@ -37,6 +37,7 @@
#define MACHINE_BUS_PCI 0x00000200 /* sys has PCI bus */
#define MACHINE_BUS_PCMCIA 0x00000400 /* sys has PCMCIA bus */
#define MACHINE_BUS_AGP 0x00000800 /* sys has AGP bus */
#define MACHINE_BUS_AC97 0x00080000 /* sys has AC97 bus (ACR/AMR/CNR slot) */
/* Combined flags. */
#define MACHINE_PC 0x00000004 /* sys is PC/XT-compatible (ISA) */
#define MACHINE_AT 0x0000000C /* sys is AT-compatible (ISA + ISA16) */

View File

@@ -0,0 +1,57 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Definitions for AC'97 audio emulation.
*
*
*
* Authors: RichardG, <richardg867@gmail.com>
*
* Copyright 2021 RichardG.
*/
#ifndef EMU_SND_AC97_H
# define EMU_SND_AC97_H
typedef struct {
uint32_t vendor_id;
uint8_t codec_id, regs[128];
} ac97_codec_t;
extern uint8_t ac97_codec_read(ac97_codec_t *dev, uint8_t reg);
extern void ac97_codec_write(ac97_codec_t *dev, uint8_t reg, uint8_t val);
extern void ac97_codec_reset(void *priv);
extern void ac97_codec_getattn(void *priv, uint8_t reg, int *l, int *r);
extern uint32_t ac97_codec_getrate(void *priv, uint8_t reg);
extern void ac97_via_set_slot(void *priv, int slot, int irq_pin);
extern uint8_t ac97_via_read_status(void *priv, uint8_t modem);
extern void ac97_via_write_control(void *priv, uint8_t modem, uint8_t val);
extern void ac97_via_remap_audio_sgd(void *priv, uint16_t new_io_base, uint8_t enable);
extern void ac97_via_remap_modem_sgd(void *priv, uint16_t new_io_base, uint8_t enable);
extern void ac97_via_remap_audio_codec(void *priv, uint16_t new_io_base, uint8_t enable);
extern void ac97_via_remap_modem_codec(void *priv, uint16_t new_io_base, uint8_t enable);
#ifdef EMU_DEVICE_H
extern ac97_codec_t **ac97_codec, **ac97_modem_codec;
extern int ac97_codec_count, ac97_modem_codec_count,
ac97_codec_id, ac97_modem_codec_id;
extern const device_t ad1881_device;
extern const device_t alc100_device;
extern const device_t cs4297_device;
extern const device_t cs4297a_device;
extern const device_t wm9701a_device;
extern const device_t ac97_via_device;
#endif
#endif

View File

@@ -125,7 +125,7 @@ typedef struct sb_t
int pos;
uint8_t pos_regs[8];
uint8_t pos_regs[8], pnp_rom[512];
uint16_t opl_pnp_addr;
} sb_t;

View File

@@ -110,7 +110,7 @@ extern const device_t sb_2_device;
extern const device_t sb_pro_v1_device;
extern const device_t sb_pro_v2_device;
extern const device_t sb_pro_mcv_device;
extern const device_t sb_pro_cs423x_device;
extern const device_t sb_pro_compat_device;
extern const device_t sb_16_device;
extern const device_t sb_16_pnp_device;
extern const device_t sb_32_pnp_device;

View File

@@ -38,6 +38,7 @@
#include <86box/machine.h>
#include <86box/sound.h>
#include <86box/clock.h>
#include <86box/snd_ac97.h>
int
machine_at_p65up5_cpknd_init(const machine_t *model)
@@ -468,8 +469,10 @@ machine_at_tsunamiatx_init(const machine_t *model)
device_add(&i440bx_device);
device_add(&piix4e_device);
if (sound_card_current == SOUND_INTERNAL)
device_add(&es1371_onboard_device);
if (sound_card_current == SOUND_INTERNAL) {
device_add(&es1371_onboard_device);
device_add(&cs4297_device); /* found on other Tyan boards around the same time */
}
device_add(&pc87309_device);
device_add(&keyboard_ps2_ami_pci_device);
@@ -678,8 +681,10 @@ machine_at_ms6168_common_init(const machine_t *model)
if (gfxcard == VID_INTERNAL)
device_add(&voodoo_3_2000_agp_onboard_8m_device);
if (sound_card_current == SOUND_INTERNAL)
device_add(&es1371_onboard_device);
if (sound_card_current == SOUND_INTERNAL) {
device_add(&es1371_onboard_device);
device_add(&cs4297_device);
}
}

View File

@@ -37,6 +37,7 @@
#include "cpu.h"
#include <86box/machine.h>
#include <86box/clock.h>
#include <86box/snd_ac97.h>
int
@@ -432,7 +433,7 @@ machine_at_6via90ap_init(const machine_t *model)
pci_init(PCI_CONFIG_TYPE_1);
pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0);
pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 1, 2, 0, 0);
pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 1, 2, 3, 4);
pci_register_slot(0x09, PCI_CARD_NORMAL, 1, 2, 3, 4);
pci_register_slot(0x0A, PCI_CARD_NORMAL, 2, 3, 4, 1);
pci_register_slot(0x0B, PCI_CARD_NORMAL, 3, 4, 1, 2);
@@ -451,6 +452,8 @@ machine_at_6via90ap_init(const machine_t *model)
hwm_values.temperatures[1] += 2; /* System offset */
hwm_values.temperatures[2] = 0; /* unused */
device_add(&alc100_device); /* ALC100P identified on similar Acorp boards (694TA, 6VIA90A1) */
return ret;
}

View File

@@ -40,6 +40,7 @@
#include <86box/video.h>
#include "cpu.h"
#include <86box/machine.h>
#include <86box/snd_ac97.h>
int
@@ -112,7 +113,7 @@ machine_at_ficva503a_init(const machine_t *model)
{
int ret;
ret = bios_load_linear("roms/machines/ficva503a/jo4116.bin",
ret = bios_load_linear("roms/machines/ficva503a/jn4116.bin",
0x000c0000, 262144, 0);
if (bios_only || !ret)
@@ -140,5 +141,7 @@ machine_at_ficva503a_init(const machine_t *model)
hwm_values.temperatures[1] += 2; /* System offset */
hwm_values.temperatures[2] = 0; /* unused */
device_add(&wm9701a_device); /* on daughtercard */
return ret;
}

View File

@@ -190,7 +190,7 @@ const machine_t machines[] = {
/* 386DX machines which utilize the MCA bus */
{ "[MCA] IBM PS/2 model 70 (type 3)", "ibmps2_m70_type3", MACHINE_TYPE_386DX, CPU_PKG_386DX | CPU_PKG_486BL, 0, 0, 0, 0, 0, 0, 0, MACHINE_MCA | MACHINE_BUS_PS2 | MACHINE_VIDEO, 2048, 16384, 2048, 63, machine_ps2_model_70_type3_init, NULL },
{ "[MCA] IBM PS/2 model 80", "ibmps2_m80", MACHINE_TYPE_386DX, CPU_PKG_386DX | CPU_PKG_486BL, 0, 0, 0, 0, 0, 0, 0, MACHINE_MCA | MACHINE_BUS_PS2 | MACHINE_VIDEO, 1024, 12288, 1024, 63, machine_ps2_model_80_init, NULL },
{ "[MCA] IBM PS/2 model 80 (type 3)", "ibmps2_m80_type3", MACHINE_TYPE_386DX, CPU_PKG_386DX | CPU_PKG_486BL, 0, 0, 0, 0, 0, 0, 0, MACHINE_MCA | MACHINE_BUS_PS2 | MACHINE_VIDEO, 2048, 12288, 2048, 63, machine_ps2_model_80_axx_init, NULL },
{ "[MCA] IBM PS/2 model 80 (type 3)", "ibmps2_m80_type3", MACHINE_TYPE_386DX, CPU_PKG_386DX | CPU_PKG_486BL, 0, 0, 0, 0, 0, 0, 0, MACHINE_MCA | MACHINE_BUS_PS2 | MACHINE_VIDEO, 2048, 12288, 2048, 63, machine_ps2_model_80_axx_init, NULL },
/* 386DX/486 machines */
{ "[OPTi 495] Award 486 clone", "award486", MACHINE_TYPE_386DX_486, CPU_PKG_386DX | CPU_PKG_SOCKET1, 0, 0, 0, 0, 0, 0, 0, MACHINE_VLB | MACHINE_IDE, 1024, 32768, 1024, 127, machine_at_opti495_init, NULL },
@@ -200,11 +200,11 @@ const machine_t machines[] = {
/* 486 machines - Socket 1 */
{ "[ALi M1429] Olystar LIL1429", "ali1429", MACHINE_TYPE_486, CPU_PKG_SOCKET1, 0, 0, 0, 0, 0, 0, 0, MACHINE_VLB | MACHINE_IDE, 1024, 32768, 1024, 127, machine_at_ali1429_init, NULL },
{ "[CS4031] AMI 486 CS4031", "cs4031", MACHINE_TYPE_486, CPU_PKG_SOCKET1, 0, 0, 0, 0, 0, 0, 0, MACHINE_VLB, 1024, 65536, 1024, 127, machine_at_cs4031_init, NULL },
{ "[ETEQ ET6000] Olivetti PCS-46C", "pcs46c", MACHINE_TYPE_486, CPU_PKG_SOCKET1, 0, 0, 0, 0, 0, 0, 0, MACHINE_VLB | MACHINE_IDE | MACHINE_VIDEO, 4096, 32768, 4096, 127, machine_at_pcs46c_init, at_pcs46c_get_device },
{ "[ETEQ ET6000] Olivetti PCS-46C", "pcs46c", MACHINE_TYPE_486, CPU_PKG_SOCKET1, 0, 0, 0, 0, 0, 0, 0, MACHINE_VLB | MACHINE_IDE | MACHINE_VIDEO, 4096, 32768, 4096, 127, machine_at_pcs46c_init, at_pcs46c_get_device },
{ "[OPTi 895] Mylex MVI486", "mvi486", MACHINE_TYPE_486, CPU_PKG_SOCKET1, 0, 0, 0, 0, 0, 0, 0, MACHINE_VLB | MACHINE_IDE_DUAL, 1024, 65536, 1024, 127, machine_at_mvi486_init, NULL },
{ "[VIA VT82C495] FIC 486-VC-HD", "486vchd", MACHINE_TYPE_486, CPU_PKG_SOCKET1, 0, 0, 0, 0, 0, 0, 0, MACHINE_AT, 1024, 64512, 1024, 127, machine_at_486vchd_init, NULL },
{ "[VLSI 82C480] HP Vectra 486VL", "vect486vl", MACHINE_TYPE_486, CPU_PKG_SOCKET1, 0, 0, 0, 0, 0, 0, 0, MACHINE_AT | MACHINE_BUS_PS2 | MACHINE_IDE | MACHINE_VIDEO, 2048, 32768, 2048, 127, machine_at_vect486vl_init, at_vect486vl_get_device },
{ "[VLSI 82C481] Siemens Nixdorf D824", "d824", MACHINE_TYPE_486, CPU_PKG_SOCKET1, 0, 0, 0, 0, 0, 0, 0, MACHINE_AT | MACHINE_BUS_PS2 | MACHINE_IDE | MACHINE_VIDEO, 2048, 32768, 2048, 127, machine_at_d824_init, at_d824_get_device },
{ "[VLSI 82C481] Siemens Nixdorf D824", "d824", MACHINE_TYPE_486, CPU_PKG_SOCKET1, 0, 0, 0, 0, 0, 0, 0, MACHINE_AT | MACHINE_BUS_PS2 | MACHINE_IDE | MACHINE_VIDEO, 2048, 32768, 2048, 127, machine_at_d824_init, at_d824_get_device },
/* 486 machines - Socket 3 */
/* 486 machines with just the ISA slot */
@@ -295,7 +295,7 @@ const machine_t machines[] = {
{ "[SiS 85C50x] BCM SQ-588", "sq588", MACHINE_TYPE_SOCKET5, CPU_PKG_SOCKET5_7, CPU_BLOCK(CPU_PENTIUMMMX), 50000000, 66666667, 3520, 3520, 1.5, 1.5, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 131072, 8192, 127, machine_at_sq588_init, NULL },
/* UMC 889x */
{ "[UMC 889x] Shuttle HOT-539", "hot539", MACHINE_TYPE_SOCKET5, CPU_PKG_SOCKET5_7, CPU_BLOCK(CPU_K5, CPU_5K86), 40000000, 66666667, 3380, 3600, 1.5, 2.0, MACHINE_PCI | MACHINE_IDE_DUAL, 8192, 262144, 8192, 127, machine_at_hot539_init, NULL },
{ "[UMC 889x] Shuttle HOT-539", "hot539", MACHINE_TYPE_SOCKET5, CPU_PKG_SOCKET5_7, CPU_BLOCK(CPU_K5, CPU_5K86), 40000000, 66666667, 3380, 3600, 1.5, 2.0, MACHINE_PCI | MACHINE_IDE_DUAL, 8192, 262144, 8192, 127, machine_at_hot539_init, NULL },
/* Socket 7 (Single Voltage) machines */
/* 430FX */
@@ -318,7 +318,7 @@ const machine_t machines[] = {
{ "[i430VX] Gateway 2000 Tigereye", "gw2kte", MACHINE_TYPE_SOCKET7_3V, CPU_PKG_SOCKET5_7, 0, 50000000, 66666667, 3380, 3520, 1.5, 3.0, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 131072, 8192, 127, machine_at_gw2kte_init, NULL },
/* SiS 5511 */
{ "[SiS 5511] AOpen AP5S", "ap5s", MACHINE_TYPE_SOCKET7_3V, CPU_PKG_SOCKET5_7, 0, 50000000, 66666667, 3380, 3520, 1.5, 3.0, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 524288, 8192, 127, machine_at_ap5s_init, NULL },
{ "[SiS 5511] AOpen AP5S", "ap5s", MACHINE_TYPE_SOCKET7_3V, CPU_PKG_SOCKET5_7, 0, 50000000, 66666667, 3380, 3520, 1.5, 3.0, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 524288, 8192, 127, machine_at_ap5s_init, NULL },
/* Socket 7 (Dual Voltage) machines */
/* 430HX */
@@ -363,8 +363,8 @@ const machine_t machines[] = {
{ "[SiS 5571] MSI MS-5146", "ms5146", MACHINE_TYPE_SOCKET7, CPU_PKG_SOCKET5_7, 0, 50000000, 66666667, 2500, 3520, 1.5, 3.0, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 262144, 8192, 127, machine_at_ms5146_init, NULL },
/* SiS 5598 */
{ "[SiS 5598] ASUS SP97-XV", "sp97xv", MACHINE_TYPE_SOCKET7, CPU_PKG_SOCKET5_7, 0, 60000000, 66666667, 2100, 3200, 1.5, 2.5, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 262144, 8192, 255, machine_at_sp97xv_init, NULL },
{ "[SiS 5598] PC Chips M571", "m571", MACHINE_TYPE_SOCKET7, CPU_PKG_SOCKET5_7, 0, 50000000, 75000000, 2500, 3500, 1.5, 3.5, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 262144, 8192, 255, machine_at_m571_init, NULL },
{ "[SiS 5598] ASUS SP97-XV", "sp97xv", MACHINE_TYPE_SOCKET7, CPU_PKG_SOCKET5_7, 0, 60000000, 66666667, 2100, 3200, 1.5, 2.5, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 262144, 8192, 255, machine_at_sp97xv_init, NULL },
{ "[SiS 5598] PC Chips M571", "m571", MACHINE_TYPE_SOCKET7, CPU_PKG_SOCKET5_7, 0, 50000000, 75000000, 2500, 3500, 1.5, 3.5, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 262144, 8192, 255, machine_at_m571_init, NULL },
/* ALi ALADDiN IV */
#if defined(DEV_BRANCH) && defined(USE_M154X)
@@ -460,8 +460,8 @@ const machine_t machines[] = {
{ "[VIA Apollo Pro] PC Partner APAS3", "apas3", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 100000000, 1800, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 786432, 8192, 255, machine_at_apas3_init, NULL },
{ "[VIA Apollo Pro133] ECS P6BAP", "p6bap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1572864, 8192, 255, machine_at_p6bap_init, NULL },
{ "[VIA Apollo Pro133A] AEWIN WCF-681", "wcf681", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 133333333, 1300, 3500, 1.5, 8.0, /* limits assumed */ MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_wcf681_init, NULL },
{ "[VIA Apollo Pro133A] ASUS CUV4X-LS", "cuv4xls", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, (MACHINE_AGP & ~MACHINE_AT) | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 16384,1572864, 8192, 255, machine_at_cuv4xls_init, NULL },
{ "[VIA Apollo Pro133A] Acorp 6VIA90AP", "6via90ap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, MACHINE_MULTIPLIER_FIXED, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1572864, 8192, 255, machine_at_6via90ap_init, NULL },
{ "[VIA Apollo Pro133A] ASUS CUV4X-LS", "cuv4xls", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, (MACHINE_AGP & ~MACHINE_AT) | MACHINE_BUS_PS2 | MACHINE_BUS_AC97 | MACHINE_IDE_DUAL,16384,1572864, 8192, 255, machine_at_cuv4xls_init, NULL },
{ "[VIA Apollo Pro133A] Acorp 6VIA90AP", "6via90ap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, MACHINE_MULTIPLIER_FIXED, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL | MACHINE_GAMEPORT, 8192,1572864, 8192, 255, machine_at_6via90ap_init, NULL },
{ "[VIA Apollo ProMedia] Jetway 603TCF", "603tcf", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_603tcf_init, NULL },
/* Miscellaneous/Fake/Hypervisor machines */

View File

@@ -39,7 +39,7 @@
#include <86box/isapnp.h>
/* This ROM is reconstructed out of several assumptions, some of which are based on the IT8671F. */
/* This ROM was reconstructed out of many assumptions, some of which based on the IT8671F. */
static uint8_t um8669f_pnp_rom[] = {
0x55, 0xa3, 0x86, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, /* UMC8669, dummy checksum (filled in by isapnp_add_card) */
0x0a, 0x10, 0x10, /* PnP version 1.0, vendor version 1.0 */
@@ -61,7 +61,7 @@ static uint8_t um8669f_pnp_rom[] = {
0x22, 0xfa, 0x1f, /* IRQ 1/3/4/5/6/7/8/9/10/11/12 */
0x47, 0x00, 0x00, 0x01, 0xf8, 0x03, 0x08, 0x08, /* I/O 0x100-0x3F8, decodes 10-bit, 8-byte alignment, 8 addresses */
0x15, 0x55, 0xa3, 0x86, 0x69, 0x00, /* logical device UMC8669 (just a dummy to create a gap in LDNs) */
0x15, 0x41, 0xd0, 0xff, 0xff, 0x00, /* logical device PNPFFFF (just a dummy to create a gap in LDNs) */
0x15, 0x41, 0xd0, 0xb0, 0x2f, 0x01, /* logical device PNPB02F, can participate in boot */
0x47, 0x00, 0x00, 0x01, 0xf8, 0x03, 0x08, 0x08, /* I/O 0x100-0x3F8, decodes 10-bit, 8-byte alignment, 8 addresses */
@@ -151,8 +151,9 @@ um8669f_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *pri
fdc_set_irq(dev->fdc, config->irq[0].irq);
fdc_set_dma_ch(dev->fdc, (config->dma[0].dma == ISAPNP_DMA_DISABLED) ? -1 : config->dma[0].dma);
} else
} else {
um8669f_log("UM8669F: FDC disabled\n");
}
break;
@@ -163,8 +164,9 @@ um8669f_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *pri
if (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) {
um8669f_log("UM8669F: UART %d enabled at port %04X IRQ %d\n", ld - 1, config->io[0].base, config->irq[0].irq);
serial_setup(dev->uart[ld - 1], config->io[0].base, config->irq[0].irq);
} else
} else {
um8669f_log("UM8669F: UART %d disabled\n", ld - 1);
}
break;
@@ -174,8 +176,9 @@ um8669f_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *pri
if (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) {
um8669f_log("UM8669F: LPT enabled at port %04X IRQ %d\n", config->io[0].base, config->irq[0].irq);
lpt1_init(config->io[0].base);
} else
} else {
um8669f_log("UM8669F: LPT disabled\n");
}
break;

View File

@@ -34,7 +34,8 @@
typedef struct {
uint8_t cur_reg, regs[32], fdc_dma, fdc_irq, uart_irq[2], lpt_dma, lpt_irq;
uint8_t cur_reg, last_val, regs[25],
fdc_dma, fdc_irq, uart_irq[2], lpt_dma, lpt_irq;
fdc_t *fdc;
serial_t *uart[2];
} vt82c686_t;
@@ -43,10 +44,10 @@ typedef struct {
static uint8_t
get_lpt_length(vt82c686_t *dev)
{
uint8_t length = 4;
uint8_t length = 4; /* non-EPP */
if ((dev->regs[0x02] & 0x03) == 0x2)
length = 8;
if ((dev->regs[0x02] & 0x03) == 0x02)
length = 8; /* EPP */
return length;
}
@@ -64,6 +65,7 @@ vt82c686_fdc_handler(vt82c686_t *dev)
fdc_set_dma_ch(dev->fdc, dev->fdc_dma);
fdc_set_irq(dev->fdc, dev->fdc_irq);
fdc_set_swap(dev->fdc, dev->regs[0x16] & 0x01);
}
@@ -73,16 +75,20 @@ vt82c686_lpt_handler(vt82c686_t *dev)
uint16_t io_mask, io_base = dev->regs[0x06] << 2;
int io_len = get_lpt_length(dev);
io_base &= (0xff8 | io_len);
io_mask = 0x3fc;
io_mask = 0x3fc; /* non-EPP */
if (io_len == 8)
io_mask = 0x3f8;
io_mask = 0x3f8; /* EPP */
lpt1_remove();
if (((dev->regs[0x02] & 0x03) != 0x03) && (io_base >= 0x100) && (io_base <= io_mask))
lpt1_init(io_base);
lpt1_irq(dev->lpt_irq);
if (dev->lpt_irq) {
lpt1_irq(dev->lpt_irq);
} else {
lpt1_irq(0xff);
}
}
@@ -91,8 +97,8 @@ vt82c686_serial_handler(vt82c686_t *dev, int uart)
{
serial_remove(dev->uart[uart]);
if (dev->regs[0x02] & (uart ? 0x08 : 0x04))
serial_setup(dev->uart[uart], (dev->regs[0x07 + uart] & 0xfe) << 2, dev->uart_irq[uart]);
if (dev->regs[0x02] & (0x04 << uart))
serial_setup(dev->uart[uart], dev->regs[0x07 + uart] << 2, dev->uart_irq[uart]);
}
@@ -101,25 +107,31 @@ vt82c686_write(uint16_t port, uint8_t val, void *priv)
{
vt82c686_t *dev = (vt82c686_t *) priv;
/* Store last written value for echo (see comment on read). */
dev->last_val = val;
/* Write current register index on port 0. */
if (!(port & 1)) {
dev->cur_reg = val;
return;
}
/* NOTE: Registers are [0xE0:0xFF] but we store them as [0x00:0x1F]. */
if (dev->cur_reg < 0xe0)
return;
/* NOTE: Registers are [0xE0:0xF8] but we store them as [0x00:0x18]. */
if ((dev->cur_reg < 0xe0) || (dev->cur_reg > 0xf8))
return;
uint8_t reg = dev->cur_reg & 0x1f;
/* Read-only registers */
if ((reg < 0x02) || (reg == 0x04) || (reg == 0x05) || ((reg >= 0x09) && (reg < 0x0e)) ||
(reg == 0x13) || (reg == 0x15) || (reg == 0x17) || (reg >= 0x19))
/* Read-only registers. */
if ((reg < 0x02) || (reg == 0x0c))
return;
/* Write current register value on port 1. */
dev->regs[reg] = val;
/* Update device state. */
switch (reg) {
case 0x02:
dev->regs[reg] &= 0xbf;
vt82c686_lpt_handler(dev);
vt82c686_serial_handler(dev, 0);
vt82c686_serial_handler(dev, 1);
@@ -127,19 +139,54 @@ vt82c686_write(uint16_t port, uint8_t val, void *priv)
break;
case 0x03:
dev->regs[reg] &= 0xfc;
vt82c686_fdc_handler(dev);
break;
case 0x04:
dev->regs[reg] &= 0xfc;
break;
case 0x05:
dev->regs[reg] |= 0x03;
break;
case 0x06:
vt82c686_lpt_handler(dev);
break;
case 0x07:
vt82c686_serial_handler(dev, 0);
case 0x07: case 0x08:
dev->regs[reg] &= 0xfe;
vt82c686_serial_handler(dev, reg == 0x08);
break;
case 0x08:
vt82c686_serial_handler(dev, 1);
case 0x0d:
dev->regs[reg] &= 0x0f;
break;
case 0x0f:
dev->regs[reg] &= 0x7f;
break;
case 0x10:
dev->regs[reg] &= 0xf4;
break;
case 0x11:
dev->regs[reg] &= 0x3f;
break;
case 0x13:
dev->regs[reg] &= 0xfb;
break;
case 0x14: case 0x17:
dev->regs[reg] &= 0xfe;
break;
case 0x16:
dev->regs[reg] &= 0xf7;
vt82c686_fdc_handler(dev);
break;
}
}
@@ -149,17 +196,16 @@ static uint8_t
vt82c686_read(uint16_t port, void *priv)
{
vt82c686_t *dev = (vt82c686_t *) priv;
uint8_t ret = 0xff;
/* NOTE: Registers are [0xE0:0xFF] but we store them as [0x00:0x1F]. */
/* NOTE: Registers are [0xE0:0xF8] but we store them as [0x00:0x18].
Real 686B echoes the last read/written value when reading from
registers outside that range. */
if (!(port & 1))
ret = dev->cur_reg;
else if (dev->cur_reg < 0xe0)
ret = 0xff;
else
ret = dev->regs[dev->cur_reg & 0x1f];
dev->last_val = dev->cur_reg;
else if ((dev->cur_reg >= 0xe0) && (dev->cur_reg <= 0xf8))
dev->last_val = dev->regs[dev->cur_reg & 0x1f];
return ret;
return dev->last_val;
}
@@ -205,7 +251,7 @@ static void
vt82c686_reset(vt82c686_t *dev)
{
memset(dev->regs, 0, 20);
dev->regs[0x00] = 0x3c;
dev->regs[0x02] = 0x03;
dev->regs[0x03] = 0xfc;

View File

@@ -14,7 +14,7 @@
#
add_library(snd OBJECT sound.c openal.c snd_opl.c snd_opl_nuked.c snd_resid.cc
midi.c midi_system.c snd_speaker.c snd_pssj.c snd_lpt_dac.c
midi.c midi_system.c snd_speaker.c snd_pssj.c snd_lpt_dac.c snd_ac97_codec.c snd_ac97_via.c
snd_lpt_dss.c snd_adlib.c snd_adlibgold.c snd_ad1848.c snd_audiopci.c
snd_azt2316a.c snd_cms.c snd_cs423x.c snd_gus.c snd_sb.c snd_sb_dsp.c
snd_emu8k.c snd_mpu401.c snd_sn76489.c snd_ssi2001.c snd_wss.c snd_ym7128.c)

394
src/sound/snd_ac97_codec.c Normal file
View File

@@ -0,0 +1,394 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* AC'97 audio codec emulation.
*
*
*
* Authors: RichardG, <richardg867@gmail.com>
*
* Copyright 2021 RichardG.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/io.h>
#include <86box/snd_ac97.h>
#define AC97_VENDOR_ID(f, s, t, dev) ((((f) & 0xff) << 24) | (((s) & 0xff) << 16) | (((t) & 0xff) << 8) | ((dev) & 0xff))
enum {
AC97_CODEC_AD1881 = AC97_VENDOR_ID('A', 'D', 'S', 0x40),
AC97_CODEC_ALC100 = AC97_VENDOR_ID('A', 'L', 'C', 0x20),
AC97_CODEC_CS4297 = AC97_VENDOR_ID('C', 'R', 'Y', 0x03),
AC97_CODEC_CS4297A = AC97_VENDOR_ID('C', 'R', 'Y', 0x13),
AC97_CODEC_WM9701A = AC97_VENDOR_ID('W', 'M', 'L', 0x00)
};
#ifdef ENABLE_AC97_CODEC_LOG
int ac97_codec_do_log = ENABLE_AC97_CODEC_LOG;
static void
ac97_codec_log(const char *fmt, ...)
{
va_list ap;
if (ac97_codec_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define ac97_codec_log(fmt, ...)
#endif
static 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,
41305, 52068, 65636, 82739, 104299, 131477, 165737, 208925
};
ac97_codec_t **ac97_codec = NULL, **ac97_modem_codec = NULL;
int ac97_codec_count = 0, ac97_modem_codec_count = 0,
ac97_codec_id = 0, ac97_modem_codec_id = 0;
uint8_t
ac97_codec_read(ac97_codec_t *dev, uint8_t reg)
{
uint8_t ret = dev->regs[reg & 0x7f];
ac97_codec_log("AC97 Codec %d: read(%02X) = %02X\n", dev->codec_id, reg, ret);
return ret;
}
void
ac97_codec_write(ac97_codec_t *dev, uint8_t reg, uint8_t val)
{
uint8_t i;
ac97_codec_log("AC97 Codec %d: write(%02X, %02X)\n", dev->codec_id, reg, val);
reg &= 0x7f;
switch (reg) {
case 0x00: case 0x01: /* Reset / ID code */
ac97_codec_reset(dev);
return;
case 0x08: case 0x09: /* Master Tone Control (optional) */
case 0x0d: /* Phone Volume MSB */
case 0x0f: /* Mic Volume MSB */
case 0x1e: case 0x1f: /* Record Gain Mic (optional) */
case 0x22: case 0x23: /* 3D Control (optional) */
case 0x24: case 0x25: /* Audio Interrupt and Paging Mechanism (optional) */
case 0x26: /* Powerdown Ctrl/Stat LSB */
case 0x28: case 0x29: /* Extended Audio ID */
case 0x2b: /* Extended Audio Status/Control MSB */
//case 0x36 ... 0x59: /* Linux tests for audio capability by writing to 38-39 */
case 0x5a ... 0x5f: /* Vendor Reserved */
//case 0x60 ... 0x6f:
case 0x70 ... 0x7f: /* Vendor Reserved */
/* Read-only registers. */
return;
case 0x02: /* Master Volume LSB */
case 0x04: /* Aux Out Volume LSB */
case 0x06: /* Mono Volume LSB */
val &= 0x3f;
/* fall-through */
case 0x03: /* Master Volume MSB */
case 0x05: /* Aux Out Volume MSB */
val &= 0xbf;
/* Limit level to a maximum of 011111. */
if (val & 0x20) {
val &= ~0x20;
val |= 0x1f;
}
break;
case 0x07: /* Mono Volume MSB */
case 0x0b: /* PC Beep Volume MSB */
case 0x20: /* General Purpose LSB */
val &= 0x80;
break;
case 0x0a: /* PC Beep Volume LSB */
val &= 0x1e;
break;
case 0x0c: /* Phone Volume LSB */
case 0x10: /* Line In Volume LSB */
case 0x12: /* CD Volume LSB */
case 0x14: /* Video Volume LSB */
case 0x16: /* Aux In Volume LSB */
case 0x18: /* PCM Out Volume LSB */
val &= 0x1f;
break;
case 0x0e: /* Mic Volume LSB */
val &= 0x5f;
break;
case 0x11: /* Line In Volume MSB */
case 0x13: /* CD Volume MSB */
case 0x15: /* Video Volume MSB */
case 0x17: /* Aux In Volume MSB */
case 0x19: /* PCM Out Volume MSB */
val &= 0x9f;
break;
case 0x1a: case 0x1b: /* Record Select */
val &= 0x07;
break;
case 0x1c: /* Record Gain LSB */
val &= 0x0f;
break;
case 0x1d: /* Record Gain MSB */
val &= 0x8f;
break;
case 0x21: /* General Purpose MSB */
val &= 0x83;
break;
case 0x2a: /* Extended Audio Status/Control LSB */
#ifdef AC97_CODEC_FULL_RATE_RANGE /* enable DRA (double rate) support */
val &= 0x0b;
#else
val &= 0x09;
#endif
/* Reset DAC sample rates to 48 KHz (96 KHz with DRA) if VRA is being cleared. */
if (!(val & 0x01)) {
for (i = 0x2c; i <= 0x30; i += 2)
*((uint16_t *) &dev->regs[i]) = 48000;
}
/* Reset ADC sample rates to 48 KHz if VRM is being cleared. */
if (!(val & 0x08)) {
for (i = 0x32; i <= 0x34; i += 2)
*((uint16_t *) &dev->regs[i]) = 48000;
}
break;
case 0x2c ... 0x35: /* DAC/ADC Rates */
/* Writable only if VRA/VRM is set. */
i = (reg >= 0x32) ? 0x08 : 0x01;
if (!(dev->regs[0x2a] & i))
return;
#ifndef AC97_CODEC_FULL_RATE_RANGE
/* Limit to 48 KHz on MSB write. */
if ((reg & 1) && (((val << 8) | dev->regs[reg & 0x7e]) > 48000)) {
*((uint16_t *) &dev->regs[reg & 0x7e]) = 48000;
return;
}
#endif
break;
}
dev->regs[reg] = val;
}
void
ac97_codec_reset(void *priv)
{
ac97_codec_t *dev = (ac97_codec_t *) priv;
uint8_t i;
ac97_codec_log("AC97 Codec %d: reset()\n", dev->codec_id);
memset(dev->regs, 0, sizeof(dev->regs));
/* Set default level and gain values. */
for (i = 0x02; i <= 0x18; i += 2) {
if (i == 0x08)
continue;
if (i >= 0x0c)
dev->regs[i] = 0x08;
dev->regs[i | 1] = (i >= 0x10) ? 0x88 : 0x80;
}
/* Flag codec as ready. */
dev->regs[0x26] = 0x0f;
/* Set up variable sample rate support. */
#ifdef AC97_CODEC_FULL_RATE_RANGE /* enable DRA (double rate) support */
dev->regs[0x28] = 0x0b;
#else
dev->regs[0x28] = 0x09;
#endif
ac97_codec_write(dev, 0x2a, 0x00); /* reset DAC/ADC sample rates */
/* Set codec and vendor IDs. */
dev->regs[0x29] = (dev->codec_id << 6) | 0x02;
dev->regs[0x7c] = dev->vendor_id >> 16;
dev->regs[0x7d] = dev->vendor_id >> 24;
dev->regs[0x7e] = dev->vendor_id;
dev->regs[0x7f] = dev->vendor_id >> 8;
}
void
ac97_codec_getattn(void *priv, uint8_t reg, int *l, int *r)
{
ac97_codec_t *dev = (ac97_codec_t *) priv;
uint8_t r_val = dev->regs[reg],
l_val = dev->regs[reg | 1];
if (l_val & 0x80) { /* mute */
*l = 0;
*r = 0;
return;
}
l_val &= 0x1f;
r_val &= 0x1f;
if (reg < 0x10) { /* 5-bit level (converted from 6-bit on register write) */
*l = codec_attn[0x1f - l_val];
*r = codec_attn[0x1f - r_val];
} else { /* 5-bit gain */
*l = codec_attn[0x27 - l_val];
*r = codec_attn[0x27 - r_val];
}
}
uint32_t
ac97_codec_getrate(void *priv, uint8_t reg)
{
ac97_codec_t *dev = (ac97_codec_t *) priv;
/* Get configured sample rate, which is always 48000 if VRA/VRM is not set. */
uint32_t ret = *((uint16_t *) &dev->regs[reg]);
#ifdef AC97_CODEC_FULL_RATE_RANGE
/* If this is a DAC, double sample rate if DRA is set. */
if ((reg < 0x32) && (dev->regs[0x2a] & 0x02))
ret <<= 1;
#endif
ac97_codec_log("AC97 Codec %d: getrate(%02X) = %d\n", dev->codec_id, reg, ret);
return ret;
}
static void *
ac97_codec_init(const device_t *info)
{
ac97_codec_t *dev = malloc(sizeof(ac97_codec_t));
memset(dev, 0, sizeof(ac97_codec_t));
dev->vendor_id = info->local;
ac97_codec_log("AC97 Codec %d: init(%c%c%c%02X)\n", ac97_codec_id, (dev->vendor_id >> 24) & 0xff, (dev->vendor_id >> 16) & 0xff, (dev->vendor_id >> 8) & 0xff, dev->vendor_id & 0xff);
/* Associate this codec to the current controller. */
if (!ac97_codec || (ac97_codec_count <= 0)) {
fatal("AC97 Codec %d: No controller to associate codec\n", ac97_codec_id);
return NULL;
}
*ac97_codec = dev;
if (--ac97_codec_count == 0)
ac97_codec = NULL;
else
ac97_codec += sizeof(ac97_codec_t *);
dev->codec_id = ac97_codec_id++;
/* Initialize codec registers. */
ac97_codec_reset(dev);
return dev;
}
static void
ac97_codec_close(void *priv)
{
ac97_codec_t *dev = (ac97_codec_t *) priv;
ac97_codec_log("AC97 Codec %d: close()\n", dev->codec_id);
free(dev);
}
const device_t ad1881_device =
{
"Analog Devices AD1881",
DEVICE_AC97,
AC97_CODEC_AD1881,
ac97_codec_init, ac97_codec_close, ac97_codec_reset,
{ NULL },
NULL,
NULL,
NULL
};
const device_t alc100_device =
{
"Avance Logic ALC100",
DEVICE_AC97,
AC97_CODEC_ALC100,
ac97_codec_init, ac97_codec_close, ac97_codec_reset,
{ NULL },
NULL,
NULL,
NULL
};
const device_t cs4297_device =
{
"Crystal CS4297",
DEVICE_AC97,
AC97_CODEC_CS4297,
ac97_codec_init, ac97_codec_close, ac97_codec_reset,
{ NULL },
NULL,
NULL,
NULL
};
const device_t cs4297a_device =
{
"Crystal CS4297A",
DEVICE_AC97,
AC97_CODEC_CS4297A,
ac97_codec_init, ac97_codec_close, ac97_codec_reset,
{ NULL },
NULL,
NULL,
NULL
};
const device_t wm9701a_device =
{
"Wolfson WM9701A",
DEVICE_AC97,
AC97_CODEC_WM9701A,
ac97_codec_init, ac97_codec_close, ac97_codec_reset,
{ NULL },
NULL,
NULL,
NULL
};

843
src/sound/snd_ac97_via.c Normal file
View File

@@ -0,0 +1,843 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* VIA AC'97 audio controller emulation.
*
*
*
* Authors: RichardG, <richardg867@gmail.com>
*
* Copyright 2021 RichardG.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/io.h>
#include <86box/mem.h>
#include <86box/pic.h>
#include <86box/timer.h>
#include <86box/pci.h>
#include <86box/sound.h>
#include <86box/snd_ac97.h>
typedef struct {
uint8_t id, always_run;
struct _ac97_via_ *dev;
uint32_t entry_ptr, sample_ptr, fifo_pos, fifo_end;
int32_t sample_count;
uint8_t entry_flags, fifo[32], restart;
pc_timer_t timer;
} ac97_via_sgd_t;
typedef struct _ac97_via_ {
uint16_t audio_sgd_base, audio_codec_base, modem_sgd_base, modem_codec_base;
uint8_t sgd_regs[256], pcm_enabled: 1, fm_enabled: 1, vsr_enabled: 1;
struct {
union {
uint8_t regs_codec[2][128];
uint8_t regs_linear[256];
};
} codec_shadow[2];
int slot, irq_pin;
ac97_codec_t *codec[2][2];
ac97_via_sgd_t sgd[6];
pc_timer_t timer_count, timer_count_fm;
uint64_t timer_latch, timer_latch_fm;
int16_t out_l, out_r, fm_out_l, fm_out_r;
int master_vol_l, master_vol_r, pcm_vol_l, pcm_vol_r, cd_vol_l, cd_vol_r;
int32_t buffer[SOUNDBUFLEN * 2], fm_buffer[SOUNDBUFLEN * 2];
int pos, fm_pos;
} ac97_via_t;
#ifdef ENABLE_AC97_VIA_LOG
int ac97_via_do_log = ENABLE_AC97_VIA_LOG;
static void
ac97_via_log(const char *fmt, ...)
{
va_list ap;
if (ac97_via_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define ac97_via_log(fmt, ...)
#endif
static void ac97_via_sgd_process(void *priv);
static void ac97_via_update_codec(ac97_via_t *dev);
static void ac97_via_speed_changed(void *priv);
void
ac97_via_set_slot(void *priv, int slot, int irq_pin)
{
ac97_via_t *dev = (ac97_via_t *) priv;
ac97_via_log("AC97 VIA: set_slot(%d, %d)\n", slot, irq_pin);
dev->slot = slot;
dev->irq_pin = irq_pin;
}
uint8_t
ac97_via_read_status(void *priv, uint8_t modem)
{
ac97_via_t *dev = (ac97_via_t *) priv;
uint8_t ret = 0x00;
/* Flag each codec as ready if present. */
for (uint8_t i = 0; i <= 1; i++) {
if (dev->codec[modem][i])
ret |= 0x01 << (i << 1);
}
ac97_via_log("AC97 VIA %d: read_status() = %02X\n", modem, ret);
return ret;
}
void
ac97_via_write_control(void *priv, uint8_t modem, uint8_t val)
{
ac97_via_t *dev = (ac97_via_t *) priv;
uint8_t i;
ac97_via_log("AC97 VIA %d: write_control(%02X)\n", modem, val);
/* Reset codecs if requested. */
if (!(val & 0x40)) {
for (i = 0; i <= 1; i++) {
if (dev->codec[modem][i])
ac97_codec_reset(dev->codec[modem][i]);
}
}
if (!modem) {
/* Set the variable sample rate flag. */
dev->vsr_enabled = (val & 0xf8) == 0xc8;
/* Start or stop PCM playback. */
i = (val & 0xf4) == 0xc4;
if (i && !dev->pcm_enabled)
timer_advance_u64(&dev->timer_count, dev->timer_latch);
dev->pcm_enabled = i;
/* Start or stop FM playback. */
i = (val & 0xf2) == 0xc2;
if (i && !dev->fm_enabled)
timer_advance_u64(&dev->timer_count_fm, dev->timer_latch);
dev->fm_enabled = i;
/* Update primary audio codec state. */
if (dev->codec[0][0])
ac97_via_update_codec(dev);
}
}
static void
ac97_via_update_irqs(ac97_via_t *dev)
{
/* Check interrupt flags in all SGDs. */
for (uint8_t i = 0x00; i < ((sizeof(dev->sgd) / sizeof(dev->sgd[0])) << 4); i += 0x10) {
/* Stop immediately if any flag is set. Doing it this way optimizes
rising edges for the playback SGD (0 - first to be checked). */
if (dev->sgd_regs[i] & (dev->sgd_regs[i | 0x2] & 0x03)) {
pci_set_irq(dev->slot, dev->irq_pin);
return;
}
}
pci_clear_irq(dev->slot, dev->irq_pin);
}
static void
ac97_via_update_codec(ac97_via_t *dev) {
/* Get primary audio codec. */
ac97_codec_t *codec = dev->codec[0][0];
/* Update volumes according to codec registers. */
ac97_codec_getattn(codec, 0x02, &dev->master_vol_l, &dev->master_vol_r);
ac97_codec_getattn(codec, 0x18, &dev->pcm_vol_l, &dev->pcm_vol_r);
ac97_codec_getattn(codec, 0x12, &dev->cd_vol_l, &dev->cd_vol_r);
/* Update sample rate according to codec registers and the variable sample rate flag. */
ac97_via_speed_changed(dev);
}
uint8_t
ac97_via_sgd_read(uint16_t addr, void *priv)
{
ac97_via_t *dev = (ac97_via_t *) priv;
#ifdef ENABLE_AC97_VIA_LOG
uint8_t modem = (addr & 0xff00) == dev->modem_sgd_base;
#endif
addr &= 0xff;
uint8_t ret;
if (!(addr & 0x80)) {
/* Process SGD channel registers. */
switch (addr & 0xf) {
case 0x4:
ret = dev->sgd[addr >> 4].entry_ptr;
break;
case 0x5:
ret = dev->sgd[addr >> 4].entry_ptr >> 8;
break;
case 0x6:
ret = dev->sgd[addr >> 4].entry_ptr >> 16;
break;
case 0x7:
ret = dev->sgd[addr >> 4].entry_ptr >> 24;
break;
case 0xc:
ret = dev->sgd[addr >> 4].sample_count;
break;
case 0xd:
ret = dev->sgd[addr >> 4].sample_count >> 8;
break;
case 0xe:
ret = dev->sgd[addr >> 4].sample_count >> 16;
break;
default:
ret = dev->sgd_regs[addr];
break;
}
} else {
/* Process regular registers. */
switch (addr) {
case 0x84:
ret = (dev->sgd_regs[0x00] & 0x01);
ret |= (dev->sgd_regs[0x10] & 0x01) << 1;
ret |= (dev->sgd_regs[0x20] & 0x01) << 2;
ret |= (dev->sgd_regs[0x00] & 0x02) << 3;
ret |= (dev->sgd_regs[0x10] & 0x02) << 4;
ret |= (dev->sgd_regs[0x20] & 0x02) << 5;
break;
case 0x85:
ret = (dev->sgd_regs[0x00] & 0x04) >> 2;
ret |= (dev->sgd_regs[0x10] & 0x04) >> 1;
ret |= (dev->sgd_regs[0x20] & 0x04);
ret |= (dev->sgd_regs[0x00] & 0x80) >> 3;
ret |= (dev->sgd_regs[0x10] & 0x80) >> 2;
ret |= (dev->sgd_regs[0x20] & 0x80) >> 1;
break;
case 0x86:
ret = (dev->sgd_regs[0x40] & 0x01);
ret |= (dev->sgd_regs[0x50] & 0x01) << 1;
ret |= (dev->sgd_regs[0x40] & 0x02) << 3;
ret |= (dev->sgd_regs[0x50] & 0x02) << 4;
break;
case 0x87:
ret = (dev->sgd_regs[0x40] & 0x04) >> 2;
ret |= (dev->sgd_regs[0x50] & 0x04) >> 1;
ret |= (dev->sgd_regs[0x40] & 0x80) >> 3;
ret |= (dev->sgd_regs[0x50] & 0x80) >> 2;
break;
default:
ret = dev->sgd_regs[addr];
break;
}
}
ac97_via_log("AC97 VIA %d: sgd_read(%02X) = %02X\n", modem, addr, ret);
return ret;
}
void
ac97_via_sgd_write(uint16_t addr, uint8_t val, void *priv)
{
ac97_via_t *dev = (ac97_via_t *) priv;
uint8_t modem = (addr & 0xff00) == dev->modem_sgd_base, i;
ac97_codec_t *codec;
addr &= 0xff;
ac97_via_log("AC97 VIA %d: sgd_write(%02X, %02X)\n", modem, addr, val);
/* Check function-specific read only registers. */
if ((addr >= (modem ? 0x00 : 0x40)) && (addr < (modem ? 0x40 : 0x60)))
return;
if (addr >= (modem ? 0x90 : 0x88))
return;
if (!(addr & 0x80)) {
/* Process SGD channel registers. */
switch (addr & 0xf) {
case 0x0:
/* Clear RWC status bits. */
dev->sgd_regs[addr] &= ~(val & 0x07);
/* Update status interrupts. */
ac97_via_update_irqs(dev);
return;
case 0x1:
/* Start SGD if requested. */
if (val & 0x80) {
if (dev->sgd_regs[addr & 0xf0] & 0x80) {
/* Queue SGD trigger if already running. */
dev->sgd_regs[addr & 0xf0] |= 0x08;
} else {
/* Start SGD immediately. */
dev->sgd_regs[addr & 0xf0] |= 0x80;
dev->sgd_regs[addr & 0xf0] &= ~0x44;
/* Start at the specified entry pointer. */
dev->sgd[addr >> 4].sample_ptr = 0;
dev->sgd[addr >> 4].entry_ptr = *((uint32_t *) &dev->sgd_regs[(addr & 0xf0) | 0x4]) & 0xfffffffe;
dev->sgd[addr >> 4].restart = 1;
/* Start the actual SGD process. */
ac97_via_sgd_process(&dev->sgd[addr >> 4]);
}
}
/* Stop SGD if requested. */
if (val & 0x40)
dev->sgd_regs[addr & 0xf0] &= ~0x88;
val &= 0x08;
/* (Un)pause SGD if requested. */
if (val & 0x08)
dev->sgd_regs[addr & 0xf0] |= 0x40;
else
dev->sgd_regs[addr & 0xf0] &= ~0x40;
break;
case 0x2:
if (addr & 0x10)
val &= 0xf3;
break;
case 0x3: case 0x8 ... 0xf:
/* Read-only registers. */
return;
}
} else {
/* Process regular registers. */
switch (addr) {
case 0x30 ... 0x3f:
case 0x60 ... 0x7f:
case 0x84 ... 0x87:
/* Read-only registers. */
return;
case 0x82:
/* Determine the selected codec. */
i = !!(dev->sgd_regs[0x83] & 0x40);
codec = dev->codec[modem][i];
/* Keep value in register if this codec is not present. */
if (codec) {
/* Read from or write to codec. */
if (val & 0x80) {
if (val & 1) { /* return 0x00 on unaligned reads */
dev->sgd_regs[0x80] = dev->sgd_regs[0x81] = 0x00;
} else {
dev->sgd_regs[0x80] = dev->codec_shadow[modem].regs_codec[i][val] = ac97_codec_read(codec, val);
dev->sgd_regs[0x81] = dev->codec_shadow[modem].regs_codec[i][val | 1] = ac97_codec_read(codec, val | 1);
}
/* Flag data/status/index for this codec as valid. */
if (val & 0x80)
dev->sgd_regs[0x83] |= 0x02 << (i << 1);
} else if (!(val & 1)) { /* do nothing on unaligned writes */
ac97_codec_write(codec, val, dev->codec_shadow[modem].regs_codec[i][val] = dev->sgd_regs[0x80]);
ac97_codec_write(codec, val | 1, dev->codec_shadow[modem].regs_codec[i][val | 1] = dev->sgd_regs[0x81]);
/* Update primary audio codec state if that codec was written to. */
if (!modem && !i)
ac97_via_update_codec(dev);
}
}
break;
case 0x83:
/* Clear RWC status bits. */
#if 0 /* race condition with Linux accessing a register and clearing status bits on the same dword write */
val = (dev->sgd_regs[addr] & ~(val & 0x0a)) | (val & 0xc0);
#else
val = dev->sgd_regs[addr] | (val & 0xc0);
#endif
break;
}
}
dev->sgd_regs[addr] = val;
}
void
ac97_via_remap_audio_sgd(void *priv, uint16_t new_io_base, uint8_t enable)
{
ac97_via_t *dev = (ac97_via_t *) priv;
if (dev->audio_sgd_base)
io_removehandler(dev->audio_sgd_base, 256, ac97_via_sgd_read, NULL, NULL, ac97_via_sgd_write, NULL, NULL, dev);
dev->audio_sgd_base = new_io_base;
if (dev->audio_sgd_base && enable)
io_sethandler(dev->audio_sgd_base, 256, ac97_via_sgd_read, NULL, NULL, ac97_via_sgd_write, NULL, NULL, dev);
}
void
ac97_via_remap_modem_sgd(void *priv, uint16_t new_io_base, uint8_t enable)
{
ac97_via_t *dev = (ac97_via_t *) priv;
if (dev->modem_sgd_base)
io_removehandler(dev->modem_sgd_base, 256, ac97_via_sgd_read, NULL, NULL, ac97_via_sgd_write, NULL, NULL, dev);
dev->modem_sgd_base = new_io_base;
if (dev->modem_sgd_base && enable)
io_sethandler(dev->modem_sgd_base, 256, ac97_via_sgd_read, NULL, NULL, ac97_via_sgd_write, NULL, NULL, dev);
}
uint8_t
ac97_via_codec_read(uint16_t addr, void *priv)
{
ac97_via_t *dev = (ac97_via_t *) priv;
uint8_t modem = (addr & 0xff00) == dev->modem_codec_base;
addr &= 0xff;
uint8_t ret = 0xff;
ret = dev->codec_shadow[modem].regs_linear[addr];
ac97_via_log("AC97 VIA %d: codec_read(%02X) = %02X\n", modem, addr, ret);
return ret;
}
void
ac97_via_codec_write(uint16_t addr, uint8_t val, void *priv)
{
ac97_via_t *dev = (ac97_via_t *) priv;
uint8_t modem = (addr & 0xff00) == dev->modem_codec_base;
addr &= 0xff;
ac97_via_log("AC97 VIA %d: codec_write(%02X, %02X)\n", modem, addr, val);
/* Unknown behavior, maybe it does write to the shadow registers? */
dev->codec_shadow[modem].regs_linear[addr] = val;
}
void
ac97_via_remap_audio_codec(void *priv, uint16_t new_io_base, uint8_t enable)
{
ac97_via_t *dev = (ac97_via_t *) priv;
if (dev->audio_codec_base)
io_removehandler(dev->audio_codec_base, 256, ac97_via_codec_read, NULL, NULL, ac97_via_codec_write, NULL, NULL, dev);
dev->audio_codec_base = new_io_base;
if (dev->audio_codec_base && enable)
io_sethandler(dev->audio_codec_base, 256, ac97_via_codec_read, NULL, NULL, ac97_via_codec_write, NULL, NULL, dev);
}
void
ac97_via_remap_modem_codec(void *priv, uint16_t new_io_base, uint8_t enable)
{
ac97_via_t *dev = (ac97_via_t *) priv;
if (dev->modem_codec_base)
io_removehandler(dev->modem_codec_base, 256, ac97_via_codec_read, NULL, NULL, ac97_via_codec_write, NULL, NULL, dev);
dev->modem_codec_base = new_io_base;
if (dev->modem_codec_base && enable)
io_sethandler(dev->modem_codec_base, 256, ac97_via_codec_read, NULL, NULL, ac97_via_codec_write, NULL, NULL, dev);
}
static void
ac97_via_update(ac97_via_t *dev)
{
int32_t l = (((dev->out_l * dev->pcm_vol_l) >> 15) * dev->master_vol_l) >> 15,
r = (((dev->out_r * dev->pcm_vol_r) >> 15) * dev->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 (; dev->pos < sound_pos_global; dev->pos++) {
dev->buffer[dev->pos*2] = l;
dev->buffer[dev->pos*2 + 1] = r;
}
}
static void
ac97_via_update_fm(ac97_via_t *dev)
{
for (; dev->fm_pos < sound_pos_global; dev->fm_pos++) {
dev->fm_buffer[dev->fm_pos*2] = dev->fm_out_l;
dev->fm_buffer[dev->fm_pos*2 + 1] = dev->fm_out_r;
}
}
static void
ac97_via_sgd_process(void *priv)
{
ac97_via_sgd_t *sgd = (ac97_via_sgd_t *) priv;
ac97_via_t *dev = sgd->dev;
/* Stop if this SGD is not active. */
uint8_t sgd_status = dev->sgd_regs[sgd->id] & 0xc4;
if (!(sgd_status & 0x80))
return;
/* Schedule next run. */
timer_on_auto(&sgd->timer, 10.0);
/* Process SGD if it's active, and the FIFO has room or is disabled. */
if ((sgd_status == 0x80) && (sgd->always_run || ((sgd->fifo_end - sgd->fifo_pos) <= (sizeof(sgd->fifo) - 4)))) {
/* Move on to the next block if no entry is present. */
if (sgd->restart) {
sgd->restart = 0;
/* Start at first entry if no pointer is present. */
if (!sgd->entry_ptr)
sgd->entry_ptr = *((uint32_t *) &dev->sgd_regs[sgd->id | 0x4]) & 0xfffffffe;
/* Read entry. */
sgd->sample_ptr = mem_readl_phys(sgd->entry_ptr);
sgd->entry_ptr += 4;
sgd->sample_count = mem_readl_phys(sgd->entry_ptr);
sgd->entry_ptr += 4;
#ifdef ENABLE_AC97_VIA_LOG
if (((sgd->sample_ptr == 0xffffffff) && (sgd->sample_count == 0xffffffff)) ||
((sgd->sample_ptr == 0x00000000) && (sgd->sample_count == 0x00000000)))
fatal("AC97 VIA: Invalid SGD %d entry %08X%08X at %08X\n", sgd->id >> 4,
sgd->sample_ptr, sgd->sample_count, sgd->entry_ptr - 8);
#endif
/* Extract flags from the most significant byte. */
sgd->entry_flags = sgd->sample_count >> 24;
sgd->sample_count &= 0xffffff;
ac97_via_log("AC97 VIA: Starting SGD %d block at %08X start %08X len %06X flags %02X\n", sgd->id >> 4,
sgd->entry_ptr - 8, sgd->sample_ptr, sgd->sample_count, sgd->entry_flags);
}
if (sgd->id & 0x10) {
/* Write channel: read data from FIFO. */
mem_writel_phys(sgd->sample_ptr, *((uint32_t *) &sgd->fifo[sgd->fifo_end & (sizeof(sgd->fifo) - 1)]));
} else {
/* Read channel: write data to FIFO. */
*((uint32_t *) &sgd->fifo[sgd->fifo_end & (sizeof(sgd->fifo) - 1)]) = mem_readl_phys(sgd->sample_ptr);
}
sgd->fifo_end += 4;
sgd->sample_ptr += 4;
sgd->sample_count -= 4;
/* Check if we've hit the end of this block. */
if (sgd->sample_count <= 0) {
ac97_via_log("AC97 VIA: Ending SGD %d block", sgd->id >> 4);
if (sgd->entry_flags & 0x20) {
ac97_via_log(" with STOP");
/* Raise STOP to pause SGD. */
dev->sgd_regs[sgd->id] |= 0x04;
}
if (sgd->entry_flags & 0x40) {
ac97_via_log(" with FLAG");
/* Raise FLAG and STOP. */
dev->sgd_regs[sgd->id] |= 0x05;
#ifdef ENABLE_AC97_VIA_LOG
if (dev->sgd_regs[sgd->id | 0x2] & 0x01)
ac97_via_log(" interrupt");
#endif
}
if (sgd->entry_flags & 0x80) {
ac97_via_log(" with EOL");
/* Raise EOL. */
dev->sgd_regs[sgd->id] |= 0x02;
#ifdef ENABLE_AC97_VIA_LOG
if (dev->sgd_regs[sgd->id | 0x2] & 0x02)
ac97_via_log(" interrupt");
#endif
/* Restart SGD if a trigger is queued or auto-start is enabled. */
if ((dev->sgd_regs[sgd->id] & 0x08) || (dev->sgd_regs[sgd->id | 0x2] & 0x80)) {
ac97_via_log(" restart");
/* Un-queue trigger. */
dev->sgd_regs[sgd->id] &= ~0x08;
/* Go back to the starting block. */
sgd->entry_ptr = 0; /* ugly, but Windows XP plays too fast if the pointer is reloaded now */
} else {
ac97_via_log(" finish");
/* Terminate SGD. */
dev->sgd_regs[sgd->id] &= ~0x80;
}
}
ac97_via_log("\n");
/* Fire any requested status interrupts. */
ac97_via_update_irqs(dev);
/* Move on to a new block on the next run. */
sgd->restart = 1;
}
}
}
static void
ac97_via_poll(void *priv)
{
ac97_via_t *dev = (ac97_via_t *) priv;
ac97_via_sgd_t *sgd = &dev->sgd[0]; /* Audio Read */
/* Schedule next run if PCM playback is enabled. */
if (dev->pcm_enabled)
timer_advance_u64(&dev->timer_count, dev->timer_latch);
/* Update audio buffer. */
ac97_via_update(dev);
/* Feed next sample from the FIFO. */
switch (dev->sgd_regs[0x02] & 0x30) {
case 0x00: /* Mono, 8-bit PCM */
if ((sgd->fifo_end - sgd->fifo_pos) >= 1) {
dev->out_l = dev->out_r = (sgd->fifo[sgd->fifo_pos++ & (sizeof(sgd->fifo) - 1)] ^ 0x80) << 8;
return;
}
break;
case 0x10: /* Stereo, 8-bit PCM */
if ((sgd->fifo_end - sgd->fifo_pos) >= 2) {
dev->out_l = (sgd->fifo[sgd->fifo_pos++ & (sizeof(sgd->fifo) - 1)] ^ 0x80) << 8;
dev->out_r = (sgd->fifo[sgd->fifo_pos++ & (sizeof(sgd->fifo) - 1)] ^ 0x80) << 8;
return;
}
break;
case 0x20: /* Mono, 16-bit PCM */
if ((sgd->fifo_end - sgd->fifo_pos) >= 2) {
dev->out_l = dev->out_r = *((uint16_t *) &sgd->fifo[sgd->fifo_pos & (sizeof(sgd->fifo) - 1)]);
sgd->fifo_pos += 2;
return;
}
break;
case 0x30: /* Stereo, 16-bit PCM */
if ((sgd->fifo_end - sgd->fifo_pos) >= 4) {
dev->out_l = *((uint16_t *) &sgd->fifo[sgd->fifo_pos & (sizeof(sgd->fifo) - 1)]);
sgd->fifo_pos += 2;
dev->out_r = *((uint16_t *) &sgd->fifo[sgd->fifo_pos & (sizeof(sgd->fifo) - 1)]);
sgd->fifo_pos += 2;
return;
}
break;
}
/* Feed silence if the FIFO is empty. */
dev->out_l = dev->out_r = 0;
}
static void
ac97_via_poll_fm(void *priv)
{
ac97_via_t *dev = (ac97_via_t *) priv;
ac97_via_sgd_t *sgd = &dev->sgd[2]; /* FM Read */
/* Schedule next run if FM playback is enabled. */
if (dev->fm_enabled)
timer_advance_u64(&dev->timer_count_fm, dev->timer_latch_fm);
/* Update FM audio buffer. */
ac97_via_update_fm(dev);
/* Feed next sample from the FIFO.
The data format is not documented, but it probes as 16-bit stereo at 24 KHz. */
if ((sgd->fifo_end - sgd->fifo_pos) >= 4) {
dev->out_l = *((uint16_t *) &sgd->fifo[sgd->fifo_pos & (sizeof(sgd->fifo) - 1)]);
sgd->fifo_pos += 2;
dev->out_r = *((uint16_t *) &sgd->fifo[sgd->fifo_pos & (sizeof(sgd->fifo) - 1)]);
sgd->fifo_pos += 2;
return;
}
/* Feed silence if the FIFO is empty. */
dev->fm_out_l = dev->fm_out_r = 0;
}
static void
ac97_via_get_buffer(int32_t *buffer, int len, void *priv)
{
ac97_via_t *dev = (ac97_via_t *) priv;
ac97_via_update(dev);
ac97_via_update_fm(dev);
for (int c = 0; c < len * 2; c++) {
buffer[c] += dev->buffer[c] / 2;
buffer[c] += dev->fm_buffer[c] / 2;
}
dev->pos = dev->fm_pos = 0;
}
static void
via_ac97_filter_cd_audio(int channel, double *buffer, void *priv)
{
ac97_via_t *dev = (ac97_via_t *) priv;
double c, volume = channel ? dev->cd_vol_r : dev->cd_vol_l;
c = ((*buffer) * volume) / 65536.0;
*buffer = c;
}
static void
ac97_via_speed_changed(void *priv)
{
ac97_via_t *dev = (ac97_via_t *) priv;
double freq;
/* Get variable sample rate if enabled. */
if (dev->vsr_enabled && dev->codec[0][0])
freq = ac97_codec_getrate(dev->codec[0][0], 0x2c);
else
freq = 48000.0;
dev->timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / freq));
dev->timer_latch_fm = (uint64_t) ((double) TIMER_USEC * (1000000.0 / 24000.0));
}
static void *
ac97_via_init(const device_t *info)
{
ac97_via_t *dev = malloc(sizeof(ac97_via_t));
memset(dev, 0, sizeof(ac97_via_t));
ac97_via_log("AC97 VIA: init()\n");
/* Set up codecs. */
ac97_codec = &dev->codec[0][0];
ac97_modem_codec = &dev->codec[1][0];
ac97_codec_count = ac97_modem_codec_count = sizeof(dev->codec[0]) / sizeof(dev->codec[0][0]);
ac97_codec_id = ac97_modem_codec_id = 0;
/* Set up SGD channels. */
for (uint8_t i = 0; i < (sizeof(dev->sgd) / sizeof(dev->sgd[0])); i++) {
dev->sgd[i].id = i << 4;
dev->sgd[i].dev = dev;
/* Disable the FIFO on SGDs we don't care about. */
if ((i != 0) && (i != 2))
dev->sgd[i].always_run = 1;
timer_add(&dev->sgd[i].timer, ac97_via_sgd_process, &dev->sgd[i], 0);
}
/* Set up playback pollers. */
timer_add(&dev->timer_count, ac97_via_poll, dev, 0);
timer_add(&dev->timer_count_fm, ac97_via_poll_fm, dev, 0);
ac97_via_speed_changed(dev);
/* Set up playback handler. */
sound_add_handler(ac97_via_get_buffer, dev);
/* Set up CD audio filter. */
sound_set_cd_audio_filter(via_ac97_filter_cd_audio, dev);
return dev;
}
static void
ac97_via_close(void *priv)
{
ac97_via_t *dev = (ac97_via_t *) priv;
ac97_via_log("AC97 VIA: close()\n");
free(dev);
}
const device_t ac97_via_device =
{
"VIA VT82C686 Integrated AC97 Controller",
DEVICE_PCI,
0,
ac97_via_init, ac97_via_close, NULL,
{ NULL },
ac97_via_speed_changed,
NULL,
NULL
};

View File

@@ -17,6 +17,7 @@
#include <86box/sound.h>
#include <86box/midi.h>
#include <86box/snd_mpu401.h>
#include <86box/snd_ac97.h>
#define N 16
@@ -51,7 +52,7 @@ typedef struct {
uint8_t uart_ctrl;
uint8_t uart_status;
uint16_t codec_regs[128];
ac97_codec_t *codec;
uint32_t codec_ctrl;
struct {
@@ -147,12 +148,6 @@ typedef struct {
#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, uint32_t old_legacy_ctrl);
@@ -332,7 +327,8 @@ static uint16_t es1371_inw(uint16_t port, void *p)
break;
default:
audiopci_log("Bad es1371_inw: port=%04x\n", port);
ret = es1371_inb(port, p);
ret |= es1371_inb(port + 1, p) << 8;
}
// audiopci_log("es1371_inw: port=%04x ret=%04x %04x:%08x\n", port, ret, CS,cpu_state.pc);
@@ -358,11 +354,7 @@ static uint32_t es1371_inl(uint16_t port, void *p)
break;
case 0x14:
ret = es1371->codec_ctrl & 0x00ff0000;
ret |= es1371->codec_regs[(es1371->codec_ctrl >> 16) & 0x7f];
if (((es1371->codec_ctrl >> 16) & 0x7f) == 0x26)
ret |= 0x0f;
ret |= CODEC_READY;
ret = es1371->codec_ctrl | CODEC_READY;
break;
case 0x30:
@@ -416,7 +408,8 @@ static uint32_t es1371_inl(uint16_t port, void *p)
break;
default:
audiopci_log("Bad es1371_inl: port=%04x\n", port);
ret = es1371_inw(port, p);
ret |= es1371_inw(port + 2, p) << 16;
}
audiopci_log("es1371_inl: port=%04x ret=%08x\n", port, ret);
@@ -527,7 +520,8 @@ static void es1371_outw(uint16_t port, uint16_t val, void *p)
break;
default:
audiopci_log("Bad es1371_outw: port=%04x val=%04x\n", port, val);
es1371_outb(port, val & 0xff, p);
es1371_outb(port + 1, (val >> 8) & 0xff, p);
}
}
static void es1371_outl(uint16_t port, uint32_t val, void *p)
@@ -593,39 +587,18 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p)
break;
case 0x14:
es1371->codec_ctrl = val;
if (!(val & CODEC_READ))
{
// audiopci_log("Write codec %02x %04x\n", (val >> 16) & 0x7f, val & 0xffff);
if ((((val >> 16) & 0x7f) != 0x7c) && (((val >> 16) & 0x7f) != 0x7e))
es1371->codec_regs[(val >> 16) & 0x7f] = val & 0xffff;
switch ((val >> 16) & 0x7f)
{
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)
es1371->cd_vol_l = es1371->cd_vol_r = 0;
else
{
es1371->cd_vol_l = codec_attn[0x1f - ((val >> 8) & 0x1f)];
es1371->cd_vol_r = codec_attn[0x1f - (val & 0x1f)];
}
break;
}
if (val & CODEC_READ) {
es1371->codec_ctrl &= 0x00ff0000;
val = (val >> 16) & 0x7e;
es1371->codec_ctrl |= ac97_codec_read(es1371->codec, val);
es1371->codec_ctrl |= ac97_codec_read(es1371->codec, val | 1) << 8;
} else {
es1371->codec_ctrl = val & 0x00ffffff;
ac97_codec_write(es1371->codec, (val >> 16) & 0x7e, val & 0xff);
ac97_codec_write(es1371->codec, ((val >> 16) & 0x7e) | 1, val >> 8);
ac97_codec_getattn(es1371->codec, 0x02, &es1371->master_vol_l, &es1371->master_vol_r);
ac97_codec_getattn(es1371->codec, 0x12, &es1371->cd_vol_l, &es1371->cd_vol_r);
}
break;
@@ -730,7 +703,8 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p)
break;
default:
audiopci_log("Bad es1371_outl: port=%04x val=%08x\n", port, val);
es1371_outw(port, val & 0xffff, p);
es1371_outw(port + 2, (val >> 16) & 0xffff, p);
}
}
@@ -1362,9 +1336,11 @@ static void *es1371_init(const device_t *info)
generate_es1371_filter();
/* Return a CS4297A like VMware does. */
es1371->codec_regs[0x7c] = 0x4352;
es1371->codec_regs[0x7e] = 0x5910;
ac97_codec = &es1371->codec;
ac97_codec_count = 1;
ac97_codec_id = 0;
if (!info->local) /* let the machine decide the codec on onboard implementations */
device_add(&cs4297a_device);
return es1371;
}

View File

@@ -19,7 +19,7 @@
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include <math.h>
#include <math.h>
#include <86box/86box.h>
#include <86box/io.h>
#include <86box/timer.h>
@@ -34,6 +34,7 @@
#include <86box/snd_ad1848.h>
#include <86box/snd_opl.h>
#include <86box/snd_sb.h>
#include <86box/nvr.h>
enum {
@@ -132,9 +133,10 @@ typedef struct cs423x_t
void *gameport;
void *i2c, *eeprom;
uint16_t wss_base, opl_base, sb_base, ctrl_base, ram_addr, eeprom_size: 11;
uint8_t type, ad1848_type, pnp_offset, regs[8], indirect_regs[16],
eeprom_data[2048], ram_data[384], ram_dl: 2, opl_wss: 1;
uint16_t wss_base, opl_base, sb_base, ctrl_base, ram_addr, eeprom_size: 11, pnp_offset;
uint8_t type, ad1848_type, regs[8], indirect_regs[16],
eeprom_data[2048], ram_data[65536], ram_dl: 2, opl_wss: 1;
char *nvr_path;
uint8_t pnp_enable: 1, key_pos: 5, slam_enable: 1, slam_state: 2, slam_ld, slam_reg;
isapnp_device_config_t *slam_config;
@@ -146,6 +148,20 @@ static void cs423x_pnp_enable(cs423x_t *dev, uint8_t update_rom, uint8_t update_
static void cs423x_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv);
static void
cs423x_nvram(cs423x_t *dev, uint8_t save)
{
FILE *f = nvr_fopen(dev->nvr_path, save ? "wb" : "rb");
if (f) {
if (save)
fwrite(dev->eeprom_data, sizeof(dev->eeprom_data), 1, f);
else
fread(dev->eeprom_data, sizeof(dev->eeprom_data), 1, f);
fclose(f);
}
}
static uint8_t
cs423x_read(uint16_t addr, void *priv)
{
@@ -165,14 +181,9 @@ cs423x_read(uint16_t addr, void *priv)
break;
case 5: /* Control/RAM Access */
/* Reading RAM is undocumented; the WDM driver does so. */
if (dev->ram_dl == 3) {
if ((dev->ram_addr >= 0x4000) && (dev->ram_addr < 0x4180)) /* chip configuration and PnP resources */
ret = dev->ram_data[dev->ram_addr & 0x01ff];
else
ret = 0xff;
dev->ram_addr++;
}
/* Reading RAM is undocumented; the Windows drivers do so. */
if (dev->ram_dl == 3)
ret = dev->ram_data[dev->ram_addr++];
break;
case 7: /* Global Status */
@@ -295,9 +306,7 @@ cs423x_write(uint16_t addr, uint8_t val, void *priv)
break;
case 3: /* data */
if ((dev->ram_addr >= 0x4000) && (dev->ram_addr < 0x4180)) /* chip configuration and PnP resources */
dev->ram_data[dev->ram_addr & 0x01ff] = val;
dev->ram_addr++;
dev->ram_data[dev->ram_addr++] = val;
break;
}
break;
@@ -452,7 +461,7 @@ cs423x_slam_enable(cs423x_t *dev, uint8_t enable)
}
/* Enable SLAM if the CKD bit is not set. */
if (enable && !(dev->ram_data[2] & 0x10)) {
if (enable && !(dev->ram_data[0x4002] & 0x10)) {
dev->slam_enable = 1;
io_sethandler(0x279, 1, NULL, NULL, NULL, cs423x_slam_write, NULL, NULL, dev);
}
@@ -527,25 +536,22 @@ cs423x_get_buffer(int32_t *buffer, int len, void *priv)
static void
cs423x_pnp_enable(cs423x_t *dev, uint8_t update_rom, uint8_t update_hwconfig)
{
uint8_t enable = ISAPNP_CARD_ENABLE;
if (dev->pnp_card) {
/* Hide PnP card if the PKD bit is set, or if PnP was disabled by command 0x55. */
if ((dev->ram_data[2] & 0x20) || !dev->pnp_enable)
enable = ISAPNP_CARD_DISABLE;
/* Update PnP resource data if requested. */
if (update_rom)
isapnp_update_card_rom(dev->pnp_card, &dev->ram_data[dev->pnp_offset], sizeof(dev->ram_data) - dev->pnp_offset);
isapnp_update_card_rom(dev->pnp_card, &dev->ram_data[dev->pnp_offset], 384);
/* Update PnP state. */
isapnp_enable_card(dev->pnp_card, enable);
/* Hide PnP card if the PKD bit is set, or if PnP was disabled by command 0x55. */
if ((dev->ram_data[0x4002] & 0x20) || !dev->pnp_enable)
isapnp_enable_card(dev->pnp_card, ISAPNP_CARD_DISABLE);
else
isapnp_enable_card(dev->pnp_card, ISAPNP_CARD_ENABLE);
}
/* Update some register bits based on the config data in RAM if requested. */
if (update_hwconfig) {
/* Update WTEN. */
if (dev->ram_data[3] & 0x08) {
if (dev->ram_data[0x4003] & 0x08) {
dev->indirect_regs[8] |= 0x08;
dev->ad1848.wten = 1;
} else {
@@ -554,13 +560,13 @@ cs423x_pnp_enable(cs423x_t *dev, uint8_t update_rom, uint8_t update_hwconfig)
}
/* Update SPS. */
if (dev->ram_data[3] & 0x04)
if (dev->ram_data[0x4003] & 0x04)
dev->indirect_regs[8] |= 0x04;
else
dev->indirect_regs[8] &= ~0x04;
/* Update IFM. */
if (dev->ram_data[3] & 0x80)
if (dev->ram_data[0x4003] & 0x80)
dev->ad1848.xregs[4] |= 0x10;
else
dev->ad1848.xregs[4] &= ~0x10;
@@ -677,13 +683,20 @@ cs423x_reset(void *priv)
{
cs423x_t *dev = (cs423x_t *) priv;
/* Load EEPROM data to RAM, or just clear RAM if there's no EEPROM. */
if (dev->eeprom)
memcpy(dev->ram_data, &dev->eeprom_data[4], MIN(sizeof(dev->ram_data), sizeof(dev->eeprom_data) - 4));
else
memset(dev->ram_data, 0, sizeof(dev->ram_data));
/* Clear RAM. */
memset(dev->ram_data, 0, sizeof(dev->ram_data));
if (dev->eeprom) {
/* Load EEPROM data to RAM. */
memcpy(&dev->ram_data[0x4000], &dev->eeprom_data[4], MIN(384, ((dev->eeprom_data[2] << 8) | dev->eeprom_data[3]) - 4));
/* Save EEPROM contents to file. */
cs423x_nvram(dev, 1);
}
/* Reset registers. */
memset(dev->regs, 0, sizeof(dev->regs));
dev->regs[1] = 0x80;
memset(dev->indirect_regs, 0, sizeof(dev->indirect_regs));
dev->indirect_regs[1] = dev->type;
if (dev->type == CRYSTAL_CS4238B)
@@ -717,7 +730,7 @@ cs423x_init(const device_t *info)
case CRYSTAL_CS4238B:
/* Same WSS codec and EEPROM structure. */
dev->ad1848_type = AD1848_TYPE_CS4236;
dev->pnp_offset = 19;
dev->pnp_offset = 0x4013;
/* Different Chip Version and ID registers, which shouldn't be reset by ad1848_init */
dev->ad1848.xregs[25] = dev->type;
@@ -729,17 +742,26 @@ cs423x_init(const device_t *info)
dev->eeprom_data[2] = sizeof(cs4236b_eeprom) >> 8;
dev->eeprom_data[3] = sizeof(cs4236b_eeprom) & 0xff;
/* Set PnP card ID. */
/* Set PnP card ID and EEPROM file name. */
switch (dev->type) {
case CRYSTAL_CS4236B:
dev->nvr_path = "cs4236b.nvr";
break;
case CRYSTAL_CS4237B:
dev->eeprom_data[26] = 0x37;
dev->nvr_path = "cs4237b.nvr";
break;
case CRYSTAL_CS4238B:
dev->eeprom_data[26] = 0x38;
dev->nvr_path = "cs4238b.nvr";
break;
}
/* Load EEPROM contents from file if present. */
cs423x_nvram(dev, 0);
/* Initialize game port. The '7B and '8B game port only responds to 6 I/O ports; the remaining
2 ports are reserved on those chips, and probably connected to the Digital Assist feature. */
dev->gameport = gameport_add((dev->type == CRYSTAL_CS4236B) ? &gameport_pnp_device : &gameport_pnp_6io_device);
@@ -759,7 +781,7 @@ cs423x_init(const device_t *info)
/* Initialize SBPro codec first to get the correct CD audio filter for the default
context, which is SBPro. The WSS codec is initialized later by cs423x_reset */
dev->sb = (sb_t *) device_add(&sb_pro_cs423x_device);
dev->sb = device_add(&sb_pro_compat_device);
/* Initialize RAM, registers and WSS codec. */
cs423x_reset(dev);
@@ -774,8 +796,11 @@ cs423x_close(void *priv)
{
cs423x_t *dev = (cs423x_t *) priv;
if (dev->eeprom)
/* Save EEPROM contents to file. */
if (dev->eeprom) {
cs423x_nvram(dev, 1);
i2c_eeprom_close(dev->eeprom);
}
i2c_gpio_close(dev->i2c);

View File

@@ -38,6 +38,8 @@
#include <86box/filters.h>
#include <86box/isapnp.h>
#include <86box/snd_sb.h>
#include <86box/hdc.h>
#include <86box/hdc_ide.h>
/* 0 to 7 -> -14dB to 0dB i 2dB steps. 8 to 15 -> 0 to +14dB in 2dB steps.
@@ -73,24 +75,81 @@ static const int sb_pro_mcv_irqs[4] = {7, 5, 3, 3};
/* Each card in the SB16 family has a million variants, and it shows in the large variety of device IDs for the PnP models.
These ROMs were reconstructed in a best-effort basis, around what Linux pnpdump configs and kernel logs could be found
in mailing lists, forums and other places, as well as Linux's own SB PnP card tables for ALSA and OSS. */
This ROM was reconstructed in a best-effort basis around a pnpdump output log found in a forum. */
static uint8_t sb_16_pnp_rom[] = {
0x0e, 0x8c, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, /* CTL0028, dummy checksum (filled in by isapnp_add_card) */
0x0e, 0x8c, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, /* CTL0024, dummy checksum (filled in by isapnp_add_card) */
0x0a, 0x10, 0x10, /* PnP version 1.0, vendor version 1.0 */
0x82, 0x11, 0x00, 'C', 'r', 'e', 'a', 't', 'i', 'v', 'e', ' ', 'S', 'B', '1', '6', ' ', 'P', 'n', 'P', /* ANSI identifier */
0x15, 0x0e, 0x8c, 0x00, 0x31, 0x00, /* logical device CTL0031 */
0x16, 0x0e, 0x8c, 0x00, 0x31, 0x00, 0x65, /* logical device CTL0031, supports vendor-specific registers 0x39/0x3A/0x3D/0x3F */
0x82, 0x05, 0x00, 'A', 'u', 'd', 'i', 'o', /* ANSI identifier */
0x30, /* start dependent functions, acceptable */
0x31, 0x00, /* start dependent functions, preferred */
0x22, 0x20, 0x00, /* IRQ 5 */
0x2a, 0x02, 0x08, /* DMA 1, compatibility, no count by word, count by byte, not bus master, 8-bit only */
0x2a, 0x20, 0x12, /* DMA 5, compatibility, count by word, no count by byte, not bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x20, 0x02, 0x01, 0x10, /* I/O 0x220, decodes 16-bit, 1-byte alignment, 16 addresses */
0x47, 0x01, 0x30, 0x03, 0x30, 0x03, 0x01, 0x02, /* I/O 0x330, decodes 16-bit, 1-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0x88, 0x03, 0x01, 0x04, /* I/O 0x388, decodes 16-bit, 1-byte alignment, 4 addresses */
0x31, 0x01, /* start dependent functions, acceptable */
0x22, 0xa0, 0x04, /* IRQ 5/7/10 */
0x2a, 0x0b, 0x08, /* DMA 0/1/3, compatibility, no count by word, count by byte, not bus master, 8-bit only */
0x2a, 0xe0, 0x16, /* DMA 5/6/7, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x2a, 0xe0, 0x12, /* DMA 5/6/7, compatibility, count by word, no count by byte, not bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0x88, 0x03, 0x01, 0x04, /* I/O 0x388, decodes 16-bit, 1-byte alignment, 4 addresses */
0x31, 0x01, /* start dependent functions, acceptable */
0x22, 0xa0, 0x04, /* IRQ 5/7/10 */
0x2a, 0x0b, 0x08, /* DMA 0/1/3, compatibility, no count by word, count by byte, not bus master, 8-bit only */
0x2a, 0xe0, 0x12, /* DMA 5/6/7, compatibility, count by word, no count by byte, not bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x31, 0x02, /* start dependent functions, functional */
0x22, 0xa0, 0x04, /* IRQ 5/7/10 */
0x2a, 0x0b, 0x08, /* DMA 0/1/3, compatibility, no count by word, count by byte, not bus master, 8-bit only */
0x2a, 0xe0, 0x12, /* DMA 5/6/7, compatibility, count by word, no count by byte, not bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x31, 0x02, /* start dependent functions, functional */
0x22, 0xa0, 0x04, /* IRQ 5/7/10 */
0x2a, 0x0b, 0x08, /* DMA 0/1/3, compatibility, no count by word, count by byte, not bus master, 8-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0x88, 0x03, 0x01, 0x04, /* I/O 0x388, decodes 16-bit, 1-byte alignment, 4 addresses */
0x31, 0x02, /* start dependent functions, functional */
0x22, 0xa0, 0x04, /* IRQ 5/7/10 */
0x2a, 0x0b, 0x08, /* DMA 0/1/3, compatibility, no count by word, count by byte, not bus master, 8-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x31, 0x02, /* start dependent functions, functional */
0x22, 0xa0, 0x04, /* IRQ 5/7/10 */
0x2a, 0x0b, 0x08, /* DMA 0/1/3, compatibility, no count by word, count by byte, not bus master, 8-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x38, /* end dependent functions */
0x16, 0x0e, 0x8c, 0x20, 0x11, 0x00, 0x5a, /* logical device CTL2011, supports vendor-specific registers 0x39/0x3B/0x3C/0x3E */
0x1c, 0x41, 0xd0, 0x06, 0x00, /* compatible device PNP0600 */
0x82, 0x03, 0x00, 'I', 'D', 'E', /* ANSI identifier */
0x31, 0x00, /* start dependent functions, preferred */
0x22, 0x00, 0x04, /* IRQ 10 */
0x47, 0x01, 0x68, 0x01, 0x68, 0x01, 0x01, 0x08, /* I/O 0x168, decodes 16-bit, 1-byte alignment, 8 addresses */
0x47, 0x01, 0x6e, 0x03, 0x6e, 0x03, 0x01, 0x02, /* I/O 0x36E, decodes 16-bit, 1-byte alignment, 2 addresses */
0x31, 0x01, /* start dependent functions, acceptable */
0x22, 0x00, 0x08, /* IRQ 11 */
0x47, 0x01, 0xe8, 0x01, 0xe8, 0x01, 0x01, 0x08, /* I/O 0x1E8, decodes 16-bit, 1-byte alignment, 8 addresses */
0x47, 0x01, 0xee, 0x03, 0xee, 0x03, 0x01, 0x02, /* I/O 0x3EE, decodes 16-bit, 1-byte alignment, 2 addresses */
0x31, 0x01, /* start dependent functions, acceptable */
0x22, 0x00, 0x8c, /* IRQ 10/11/15 */
0x47, 0x01, 0x00, 0x01, 0xf8, 0x01, 0x08, 0x08, /* I/O 0x100-0x1F8, decodes 16-bit, 8-byte alignment, 8 addresses */
0x47, 0x01, 0x00, 0x03, 0xfe, 0x03, 0x02, 0x02, /* I/O 0x300-0x3FE, decodes 16-bit, 2-byte alignment, 2 addresses */
0x31, 0x02, /* start dependent functions, functional */
0x22, 0x00, 0x80, /* IRQ 15 */
0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x01, 0x08, /* I/O 0x170, decodes 16-bit, 1-byte alignment, 8 addresses */
0x47, 0x01, 0x76, 0x03, 0x76, 0x03, 0x01, 0x02, /* I/O 0x376, decodes 16-bit, 1-byte alignment, 1 addresses */
0x38, /* end dependent functions */
0x16, 0x41, 0xd0, 0xff, 0xff, 0x00, 0xda, /* logical device PNPFFFF, supports vendor-specific registers 0x38/0x39/0x3B/0x3C/0x3E */
0x82, 0x08, 0x00, 'R', 'e', 's', 'e', 'r', 'v', 'e', 'd', /* ANSI identifier */
0x47, 0x01, 0x00, 0x01, 0xf8, 0x03, 0x08, 0x01, /* I/O 0x100-0x3F8, decodes 16-bit, 8-byte alignment, 1 address */
0x15, 0x0e, 0x8c, 0x70, 0x01, 0x00, /* logical device CTL7001 */
0x1c, 0x41, 0xd0, 0xb0, 0x2f, /* compatible device PNPB02F */
0x82, 0x04, 0x00, 'G', 'a', 'm', 'e', /* ANSI identifier */
@@ -98,227 +157,6 @@ static uint8_t sb_16_pnp_rom[] = {
0x79, 0x00 /* end tag, dummy checksum (filled in by isapnp_add_card) */
};
static uint8_t sb_32_pnp_rom[] = {
0x0e, 0x8c, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, /* CTL0048, dummy checksum (filled in by isapnp_add_card) */
0x0a, 0x10, 0x10, /* PnP version 1.0, vendor version 1.0 */
0x82, 0x11, 0x00, 'C', 'r', 'e', 'a', 't', 'i', 'v', 'e', ' ', 'S', 'B', '3', '2', ' ', 'P', 'n', 'P', /* ANSI identifier */
0x16, 0x0e, 0x8c, 0x00, 0x31, 0x00, 0xa9, /* logical device CTL0031, supports vendor-specific registers 0x38/0x3A/0x3C/0x3F */
0x82, 0x05, 0x00, 'A', 'u', 'd', 'i', 'o', /* ANSI identifier */
0x31, 0x00, /* start dependent functions, preferred */
0x22, 0x20, 0x00, /* IRQ 5 */
0x2a, 0x02, 0x0c, /* DMA 1, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0x20, 0x16, /* DMA 5, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x20, 0x02, 0x01, 0x10, /* I/O 0x220, decodes 16-bit, 1-byte alignment, 16 addresses */
0x47, 0x01, 0x30, 0x03, 0x30, 0x03, 0x01, 0x02, /* I/O 0x330, decodes 16-bit, 1-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0x88, 0x03, 0x01, 0x04, /* I/O 0x388, decodes 16-bit, 1-byte alignment, 4 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0xe0, 0x16, /* DMA 5/6/7, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0x88, 0x03, 0x01, 0x04, /* I/O 0x388, decodes 16-bit, 1-byte alignment, 4 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0xe0, 0x16, /* DMA 5/6/7, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0xe0, 0x16, /* DMA 5/6/7, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0x88, 0x03, 0x01, 0x04, /* I/O 0x388, decodes 16-bit, 1-byte alignment, 4 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0xe0, 0x16, /* DMA 5/6/7, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0x94, 0x03, 0x04, 0x04, /* I/O 0x388-0x394, decodes 16-bit, 4-byte alignment, 4 addresses */
0x38, /* end dependent functions */
0x16, 0x0e, 0x8c, 0x00, 0x21, 0x00, 0xa9, /* logical device CTL0021, supports vendor-specific registers 0x38/0x3A/0x3C/0x3F */
0x82, 0x09, 0x00, 'W', 'a', 'v', 'e', 'T', 'a', 'b', 'l', 'e', /* ANSI identifier */
0x31, 0x00, /* start dependent functions, preferred */
0x47, 0x01, 0x20, 0x06, 0x20, 0x06, 0x01, 0x04, /* I/O 0x620, decodes 16-bit, 1-byte alignment, 4 addresses */
0x30, /* start dependent functions, acceptable */
0x47, 0x01, 0x20, 0x06, 0x80, 0x06, 0x20, 0x04, /* I/O 0x620-0x680, decodes 16-bit, 32-byte alignment, 4 addresses */
0x38, /* end dependent functions */
0x15, 0x0e, 0x8c, 0x70, 0x01, 0x00, /* logical device CTL7001 */
0x1c, 0x41, 0xd0, 0xb0, 0x2f, /* compatible device PNPB02F */
0x82, 0x04, 0x00, 'G', 'a', 'm', 'e', /* ANSI identifier */
0x47, 0x01, 0x00, 0x02, 0x00, 0x02, 0x01, 0x08, /* I/O 0x200, decodes 16-bit, 1-byte alignment, 8 addresses */
0x79, 0x00 /* end tag, dummy checksum (filled in by isapnp_add_card) */
};
static uint8_t sb_awe32_pnp_rom[] = {
0x0e, 0x8c, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, /* CTL0043, dummy checksum (filled in by isapnp_add_card) */
0x0a, 0x10, 0x10, /* PnP version 1.0, vendor version 1.0 */
0x82, 0x15, 0x00, 'C', 'r', 'e', 'a', 't', 'i', 'v', 'e', ' ', 'S', 'B', ' ', 'A', 'W', 'E', '3', '2', ' ', 'P', 'n', 'P', /* ANSI identifier */
0x16, 0x0e, 0x8c, 0x00, 0x31, 0x00, 0xa9, /* logical device CTL0031, supports vendor-specific registers 0x38/0x3A/0x3C/0x3F */
0x82, 0x05, 0x00, 'A', 'u', 'd', 'i', 'o', /* ANSI identifier */
0x31, 0x00, /* start dependent functions, preferred */
0x22, 0x20, 0x00, /* IRQ 5 */
0x2a, 0x02, 0x0c, /* DMA 1, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0x20, 0x16, /* DMA 5, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x20, 0x02, 0x01, 0x10, /* I/O 0x220, decodes 16-bit, 1-byte alignment, 16 addresses */
0x47, 0x01, 0x30, 0x03, 0x30, 0x03, 0x01, 0x02, /* I/O 0x330, decodes 16-bit, 1-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0xf8, 0x03, 0x01, 0x04, /* I/O 0x388-0x3F8, decodes 16-bit, 1-byte alignment, 4 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0xe0, 0x16, /* DMA 5/6/7, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0x88, 0x03, 0x01, 0x04, /* I/O 0x388, decodes 16-bit, 1-byte alignment, 4 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0xe0, 0x16, /* DMA 5/6/7, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0xe0, 0x16, /* DMA 5/6/7, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0x88, 0x03, 0x01, 0x04, /* I/O 0x388, decodes 16-bit, 1-byte alignment, 4 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x31, 0x02, /* start dependent functions, sub-optimal */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0xe0, 0x16, /* DMA 5/6/7, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0x94, 0x03, 0x04, 0x04, /* I/O 0x388-0x394, decodes 16-bit, 4-byte alignment, 4 addresses */
0x38, /* end dependent functions */
0x16, 0x0e, 0x8c, 0x00, 0x21, 0x00, 0xa9, /* logical device CTL0021, supports vendor-specific registers 0x38/0x3A/0x3C/0x3F */
0x82, 0x09, 0x00, 'W', 'a', 'v', 'e', 'T', 'a', 'b', 'l', 'e', /* ANSI identifier */
0x31, 0x00, /* start dependent functions, preferred */
0x47, 0x01, 0x20, 0x06, 0x20, 0x06, 0x01, 0x04, /* I/O 0x620, decodes 16-bit, 1-byte alignment, 4 addresses */
0x47, 0x01, 0x20, 0x0a, 0x20, 0x0a, 0x01, 0x04, /* I/O 0xA20, decodes 16-bit, 1-byte alignment, 4 addresses */
0x47, 0x01, 0x20, 0x0e, 0x20, 0x0e, 0x01, 0x04, /* I/O 0xE20, decodes 16-bit, 1-byte alignment, 4 addresses */
0x30, /* start dependent functions, acceptable */
0x47, 0x01, 0x20, 0x06, 0x80, 0x06, 0x20, 0x04, /* I/O 0x620-0x680, decodes 16-bit, 32-byte alignment, 4 addresses */
0x47, 0x01, 0x20, 0x0a, 0x80, 0x0a, 0x20, 0x04, /* I/O 0xA20-0xA80, decodes 16-bit, 32-byte alignment, 4 addresses */
0x47, 0x01, 0x20, 0x0e, 0x80, 0x0e, 0x20, 0x04, /* I/O 0xE20-0xE80, decodes 16-bit, 32-byte alignment, 4 addresses */
0x38, /* end dependent functions */
0x15, 0x0e, 0x8c, 0x70, 0x01, 0x00, /* logical device CTL7001 */
0x1c, 0x41, 0xd0, 0xb0, 0x2f, /* compatible device PNPB02F */
0x82, 0x04, 0x00, 'G', 'a', 'm', 'e', /* ANSI identifier */
0x47, 0x01, 0x00, 0x02, 0x00, 0x02, 0x01, 0x08, /* I/O 0x200, decodes 16-bit, 1-byte alignment, 8 addresses */
0x79, 0x00 /* end tag, dummy checksum (filled in by isapnp_add_card) */
};
static uint8_t sb_awe64_gold_pnp_rom[] = {
0x0e, 0x8c, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, /* CTL009E, dummy checksum (filled in by isapnp_add_card) */
0x0a, 0x10, 0x20, /* PnP version 1.0, vendor version 2.0 */
0x82, 0x16, 0x00, 'C', 'r', 'e', 'a', 't', 'i', 'v', 'e', ' ', 'S', 'B', ' ', 'A', 'W', 'E', '6', '4', ' ', 'G', 'o', 'l', 'd', /* ANSI identifier */
0x16, 0x0e, 0x8c, 0x00, 0x44, 0x00, 0xa9, /* logical device CTL0044, supports vendor-specific registers 0x38/0x3A/0x3C/0x3F */
0x82, 0x05, 0x00, 'A', 'u', 'd', 'i', 'o', /* ANSI identifier */
0x31, 0x00, /* start dependent functions, preferred */
0x22, 0x20, 0x00, /* IRQ 5 */
0x2a, 0x02, 0x0c, /* DMA 1, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0x20, 0x16, /* DMA 5, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x20, 0x02, 0x01, 0x10, /* I/O 0x220, decodes 16-bit, 1-byte alignment, 16 addresses */
0x47, 0x01, 0x30, 0x03, 0x30, 0x03, 0x01, 0x02, /* I/O 0x330, decodes 16-bit, 1-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0xf8, 0x03, 0x01, 0x04, /* I/O 0x388-0x3F8, decodes 16-bit, 1-byte alignment, 4 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0xe0, 0x16, /* DMA 5/6/7, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0xf8, 0x03, 0x01, 0x04, /* I/O 0x388-0x3F8, decodes 16-bit, 1-byte alignment, 4 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0xe0, 0x16, /* DMA 5/6/7, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0xe0, 0x16, /* DMA 5/6/7, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0xf8, 0x03, 0x01, 0x04, /* I/O 0x388-0x3F8, decodes 16-bit, 1-byte alignment, 4 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x30, /* start dependent functions, acceptable */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x31, 0x02, /* start dependent functions, sub-optimal */
0x22, 0xa0, 0x06, /* IRQ 5/7/9/10 */
0x2a, 0x0b, 0x0c, /* DMA 0/1/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */
0x2a, 0xe0, 0x16, /* DMA 5/6/7, compatibility, count by word, no count by byte, is bus master, 16-bit only */
0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */
0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */
0x47, 0x01, 0x88, 0x03, 0x94, 0x03, 0x04, 0x04, /* I/O 0x388-0x394, decodes 16-bit, 4-byte alignment, 4 addresses */
0x38, /* end dependent functions */
0x15, 0x0e, 0x8c, 0x70, 0x02, 0x00, /* logical device CTL7002 */
0x1c, 0x41, 0xd0, 0xb0, 0x2f, /* compatible device PNPB02F */
0x82, 0x04, 0x00, 'G', 'a', 'm', 'e', /* ANSI identifier */
0x47, 0x01, 0x00, 0x02, 0x00, 0x02, 0x01, 0x08, /* I/O 0x200, decodes 16-bit, 1-byte alignment, 8 addresses */
0x16, 0x0e, 0x8c, 0x00, 0x23, 0x00, 0xa9, /* logical device CTL0023, supports vendor-specific registers 0x38/0x3A/0x3C/0x3F */
0x82, 0x09, 0x00, 'W', 'a', 'v', 'e', 'T', 'a', 'b', 'l', 'e', /* ANSI identifier */
0x31, 0x00, /* start dependent functions, preferred */
0x47, 0x01, 0x20, 0x06, 0x20, 0x06, 0x01, 0x04, /* I/O 0x620, decodes 16-bit, 1-byte alignment, 4 addresses */
0x47, 0x01, 0x20, 0x0a, 0x20, 0x0a, 0x01, 0x04, /* I/O 0xA20, decodes 16-bit, 1-byte alignment, 4 addresses */
0x47, 0x01, 0x20, 0x0e, 0x20, 0x0e, 0x01, 0x04, /* I/O 0xE20, decodes 16-bit, 1-byte alignment, 4 addresses */
0x30, /* start dependent functions, acceptable */
0x47, 0x01, 0x20, 0x06, 0x80, 0x06, 0x20, 0x04, /* I/O 0x620-0x680, decodes 16-bit, 32-byte alignment, 4 addresses */
0x47, 0x01, 0x20, 0x0a, 0x80, 0x0a, 0x20, 0x04, /* I/O 0xA20-0xA80, decodes 16-bit, 32-byte alignment, 4 addresses */
0x47, 0x01, 0x20, 0x0e, 0x80, 0x0e, 0x20, 0x04, /* I/O 0xE20-0xE80, decodes 16-bit, 32-byte alignment, 4 addresses */
0x38, /* end dependent functions */
0x79, 0x00 /* end tag, dummy checksum (filled in by isapnp_add_card) */
};
#ifdef ENABLE_SB_LOG
@@ -1348,9 +1186,21 @@ sb_16_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv)
break;
case 1: /* Game */
case 1: /* IDE */
ide_pnp_config_changed(0, config, (void *) 2);
break;
case 2: /* Reserved (16) / WaveTable (32+) */
if (sb->dsp.sb_type > SB16)
emu8k_change_addr(&sb->emu8k, (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) ? config->io[0].base : 0);
break;
case 3: /* Game */
gameport_remap(sb->gameport, (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) ? config->io[0].base : 0);
break;
case 4: /* StereoEnhance (32) */
break;
}
}
@@ -1362,15 +1212,13 @@ sb_awe32_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *pr
switch (ld) {
case 0: /* Audio */
sb_16_pnp_config_changed(0, config, sb);
break;
case 1: /* WaveTable */
emu8k_change_addr(&sb->emu8k, (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) ? config->io[0].base : 0);
case 1: /* IDE */
sb_16_pnp_config_changed(ld, config, sb);
break;
case 2: /* Game */
sb_16_pnp_config_changed(1, config, sb);
case 3: /* WaveTable */
sb_16_pnp_config_changed(ld ^ 1, config, sb);
break;
}
}
@@ -1383,15 +1231,12 @@ sb_awe64_gold_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, voi
switch (ld) {
case 0: /* Audio */
sb_16_pnp_config_changed(0, config, sb);
case 2: /* WaveTable */
sb_16_pnp_config_changed(ld, config, sb);
break;
case 1: /* Game */
sb_16_pnp_config_changed(1, config, sb);
break;
case 2: /* WaveTable */
emu8k_change_addr(&sb->emu8k, (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) ? config->io[0].base : 0);
sb_16_pnp_config_changed(3, config, sb);
break;
}
}
@@ -1721,12 +1566,11 @@ sb_pro_mcv_init(const device_t *info)
static void *
sb_pro_cs423x_init(const device_t *info)
sb_pro_compat_init(const device_t *info)
{
sb_t *sb = malloc(sizeof(sb_t));
memset(sb, 0, sizeof(sb_t));
sb->opl_enabled = 0; /* updated by cs423x code */
opl3_init(&sb->opl);
sb_dsp_init(&sb->dsp, SBPRO2, SB_SUBTYPE_DEFAULT, sb);
@@ -1821,6 +1665,8 @@ sb_16_pnp_init(const device_t *info)
sb->gameport = gameport_add(&gameport_pnp_device);
device_add(&ide_ter_pnp_device);
isapnp_add_card(sb_16_pnp_rom, sizeof(sb_16_pnp_rom), sb_16_pnp_config_changed, NULL, NULL, NULL, sb);
return sb;
@@ -1834,6 +1680,27 @@ sb_awe32_available()
}
static int
sb_32_pnp_available()
{
return sb_awe32_available() && rom_present("roms/sound/CT3600 PnP.BIN");
}
static int
sb_awe32_pnp_available()
{
return sb_awe32_available() && rom_present("roms/sound/CT3980 PnP.BIN");
}
static int
sb_awe64_gold_available()
{
return sb_awe32_available() && rom_present("roms/sound/CT4540 PnP.BIN");
}
static void *
sb_awe32_init(const device_t *info)
{
@@ -1899,7 +1766,7 @@ sb_awe32_pnp_init(const device_t *info)
sb->opl_enabled = 1;
opl3_init(&sb->opl);
sb_dsp_init(&sb->dsp, ((info->local == 2) ? SBAWE64 : SBAWE32), SB_SUBTYPE_DEFAULT, sb);
sb_dsp_init(&sb->dsp, (info->local == 2) ? SBAWE64 : SBAWE32, SB_SUBTYPE_DEFAULT, sb);
sb_ct1745_mixer_reset(sb);
sb->mixer_enabled = 1;
@@ -1918,12 +1785,47 @@ sb_awe32_pnp_init(const device_t *info)
sb->gameport = gameport_add(&gameport_pnp_device);
if (info->local == 2)
isapnp_add_card(sb_awe64_gold_pnp_rom, sizeof(sb_awe64_gold_pnp_rom), sb_awe64_gold_pnp_config_changed, NULL, NULL, NULL, sb);
else if (info->local == 1)
isapnp_add_card(sb_32_pnp_rom, sizeof(sb_32_pnp_rom), sb_awe32_pnp_config_changed, NULL, NULL, NULL, sb);
else
isapnp_add_card(sb_awe32_pnp_rom, sizeof(sb_awe32_pnp_rom), sb_awe32_pnp_config_changed, NULL, NULL, NULL, sb);
if (info->local != 2)
device_add(&ide_ter_pnp_device);
char *pnp_rom_file = NULL;
switch (info->local) {
case 0:
pnp_rom_file = "roms/sound/CT3600 PnP.BIN";
break;
case 1:
pnp_rom_file = "roms/sound/CT3980 PnP.BIN";
break;
case 2:
pnp_rom_file = "roms/sound/CT4540 PnP.BIN";
break;
}
uint8_t *pnp_rom = NULL;
if (pnp_rom_file) {
FILE *f = rom_fopen(pnp_rom_file, "rb");
if (f) {
if (fread(sb->pnp_rom, 1, 512, f) == 512)
pnp_rom = sb->pnp_rom;
fclose(f);
}
}
switch (info->local) {
case 0:
isapnp_add_card(pnp_rom, sizeof(sb->pnp_rom), sb_16_pnp_config_changed, NULL, NULL, NULL, sb);
break;
case 1:
isapnp_add_card(pnp_rom, sizeof(sb->pnp_rom), sb_awe32_pnp_config_changed, NULL, NULL, NULL, sb);
break;
case 2:
isapnp_add_card(pnp_rom, sizeof(sb->pnp_rom), sb_awe64_gold_pnp_config_changed, NULL, NULL, NULL, sb);
break;
}
return sb;
}
@@ -2665,12 +2567,12 @@ const device_t sb_pro_mcv_device =
NULL
};
const device_t sb_pro_cs423x_device =
const device_t sb_pro_compat_device =
{
"Crystal CS423x Sound Blaster Pro compatibility",
"Sound Blaster Pro (Compatibility)",
DEVICE_ISA | DEVICE_AT,
0,
sb_pro_cs423x_init, sb_close, NULL, { NULL },
sb_pro_compat_init, sb_close, NULL, { NULL },
sb_speed_changed,
NULL,
NULL
@@ -2702,9 +2604,9 @@ const device_t sb_32_pnp_device =
{
"Sound Blaster 32 PnP",
DEVICE_ISA | DEVICE_AT,
1,
0,
sb_awe32_pnp_init, sb_awe32_close, NULL,
{ sb_awe32_available },
{ sb_32_pnp_available },
sb_speed_changed,
NULL,
sb_32_pnp_config
@@ -2727,9 +2629,9 @@ const device_t sb_awe32_pnp_device =
{
"Sound Blaster AWE32 PnP",
DEVICE_ISA | DEVICE_AT,
0,
1,
sb_awe32_pnp_init, sb_awe32_close, NULL,
{ sb_awe32_available },
{ sb_awe32_pnp_available },
sb_speed_changed,
NULL,
sb_awe32_pnp_config
@@ -2741,7 +2643,7 @@ const device_t sb_awe64_gold_device =
DEVICE_ISA | DEVICE_AT,
2,
sb_awe32_pnp_init, sb_awe32_close, NULL,
{ sb_awe32_available },
{ sb_awe64_gold_available },
sb_speed_changed,
NULL,
sb_awe64_gold_config

View File

@@ -37,6 +37,7 @@
#include <86box/snd_mpu401.h>
#include <86box/snd_sb_dsp.h>
#include <86box/snd_azt2316a.h>
#include <86box/snd_ac97.h>
#include <86box/filters.h>
@@ -109,6 +110,7 @@ static const SOUND_CARD sound_cards[] =
{ "sbmcv", &sb_mcv_device },
{ "sbpromcv", &sb_pro_mcv_device },
{ "es1371", &es1371_device },
{ "cs4297a", &cs4297a_device },
{ "", NULL }
};

View File

@@ -724,6 +724,7 @@ SNDOBJ := sound.o \
snd_pssj.o \
snd_lpt_dac.o snd_lpt_dss.o \
snd_adlib.o snd_adlibgold.o snd_ad1848.o snd_audiopci.o \
snd_ac97_codec.o snd_ac97_via.o \
snd_azt2316a.o snd_cs423x.o \
snd_cms.o \
snd_gus.o \