diff --git a/src/chipset/neat.c b/src/chipset/neat.c index 73bd1b4c2..000492d14 100644 --- a/src/chipset/neat.c +++ b/src/chipset/neat.c @@ -239,7 +239,7 @@ typedef struct neat_t { ram_page_t shadow[32]; /* Shadow RAM pages */ } neat_t; -static uint8_t defaults[16] = { 0x0a, 0x45, 0xfc, 0x00, 0x00, 0x7f, 0x00, 0x00, +static uint8_t defaults[16] = { 0x0a, 0x45, 0xfc, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x63, 0x10, 0x00, 0x00, 0x12 }; static uint8_t masks[4] = { RB10_P0EXT, RB10_P1EXT, RB10_P2EXT, RB10_P3EXT }; @@ -924,6 +924,16 @@ neat_init(UNUSED(const device_t *info)) mem_mapping_disable(&ram_mid_mapping); + for (int i = 0; i < 24; i++) { + if (i >= 20) + neat_mem_update_state(dev, 0x000a0000 + (i * EMS_PGSIZE), EMS_PGSIZE, MEM_FLAG_ROMCS, MEM_FMASK_SHADOW); + else { + /* This is needed to actually trigger an update. */ + dev->mem_flags[i + 8] = MEM_FLAG_ROMCS; + neat_mem_update_state(dev, 0x000a0000 + (i * EMS_PGSIZE), EMS_PGSIZE, 0x00, MEM_FMASK_SHADOW); + } + } + /* * For each supported page (we can have a maximum of 4), * create, initialize and disable the mappings, and set @@ -1096,7 +1106,7 @@ neat_init(UNUSED(const device_t *info)) neat_log("NEAT: **INVALID DRAM SIZE %iKB !**\n", mem_size); } if (dram_mode > 0) { - neat_log("NEAT: using DRAM mode #%i (mem=%iKB)\n", i, mem_size); + neat_log("NEAT: using DRAM mode #%i (mem=%iKB)\n", dram_mode, mem_size); } /* Set up an I/O handler for the chipset. */ diff --git a/src/cpu/386.c b/src/cpu/386.c index 8b612604d..21db155e9 100644 --- a/src/cpu/386.c +++ b/src/cpu/386.c @@ -268,7 +268,8 @@ exec386_2386(int32_t cycs) } else { CHECK_READ_CS(MIN(ol, 4)); } - ins_fetch_fault = cpu_386_check_instruction_fault(); + if (is386) + ins_fetch_fault = cpu_386_check_instruction_fault(); /* Breakpoint fault has priority over other faults. */ if (ins_fetch_fault) { @@ -336,6 +337,14 @@ exec386_2386(int32_t cycs) #endif } } + } else if (new_ne) { + flags_rebuild(); + new_ne = 0; +#ifndef USE_NEW_DYNAREC + oldcs = CS; +#endif + cpu_state.oldpc = cpu_state.pc; + x86_int(16); } else if (trap) { flags_rebuild(); if (trap & 2) dr[6] |= 0x8000; diff --git a/src/cpu/386_common.c b/src/cpu/386_common.c index dcbe3608c..a68d866bf 100644 --- a/src/cpu/386_common.c +++ b/src/cpu/386_common.c @@ -72,6 +72,7 @@ extern uint8_t *pccache2; extern int optype; extern uint32_t pccache; +int new_ne = 0; int in_sys = 0; int unmask_a20_in_smm = 0; uint32_t old_rammask = 0xffffffff; diff --git a/src/cpu/386_dynarec.c b/src/cpu/386_dynarec.c index 5991cf100..c4c095735 100644 --- a/src/cpu/386_dynarec.c +++ b/src/cpu/386_dynarec.c @@ -357,6 +357,8 @@ exec386_dynarec_int(void) CPU_BLOCK_END(); if (smi_line) CPU_BLOCK_END(); + else if (new_ne) + CPU_BLOCK_END(); else if (trap) CPU_BLOCK_END(); else if (nmi && nmi_enable && nmi_mask) @@ -366,7 +368,7 @@ exec386_dynarec_int(void) } block_ended: - if (!cpu_state.abrt && trap) { + if (!cpu_state.abrt && !new_ne && trap) { # ifdef USE_DEBUG_REGS_486 //pclog("Debug trap 0x%X\n", trap); if (trap & 2) dr[6] |= 0x8000; @@ -602,6 +604,8 @@ exec386_dynarec_dyn(void) if (cpu_init) CPU_BLOCK_END(); + if (new_ne) + CPU_BLOCK_END(); if ((cpu_state.flags & T_FLAG) || (trap == 2)) CPU_BLOCK_END(); if (smi_line) @@ -626,7 +630,7 @@ exec386_dynarec_dyn(void) cpu_end_block_after_ins = 0; - if ((!cpu_state.abrt || (cpu_state.abrt & ABRT_EXPECTED)) && !x86_was_reset) + if ((!cpu_state.abrt || (cpu_state.abrt & ABRT_EXPECTED)) && !new_ne && !x86_was_reset) codegen_block_end_recompile(block); if (x86_was_reset) @@ -702,6 +706,8 @@ exec386_dynarec_dyn(void) if (cpu_init) CPU_BLOCK_END(); + if (new_ne) + CPU_BLOCK_END(); if (cpu_state.flags & T_FLAG) CPU_BLOCK_END(); if (smi_line) @@ -726,7 +732,7 @@ exec386_dynarec_dyn(void) cpu_end_block_after_ins = 0; - if ((!cpu_state.abrt || (cpu_state.abrt & ABRT_EXPECTED)) && !x86_was_reset) + if ((!cpu_state.abrt || (cpu_state.abrt & ABRT_EXPECTED)) && !new_ne && !x86_was_reset) codegen_block_end(); if (x86_was_reset) @@ -809,6 +815,15 @@ exec386_dynarec(int32_t cycs) } } + if (new_ne) { +# ifndef USE_NEW_DYNAREC + oldcs = CS; +# endif + cpu_state.oldpc = cpu_state.pc; + new_ne = 0; + x86_int(16); + } + if (smi_line) enter_smm_check(0); else if (nmi && nmi_enable && nmi_mask) { @@ -977,6 +992,15 @@ block_ended: #endif } } + } else if (new_ne) { + flags_rebuild(); + + new_ne = 0; +#ifndef USE_NEW_DYNAREC + oldcs = CS; +#endif + cpu_state.oldpc = cpu_state.pc; + x86_int(16); } else if (trap) { flags_rebuild(); #ifdef USE_DEBUG_REGS_486 diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index f969390d2..bd841ccc6 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -814,6 +814,8 @@ extern int lock_legal_80[8]; extern int lock_legal_f6[8]; extern int lock_legal_fe[8]; +extern int new_ne; + extern int in_lock; extern int cpu_override_interpreter; diff --git a/src/cpu/x86.c b/src/cpu/x86.c index 8a0bd830f..6f15560f3 100644 --- a/src/cpu/x86.c +++ b/src/cpu/x86.c @@ -271,6 +271,7 @@ reset_common(int hard) stack32 = 0; msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21); msw = 0; + new_ne = 0; if (hascache) cr0 = 1 << 30; else diff --git a/src/cpu/x86_ops_fpu.h b/src/cpu/x86_ops_fpu.h index 11f603c19..74c67c2ee 100644 --- a/src/cpu/x86_ops_fpu.h +++ b/src/cpu/x86_ops_fpu.h @@ -99,8 +99,8 @@ opWAIT(uint32_t fetchdat) if (fpu_softfloat) { if (fpu_state.swd & FPU_SW_Summary) { - if (is486 && (cr0 & 0x20)) - x86_int(16); + if (cr0 & 0x20) + new_ne = 1; else picint(1 << 13); return 1; diff --git a/src/cpu/x86_ops_fpu_2386.h b/src/cpu/x86_ops_fpu_2386.h index d8996d2e1..cc9c6dcc2 100644 --- a/src/cpu/x86_ops_fpu_2386.h +++ b/src/cpu/x86_ops_fpu_2386.h @@ -99,7 +99,10 @@ opWAIT(uint32_t fetchdat) if (fpu_softfloat) { if (fpu_state.swd & FPU_SW_Summary) { - picint(1 << 13); + if (cr0 & 0x20) + new_ne = 1; + else + picint(1 << 13); return 1; } } diff --git a/src/cpu/x87.c b/src/cpu/x87.c index 577fa1a40..3b06cab3c 100644 --- a/src/cpu/x87.c +++ b/src/cpu/x87.c @@ -355,10 +355,10 @@ FPU_exception(uint32_t fetchdat, uint16_t exceptions, int store) nmi = 1; } #else - if (is486 && (cr0 & 0x20)) - x86_int(16); - else - picint(1 << 13); + if (cr0 & 0x20) + new_ne = 1; + else + picint(1 << 13); #endif // FPU_8087 } return unmasked; diff --git a/src/cpu/x87.h b/src/cpu/x87.h index 2ad0c7b10..4d53725c9 100644 --- a/src/cpu/x87.h +++ b/src/cpu/x87.h @@ -228,8 +228,8 @@ FPU_save_regi_tag(extFloat80_t reg, int tag, int stnr) #define FPU_check_pending_exceptions() \ do { \ if (fpu_state.swd & FPU_SW_Summary) { \ - if (is486 && (cr0 & 0x20)) \ - x86_int(16); \ + if (cr0 & 0x20) \ + new_ne = 1; \ else \ picint(1 << 13); \ return 1; \ diff --git a/src/cpu/x87_ops.h b/src/cpu/x87_ops.h index e1bc5858a..d33122b5c 100644 --- a/src/cpu/x87_ops.h +++ b/src/cpu/x87_ops.h @@ -99,8 +99,8 @@ typedef union { dst = src1 / (double) src2; \ else { \ fpu_log("FPU : divide by zero\n"); \ - if (is486 && (cr0 & 0x20)) \ - x86_int(16); \ + if (cr0 & 0x20) \ + new_ne = 1; \ else \ picint(1 << 13); \ return 1; \ diff --git a/src/device/isapnp.c b/src/device/isapnp.c index 470147953..f69c69612 100644 --- a/src/device/isapnp.c +++ b/src/device/isapnp.c @@ -121,6 +121,25 @@ typedef struct { isapnp_device_t *current_ld; } isapnp_t; +static isapnp_device_t * +isapnp_create_ld(isapnp_card_t *card) +{ + /* Allocate logical device. */ + isapnp_device_t *ld = calloc(1, sizeof(isapnp_device_t)); + + /* Add to the end of the card's logical device list. */ + isapnp_device_t *prev_ld = card->first_ld; + if (prev_ld) { + while (prev_ld->next) + prev_ld = prev_ld->next; + prev_ld->next = ld; + } else { + card->first_ld = ld; + } + + return ld; +} + static void isapnp_device_config_changed(isapnp_card_t *card, isapnp_device_t *ld) { @@ -532,8 +551,12 @@ isapnp_write_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uin ld = ld->next; } - if (!ld) - isapnp_log("ISAPnP: CSN %02X has no device %02X\n", card->csn, val); + if (!ld) { + isapnp_log("ISAPnP: CSN %02X has no device %02X, creating one\n", card->csn, val); + dev->current_ld_card = card; + dev->current_ld = isapnp_create_ld(card); + dev->current_ld->number = val; + } break; @@ -763,8 +786,7 @@ isapnp_update_card_rom(void *priv, uint8_t *rom, uint16_t rom_size) uint8_t mem_range_df = 0; uint8_t mem_range_32_df = 0; uint32_t len; - isapnp_device_t *ld = NULL; - isapnp_device_t *prev_ld = NULL; + isapnp_device_t *ld = NULL; /* Check if this is an existing card which already has logical devices. Any new logical devices will be added to the list after existing ones. @@ -912,18 +934,7 @@ isapnp_update_card_rom(void *priv, uint8_t *rom, uint16_t rom_size) memset(ld->io_len, 0, sizeof(ld->io_len)); } else { /* Create logical device. */ - ld = (isapnp_device_t *) malloc(sizeof(isapnp_device_t)); - memset(ld, 0, sizeof(isapnp_device_t)); - - /* Add to end of list. */ - prev_ld = card->first_ld; - if (prev_ld) { - while (prev_ld->next) - prev_ld = prev_ld->next; - prev_ld->next = ld; - } else { - card->first_ld = ld; - } + ld = isapnp_create_ld(card); } /* Set and increment logical device number. */ diff --git a/src/include/86box/snd_ad1848.h b/src/include/86box/snd_ad1848.h index 319f9bf24..a7e38a6f8 100644 --- a/src/include/86box/snd_ad1848.h +++ b/src/include/86box/snd_ad1848.h @@ -16,7 +16,7 @@ * * Copyright 2008-2020 Sarah Walker. * Copyright 2018-2020 TheCollector1995. - * Copyright 2021 RichardG. + * Copyright 2021-2025 RichardG. */ #ifndef SOUND_AD1848_H @@ -26,8 +26,10 @@ enum { AD1848_TYPE_DEFAULT = 0, AD1848_TYPE_CS4248 = 1, AD1848_TYPE_CS4231 = 2, - AD1848_TYPE_CS4235 = 3, - AD1848_TYPE_CS4236 = 4 + AD1848_TYPE_CS4232 = 3, + AD1848_TYPE_CS4236 = 4, + AD1848_TYPE_CS4236B = 5, + AD1848_TYPE_CS4235 = 6 }; typedef struct ad1848_t { diff --git a/src/sound/snd_ad1848.c b/src/sound/snd_ad1848.c index 4b7941959..a0166c4e5 100644 --- a/src/sound/snd_ad1848.c +++ b/src/sound/snd_ad1848.c @@ -16,7 +16,7 @@ * * Copyright 2008-2020 Sarah Walker. * Copyright 2018-2020 TheCollector1995. - * Copyright 2021-2022 RichardG. + * Copyright 2021-2025 RichardG. */ #include #include @@ -33,6 +33,7 @@ #include <86box/plat_fallthrough.h> #define CS4231 0x80 +#define CS4232 0x02 #define CS4236 0x03 static int ad1848_vols_7bits[128]; @@ -57,10 +58,10 @@ ad1848_setdma(ad1848_t *ad1848, int newdma) void ad1848_updatevolmask(ad1848_t *ad1848) { - if ((ad1848->type >= AD1848_TYPE_CS4235) && ((ad1848->xregs[4] & 0x10) || ad1848->wten)) - ad1848->wave_vol_mask = 0x3f; - else + if ((ad1848->type == AD1848_TYPE_CS4236B) && !(ad1848->xregs[4] & 0x10) && !ad1848->wten) ad1848->wave_vol_mask = 0x7f; + else + ad1848->wave_vol_mask = 0x3f; } static double @@ -106,8 +107,8 @@ ad1848_updatefreq(ad1848_t *ad1848) { double freq; - if (ad1848->type >= AD1848_TYPE_CS4235) { - if (ad1848->xregs[11] & 0x20) { + if (ad1848->type >= AD1848_TYPE_CS4232) { + if (ad1848->xregs[11] & 0x20) { /* CS4236B+ only */ freq = 16934400.0; switch (ad1848->xregs[13]) { default: @@ -182,7 +183,7 @@ ad1848_read(uint16_t addr, void *priv) case 18: case 19: - if (ad1848->type >= AD1848_TYPE_CS4235) { + if (ad1848->type >= AD1848_TYPE_CS4236B) { if ((ad1848->xregs[4] & 0x14) == 0x14) /* FM remapping */ ret = ad1848->xregs[ad1848->index - 12]; /* real FM volume on registers 6 and 7 */ else if (ad1848->wten && !(ad1848->xregs[4] & 0x08)) /* wavetable remapping */ @@ -192,13 +193,13 @@ ad1848_read(uint16_t addr, void *priv) case 20: case 21: - /* Backdoor to the Control/RAM registers on CS4235. */ - if ((ad1848->type == AD1848_TYPE_CS4235) && (ad1848->xregs[18] & 0x80)) + /* Backdoor to the Control/RAM registers on CS4235+. */ + if ((ad1848->type >= AD1848_TYPE_CS4235) && (ad1848->xregs[18] & 0x80)) ret = ad1848->cram_read(ad1848->index - 15, ad1848->cram_priv); break; case 23: - if ((ad1848->type >= AD1848_TYPE_CS4235) && (ad1848->regs[23] & 0x08)) { + if ((ad1848->type >= AD1848_TYPE_CS4236B) && (ad1848->regs[23] & 0x08)) { if ((ad1848->xindex & 0xfe) == 0x00) /* remapped line volume */ ret = ad1848->regs[18 + ad1848->xindex]; else @@ -235,7 +236,7 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) 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_CS4235) + if (ad1848->type >= AD1848_TYPE_CS4236B) ad1848->regs[23] &= ~0x08; /* clear XRAE */ ad1848->trd = val & 0x20; ad1848->mce = val & 0x40; @@ -244,7 +245,7 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) case 1: switch (ad1848->index) { case 10: - if (ad1848->type < AD1848_TYPE_CS4235) + if (ad1848->type < AD1848_TYPE_CS4232) break; fallthrough; @@ -282,13 +283,13 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) case 17: /* Enable additional data formats on modes 2 and 3 where supported. */ - if ((ad1848->type == AD1848_TYPE_CS4231) || (ad1848->type == AD1848_TYPE_CS4236)) + if ((ad1848->type == AD1848_TYPE_CS4231) || (ad1848->type == AD1848_TYPE_CS4236B)) ad1848->fmt_mask = (val & 0x40) ? 0xf0 : 0x70; break; case 18: case 19: - if (ad1848->type >= AD1848_TYPE_CS4235) { + if (ad1848->type >= AD1848_TYPE_CS4236B) { if ((ad1848->xregs[4] & 0x14) == 0x14) { /* FM remapping */ ad1848->xregs[ad1848->index - 12] = val; /* real FM volume on extended registers 6 and 7 */ temp = 1; @@ -332,8 +333,8 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) case 20: case 21: - /* Backdoor to the Control/RAM registers on CS4235. */ - if ((ad1848->type == AD1848_TYPE_CS4235) && (ad1848->xregs[18] & 0x80)) { + /* Backdoor to the Control/RAM registers on CS4235+. */ + if ((ad1848->type >= AD1848_TYPE_CS4235) && (ad1848->xregs[18] & 0x80)) { ad1848->cram_write(ad1848->index - 15, val, ad1848->cram_priv); val = ad1848->regs[ad1848->index]; } @@ -344,7 +345,7 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) break; case 23: - if ((ad1848->type >= AD1848_TYPE_CS4235) && ((ad1848->regs[12] & 0x60) == 0x60)) { + if ((ad1848->type >= AD1848_TYPE_CS4236B) && ((ad1848->regs[12] & 0x60) == 0x60)) { if (!(ad1848->regs[23] & 0x08)) { /* existing (not new) XRAE is clear */ ad1848->xindex = ((val & 0x04) << 2) | (val >> 4); break; @@ -401,7 +402,11 @@ ad1848_write(uint16_t addr, uint8_t val, void *priv) case 25: return; case 27: - if (ad1848->type != AD1848_TYPE_DEFAULT) + if ((ad1848->type != AD1848_TYPE_CS4232) && (ad1848->type != AD1848_TYPE_CS4236)) + return; + break; + case 29: + if ((ad1848->type != AD1848_TYPE_CS4232) && (ad1848->type != AD1848_TYPE_CS4236)) return; break; @@ -456,17 +461,13 @@ ad1848_process_mulaw(uint8_t byte) { byte = ~byte; int temp = (((byte & 0x0f) << 3) + 0x84); - int16_t dec; temp <<= ((byte & 0x70) >> 4); temp = (byte & 0x80) ? (0x84 - temp) : (temp - 0x84); if (temp > 32767) - dec = 32767; + return 32767; else if (temp < -32768) - dec = -32768; - else - dec = (int16_t) temp; - - return dec; + return -32768; + return (int16_t) temp; } static int16_t @@ -489,8 +490,7 @@ ad1848_process_alaw(uint8_t byte) dec |= 0x108; break; } - dec = (byte & 0x80) ? dec : -dec; - return (int16_t) dec; + return (int16_t) ((byte & 0x80) ? dec : -dec); } static uint32_t @@ -704,10 +704,7 @@ ad1848_init(ad1848_t *ad1848, uint8_t type) ad1848->regs[8] = 0; ad1848->regs[9] = 0x08; ad1848->regs[10] = ad1848->regs[11] = 0; - if ((type == AD1848_TYPE_CS4248) || (type == AD1848_TYPE_CS4231) || (type >= AD1848_TYPE_CS4235)) - ad1848->regs[12] = 0x8a; - else - ad1848->regs[12] = 0xa; + ad1848->regs[12] = (type >= AD1848_TYPE_CS4248) ? 0x8a : 0xa; ad1848->regs[13] = 0; ad1848->regs[14] = ad1848->regs[15] = 0; @@ -719,27 +716,29 @@ ad1848_init(ad1848_t *ad1848, uint8_t type) ad1848->regs[25] = CS4231; ad1848->regs[26] = 0x80; ad1848->regs[29] = 0x80; - } else if (type >= AD1848_TYPE_CS4235) { + } else if (type >= AD1848_TYPE_CS4232) { ad1848->regs[16] = ad1848->regs[17] = 0; ad1848->regs[18] = ad1848->regs[19] = 0; ad1848->regs[20] = ad1848->regs[21] = 0; ad1848->regs[22] = ad1848->regs[23] = 0; ad1848->regs[24] = 0; - ad1848->regs[25] = CS4236; + ad1848->regs[25] = (type == AD1848_TYPE_CS4232) ? CS4232 : CS4236; ad1848->regs[26] = 0xa0; ad1848->regs[27] = ad1848->regs[29] = 0; ad1848->regs[30] = ad1848->regs[31] = 0; - 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; + if (type >= AD1848_TYPE_CS4236B) { + 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_updatefreq(ad1848); @@ -747,7 +746,7 @@ ad1848_init(ad1848_t *ad1848, uint8_t type) ad1848->out_l = ad1848->out_r = 0; ad1848->fm_vol_l = ad1848->fm_vol_r = 65536; ad1848_updatevolmask(ad1848); - if (type == AD1848_TYPE_CS4235) + if (type >= AD1848_TYPE_CS4235) ad1848->fmt_mask = 0x50; else ad1848->fmt_mask = 0x70; diff --git a/src/sound/snd_cs423x.c b/src/sound/snd_cs423x.c index f28be9778..0d9af74f2 100644 --- a/src/sound/snd_cs423x.c +++ b/src/sound/snd_cs423x.c @@ -15,12 +15,13 @@ * Copyright 2021-2022 RichardG. */ #include +#include #include #include #include #include #include - +#define HAVE_STDARG_H #include <86box/86box.h> #include <86box/device.h> #include <86box/dma.h> @@ -46,10 +47,19 @@ #define CRYSTAL_NOEEPROM 0x100 enum { - CRYSTAL_CS4235 = 0xdd, - CRYSTAL_CS4236B = 0xcb, + CRYSTAL_CS4232 = 0x32, /* no chip ID; dummy value */ + CRYSTAL_CS4236 = 0x36, /* no chip ID; dummy value */ + CRYSTAL_CS4236B = 0xab, /* report an older revision ID to make the values nice and incremental */ CRYSTAL_CS4237B = 0xc8, - CRYSTAL_CS4238B = 0xc9 + CRYSTAL_CS4238B = 0xc9, + CRYSTAL_CS4235 = 0xdd, + CRYSTAL_CS4239 = 0xde +}; +enum { + CRYSTAL_RAM_CMD = 0, + CRYSTAL_RAM_ADDR_LO = 1, + CRYSTAL_RAM_ADDR_HI = 2, + CRYSTAL_RAM_DATA = 3 }; enum { CRYSTAL_SLAM_NONE = 0, @@ -58,15 +68,31 @@ enum { CRYSTAL_SLAM_BYTE2 = 3 }; +#ifdef ENABLE_CS423X_LOG +int cs423x_do_log = ENABLE_CS423X_LOG; + +static void +cs423x_log(const char *fmt, ...) +{ + va_list ap; + + if (cs423x_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define cs423x_log(fmt, ...) +#endif + 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 cs4236b_eeprom[8224] = { +static const uint8_t cs4236b_default[] = { // clang-format off /* Chip configuration */ - 0x55, 0xbb, /* magic */ - 0x00, 0x00, /* length */ 0x00, 0x03, /* CD-ROM and modem decode */ 0x80, /* misc. config */ 0x80, /* global config */ @@ -77,8 +103,8 @@ static const uint8_t cs4236b_eeprom[8224] = { 0x75, 0xb9, 0xfc, /* IRQ routing */ 0x10, 0x03, /* DMA routing */ - /* PnP resources */ - 0x00 + /* Default PnP data */ + 0x0e, 0x63, 0x42, 0x35, 0xff, 0xff, 0xff, 0xff, 0x00 /* hinted by documentation to be just the header */ // clang-format on }; @@ -97,6 +123,7 @@ typedef struct cs423x_t { uint16_t ram_addr; uint16_t eeprom_size : 11; uint16_t pnp_offset; + uint16_t pnp_size; uint8_t type; uint8_t ad1848_type; uint8_t regs[8]; @@ -119,6 +146,7 @@ typedef struct cs423x_t { static void cs423x_slam_enable(cs423x_t *dev, uint8_t enable); static void cs423x_pnp_enable(cs423x_t *dev, uint8_t update_rom, uint8_t update_hwconfig); static void cs423x_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv); +static void cs423x_reset(void *priv); static void cs423x_nvram(cs423x_t *dev, uint8_t save) @@ -130,6 +158,8 @@ cs423x_nvram(cs423x_t *dev, uint8_t save) else (void) !fread(dev->eeprom_data, sizeof(dev->eeprom_data), 1, fp); fclose(fp); + } else { + cs423x_log("CS423x: EEPROM data %s failed\n", save ? "save" : "load"); } } @@ -147,17 +177,32 @@ cs423x_read(uint16_t addr, void *priv) ret |= 0x04; break; - case 4: /* Control Indirect Data Register */ - ret = dev->indirect_regs[dev->regs[3]]; + case 3: /* Control Indirect Access Register (CS4236B+) */ + /* Intel VS440FX BIOS tells CS4236 from CS4232 through the upper bits. Setting them is enough. */ + if (dev->type >= CRYSTAL_CS4236) + ret |= 0xf0; + break; + + case 4: /* Control Indirect Data Register (CS4236B+) / Control Data Register (CS4236) */ + if (dev->type >= CRYSTAL_CS4236B) + ret = dev->indirect_regs[dev->regs[3]]; break; case 5: /* Control/RAM Access */ - /* Reading RAM is undocumented; the Windows drivers do so. */ - if (dev->ram_dl == 3) - ret = dev->ram_data[dev->ram_addr++]; + /* Reading RAM is undocumented, but performed by: + - Windows drivers (unknown purpose) + - Intel VS440FX BIOS (PnP ROM checksum recalculation) */ + if (dev->ram_dl == CRYSTAL_RAM_DATA) { + ret = dev->ram_data[dev->ram_addr]; + cs423x_log("CS423x: RAM read(%04X) = %02X\n", dev->ram_addr, ret); + dev->ram_addr++; + } break; - case 7: /* Global Status */ + case 7: /* Global Status (CS4236+) */ + if (dev->type < CRYSTAL_CS4236) + break; + /* Context switching: take active context and interrupt flag, then clear interrupt flag. */ ret &= 0xc0; dev->regs[7] &= 0x80; @@ -175,6 +220,8 @@ cs423x_read(uint16_t addr, void *priv) break; } + cs423x_log("CS423x: read(%X) = %02X\n", reg, ret); + return ret; } @@ -182,53 +229,80 @@ static void cs423x_write(uint16_t addr, uint8_t val, void *priv) { cs423x_t *dev = (cs423x_t *) priv; - uint8_t reg = addr & 0x07; + uint8_t reg = addr & 7; + + cs423x_log("CS423x: write(%X, %02X)\n", reg, val); switch (reg) { + case 0: /* Joystick and Power Control */ + if (dev->type <= CRYSTAL_CS4232) + val &= 0xeb; + break; + case 1: /* EEPROM Interface */ + if (dev->type <= CRYSTAL_CS4232) + val &= 0x37; if (val & 0x04) i2c_gpio_set(dev->i2c, val & 0x01, val & 0x02); break; - case 3: /* Control Indirect Access Register */ + case 2: /* Block Power Down (CS4236+) */ + if (dev->type < CRYSTAL_CS4236) + return; + break; + + case 3: /* Control Indirect Access Register (CS4236B+) */ + if (dev->type < CRYSTAL_CS4236B) + return; val &= 0x0f; break; - case 4: /* Control Indirect Data Register */ + case 4: /* Control Indirect Data Register (CS4236B+) / Control Data Register (CS4236) */ + if (dev->type < CRYSTAL_CS4236) { + return; + } else if (dev->type == CRYSTAL_CS4236) { + val &= 0x40; + break; + } switch (dev->regs[3] & 0x0f) { case 0: /* WSS Master Control */ - if (val & 0x80) + if ((dev->type < CRYSTAL_CS4235) && (val & 0x80)) ad1848_init(&dev->ad1848, dev->ad1848_type); val = 0x00; break; - case 1: /* Version / Chip ID */ - case 7: /* Reserved */ - case 9 ... 15: /* unspecified */ + case 1: /* Version / Chip ID */ + case 7: /* Reserved */ + case 10 ... 15: /* unspecified */ return; - case 2: /* 3D Space and {Center|Volume} */ - case 6: /* Upper Channel Status */ + case 2: /* 3D Space and {Center|Volume} (CS4237B+) */ if (dev->type < CRYSTAL_CS4237B) return; break; - case 3: /* 3D Enable */ + case 3: /* 3D Enable (CS4237B+) */ if (dev->type < CRYSTAL_CS4237B) return; val &= 0xe0; break; - case 4: /* Consumer Serial Port Enable */ + case 4: /* Consumer Serial Port Enable (CS423[78]B, unused on CS4235+) */ if (dev->type < CRYSTAL_CS4237B) return; val &= 0xf0; break; - case 5: /* Lower Channel Status */ + case 5: /* Lower Channel Status (CS423[78]B, unused on CS4235+) */ + if (dev->type < CRYSTAL_CS4237B) + return; + if (dev->type < CRYSTAL_CS4235) /* bit 0 changed from reserved to unused on CS4235 */ + val &= 0xfe; + break; + + case 6: /* Upper Channel Status (CS423[78]B, unused on CS4235+) */ if (dev->type < CRYSTAL_CS4237B) return; - val &= 0xfe; break; case 8: /* CS9236 Wavetable Control */ @@ -240,6 +314,16 @@ cs423x_write(uint16_t addr, uint8_t val, void *priv) ad1848_updatevolmask(&dev->ad1848); break; + case 9: /* Power Management (CS4235+) */ + if (dev->type < CRYSTAL_CS4235) + return; + if ((dev->indirect_regs[dev->regs[3]] & 0x80) && !(val & 0x80)) { + cs423x_reset(dev); + return; + } + val &= 0x83; + break; + default: break; } @@ -248,7 +332,7 @@ cs423x_write(uint16_t addr, uint8_t val, void *priv) case 5: /* Control/RAM Access */ switch (dev->ram_dl) { - case 0: /* commands */ + case CRYSTAL_RAM_CMD: /* commands */ switch (val) { case 0x55: /* Disable PnP Key */ dev->pnp_enable = 0; @@ -266,7 +350,7 @@ cs423x_write(uint16_t addr, uint8_t val, void *priv) break; case 0xaa: /* Download RAM */ - dev->ram_dl = 1; + dev->ram_dl = CRYSTAL_RAM_ADDR_LO; break; default: @@ -274,17 +358,19 @@ cs423x_write(uint16_t addr, uint8_t val, void *priv) } break; - case 1: /* low address byte */ + case CRYSTAL_RAM_ADDR_LO: /* low address byte */ dev->ram_addr = val; - dev->ram_dl++; + dev->ram_dl = CRYSTAL_RAM_ADDR_HI; break; - case 2: /* high address byte */ - dev->ram_addr |= (val << 8); - dev->ram_dl++; + case CRYSTAL_RAM_ADDR_HI: /* high address byte */ + dev->ram_addr |= val << 8; + dev->ram_dl = CRYSTAL_RAM_DATA; + cs423x_log("CS423x: RAM start(%04X)\n", dev->ram_addr); break; - case 3: /* data */ + case CRYSTAL_RAM_DATA: /* data */ + cs423x_log("CS423x: RAM write(%04X, %02X)\n", dev->ram_addr, val); dev->ram_data[dev->ram_addr++] = val; break; @@ -296,14 +382,16 @@ cs423x_write(uint16_t addr, uint8_t val, void *priv) case 6: /* RAM Access End */ /* TriGem Delhi-III BIOS writes undocumented value 0x40 instead of 0x00. */ if ((val == 0x00) || (val == 0x40)) { - dev->ram_dl = 0; + cs423x_log("CS423x: RAM end\n"); + dev->ram_dl = CRYSTAL_RAM_CMD; /* Update PnP state and resource data. */ + dev->pnp_size = 384; /* we don't know the length */ cs423x_pnp_enable(dev, 1, 0); } break; - case 7: /* Global Status */ + case 7: /* Global Status (CS4236+) */ return; default: @@ -319,6 +407,9 @@ cs423x_slam_write(UNUSED(uint16_t addr), uint8_t val, void *priv) cs423x_t *dev = (cs423x_t *) priv; uint8_t idx; + if ((dev->slam_state != CRYSTAL_SLAM_NONE) || (val == slam_init_key[dev->key_pos])) /* cut down on ISAPnP-related noise */ + cs423x_log("CS423x: slam_write(%02X)\n", val); + switch (dev->slam_state) { case CRYSTAL_SLAM_NONE: /* Not in SLAM: read and compare Crystal key. */ @@ -333,6 +424,7 @@ cs423x_slam_write(UNUSED(uint16_t addr), uint8_t val, void *priv) } /* Enter SLAM. */ + cs423x_log("CS423x: SLAM unlocked\n"); dev->slam_state = CRYSTAL_SLAM_INDEX; } } else { @@ -343,6 +435,8 @@ cs423x_slam_write(UNUSED(uint16_t addr), uint8_t val, void *priv) case CRYSTAL_SLAM_INDEX: /* Intercept the Activate Audio Device command. */ if (val == 0x79) { + cs423x_log("CS423x: Exiting SLAM\n"); + /* Apply the last logical device's configuration. */ if (dev->slam_config) { cs423x_pnp_config_changed(dev->slam_ld, dev->slam_config, dev); @@ -363,6 +457,7 @@ cs423x_slam_write(UNUSED(uint16_t addr), uint8_t val, void *priv) case CRYSTAL_SLAM_BYTE1: case CRYSTAL_SLAM_BYTE2: /* Write register value: two bytes for I/O ports, single byte otherwise. */ + cs423x_log("CS423x: SLAM write(%02X, %02X)\n", dev->slam_reg, val); switch (dev->slam_reg) { case 0x06: /* Card Select Number */ isapnp_set_csn(dev->pnp_card, val); @@ -433,7 +528,7 @@ cs423x_slam_write(UNUSED(uint16_t addr), uint8_t val, void *priv) break; } - /* Prepare for the next register, unless a two-byte read returns above. */ + /* Prepare for the next register, unless a two-byte write returns above. */ dev->slam_state = CRYSTAL_SLAM_INDEX; break; @@ -454,8 +549,11 @@ cs423x_slam_enable(cs423x_t *dev, uint8_t enable) /* Enable SLAM if the CKD bit is not set. */ if (enable && !(dev->ram_data[0x4002] & 0x10)) { + cs423x_log("CS423x: Enabling SLAM\n"); dev->slam_enable = 1; io_sethandler(0x279, 1, NULL, NULL, NULL, cs423x_slam_write, NULL, NULL, dev); + } else { + cs423x_log("CS423x: Disabling SLAM\n"); } } @@ -471,6 +569,7 @@ cs423x_ctxswitch_write(uint16_t addr, UNUSED(uint8_t val), void *priv) /* Flip context bit. */ dev->regs[7] ^= 0x80; ctx ^= 0x80; + cs423x_log("CS423x: Context switch to %s\n", ctx ? "WSS" : "SBPro"); /* Update CD audio filter. FIXME: not thread-safe: filter function TOCTTOU in sound_cd_thread! */ @@ -502,7 +601,7 @@ cs423x_get_buffer(int32_t *buffer, int len, void *priv) ad1848_update(&dev->ad1848); /* Don't output anything if the analog section is powered down. */ - if (!(dev->indirect_regs[2] & 0xa4)) { + if (!(dev->indirect_regs[2] & 0xa4) && !(dev->indirect_regs[9] & 0x04)) { for (int c = 0; c < len * 2; c += 2) { buffer[c] += dev->ad1848.buffer[c] / 2; buffer[c + 1] += dev->ad1848.buffer[c + 1] / 2; @@ -515,35 +614,33 @@ cs423x_get_buffer(int32_t *buffer, int len, void *priv) static void cs423x_get_music_buffer(int32_t *buffer, int len, void *priv) { - cs423x_t *dev = (cs423x_t *) priv; - int opl_wss = dev->opl_wss; - const int32_t *opl_buf = NULL; + cs423x_t *dev = (cs423x_t *) priv; /* Output audio from the WSS codec, and also the OPL if we're in charge of it. */ - if (opl_wss) - opl_buf = dev->sb->opl.update(dev->sb->opl.priv); + if (dev->opl_wss) { + const int32_t *opl_buf = dev->sb->opl.update(dev->sb->opl.priv); - /* Don't output anything if the analog section is powered down. */ - if (!(dev->indirect_regs[2] & 0xa4)) { - for (int c = 0; c < len * 2; c += 2) { - if (opl_wss) { + /* Don't output anything if the analog section or DAC2 (CS4235+) is powered down. */ + if (!(dev->indirect_regs[2] & 0xa4) && !(dev->indirect_regs[9] & 0x06)) { + for (int c = 0; c < len * 2; c += 2) { buffer[c] += (opl_buf[c] * dev->ad1848.fm_vol_l) >> 16; buffer[c + 1] += (opl_buf[c + 1] * dev->ad1848.fm_vol_r) >> 16; } } - } - if (opl_wss) dev->sb->opl.reset_buffer(dev->sb->opl.priv); + } } static void cs423x_pnp_enable(cs423x_t *dev, uint8_t update_rom, uint8_t update_hwconfig) { + cs423x_log("CS423x: Updating PnP ROM=%d hwconfig=%d\n", update_rom, update_hwconfig); + if (dev->pnp_card) { /* Update PnP resource data if requested. */ if (update_rom) - isapnp_update_card_rom(dev->pnp_card, &dev->ram_data[dev->pnp_offset], 384); + isapnp_update_card_rom(dev->pnp_card, &dev->ram_data[dev->pnp_offset], dev->pnp_size); /* Disable PnP key if the PKD bit is set, or if it was disabled by command 0x55. */ /* But wait! The TriGem Delhi-III BIOS sends command 0x55, and its behavior doesn't @@ -568,7 +665,7 @@ cs423x_pnp_enable(cs423x_t *dev, uint8_t update_rom, uint8_t update_hwconfig) } /* Update SPS. */ - if (dev->type != CRYSTAL_CS4235) { + if ((dev->type >= CRYSTAL_CS4236B) && (dev->type <= CRYSTAL_CS4238B)) { if (dev->ram_data[0x4003] & 0x04) dev->indirect_regs[8] |= 0x04; else @@ -581,6 +678,14 @@ cs423x_pnp_enable(cs423x_t *dev, uint8_t update_rom, uint8_t update_hwconfig) else dev->ad1848.xregs[4] &= ~0x10; + /* Update VCEN. */ + if (dev->type == CRYSTAL_CS4236) { + if (dev->ram_data[0x4002] & 0x04) + dev->regs[4] |= 0x40; + else + dev->regs[4] &= ~0x40; + } + /* Inform WSS codec of the changes. */ ad1848_updatevolmask(&dev->ad1848); } @@ -697,9 +802,21 @@ cs423x_reset(void *priv) /* Clear RAM. */ memset(dev->ram_data, 0, sizeof(dev->ram_data)); + /* Load default configuration data to RAM. */ + memcpy(&dev->ram_data[0x4000], cs4236b_default, sizeof(cs4236b_default)); + dev->pnp_size = 9; + if (dev->eeprom) { - /* Load EEPROM data to RAM. */ - memcpy(&dev->ram_data[0x4000], &dev->eeprom_data[4], MIN(384, ((dev->eeprom_data[2] << 8) | dev->eeprom_data[3]) - 4)); + /* Load EEPROM data to RAM if the magic bytes are present. */ + if ((dev->eeprom_data[0] == 0x55) && (dev->eeprom_data[1] == 0xbb)) { + cs423x_log("CS423x: EEPROM data valid, loading to RAM\n"); + dev->pnp_size = (dev->eeprom_data[2] << 8) | dev->eeprom_data[3]; + if (dev->pnp_size > 384) + dev->pnp_size = 384; + memcpy(&dev->ram_data[0x4000], &dev->eeprom_data[4], sizeof(dev->eeprom_data) - 4); + } else { + cs423x_log("CS423x: EEPROM data invalid, ignoring\n"); + } /* Save EEPROM contents to file. */ cs423x_nvram(dev, 1); @@ -734,6 +851,7 @@ cs423x_init(const device_t *info) /* Initialize model-specific data. */ dev->type = info->local & 0xff; + cs423x_log("CS423x: init(%02X)\n", dev->type); switch (dev->type) { case CRYSTAL_CS4235: case CRYSTAL_CS4236B: @@ -743,30 +861,41 @@ cs423x_init(const device_t *info) dev->ad1848_type = (dev->type == CRYSTAL_CS4235) ? AD1848_TYPE_CS4235 : AD1848_TYPE_CS4236; dev->pnp_offset = 0x4013; - /* Different Chip Version and ID registers, which shouldn't be reset by ad1848_init */ + /* Different Chip Version and ID registers, which shouldn't be reset by ad1848_init. */ dev->ad1848.xregs[25] = dev->type; if (!(info->local & CRYSTAL_NOEEPROM)) { - /* Load EEPROM contents from template. */ - memcpy(dev->eeprom_data, cs4236b_eeprom, sizeof(cs4236b_eeprom)); + /* Copy default configuration data. */ + memcpy(&dev->eeprom_data[4], cs4236b_default, sizeof(cs4236b_default)); + /* Load PnP resource data ROM. */ FILE *fp = rom_fopen(PNP_ROM_CS4236B, "rb"); if (fp) { - (void) !fread(&(dev->eeprom_data[23]), 1, 8201, fp); + uint16_t eeprom_pnp_offset = (dev->pnp_offset & 0x1ff) + 4; + /* This is wrong. The header field only indicates PnP resource data length, and real chips use + it to locate the firmware patch area, but we don't need any of that, so we can get away + with pretending the whole ROM is PnP data, at least until we can get full EEPROM dumps. */ + dev->pnp_size = fread(&dev->eeprom_data[eeprom_pnp_offset], 1, sizeof(dev->eeprom_data) - eeprom_pnp_offset, fp); fclose(fp); } - /* Set content size. */ - dev->eeprom_data[2] = sizeof(cs4236b_eeprom) >> 8; - dev->eeprom_data[3] = sizeof(cs4236b_eeprom) & 0xff; + /* Populate EEPROM header if the PnP ROM was loaded. */ + if (dev->pnp_size) { + dev->eeprom_data[0] = 0x55; + dev->eeprom_data[1] = 0xbb; + dev->eeprom_data[2] = dev->pnp_size >> 8; + dev->eeprom_data[3] = dev->pnp_size; + } - /* Set PnP card ID and EEPROM file name. */ + /* Patch PnP ROM and set EEPROM file name. */ switch (dev->type) { case CRYSTAL_CS4235: - dev->eeprom_data[8] = 0x05; - dev->eeprom_data[16] = 0x08; - dev->eeprom_data[26] = 0x25; - dev->eeprom_data[44] = '5'; + if (dev->pnp_size) { + dev->eeprom_data[8] = 0x05; + dev->eeprom_data[16] = 0x08; + dev->eeprom_data[26] = 0x25; + dev->eeprom_data[44] = '5'; + } dev->nvr_path = "cs4235.nvr"; break; @@ -775,14 +904,18 @@ cs423x_init(const device_t *info) break; case CRYSTAL_CS4237B: - dev->eeprom_data[26] = 0x37; - dev->eeprom_data[44] = '7'; + if (dev->pnp_size) { + dev->eeprom_data[26] = 0x37; + dev->eeprom_data[44] = '7'; + } dev->nvr_path = "cs4237b.nvr"; break; case CRYSTAL_CS4238B: - dev->eeprom_data[26] = 0x38; - dev->eeprom_data[44] = '8'; + if (dev->pnp_size) { + dev->eeprom_data[26] = 0x38; + dev->eeprom_data[44] = '8'; + } dev->nvr_path = "cs4238b.nvr"; break; @@ -794,9 +927,9 @@ cs423x_init(const device_t *info) cs423x_nvram(dev, 0); } - /* Initialize game port. The '7B and '8B game port only responds to 6 I/O ports; the remaining - 2 ports are reserved on those chips, and probably connected to the Digital Assist feature. */ - dev->gameport = gameport_add(((dev->type == CRYSTAL_CS4235) || (dev->type == CRYSTAL_CS4236B)) ? &gameport_pnp_device : &gameport_pnp_6io_device); + /* Initialize game port. The game port on all B chips only + responds to 6 I/O ports; the remaining 2 are reserved. */ + dev->gameport = gameport_add((dev->type == CRYSTAL_CS4235) ? &gameport_pnp_device : &gameport_pnp_6io_device); break; @@ -807,8 +940,8 @@ cs423x_init(const device_t *info) /* Initialize I2C bus for the EEPROM. */ dev->i2c = i2c_gpio_init("nvr_cs423x"); - /* Initialize I2C EEPROM if the contents are valid. */ - if ((dev->eeprom_data[0] == 0x55) && (dev->eeprom_data[1] == 0xbb)) + /* Initialize I2C EEPROM if enabled. */ + if (!(info->local & CRYSTAL_NOEEPROM)) dev->eeprom = i2c_eeprom_init(i2c_gpio_get_bus(dev->i2c), 0x50, dev->eeprom_data, sizeof(dev->eeprom_data), 1); /* Initialize ISAPnP. */ @@ -836,6 +969,8 @@ cs423x_close(void *priv) { cs423x_t *dev = (cs423x_t *) priv; + cs423x_log("CS423x: close()\n"); + /* Save EEPROM contents to file. */ if (dev->eeprom) { cs423x_nvram(dev, 1);