This commit is contained in:
starfrost013
2025-01-20 01:09:33 +00:00
15 changed files with 353 additions and 156 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,7 +16,7 @@
*
* Copyright 2008-2020 Sarah Walker.
* Copyright 2018-2020 TheCollector1995.
* Copyright 2021-2022 RichardG.
* Copyright 2021-2025 RichardG.
*/
#include <math.h>
#include <stdint.h>
@@ -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;

View File

@@ -15,12 +15,13 @@
* Copyright 2021-2022 RichardG.
*/
#include <math.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#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);