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;