From 18289a9a647c6140145e7eeda10956643ee5d573 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 22 May 2021 22:29:25 -0300 Subject: [PATCH] Crystal CS4236, part 4: the one that took two days --- src/include/86box/snd_ad1848.h | 15 +- src/include/86box/sound.h | 2 +- src/sound/snd_ad1848.c | 334 +++++++++++++++++++++++++-------- src/sound/snd_cs423x.c | 220 ++++++++++++++-------- src/sound/sound.c | 2 +- 5 files changed, 413 insertions(+), 160 deletions(-) diff --git a/src/include/86box/snd_ad1848.h b/src/include/86box/snd_ad1848.h index 51579cd09..ac1b1a85f 100644 --- a/src/include/86box/snd_ad1848.h +++ b/src/include/86box/snd_ad1848.h @@ -24,14 +24,15 @@ typedef struct { - int index; - uint8_t regs[32], status; /* 16 original registers + 16 CS4231A extensions */ + int index, xindex; + uint8_t regs[32], xregs[32], status; /* 16 original registers + 16 CS4231A extensions + 32 CS4236 extensions */ - int trd, mce, count; + int trd, mce, count, wten; 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; @@ -45,11 +46,13 @@ typedef struct { extern void ad1848_setirq(ad1848_t *ad1848, int irq); extern void ad1848_setdma(ad1848_t *ad1848, int dma); +extern void ad1848_updatevolmask(ad1848_t *ad1848); -extern uint8_t ad1848_read(uint16_t addr, void *p); -extern void ad1848_write(uint16_t addr, uint8_t val, void *p); +extern uint8_t ad1848_read(uint16_t addr, void *priv); +extern void ad1848_write(uint16_t addr, uint8_t val, void *priv); 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); diff --git a/src/include/86box/sound.h b/src/include/86box/sound.h index af10917e0..d280460e5 100644 --- a/src/include/86box/sound.h +++ b/src/include/86box/sound.h @@ -125,7 +125,7 @@ extern const device_t wss_device; extern const device_t ncr_business_audio_device; /* Crystal CS423x */ -extern const device_t cs4237b_device; +extern const device_t cs4236b_device; #endif #endif /*EMU_SOUND_H*/ diff --git a/src/sound/snd_ad1848.c b/src/sound/snd_ad1848.c index c991ff381..3b452a607 100644 --- a/src/sound/snd_ad1848.c +++ b/src/sound/snd_ad1848.c @@ -29,12 +29,14 @@ #include <86box/snd_ad1848.h> -#define CS4231 0x80 -#define CS4236 0x03 +#define CS4231 0x80 +#define CS4236 0x03 -static int ad1848_vols_6bits[64]; +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 @@ -51,6 +53,67 @@ ad1848_setdma(ad1848_t *ad1848, int dma) } +void +ad1848_updatevolmask(ad1848_t *ad1848) +{ + if ((ad1848->type == AD1848_TYPE_CS4236) && ((ad1848->xregs[4] & 0x10) || ad1848->wten)) + ad1848->wave_vol_mask = 0x3f; + else + ad1848->wave_vol_mask = 0x7f; +} + + +static void +ad1848_updatefreq(ad1848_t *ad1848) +{ + double freq; + uint8_t set = 0; + + if (ad1848->type == AD1848_TYPE_CS4236) { + if (ad1848->xregs[11] & 0x20) { + freq = 16934400LL; + switch (ad1848->xregs[13]) { + case 1: freq /= 353; break; + case 2: freq /= 529; break; + case 3: freq /= 617; break; + case 4: freq /= 1058; break; + case 5: freq /= 1764; break; + case 6: freq /= 2117; break; + case 7: freq /= 2558; break; + default: freq /= 16 * MAX(ad1848->xregs[13], 21); break; + } + set = 1; + } else if (ad1848->regs[22] & 0x80) { + freq = (ad1848->regs[22] & 1) ? 33868800LL : 49152000LL; + set = (ad1848->regs[22] >> 1) & 0x3f; + switch (ad1848->regs[10] & 0x30) { + case 0x00: freq /= 128 * set; break; + case 0x10: freq /= 64 * set; break; + case 0x20: freq /= 256 * set; break; + } + set = 1; + } + } + + if (!set) { + freq = (ad1848->regs[8] & 1) ? 16934400LL : 24576000LL; + switch ((ad1848->regs[8] >> 1) & 7) { + case 0: freq /= 3072; break; + case 1: freq /= 1536; break; + case 2: freq /= 896; break; + case 3: freq /= 768; break; + case 4: freq /= 448; break; + case 5: freq /= 384; break; + case 6: freq /= 512; break; + case 7: freq /= 2560; break; + } + } + + ad1848->freq = freq; + ad1848->timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / (double) ad1848->freq)); +} + + uint8_t ad1848_read(uint16_t addr, void *priv) { @@ -58,15 +121,35 @@ ad1848_read(uint16_t addr, void *priv) uint8_t ret = 0xff; switch (addr & 3) { - case 0: /*Index*/ + case 0: /* Index */ ret = ad1848->index | ad1848->trd | ad1848->mce; break; case 1: ret = ad1848->regs[ad1848->index]; - if (ad1848->index == 0x0b) { - ret ^= 0x20; - ad1848->regs[ad1848->index] = ret; + switch (ad1848->index) { + case 11: + ret ^= 0x20; + ad1848->regs[ad1848->index] = ret; + break; + + 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 */ + else if (ad1848->xregs[4] & 0x08) /* wavetable remapping */ + return ad1848->xregs[ad1848->index - 2]; /* real wavetable volume on registers 16 and 17 */ + } + break; + + case 23: + if ((ad1848->type == AD1848_TYPE_CS4236) && (ad1848->regs[23] & 0x08)) { + if ((ad1848->xindex & 0xfe) == 0x00) /* remapped line volume */ + ret = ad1848->regs[18 + ad1848->xindex]; + else + ret = ad1848->xregs[ad1848->xindex]; + } + break; } break; @@ -83,33 +166,29 @@ void ad1848_write(uint16_t addr, uint8_t val, void *priv) { ad1848_t *ad1848 = (ad1848_t *) priv; - double freq; + uint8_t temp, updatefreq = 0; + switch (addr & 3) { case 0: /* Index */ if ((ad1848->regs[12] & 0x40) && (ad1848->type >= AD1848_TYPE_CS4231)) ad1848->index = val & 0x1f; /* cs4231a extended mode enabled */ else ad1848->index = val & 0x0f; /* ad1848/cs4248 mode TODO: some variants/clones DO NOT mirror, just ignore the writes? */ + if (ad1848->type == AD1848_TYPE_CS4236) + ad1848->regs[23] &= ~0x08; /* clear XRAE */ ad1848->trd = val & 0x20; ad1848->mce = val & 0x40; break; case 1: switch (ad1848->index) { + case 10: + if (ad1848->type != AD1848_TYPE_CS4236) + break; + /* fall-through */ + case 8: - freq = (val & 1) ? 16934400LL : 24576000LL; - switch ((val >> 1) & 7) { - case 0: freq /= 3072; break; - case 1: freq /= 1536; break; - case 2: freq /= 896; break; - case 3: freq /= 768; break; - case 4: freq /= 448; break; - case 5: freq /= 384; break; - case 6: freq /= 512; break; - case 7: freq /= 2560; break; - } - ad1848->freq = freq; - ad1848->timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / (double) ad1848->freq)); + updatefreq = 1; break; case 9: @@ -127,7 +206,7 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) break; case 11: - break; + return; case 12: if (ad1848->type != AD1848_TYPE_DEFAULT) @@ -138,25 +217,94 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) ad1848->count = ad1848->regs[15] | (val << 8); break; + case 17: + if (ad1848->type >= AD1848_TYPE_CS4231) /* enable additional data formats on modes 2 and 3 */ + ad1848->fmt_mask = (val & 0x40) ? 0xf0 : 0x70; + break; + + 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 */ + } + } + break; + + case 22: + updatefreq = 1; + break; + + 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; + break; + } + switch (ad1848->xindex) { + case 0: case 1: /* remapped line volume */ + ad1848->regs[18 + ad1848->xindex] = val; + return; + + case 6: + if (val & 0x80) + ad1848->fm_vol_l = 0; + else + ad1848->fm_vol_l = ad1848_vols_7bits[val & 0x3f]; + break; + + case 7: + if (val & 0x80) + ad1848->fm_vol_r = 0; + else + ad1848->fm_vol_r = ad1848_vols_7bits[val & 0x3f]; + break; + + case 11: case 13: + updatefreq = 1; + break; + + case 25: + return; + } + ad1848->xregs[ad1848->xindex] = val; + + if (updatefreq) + ad1848_updatefreq(ad1848); + + return; + } + break; + case 24: if (!(val & 0x70)) ad1848->status &= 0xfe; break; case 25: - break; + return; } ad1848->regs[ad1848->index] = val; - if (ad1848->type == AD1848_TYPE_CS4231) { /* TODO: configure CD volume for CS4248/AD1848 too */ - if (ad1848->regs[0x12] & 0x80) + if (updatefreq) + ad1848_updatefreq(ad1848); + + if ((ad1848->type == AD1848_TYPE_CS4231) || (ad1848->type == AD1848_TYPE_CS4236)) { /* TODO: configure CD volume for CS4248/AD1848 too */ + temp = (ad1848->type == AD1848_TYPE_CS4231) ? 18 : 4; + if (ad1848->regs[temp] & 0x80) ad1848->cd_vol_l = 0; else - ad1848->cd_vol_l = ad1848_vols_5bits_aux_gain[ad1848->regs[0x12] & 0x1f]; - if (ad1848->regs[0x13] & 0x80) + ad1848->cd_vol_l = ad1848_vols_5bits_aux_gain[ad1848->regs[temp] & 0x1f]; + temp++; + if (ad1848->regs[temp] & 0x80) ad1848->cd_vol_r = 0; else - ad1848->cd_vol_r = ad1848_vols_5bits_aux_gain[ad1848->regs[0x13] & 0x1f]; + ad1848->cd_vol_r = ad1848_vols_5bits_aux_gain[ad1848->regs[temp] & 0x1f]; } break; @@ -198,43 +346,55 @@ ad1848_poll(void *priv) if (ad1848->enable) { int32_t temp; + + switch (ad1848->regs[8] & ad1848->fmt_mask) { + case 0x00: /* Mono, 8-bit PCM */ + ad1848->out_l = ad1848->out_r = (dma_channel_read(ad1848->dma) ^ 0x80) * 256; + break; + + case 0x10: /* Stereo, 8-bit PCM */ + ad1848->out_l = (dma_channel_read(ad1848->dma) ^ 0x80) * 256; + ad1848->out_r = (dma_channel_read(ad1848->dma) ^ 0x80) * 256; + break; - switch (ad1848->regs[8] & 0x70) { - case 0x00: /*Mono, 8-bit PCM*/ - ad1848->out_l = ad1848->out_r = (dma_channel_read(ad1848->dma) ^ 0x80) * 256; - break; - case 0x10: /*Stereo, 8-bit PCM*/ - ad1848->out_l = (dma_channel_read(ad1848->dma) ^ 0x80) * 256; - ad1848->out_r = (dma_channel_read(ad1848->dma) ^ 0x80) * 256; - break; - - case 0x40: /*Mono, 16-bit PCM*/ - temp = dma_channel_read(ad1848->dma); - ad1848->out_l = ad1848->out_r = (dma_channel_read(ad1848->dma) << 8) | temp; - break; - case 0x50: /*Stereo, 16-bit PCM*/ - temp = dma_channel_read(ad1848->dma); - ad1848->out_l = (dma_channel_read(ad1848->dma) << 8) | temp; - temp = dma_channel_read(ad1848->dma); - ad1848->out_r = (dma_channel_read(ad1848->dma) << 8) | temp; - break; + case 0x40: /* Mono, 16-bit PCM little endian */ + temp = dma_channel_read(ad1848->dma); + ad1848->out_l = ad1848->out_r = (dma_channel_read(ad1848->dma) << 8) | temp; + break; + + case 0x50: /* Stereo, 16-bit PCM little endian */ + temp = dma_channel_read(ad1848->dma); + ad1848->out_l = (dma_channel_read(ad1848->dma) << 8) | temp; + temp = dma_channel_read(ad1848->dma); + ad1848->out_r = (dma_channel_read(ad1848->dma) << 8) | temp; + break; + + case 0xc0: /* Mono, 16-bit PCM big endian */ + temp = dma_channel_read(ad1848->dma); + ad1848->out_l = ad1848->out_r = dma_channel_read(ad1848->dma) | (temp << 8); + break; + + case 0xd0: /* Stereo, 16-bit PCM big endian */ + temp = dma_channel_read(ad1848->dma); + ad1848->out_l = dma_channel_read(ad1848->dma) | (temp << 8); + temp = dma_channel_read(ad1848->dma); + ad1848->out_r = dma_channel_read(ad1848->dma) | (temp << 8); + break; } if (ad1848->regs[6] & 0x80) ad1848->out_l = 0; else - ad1848->out_l = (ad1848->out_l * ad1848_vols_6bits[ad1848->regs[6] & 0x3f]) >> 16; + ad1848->out_l = (ad1848->out_l * ad1848_vols_7bits[ad1848->regs[6] & ad1848->wave_vol_mask]) >> 16; if (ad1848->regs[7] & 0x80) ad1848->out_r = 0; else - ad1848->out_r = (ad1848->out_r * ad1848_vols_6bits[ad1848->regs[7] & 0x3f]) >> 16; + ad1848->out_r = (ad1848->out_r * ad1848_vols_7bits[ad1848->regs[7] & ad1848->wave_vol_mask]) >> 16; - if (ad1848->count < 0) - { + if (ad1848->count < 0) { ad1848->count = ad1848->regs[15] | (ad1848->regs[14] << 8); - if (!(ad1848->status & 0x01)) - { + if (!(ad1848->status & 0x01)) { ad1848->status |= 0x01; if (ad1848->regs[10] & 2) picint(1 << ad1848->irq); @@ -249,7 +409,7 @@ ad1848_poll(void *priv) } -static void +void ad1848_filter_cd_audio(int channel, double *buffer, void *priv) { ad1848_t *ad1848 = (ad1848_t *) priv; @@ -270,7 +430,8 @@ ad1848_init(ad1848_t *ad1848, int type) ad1848->status = 0xcc; ad1848->index = ad1848->trd = 0; ad1848->mce = 0x40; - + ad1848->wten = 0; + ad1848->regs[0] = ad1848->regs[1] = 0; ad1848->regs[2] = ad1848->regs[3] = 0x80; /* Line-in */ ad1848->regs[4] = ad1848->regs[5] = 0x80; @@ -284,7 +445,7 @@ ad1848_init(ad1848_t *ad1848, int type) ad1848->regs[12] = 0xa; ad1848->regs[13] = 0; ad1848->regs[14] = ad1848->regs[15] = 0; - + if (type == AD1848_TYPE_CS4231) { ad1848->regs[16] = ad1848->regs[17] = 0; ad1848->regs[18] = ad1848->regs[19] = 0x88; @@ -303,25 +464,42 @@ ad1848_init(ad1848_t *ad1848, int type) ad1848->regs[26] = 0xa0; ad1848->regs[27] = ad1848->regs[29] = 0; ad1848->regs[30] = ad1848->regs[31] = 0; - } - - ad1848->out_l = 0; - ad1848->out_r = 0; - for (c = 0; c < 64; c++) { - attenuation = 0.0; - if (c & 0x01) attenuation -= 1.5; - if (c & 0x02) attenuation -= 3.0; - if (c & 0x04) attenuation -= 6.0; - if (c & 0x08) attenuation -= 12.0; - if (c & 0x10) attenuation -= 24.0; - if (c & 0x20) attenuation -= 48.0; - - attenuation = pow(10, attenuation / 10); - - ad1848_vols_6bits[c] = (int)(attenuation * 65536); + ad1848->xregs[0] = ad1848->xregs[1] = 0xe8; + ad1848->xregs[2] = ad1848->xregs[3] = 0xcf; + ad1848->xregs[4] = 0x84; + ad1848->xregs[5] = 0; + ad1848->xregs[6] = ad1848->xregs[7] = 0x80; + ad1848->xregs[8] = ad1848->xregs[9] = 0; + ad1848->xregs[10] = 0x3f; + ad1848->xregs[11] = 0xc0; + ad1848->xregs[14] = ad1848->xregs[15] = 0; + ad1848->xregs[16] = ad1848->xregs[17] = 0; } - + + ad1848->out_l = ad1848->out_r = 0; + ad1848->fm_vol_l = ad1848->fm_vol_r = 1; + ad1848_updatevolmask(ad1848); + ad1848->fmt_mask = 0x70; + + for (c = 0; c < 128; c++) { + attenuation = 0.0; + if (c & 0x40) { + if (c < 72) attenuation = (c - 72) * -1.5; + } else { + if (c & 0x01) attenuation -= 1.5; + if (c & 0x02) attenuation -= 3.0; + if (c & 0x04) attenuation -= 6.0; + if (c & 0x08) attenuation -= 12.0; + 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); + } + for (c = 0; c < 32; c++) { attenuation = 12.0; if (c & 0x01) attenuation -= 1.5; @@ -329,16 +507,16 @@ ad1848_init(ad1848_t *ad1848, int type) 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); - } - + } + ad1848->type = type; - + timer_add(&ad1848->timer_count, ad1848_poll, ad1848, 0); - if (ad1848->type != AD1848_TYPE_DEFAULT && ad1848->type != AD1848_TYPE_CS4248) + if ((ad1848->type != AD1848_TYPE_DEFAULT) && (ad1848->type != AD1848_TYPE_CS4248)) sound_set_cd_audio_filter(ad1848_filter_cd_audio, ad1848); } diff --git a/src/sound/snd_cs423x.c b/src/sound/snd_cs423x.c index d7a332b7b..b95d0618d 100644 --- a/src/sound/snd_cs423x.c +++ b/src/sound/snd_cs423x.c @@ -37,7 +37,9 @@ enum { - CRYSTAL_CS4237B = 0xc8 + CRYSTAL_CS4236B = 0xcb, + CRYSTAL_CS4237B = 0xc8, + CRYSTAL_CS4238B = 0xc9 }; enum { CRYSTAL_SLAM_NONE = 0, @@ -51,8 +53,8 @@ static const uint8_t slam_init_key[32] = { 0x96, 0x35, 0x9A, 0xCD, 0xE6, 0xF3, 0 0x5E, 0xAF, 0x57, 0x2B, 0x15, 0x8A, 0xC5, 0xE2, 0xF1, 0xF8, 0x7C, 0x3E, 0x9F, 0x4F, 0x27, 0x13, 0x09, 0x84, 0x42, 0xA1, 0xD0, 0x68, 0x34, 0x1A }; -static const uint8_t cs4237b_eeprom[] = { - /* CS4237B configuration */ +static const uint8_t cs4236b_eeprom[] = { + /* Chip configuration */ 0x55, 0xbb, /* magic */ 0x00, 0x00, /* length */ 0x00, 0x03, /* CD-ROM and modem decode */ @@ -66,7 +68,7 @@ static const uint8_t cs4237b_eeprom[] = { 0x10, 0x03, /* DMA routing */ /* PnP resources */ - 0x0e, 0x63, 0x42, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, /* CSC4237, dummy checksum (filled in by isapnp_add_card) */ + 0x0e, 0x63, 0x42, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, /* CSC4236, dummy checksum (filled in by isapnp_add_card) */ 0x0a, 0x10, 0x01, /* PnP version 1.0, vendor version 0.1 */ 0x82, 0x0e, 0x00, 'C', 'r', 'y', 's', 't', 'a', 'l', ' ', 'C', 'o', 'd', 'e' ,'c', 0x00, /* ANSI identifier */ @@ -131,14 +133,16 @@ 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, regs[8], indirect_regs[16], eeprom_data[2048], ram_dl; + uint8_t type, pnp_offset, regs[8], indirect_regs[16], eeprom_data[2048], ram_data[384], ram_dl; + int ad1848_type; - uint8_t key_pos: 5, enable_slam: 1, slam_state: 2, slam_ld, slam_reg; + uint8_t pnp_enable: 1, key_pos: 5, slam_enable: 1, slam_state: 2, slam_ld, slam_reg; isapnp_device_config_t *slam_config; } cs423x_t; -static void cs423x_slam_remap(cs423x_t *dev, uint8_t enable); +static void cs423x_slam_enable(cs423x_t *dev, uint8_t enable); +static void cs423x_pnp_enable(cs423x_t *dev, uint8_t update_rom); static void cs423x_ctxswitch_write(uint16_t addr, uint8_t val, void *priv); static void cs423x_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv); @@ -183,7 +187,6 @@ cs423x_write(uint16_t port, uint8_t val, void *priv) { cs423x_t *dev = (cs423x_t *) priv; uint8_t reg = port & 7; - uint16_t eeprom_addr; switch (reg) { case 1: /* EEPROM Interface */ @@ -199,7 +202,7 @@ cs423x_write(uint16_t port, uint8_t val, void *priv) switch (dev->regs[3] & 15) { case 0: /* WSS Master Control */ if (val & 0x80) - ad1848_init(&dev->ad1848, AD1848_TYPE_DEFAULT); + ad1848_init(&dev->ad1848, dev->ad1848_type); val = 0x00; break; @@ -221,10 +224,15 @@ cs423x_write(uint16_t port, uint8_t val, void *priv) break; case 8: /* CS9236 Wavetable Control */ - val &= 0xf0; + val &= 0x0f; 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 */ @@ -232,20 +240,20 @@ cs423x_write(uint16_t port, uint8_t val, void *priv) case 0: /* commands */ switch (val) { case 0x55: /* Disable PnP Key */ - isapnp_enable_card(dev->pnp_card, 0); + dev->pnp_enable = 0; + /* fall-through */ + + case 0x5a: /* Update Hardware Configuration Data */ + cs423x_pnp_enable(dev, 0); break; case 0x56: /* Disable Crystal Key */ - cs423x_slam_remap(dev, 0); + cs423x_slam_enable(dev, 0); break; case 0x57: /* Jump to ROM */ break; - case 0x5a: /* Update Hardware Configuration Data */ - isapnp_update_card_rom(dev->pnp_card, dev->eeprom_data + 23, dev->eeprom_size - 23); - break; - case 0xaa: /* Download RAM */ dev->ram_dl = 1; break; @@ -264,21 +272,21 @@ cs423x_write(uint16_t port, uint8_t val, void *priv) case 3: /* data */ /* The only documented RAM region is 0x4000 (384 bytes in size), for - loading the chip's configuration and PnP ROM without an EEPROM. */ - if ((dev->ram_addr >= 0x4000) && (dev->ram_addr < 0x4180)) { - eeprom_addr = dev->ram_addr - 0x3ffc; /* skip first 4 bytes (header on real EEPROM) */ - dev->eeprom_data[eeprom_addr] = val; - if (dev->eeprom_size < ++eeprom_addr) /* update EEPROM size if required */ - dev->eeprom_size = eeprom_addr; - } + loading chip configuration and PnP resources without an EEPROM. */ + if ((dev->ram_addr >= 0x4000) && (dev->ram_addr < 0x4180)) + dev->ram_data[dev->ram_addr & 0x01ff] = val; dev->ram_addr++; break; } break; case 6: /* RAM Access End */ - if (!val) + if (!val) { dev->ram_dl = 0; + + /* Update PnP resource data and state. */ + cs423x_pnp_enable(dev, 1); + } break; case 7: /* Global Status */ @@ -412,17 +420,18 @@ cs423x_slam_write(uint16_t addr, uint8_t val, void *priv) static void -cs423x_slam_remap(cs423x_t *dev, uint8_t enable) +cs423x_slam_enable(cs423x_t *dev, uint8_t enable) { /* Disable SLAM. */ - if (dev->enable_slam) { - dev->enable_slam = 0; + if (dev->slam_enable) { + dev->slam_state = CRYSTAL_SLAM_NONE; + dev->slam_enable = 0; io_removehandler(0x279, 1, NULL, NULL, NULL, cs423x_slam_write, NULL, NULL, dev); } - /* Enable SLAM if not blocked by EEPROM configuration. */ - if (enable && !(dev->eeprom_data[7] & 0x10)) { - dev->enable_slam = 1; + /* Enable SLAM if the CKD bit is not set. */ + if (enable && !(dev->ram_data[2] & 0x10)) { + dev->slam_enable = 1; io_sethandler(0x279, 1, NULL, NULL, NULL, cs423x_slam_write, NULL, NULL, dev); } } @@ -440,21 +449,26 @@ static void cs423x_ctxswitch_write(uint16_t addr, uint8_t val, void *priv) { cs423x_t *dev = (cs423x_t *) priv; - uint8_t prev_context = dev->regs[7] & 0x80, switched = 0; - /* Determine the active context (WSS or SBPro) through the address being read/written. */ - if ((prev_context == 0x80) && ((addr & 0xfff0) == dev->sb_base)) { - dev->regs[7] &= ~0x80; - switched = 1; - } else if ((prev_context == 0x00) && ((addr & 0xfffc) == dev->wss_base)) { - dev->regs[7] |= 0x80; - switched = 1; - } + /* Check if a context switch (WSS=1 <-> SBPro=0) occurred through the address being read/written. */ + if ((dev->regs[7] & 0x80) ? ((addr & 0xfff0) == dev->sb_base) : ((addr & 0xfffc) == dev->wss_base)) { + /* Flip context bit. */ + dev->regs[7] ^= 0x80; - /* Fire the context switch interrupt if enabled. */ - if (switched && (dev->regs[0] & 0x20) && (dev->ad1848.irq > 0)) { - dev->regs[7] |= 0x40; /* set interrupt flag */ - picint(1 << dev->ad1848.irq); /* control device shares IRQ with WSS and SBPro */ + /* Switch the CD audio filter and OPL ownership. + 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); + if (dev->sb->opl_enabled) /* SBPro */ + sound_set_cd_audio_filter(sbpro_filter_cd_audio, dev->sb); + else /* WSS */ + sound_set_cd_audio_filter(ad1848_filter_cd_audio, &dev->ad1848); + + /* Fire a context switch interrupt if enabled. */ + if ((dev->regs[0] & 0x20) && (dev->ad1848.irq > 0)) { + dev->regs[7] |= 0x40; /* set interrupt flag */ + picint(1 << dev->ad1848.irq); /* control device shares IRQ with WSS and SBPro */ + } } } @@ -463,17 +477,44 @@ static void cs423x_get_buffer(int32_t *buffer, int len, void *priv) { cs423x_t *dev = (cs423x_t *) priv; - int c; + int c, opl_wss = !dev->sb->opl_enabled; - /* Output audio from the WSS codec. SBPro and OPL3 are - already handled by the Sound Blaster emulation. */ + /* Output audio from the WSS codec, and also the OPL if we're in WSS mode. */ ad1848_update(&dev->ad1848); + if (opl_wss) + opl3_update(&dev->sb->opl); - for (c = 0; c < len * 2; c++) { - buffer[c] += (dev->ad1848.buffer[c] / 2); + for (c = 0; c < len * 2; c += 2) { + if (opl_wss) { + buffer[c] += (dev->sb->opl.buffer[c] * dev->ad1848.fm_vol_l) >> 16; + buffer[c + 1] += (dev->sb->opl.buffer[c + 1] * dev->ad1848.fm_vol_r) >> 16; + } + + buffer[c] += dev->ad1848.buffer[c] / 2; + buffer[c + 1] += dev->ad1848.buffer[c + 1] / 2; } dev->ad1848.pos = 0; + if (opl_wss) + dev->sb->opl.pos = 0; +} + + +static void +cs423x_pnp_enable(cs423x_t *dev, uint8_t update_rom) +{ + uint8_t enable = ISAPNP_CARD_ENABLE; + + /* 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); + + /* Update PnP state. */ + isapnp_enable_card(dev->pnp_card, enable); } @@ -541,7 +582,6 @@ cs423x_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv sb_dsp_setdma8(&dev->sb->dsp, config->dma[0].dma); } } - break; case 1: /* Game Port */ @@ -583,16 +623,28 @@ 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)); + /* Reset registers. */ memset(dev->indirect_regs, 0, sizeof(dev->indirect_regs)); dev->indirect_regs[1] = dev->type; - /* Reset logical devices. */ - for (uint8_t i = 0; i < 6; i++) - isapnp_reset_device(dev->pnp_card, i); + /* Reset WSS codec. */ + ad1848_init(&dev->ad1848, dev->ad1848_type); - /* Enable SLAM. */ - cs423x_slam_remap(dev, 1); + /* Reset PnP resource data, state and logical devices. */ + dev->pnp_enable = 1; + if (dev->pnp_card) { + cs423x_pnp_enable(dev, 1); + isapnp_reset_card(dev->pnp_card); + } + + /* Reset SLAM. */ + cs423x_slam_enable(dev, 1); } @@ -602,17 +654,46 @@ cs423x_init(const device_t *info) cs423x_t *dev = malloc(sizeof(cs423x_t)); memset(dev, 0, sizeof(cs423x_t)); + /* Initialize model-specific data. */ dev->type = info->local; switch (dev->type) { + case CRYSTAL_CS4236B: case CRYSTAL_CS4237B: - dev->eeprom_size = sizeof(cs4237b_eeprom); - memcpy(dev->eeprom_data, cs4237b_eeprom, dev->eeprom_size); + case CRYSTAL_CS4238B: + /* Same WSS codec and EEPROM structure. */ + dev->ad1848_type = AD1848_TYPE_CS4236; + dev->pnp_offset = 19; + + /* Different Chip Version and ID registers, which shouldn't be reset by ad1848_init */ + dev->ad1848.xregs[25] = dev->type; + + /* Load EEPROM contents from template. */ + memcpy(dev->eeprom_data, cs4236b_eeprom, sizeof(cs4236b_eeprom)); + + /* Set content size. */ + dev->eeprom_data[2] = sizeof(cs4236b_eeprom) >> 8; + dev->eeprom_data[3] = sizeof(cs4236b_eeprom) & 0xff; + + /* Set PnP card ID. */ + switch (dev->type) { + case CRYSTAL_CS4237B: + dev->eeprom_data[26] = 0x37; + break; + + case CRYSTAL_CS4238B: + dev->eeprom_data[26] = 0x38; + break; + } + break; } - /* Initialize codecs. */ + /* 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); - ad1848_init(&dev->ad1848, AD1848_TYPE_DEFAULT); + + /* Initialize RAM, registers and WSS codec. */ + cs423x_reset(dev); sound_add_handler(cs423x_get_buffer, dev); /* Initialize game port. */ @@ -621,22 +702,13 @@ cs423x_init(const device_t *info) /* Initialize I2C bus for the EEPROM. */ dev->i2c = i2c_gpio_init("nvr_cs423x"); - if (dev->eeprom_size) { - /* Set EEPROM length. */ - dev->eeprom_data[2] = dev->eeprom_size >> 8; - dev->eeprom_data[3] = dev->eeprom_size & 0xff; - - /* Initialize I2C EEPROM. */ + /* Initialize I2C EEPROM if the contents are valid. */ + if ((dev->eeprom_data[0] == 0x55) && (dev->eeprom_data[1] == 0xbb)) dev->eeprom = i2c_eeprom_init(i2c_gpio_get_bus(dev->i2c), 0x50, dev->eeprom_data, sizeof(dev->eeprom_data), 1); - } /* Initialize ISAPnP. */ - dev->pnp_card = isapnp_add_card(&dev->eeprom_data[23], dev->eeprom_size - 23, cs423x_pnp_config_changed, NULL, NULL, NULL, dev); - if (dev->eeprom_data[7] & 0x20) /* hide PnP card if PKD is set */ - isapnp_enable_card(dev->pnp_card, 0); - - /* Initialize registers. */ - cs423x_reset(dev); + dev->pnp_card = isapnp_add_card(NULL, 0, cs423x_pnp_config_changed, NULL, NULL, NULL, dev); + cs423x_pnp_enable(dev, 1); return dev; } @@ -665,11 +737,11 @@ cs423x_speed_changed(void *priv) } -const device_t cs4237b_device = +const device_t cs4236b_device = { - "Crystal CS4237B", + "Crystal CS4236B", DEVICE_ISA | DEVICE_AT, - CRYSTAL_CS4237B, + CRYSTAL_CS4236B, cs423x_init, cs423x_close, cs423x_reset, { NULL }, cs423x_speed_changed, diff --git a/src/sound/sound.c b/src/sound/sound.c index 39a3a44b6..851ccc92a 100644 --- a/src/sound/sound.c +++ b/src/sound/sound.c @@ -88,7 +88,7 @@ static const SOUND_CARD sound_cards[] = { "adlibgold", &adgold_device }, { "azt2316a", &azt2316a_device }, { "azt1605", &azt1605_device }, - { "cs4237b", &cs4237b_device }, + { "cs4236b", &cs4236b_device }, { "sb", &sb_1_device }, { "sb1.5", &sb_15_device }, { "sb2.0", &sb_2_device },