Crystal CS4236, part 4: the one that took two days
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 },
|
||||
|
||||
Reference in New Issue
Block a user