Crystal CS4236, part 4: the one that took two days

This commit is contained in:
RichardG867
2021-05-22 22:29:25 -03:00
parent 8b9b6c885d
commit 18289a9a64
5 changed files with 413 additions and 160 deletions

View File

@@ -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);

View File

@@ -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*/

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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 },