From 1de2e3dd2fb95c7084071bb8bb1080e209685583 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Thu, 20 May 2021 00:30:12 -0300 Subject: [PATCH] Crystal CS4237, part 2 --- src/device/isapnp.c | 40 ++++-- src/include/86box/isapnp.h | 8 ++ src/include/86box/snd_sb.h | 3 - src/sio/sio_um8669f.c | 4 +- src/sound/snd_cs423x.c | 284 ++++++++++++++++++++++++++++++++----- 5 files changed, 283 insertions(+), 56 deletions(-) diff --git a/src/device/isapnp.c b/src/device/isapnp.c index f611a18ae..e08e2d300 100644 --- a/src/device/isapnp.c +++ b/src/device/isapnp.c @@ -478,9 +478,7 @@ isapnp_write_data(uint16_t addr, uint8_t val, void *priv) isapnp_log("ISAPnP: Reset CSN\n"); card = dev->first_card; while (card) { - card->csn = 0; - if (card->csn_changed) - card->csn_changed(card->csn, card->priv); + isapnp_set_csn(card, 0); card = card->next; } } @@ -506,9 +504,7 @@ isapnp_write_data(uint16_t addr, uint8_t val, void *priv) case 0x06: /* Card Select Number */ if (dev->isolated_card) { isapnp_log("ISAPnP: Set CSN %02X\n", val); - dev->isolated_card->csn = val; - if (dev->isolated_card->csn_changed) - dev->isolated_card->csn_changed(dev->isolated_card->csn, dev->isolated_card->priv); + isapnp_set_csn(dev->isolated_card, val); dev->isolated_card->state = PNP_STATE_CONFIG; dev->isolated_card = NULL; } else { @@ -685,8 +681,6 @@ isapnp_add_card(uint8_t *rom, uint16_t rom_size, memset(card, 0, sizeof(isapnp_card_t)); card->enable = 1; - card->rom = rom; - card->rom_size = rom_size; card->priv = priv; card->config_changed = config_changed; card->csn_changed = csn_changed; @@ -702,6 +696,18 @@ isapnp_add_card(uint8_t *rom, uint16_t rom_size, prev_card->next = card; } + isapnp_update_card_rom(card, rom, rom_size); + return card; +} + + +void +isapnp_update_card_rom(void *priv, uint8_t *rom, uint16_t rom_size) +{ + isapnp_card_t *card = (isapnp_card_t *) priv; + card->rom = rom; + card->rom_size = rom_size; + /* Parse resources in ROM to allocate logical devices, and determine the state of read-only register bits. */ #ifdef ENABLE_ISAPNP_LOG @@ -712,8 +718,16 @@ isapnp_add_card(uint8_t *rom, uint16_t rom_size, uint8_t ldn = 0, res, in_df = 0; uint8_t irq = 0, io = 0, mem_range = 0, mem_range_32 = 0, irq_df = 0, io_df = 0, mem_range_df = 0, mem_range_32_df = 0; uint32_t len; - isapnp_device_t *ld = NULL, *prev_ld = NULL; + isapnp_device_t *ld = card->first_ld, *prev_ld = NULL; + /* Clear any existing logical devices. */ + while (ld) { + prev_ld = ld->next; + free(ld); + ld = prev_ld; + } + + /* Iterate through ROM resources. */ while (i < card->rom_size) { if (card->rom[i] & 0x80) { /* large resource */ res = card->rom[i] & 0x7f; @@ -890,8 +904,6 @@ isapnp_add_card(uint8_t *rom, uint16_t rom_size, /* We're done with the last logical device. */ if (ld) isapnp_reset_ld_regs(ld); - - return card; } @@ -907,10 +919,8 @@ isapnp_enable_card(void *priv, uint8_t enable) while (card) { if (card == priv) { /* Enable or disable the card. */ - card->enable = !!enable; - - /* enable=2 is a cheat code to jump straight into CONFIG state. */ - card->state = (enable == 2) ? PNP_STATE_CONFIG : PNP_STATE_WAIT_FOR_KEY; + card->enable = (enable >= ISAPNP_CARD_ENABLE); + card->state = (enable == ISAPNP_CARD_FORCE_CONFIG) ? PNP_STATE_CONFIG : PNP_STATE_WAIT_FOR_KEY; /* Invalidate other references if we're disabling this card. */ if (!card->enable) { diff --git a/src/include/86box/isapnp.h b/src/include/86box/isapnp.h index d241d74f3..38fc59d07 100644 --- a/src/include/86box/isapnp.h +++ b/src/include/86box/isapnp.h @@ -25,6 +25,13 @@ #define ISAPNP_DMA_DISABLED 4 +enum { + ISAPNP_CARD_DISABLE = 0, + ISAPNP_CARD_ENABLE = 1, + ISAPNP_CARD_FORCE_CONFIG /* cheat code for UMC UM8669F */ +}; + + typedef struct { uint8_t activate; struct { @@ -51,6 +58,7 @@ void *isapnp_add_card(uint8_t *rom, uint16_t rom_size, uint8_t (*read_vendor_reg)(uint8_t ld, uint8_t reg, void *priv), void (*write_vendor_reg)(uint8_t ld, uint8_t reg, uint8_t val, void *priv), void *priv); +void isapnp_update_card_rom(void *priv, uint8_t *rom, uint16_t rom_size); void isapnp_enable_card(void *priv, uint8_t enable); void isapnp_set_csn(void *priv, uint8_t csn); void isapnp_set_device_defaults(void *priv, uint8_t ldn, const isapnp_device_config_t *config); diff --git a/src/include/86box/snd_sb.h b/src/include/86box/snd_sb.h index 8195345df..3bb2a6496 100644 --- a/src/include/86box/snd_sb.h +++ b/src/include/86box/snd_sb.h @@ -134,9 +134,6 @@ extern void sb_ct1345_mixer_write(uint16_t addr, uint8_t val, void *p); extern uint8_t sb_ct1345_mixer_read(uint16_t addr, void *p); extern void sb_ct1345_mixer_reset(sb_t* sb); -extern uint8_t sb_pro_v1_opl_read(uint16_t port, void *priv); -extern void sb_pro_v1_opl_write(uint16_t port, uint8_t val, void *priv); - extern void sb_get_buffer_sbpro(int32_t *buffer, int len, void *p); extern void sbpro_filter_cd_audio(int channel, double *buffer, void *p); extern void sb_close(void *p); diff --git a/src/sio/sio_um8669f.c b/src/sio/sio_um8669f.c index 2cfff686c..4a6869a02 100644 --- a/src/sio/sio_um8669f.c +++ b/src/sio/sio_um8669f.c @@ -39,7 +39,7 @@ #include <86box/isapnp.h> -/* This ROM is reconstructed out of the several assumptions, some of which are based on the IT8671F. */ +/* This ROM is reconstructed out of several assumptions, some of which are based on the IT8671F. */ static uint8_t um8669f_pnp_rom[] = { 0x55, 0xa3, 0x86, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, /* UMC8669, dummy checksum (filled in by isapnp_add_card) */ 0x0a, 0x10, 0x10, /* PnP version 1.0, vendor version 1.0 */ @@ -211,7 +211,7 @@ um8669f_write(uint16_t port, uint8_t val, void *priv) if (dev->cur_reg_108 == 0xc1) { um8669f_log("UM8669F: ISAPnP %sabled\n", (val & 0x80) ? "en" : "dis"); - isapnp_enable_card(dev->pnp_card, (val & 0x80) ? 2 : 0); + isapnp_enable_card(dev->pnp_card, (val & 0x80) ? ISAPNP_CARD_FORCE_CONFIG : ISAPNP_CARD_DISABLE); } } } diff --git a/src/sound/snd_cs423x.c b/src/sound/snd_cs423x.c index 6456d5a8d..88f15c49c 100644 --- a/src/sound/snd_cs423x.c +++ b/src/sound/snd_cs423x.c @@ -36,13 +36,35 @@ enum { - CRYSTAL_CS4237B = 37 + CRYSTAL_CS4237B = 0xc8 +}; +enum { + CRYSTAL_SLAM_NONE = 0, + CRYSTAL_SLAM_INDEX, + CRYSTAL_SLAM_BYTE1, + CRYSTAL_SLAM_BYTE2 }; -static const uint8_t cs4237b_eeprom[384] = { - 0x55, 0xbb, 0x00, 0x00, 0x00, 0x03, 0x80, 0x80, 0x0b, 0x20, 0x04, 0x08, 0x10, 0x80, 0x00, 0x00, 0x00, 0x48, 0x75, 0xb9, 0xfc, 0x10, 0x03, /* CS4237B stuff */ +static const uint8_t slam_init_key[32] = { 0x96, 0x35, 0x9A, 0xCD, 0xE6, 0xF3, 0x79, 0xBC, + 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 */ + 0x55, 0xbb, /* magic */ + 0x00, 0x00, /* length */ + 0x00, 0x03, /* CD-ROM and modem decode */ + 0x80, /* misc. config */ + 0x80, /* global config */ + 0x0b, /* chip ID */ + 0x20, 0x04, 0x08, 0x10, 0x80, 0x00, 0x00, /* reserved */ + 0x00, /* external decode length */ + 0x48, /* reserved */ + 0x75, 0xb9, 0xfc, /* IRQ routing */ + 0x10, 0x03, /* DMA routing */ + /* PnP resources */ 0x0e, 0x63, 0x42, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, /* CSC4237, 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 */ @@ -106,11 +128,19 @@ typedef struct cs423x_t sb_t *sb; void *i2c, *eeprom; - uint16_t wss_base, opl_base, sb_base, ctrl_base, ram_addr, eeprom_size; - uint8_t regs[8], indirect_regs[16], eeprom_data[2048], ram_dl; + 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 key_pos: 5, enable_slam: 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_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); + + static uint8_t cs423x_read(uint16_t port, void *priv) { @@ -130,12 +160,15 @@ cs423x_read(uint16_t port, void *priv) break; case 7: /* Global Status */ - ret = 0x00; - if (dev->sb->mpu->state.irq_pending) + /* Context switching: take active context and interrupt flag, then clear interrupt flag. */ + ret &= 0xc0; + dev->regs[7] &= 0x80; + + if (dev->sb->mpu->state.irq_pending) /* MPU interrupt */ ret |= 0x08; - if (dev->ad1848.regs[10] & 2) + if (dev->ad1848.regs[10] & 2) /* WSS interrupt */ ret |= 0x10; - if (dev->sb->dsp.sb_irq8 || dev->sb->dsp.sb_irq16 || dev->sb->dsp.sb_irq401) + if (dev->sb->dsp.sb_irq8 || dev->sb->dsp.sb_irq16 || dev->sb->dsp.sb_irq401) /* SBPro interrupt */ ret |= 0x20; } @@ -148,6 +181,7 @@ 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 +233,16 @@ cs423x_write(uint16_t port, uint8_t val, void *priv) isapnp_enable_card(dev->pnp_card, 0); break; - /* TODO: Crystal's PnP bypass method? */ + case 0x56: /* Disable Crystal Key */ + cs423x_slam_remap(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; @@ -220,8 +263,12 @@ 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)) - dev->eeprom_data[dev->ram_addr - 0x3ffc] = val; /* skip first 4 bytes (EEPROM header) */ + 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; + } dev->ram_addr++; break; } @@ -240,25 +287,149 @@ cs423x_write(uint16_t port, uint8_t val, void *priv) } +static void +cs423x_slam_write(uint16_t addr, uint8_t val, void *priv) +{ + cs423x_t *dev = (cs423x_t *) priv; + uint8_t idx; + + switch (dev->slam_state) { + case CRYSTAL_SLAM_NONE: + /* Not in SLAM: read and compare Crystal key. */ + if (val == slam_init_key[dev->key_pos]) { + dev->key_pos++; + /* Was the key successfully written? */ + if (!dev->key_pos) { + /* Discard any pending logical device configuration, just to be safe. */ + if (dev->slam_config) { + free(dev->slam_config); + dev->slam_config = NULL; + } + + /* Enter SLAM. */ + dev->slam_state = CRYSTAL_SLAM_INDEX; + } + } else { + dev->key_pos = 0; + } + break; + + case CRYSTAL_SLAM_INDEX: + /* Write register index. */ + dev->slam_reg = val; + dev->slam_state = CRYSTAL_SLAM_BYTE1; + break; + + case CRYSTAL_SLAM_BYTE1: + case CRYSTAL_SLAM_BYTE2: + /* Write register value: two bytes for I/O ports, single byte otherwise. */ + switch (dev->slam_reg) { + case 0x06: /* Card Select Number */ + isapnp_set_csn(dev->pnp_card, val); + break; + + case 0x15: /* Logical Device ID */ + /* Apply the previous logical device's configuration, and reuse its config structure. */ + if (dev->slam_config) + cs423x_pnp_config_changed(dev->slam_ld, dev->slam_config, dev); + else + dev->slam_config = (isapnp_device_config_t *) malloc(sizeof(isapnp_device_config_t)); + + /* Start new logical device. */ + memset(dev->slam_config, 0, sizeof(isapnp_device_config_t)); + dev->slam_ld = val; + break; + + case 0x47: /* I/O Port Base Address 0 */ + case 0x48: /* I/O Port Base Address 1 */ + case 0x42: /* I/O Port Base Address 2 */ + idx = (dev->slam_reg == 0x42) ? 2 : (dev->slam_reg - 0x47); + if (dev->slam_state == CRYSTAL_SLAM_BYTE1) { + /* Set high byte, or ignore it if no logical device is selected. */ + if (dev->slam_config) + dev->slam_config->io[idx].base = val << 8; + + /* Prepare for the second (low byte) write. */ + dev->slam_state = CRYSTAL_SLAM_BYTE2; + return; + } else if (dev->slam_config) { + /* Set low byte, or ignore it if no logical device is selected. */ + dev->slam_config->io[idx].base |= val; + } + break; + + case 0x22: /* Interrupt Select 0 */ + case 0x27: /* Interrupt Select 1 */ + /* Stop if no logical device is selected. */ + if (!dev->slam_config) + break; + + /* Set IRQ value. */ + idx = (dev->slam_reg == 0x22) ? 0 : 1; + dev->slam_config->irq[idx].irq = val & 15; + break; + + case 0x2a: /* DMA Select 0 */ + case 0x25: /* DMA Select 1 */ + /* Stop if no logical device is selected. */ + if (!dev->slam_config) + break; + + /* Set DMA value. */ + idx = (dev->slam_reg == 0x2a) ? 0 : 1; + dev->slam_config->dma[idx].dma = val & 7; + break; + + case 0x33: /* Activate Device */ + /* Stop if no logical device is selected. */ + if (!dev->slam_config) + break; + + /* Activate or deactivate the device. */ + dev->slam_config->activate = val & 0x01; + break; + + case 0x79: /* activate chip */ + /* Apply the last logical device's configuration. */ + if (dev->slam_config) { + cs423x_pnp_config_changed(dev->slam_ld, dev->slam_config, dev); + free(dev->slam_config); + dev->slam_config = NULL; + } + + /* Exit out of SLAM. */ + dev->slam_state = CRYSTAL_SLAM_NONE; + break; + } + + /* Prepare for the next register, unless a two-byte read returns above. */ + dev->slam_state = CRYSTAL_SLAM_INDEX; + break; + } +} + + +static void +cs423x_slam_remap(cs423x_t *dev, uint8_t enable) +{ + /* Disable SLAM. */ + if (dev->enable_slam) { + dev->enable_slam = 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; + io_sethandler(0x279, 1, NULL, NULL, NULL, cs423x_slam_write, NULL, NULL, dev); + } +} + + static uint8_t cs423x_ctxswitch_read(uint16_t addr, 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; - } - - /* Fire the context switch interrupt if enabled. */ - if (switched && (dev->regs[0] & 0x20) && dev->ad1848.irq) - picint(1 << dev->ad1848.irq); - + cs423x_ctxswitch_write(addr, 0, priv); return 0xff; /* don't interfere with the actual handlers */ } @@ -266,7 +437,23 @@ cs423x_ctxswitch_read(uint16_t addr, void *priv) static void cs423x_ctxswitch_write(uint16_t addr, uint8_t val, void *priv) { - cs423x_ctxswitch_read(addr, 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; + } + + /* 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 its IRQ with WSS and SBPro */ + } } @@ -395,37 +582,62 @@ cs423x_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv } +static void +cs423x_reset(void *priv) +{ + cs423x_t *dev = (cs423x_t *) priv; + + /* 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); + + /* Enable SLAM. */ + cs423x_slam_remap(dev, 1); +} + + static void * cs423x_init(const device_t *info) { cs423x_t *dev = malloc(sizeof(cs423x_t)); memset(dev, 0, sizeof(cs423x_t)); - dev->indirect_regs[1] = 0x88; - - switch (info->local) { + dev->type = info->local; + switch (dev->type) { case CRYSTAL_CS4237B: dev->eeprom_size = sizeof(cs4237b_eeprom); memcpy(dev->eeprom_data, cs4237b_eeprom, dev->eeprom_size); break; } + /* Initialize codecs. */ dev->sb = (sb_t *) device_add(&sb_pro_cs423x_device); - - ad1848_init(&dev->ad1848, AD1848_TYPE_DEFAULT); - + ad1848_init(&dev->ad1848, AD1848_TYPE_CS4236); sound_add_handler(cs423x_get_buffer, dev); + /* 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. */ 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); return dev; } @@ -459,7 +671,7 @@ const device_t cs4237b_device = "Crystal CS4237B", DEVICE_ISA | DEVICE_AT, CRYSTAL_CS4237B, - cs423x_init, cs423x_close, NULL, + cs423x_init, cs423x_close, cs423x_reset, { NULL }, cs423x_speed_changed, NULL,