diff --git a/src/include/86box/snd_ad1848.h b/src/include/86box/snd_ad1848.h index ac1b1a85f..3e61deea7 100644 --- a/src/include/86box/snd_ad1848.h +++ b/src/include/86box/snd_ad1848.h @@ -12,35 +12,40 @@ * * Authors: Sarah Walker, * TheCollector1995, + * RichardG, * * Copyright 2008-2020 Sarah Walker. * Copyright 2018-2020 TheCollector1995. + * Copyright 2021 RichardG. */ -#define AD1848_TYPE_DEFAULT 0 -#define AD1848_TYPE_CS4248 1 -#define AD1848_TYPE_CS4231 2 -#define AD1848_TYPE_CS4236 3 +enum { + AD1848_TYPE_DEFAULT = 0, + AD1848_TYPE_CS4248, + AD1848_TYPE_CS4231, + AD1848_TYPE_CS4236 +}; typedef struct { - int index, xindex; - uint8_t regs[32], xregs[32], status; /* 16 original registers + 16 CS4231A extensions + 32 CS4236 extensions */ - - int trd, mce, count, wten; + uint8_t type, index, xindex, regs[32], xregs[32], status; /* 16 original registers + 16 CS4231A extensions + 32 CS4236 extensions */ + + int count; + uint8_t trd, mce, wten: 1; int16_t out_l, out_r; double cd_vol_l, cd_vol_r; int fm_vol_l, fm_vol_r; uint8_t fmt_mask, wave_vol_mask; - int enable, irq, dma, freq; + uint8_t enable: 1, irq: 4, dma: 3; + int freq; pc_timer_t timer_count; uint64_t timer_latch; int16_t buffer[SOUNDBUFLEN * 2]; - int pos, type; + int pos; } ad1848_t; @@ -55,4 +60,4 @@ extern void ad1848_update(ad1848_t *ad1848); extern void ad1848_speed_changed(ad1848_t *ad1848); extern void ad1848_filter_cd_audio(int channel, double *buffer, void *priv); -extern void ad1848_init(ad1848_t *ad1848, int type); +extern void ad1848_init(ad1848_t *ad1848, uint8_t type); diff --git a/src/sound/snd_ad1848.c b/src/sound/snd_ad1848.c index 3b452a607..176da40da 100644 --- a/src/sound/snd_ad1848.c +++ b/src/sound/snd_ad1848.c @@ -12,9 +12,11 @@ * * Authors: Sarah Walker, * TheCollector1995, + * RichardG, * * Copyright 2008-2020 Sarah Walker. * Copyright 2018-2020 TheCollector1995. + * Copyright 2021 RichardG. */ #include #include @@ -35,8 +37,6 @@ static int ad1848_vols_7bits[128]; static double ad1848_vols_5bits_aux_gain[32]; -static double ad1848_vols_7bits_debug[128]; -static double ad1848_vols_5bits_debug[32]; void @@ -136,9 +136,9 @@ ad1848_read(uint16_t addr, void *priv) case 18: case 19: if (ad1848->type == AD1848_TYPE_CS4236) { if (ad1848->xregs[4] & 0x04) /* FM remapping */ - return ad1848->xregs[ad1848->index - 12]; /* real FM volume on registers 6 and 7 */ + ret = ad1848->xregs[ad1848->index - 12]; /* real FM volume on registers 6 and 7 */ else if (ad1848->xregs[4] & 0x08) /* wavetable remapping */ - return ad1848->xregs[ad1848->index - 2]; /* real wavetable volume on registers 16 and 17 */ + ret = ad1848->xregs[ad1848->index - 2]; /* real wavetable volume on registers 16 and 17 */ } break; @@ -166,7 +166,7 @@ void ad1848_write(uint16_t addr, uint8_t val, void *priv) { ad1848_t *ad1848 = (ad1848_t *) priv; - uint8_t temp, updatefreq = 0; + uint8_t temp = 0, updatefreq = 0; switch (addr & 3) { case 0: /* Index */ @@ -224,14 +224,43 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) case 18: case 19: if (ad1848->type == AD1848_TYPE_CS4236) { - if (ad1848->xregs[4] & 0x04) { /* FM remapping */ - temp = val; - val = ad1848->xregs[ad1848->index - 18]; /* real line volume on registers 0 and 1 */ - ad1848->xregs[ad1848->index - 12] = temp; /* real FM volume on registers 6 and 7 */ - } else if (ad1848->xregs[4] & 0x08) { /* wavetable remapping */ - temp = val; - val = ad1848->xregs[ad1848->index - 18]; /* real line volume on registers 0 and 1 */ - ad1848->xregs[ad1848->index - 2] = temp; /* real wavetable volume on registers 16 and 17 */ + if ((ad1848->xregs[4] & 0x14) == 0x14) { /* FM remapping */ + ad1848->xregs[ad1848->index - 12] = val; /* real FM volume on extended registers 6 and 7 */ + temp = 1; + + if (ad1848->index == 18) { + if (val & 0x80) + ad1848->fm_vol_l = 0; + else + ad1848->fm_vol_l = ad1848_vols_7bits[val & 0x3f]; + } else { + if (val & 0x80) + ad1848->fm_vol_r = 0; + else + ad1848->fm_vol_r = ad1848_vols_7bits[val & 0x3f]; + } + } + if (ad1848->wten && !(ad1848->xregs[4] & 0x08)) { /* wavetable remapping */ + ad1848->xregs[ad1848->index - 2] = val; /* real wavetable volume on extended registers 16 and 17 */ + temp = 1; + } + + /* Stop here if any remapping is enabled. */ + if (temp) + return; + + /* HACK: the Windows 9x driver's "Synth" control writes to this + register with no remapping, even if internal FM is enabled. */ + if (ad1848->index == 18) { + if (val & 0x80) + ad1848->fm_vol_l = 0; + else + ad1848->fm_vol_l = (int) ad1848_vols_5bits_aux_gain[val & 0x1f]; + } else { + if (val & 0x80) + ad1848->fm_vol_r = 0; + else + ad1848->fm_vol_r = (int) ad1848_vols_5bits_aux_gain[val & 0x1f]; } } break; @@ -243,9 +272,10 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) case 23: if ((ad1848->type == AD1848_TYPE_CS4236) && ((ad1848->regs[12] & 0x60) == 0x60)) { if (!(ad1848->regs[23] & 0x08)) { /* existing (not new) XRAE is clear */ - ad1848->xindex = (((val & 0x04) << 2) | (val >> 4)) & 0x1f; + ad1848->xindex = ((val & 0x04) << 2) | (val >> 4); break; } + switch (ad1848->xindex) { case 0: case 1: /* remapped line volume */ ad1848->regs[18 + ad1848->xindex] = val; @@ -282,8 +312,11 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) break; case 24: - if (!(val & 0x70)) + val = ad1848->regs[24] & ((val & 0x70) | 0x0f); + if (!(val & 0x70)) { ad1848->status &= 0xfe; + picintc(1 << ad1848->irq); + } break; case 25: @@ -310,6 +343,7 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) case 2: ad1848->status &= 0xfe; + ad1848->regs[24] &= 0x0f; break; } } @@ -391,14 +425,15 @@ ad1848_poll(void *priv) ad1848->out_r = 0; else ad1848->out_r = (ad1848->out_r * ad1848_vols_7bits[ad1848->regs[7] & ad1848->wave_vol_mask]) >> 16; - + if (ad1848->count < 0) { ad1848->count = ad1848->regs[15] | (ad1848->regs[14] << 8); if (!(ad1848->status & 0x01)) { ad1848->status |= 0x01; + ad1848->regs[24] |= 0x10; if (ad1848->regs[10] & 2) picint(1 << ad1848->irq); - } + } } ad1848->count--; @@ -422,11 +457,11 @@ ad1848_filter_cd_audio(int channel, double *buffer, void *priv) void -ad1848_init(ad1848_t *ad1848, int type) +ad1848_init(ad1848_t *ad1848, uint8_t type) { - int c; + uint8_t c; double attenuation; - + ad1848->status = 0xcc; ad1848->index = ad1848->trd = 0; ad1848->mce = 0x40; @@ -477,8 +512,10 @@ ad1848_init(ad1848_t *ad1848, int type) ad1848->xregs[16] = ad1848->xregs[17] = 0; } + ad1848_updatefreq(ad1848); + ad1848->out_l = ad1848->out_r = 0; - ad1848->fm_vol_l = ad1848->fm_vol_r = 1; + ad1848->fm_vol_l = ad1848->fm_vol_r = 65536; ad1848_updatevolmask(ad1848); ad1848->fmt_mask = 0x70; @@ -494,7 +531,7 @@ ad1848_init(ad1848_t *ad1848, int type) if (c & 0x10) attenuation -= 24.0; if (c & 0x20) attenuation -= 48.0; } -ad1848_vols_7bits_debug[c] = attenuation; + attenuation = pow(10, attenuation / 10); ad1848_vols_7bits[c] = (int) (attenuation * 65536); @@ -507,7 +544,7 @@ ad1848_vols_7bits_debug[c] = attenuation; if (c & 0x04) attenuation -= 6.0; if (c & 0x08) attenuation -= 12.0; if (c & 0x10) attenuation -= 24.0; -ad1848_vols_5bits_debug[c] = attenuation; + attenuation = pow(10, attenuation / 10); ad1848_vols_5bits_aux_gain[c] = (attenuation * 65536); diff --git a/src/sound/snd_cs423x.c b/src/sound/snd_cs423x.c index b95d0618d..57bf083ca 100644 --- a/src/sound/snd_cs423x.c +++ b/src/sound/snd_cs423x.c @@ -42,10 +42,10 @@ enum { CRYSTAL_CS4238B = 0xc9 }; enum { - CRYSTAL_SLAM_NONE = 0, - CRYSTAL_SLAM_INDEX, - CRYSTAL_SLAM_BYTE1, - CRYSTAL_SLAM_BYTE2 + CRYSTAL_SLAM_NONE = 0, + CRYSTAL_SLAM_INDEX = 1, + CRYSTAL_SLAM_BYTE1 = 2, + CRYSTAL_SLAM_BYTE2 = 3 }; @@ -133,8 +133,7 @@ typedef struct cs423x_t void *i2c, *eeprom; uint16_t wss_base, opl_base, sb_base, ctrl_base, ram_addr, eeprom_size: 11; - uint8_t type, pnp_offset, regs[8], indirect_regs[16], eeprom_data[2048], ram_data[384], ram_dl; - int ad1848_type; + uint8_t type, ad1848_type, pnp_offset, regs[8], indirect_regs[16], eeprom_data[2048], ram_data[384], ram_dl; uint8_t pnp_enable: 1, key_pos: 5, slam_enable: 1, slam_state: 2, slam_ld, slam_reg; isapnp_device_config_t *slam_config; @@ -148,10 +147,10 @@ static void cs423x_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config static uint8_t -cs423x_read(uint16_t port, void *priv) +cs423x_read(uint16_t addr, void *priv) { cs423x_t *dev = (cs423x_t *) priv; - uint8_t reg = port & 7; + uint8_t reg = addr & 7; uint8_t ret = dev->regs[reg]; switch (reg) { @@ -165,6 +164,17 @@ cs423x_read(uint16_t port, void *priv) ret = dev->indirect_regs[dev->regs[3]]; break; + case 5: /* Control/RAM Access */ + /* Reading RAM is undocumented; the WDM driver does so. */ + if (dev->ram_dl) { + 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++; + } + break; + case 7: /* Global Status */ /* Context switching: take active context and interrupt flag, then clear interrupt flag. */ ret &= 0xc0; @@ -172,10 +182,12 @@ cs423x_read(uint16_t port, void *priv) if (dev->sb->mpu->state.irq_pending) /* MPU interrupt */ ret |= 0x08; - if (dev->ad1848.regs[10] & 2) /* WSS interrupt */ + if (dev->ad1848.status & 0x01) /* WSS interrupt */ ret |= 0x10; if (dev->sb->dsp.sb_irq8 || dev->sb->dsp.sb_irq16 || dev->sb->dsp.sb_irq401) /* SBPro interrupt */ ret |= 0x20; + + break; } return ret; @@ -183,10 +195,10 @@ cs423x_read(uint16_t port, void *priv) static void -cs423x_write(uint16_t port, uint8_t val, void *priv) +cs423x_write(uint16_t addr, uint8_t val, void *priv) { cs423x_t *dev = (cs423x_t *) priv; - uint8_t reg = port & 7; + uint8_t reg = addr & 0x07; switch (reg) { case 1: /* EEPROM Interface */ @@ -199,7 +211,7 @@ cs423x_write(uint16_t port, uint8_t val, void *priv) break; case 4: /* Control Indirect Data Register */ - switch (dev->regs[3] & 15) { + switch (dev->regs[3] & 0x0f) { case 0: /* WSS Master Control */ if (val & 0x80) ad1848_init(&dev->ad1848, dev->ad1848_type); @@ -211,28 +223,36 @@ cs423x_write(uint16_t port, uint8_t val, void *priv) case 9 ... 15: /* unspecified */ return; + case 2: /* 3D Space and {Center|Volume} */ + case 6: /* Upper Channel Status */ + if (dev->type < CRYSTAL_CS4237B) + return; + break; + case 3: /* 3D Enable */ + if (dev->type < CRYSTAL_CS4237B) + return; val &= 0xe0; break; case 4: /* Consumer Serial Port Enable */ + if (dev->type < CRYSTAL_CS4237B) + return; val &= 0xf0; break; case 5: /* Lower Channel Status */ + if (dev->type < CRYSTAL_CS4237B) + return; val &= 0xfe; break; case 8: /* CS9236 Wavetable Control */ val &= 0x0f; + cs423x_pnp_enable(dev, 0); /* update WTEN bit */ break; } dev->indirect_regs[dev->regs[3]] = val; - - if (dev->ad1848.wten ^ !!(dev->indirect_regs[8] & 0x08)) { - dev->ad1848.wten ^= 1; - ad1848_updatevolmask(&dev->ad1848); - } break; case 5: /* Control/RAM Access */ @@ -271,9 +291,7 @@ cs423x_write(uint16_t port, uint8_t val, void *priv) break; case 3: /* data */ - /* The only documented RAM region is 0x4000 (384 bytes in size), for - loading chip configuration and PnP resources without an EEPROM. */ - if ((dev->ram_addr >= 0x4000) && (dev->ram_addr < 0x4180)) + 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++; break; @@ -424,7 +442,7 @@ cs423x_slam_enable(cs423x_t *dev, uint8_t enable) { /* Disable SLAM. */ if (dev->slam_enable) { - dev->slam_state = CRYSTAL_SLAM_NONE; + dev->slam_state = CRYSTAL_SLAM_NONE; dev->slam_enable = 0; io_removehandler(0x279, 1, NULL, NULL, NULL, cs423x_slam_write, NULL, NULL, dev); } @@ -455,10 +473,10 @@ cs423x_ctxswitch_write(uint16_t addr, uint8_t val, void *priv) /* Flip context bit. */ dev->regs[7] ^= 0x80; - /* Switch the CD audio filter and OPL ownership. + /* Switch OPL ownership and CD audio filter. FIXME: not thread-safe: filter function TOCTTOU in sound_cd_thread! */ - sound_set_cd_audio_filter(NULL, NULL); dev->sb->opl_enabled = !(dev->regs[7] & 0x80); + sound_set_cd_audio_filter(NULL, NULL); if (dev->sb->opl_enabled) /* SBPro */ sound_set_cd_audio_filter(sbpro_filter_cd_audio, dev->sb); else /* WSS */ @@ -496,7 +514,7 @@ cs423x_get_buffer(int32_t *buffer, int len, void *priv) dev->ad1848.pos = 0; if (opl_wss) - dev->sb->opl.pos = 0; + dev->sb->opl.pos = 0; } @@ -515,6 +533,21 @@ cs423x_pnp_enable(cs423x_t *dev, uint8_t update_rom) /* Update PnP state. */ isapnp_enable_card(dev->pnp_card, enable); + + /* While we're here, update FM and wavetable enable bits based on the config data in RAM. */ + if (dev->ram_data[3] & 0x08) { + dev->indirect_regs[8] |= 0x08; + dev->ad1848.wten = 1; + } else { + dev->indirect_regs[8] &= ~0x08; + dev->ad1848.wten = 0; + } + if (dev->ram_data[3] & 0x80) + dev->ad1848.xregs[4] |= 0x10; + else + dev->ad1848.xregs[4] &= ~0x10; + + ad1848_updatevolmask(&dev->ad1848); } @@ -632,6 +665,8 @@ cs423x_reset(void *priv) /* Reset registers. */ memset(dev->indirect_regs, 0, sizeof(dev->indirect_regs)); dev->indirect_regs[1] = dev->type; + if (dev->type == CRYSTAL_CS4238B) + dev->indirect_regs[2] = 0x20; /* Reset WSS codec. */ ad1848_init(&dev->ad1848, dev->ad1848_type); diff --git a/src/sound/snd_wss.c b/src/sound/snd_wss.c index 80b27cf9d..9017fe2c1 100644 --- a/src/sound/snd_wss.c +++ b/src/sound/snd_wss.c @@ -53,7 +53,7 @@ static const int wss_irq[8] = {5, 7, 9, 10, 11, 12, 14, 15}; /* W95 only uses 7- typedef struct wss_t { uint8_t config; - ad1848_t ad1848; + ad1848_t ad1848; opl_t opl; int opl_enabled;