[Core/CD] added limited support for LC89513K extended registers (only when Wondermega M2, X'Eye, CDX or Multi-Mega BIOS is detected) and improved accuracy of Main-CPU & Sub-CPU access to CDC registers (fixes mcd-verificator CDC REGS tests)

This commit is contained in:
ekeeke 2023-11-25 19:09:03 +01:00
parent b330eb85cf
commit fcb6620202
12 changed files with 181 additions and 83 deletions

View File

@ -20,8 +20,10 @@ Genesis Plus GX 1.7.5 (xx/xx/xxxx) (Eke-Eke)
* added configurable CD-DA and PCM outputs mixing volume
* added setting to enable/disable CD access time simulation
* added emulation of Word-RAM access limitations in 2M mode (fixes graphical issues in Marko's Magic Football)
* added limited support for LC89513K extended registers when Wondermega M2, X'Eye, CDX or Multi-Mega BIOS is detected (fixes Krikzz's mcd-verificator CDC REGS tests)
* improved Timer interrupt timings and CDD interrupt accuracy (fixes audio stutters during Popful Mail FMV)
* improved CDC emulation (fixes random freezes during Jeopardy & ESPN Sunday Night NFL intro)
* improved accuracy of Main-CPU & Sub-CPU access to CDC registers (verified on real hardware, cf. Krikzz's mcd-verificator)
* improved emulation of mirrored memory areas
* improved savestate format
* improved Sub-CPU synchronization with Main-CPU (fixes "Soul Star")

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 MiB

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 MiB

After

Width:  |  Height:  |  Size: 4.0 MiB

View File

@ -68,6 +68,18 @@
void cdc_init(void)
{
memset(&cdc, 0, sizeof(cdc_t));
/* autodetect CDC configuration */
if ((scd.type == CD_TYPE_WONDERMEGA_M2) || (scd.type == CD_TYPE_CDX))
{
/* LC89513K chip (Wondermega M2 / X'Eye / CDX / Multi-Mega) */
cdc.ar_mask = 0x1f;
}
else
{
/* LC8951 or LC89515 chip (default)*/
cdc.ar_mask = 0x0f;
}
}
void cdc_reset(void)
@ -140,7 +152,19 @@ int cdc_context_save(uint8 *state)
tmp8 = 0;
}
save_param(&cdc, sizeof(cdc));
save_param(&cdc.ifstat, sizeof(cdc.ifstat));
save_param(&cdc.ifctrl, sizeof(cdc.ifctrl));
save_param(&cdc.dbc, sizeof(cdc.dbc));
save_param(&cdc.dac, sizeof(cdc.dac));
save_param(&cdc.pt, sizeof(cdc.pt));
save_param(&cdc.wa, sizeof(cdc.wa));
save_param(&cdc.ctrl, sizeof(cdc.ctrl));
save_param(&cdc.head, sizeof(cdc.head));
save_param(&cdc.stat, sizeof(cdc.stat));
save_param(&cdc.cycles, sizeof(cdc.cycles));
save_param(&cdc.dma_w, sizeof(cdc.dma_w));
save_param(&cdc.ram, sizeof(cdc.ram));
save_param(&tmp8, 1);
return bufferptr;
@ -151,7 +175,19 @@ int cdc_context_load(uint8 *state)
uint8 tmp8;
int bufferptr = 0;
load_param(&cdc, sizeof(cdc));
load_param(&cdc.ifstat, sizeof(cdc.ifstat));
load_param(&cdc.ifctrl, sizeof(cdc.ifctrl));
load_param(&cdc.dbc, sizeof(cdc.dbc));
load_param(&cdc.dac, sizeof(cdc.dac));
load_param(&cdc.pt, sizeof(cdc.pt));
load_param(&cdc.wa, sizeof(cdc.wa));
load_param(&cdc.ctrl, sizeof(cdc.ctrl));
load_param(&cdc.head, sizeof(cdc.head));
load_param(&cdc.stat, sizeof(cdc.stat));
load_param(&cdc.cycles, sizeof(cdc.cycles));
load_param(&cdc.dma_w, sizeof(cdc.dma_w));
load_param(&cdc.ram, sizeof(cdc.ram));
load_param(&tmp8, 1);
switch (tmp8)
@ -333,9 +369,9 @@ void cdc_decoder_update(uint32 header)
void cdc_reg_w(unsigned char data)
{
#ifdef LOG_CDC
error("CDC register %X write 0x%04x (%X)\n", scd.regs[0x04>>1].byte.l & 0x0F, data, s68k.pc);
error("CDC register %d write 0x%04x (%X)\n", scd.regs[0x04>>1].byte.l, data, s68k.pc);
#endif
switch (scd.regs[0x04>>1].byte.l & 0x0F)
switch (scd.regs[0x04>>1].byte.l)
{
case 0x01: /* IFCTRL */
{
@ -370,28 +406,23 @@ void cdc_reg_w(unsigned char data)
}
cdc.ifctrl = data;
scd.regs[0x04>>1].byte.l = 0x02;
break;
}
case 0x02: /* DBCL */
cdc.dbc.byte.l = data;
scd.regs[0x04>>1].byte.l = 0x03;
break;
case 0x03: /* DBCH */
cdc.dbc.byte.h = data;
scd.regs[0x04>>1].byte.l = 0x04;
cdc.dbc.byte.h = data & 0x0f;
break;
case 0x04: /* DACL */
cdc.dac.byte.l = data;
scd.regs[0x04>>1].byte.l = 0x05;
break;
case 0x05: /* DACH */
cdc.dac.byte.h = data;
scd.regs[0x04>>1].byte.l = 0x06;
break;
case 0x06: /* DTRG */
@ -402,9 +433,6 @@ void cdc_reg_w(unsigned char data)
/* set !DTBSY and !DTEN */
cdc.ifstat &= ~(BIT_DTBSY | BIT_DTEN);
/* clear DBCH bits 4-7 */
cdc.dbc.byte.h &= 0x0f;
/* clear EDT & DSR bits (SCD register $04) */
scd.regs[0x04>>1].byte.h &= 0x07;
@ -470,7 +498,6 @@ void cdc_reg_w(unsigned char data)
}
}
scd.regs[0x04>>1].byte.l = 0x07;
break;
}
@ -479,9 +506,6 @@ void cdc_reg_w(unsigned char data)
/* clear pending data transfer end interrupt */
cdc.ifstat |= BIT_DTEI;
/* clear DBCH bits 4-7 */
cdc.dbc.byte.h &= 0x0f;
#if 0
/* no pending decoder interrupt ? */
if ((cdc.ifstat | BIT_DECI) || !(cdc.ifctrl & BIT_DECIEN))
@ -493,18 +517,15 @@ void cdc_reg_w(unsigned char data)
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
#endif
scd.regs[0x04>>1].byte.l = 0x08;
break;
}
case 0x08: /* WAL */
cdc.wa.byte.l = data;
scd.regs[0x04>>1].byte.l = 0x09;
break;
case 0x09: /* WAH */
cdc.wa.byte.h = data;
scd.regs[0x04>>1].byte.l = 0x0a;
break;
case 0x0a: /* CTRL0 */
@ -525,7 +546,6 @@ void cdc_reg_w(unsigned char data)
}
cdc.ctrl[0] = data;
scd.regs[0x04>>1].byte.l = 0x0b;
break;
}
@ -544,96 +564,125 @@ void cdc_reg_w(unsigned char data)
}
cdc.ctrl[1] = data;
scd.regs[0x04>>1].byte.l = 0x0c;
break;
}
case 0x0c: /* PTL */
cdc.pt.byte.l = data;
scd.regs[0x04>>1].byte.l = 0x0d;
break;
case 0x0d: /* PTH */
cdc.pt.byte.h = data;
scd.regs[0x04>>1].byte.l = 0x0e;
break;
case 0x0e: /* CTRL2 (unused) */
scd.regs[0x04>>1].byte.l = 0x0f;
break;
case 0x0f: /* RESET */
cdc_reset();
break;
default: /* by default, SBOUT is not used */
default: /* unemulated registers*/
break;
}
/* increment address register (except when register #0 is selected) */
if (scd.regs[0x04>>1].byte.l)
{
scd.regs[0x04>>1].byte.l = (scd.regs[0x04>>1].byte.l + 1) & cdc.ar_mask;
}
}
unsigned char cdc_reg_r(void)
{
switch (scd.regs[0x04>>1].byte.l & 0x0F)
uint8 data;
switch (scd.regs[0x04>>1].byte.l)
{
case 0x01: /* IFSTAT */
scd.regs[0x04>>1].byte.l = 0x02;
return cdc.ifstat;
{
data = cdc.ifstat;
break;
}
case 0x02: /* DBCL */
scd.regs[0x04>>1].byte.l = 0x03;
return cdc.dbc.byte.l;
{
data = cdc.dbc.byte.l;
break;
}
case 0x03: /* DBCH */
scd.regs[0x04>>1].byte.l = 0x04;
return cdc.dbc.byte.h;
{
data = cdc.dbc.byte.h;
break;
}
case 0x04: /* HEAD0 */
scd.regs[0x04>>1].byte.l = 0x05;
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][0];
{
data = cdc.head[cdc.ctrl[1] & BIT_SHDREN][0];
break;
}
case 0x05: /* HEAD1 */
scd.regs[0x04>>1].byte.l = 0x06;
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][1];
{
data = cdc.head[cdc.ctrl[1] & BIT_SHDREN][1];
break;
}
case 0x06: /* HEAD2 */
scd.regs[0x04>>1].byte.l = 0x07;
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][2];
{
data = cdc.head[cdc.ctrl[1] & BIT_SHDREN][2];
break;
}
case 0x07: /* HEAD3 */
scd.regs[0x04>>1].byte.l = 0x08;
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][3];
{
data = cdc.head[cdc.ctrl[1] & BIT_SHDREN][3];
break;
}
case 0x08: /* PTL */
scd.regs[0x04>>1].byte.l = 0x09;
return cdc.pt.byte.l;
{
data = cdc.pt.byte.l;
break;
}
case 0x09: /* PTH */
scd.regs[0x04>>1].byte.l = 0x0a;
return cdc.pt.byte.h;
{
data = cdc.pt.byte.h;
break;
}
case 0x0a: /* WAL */
scd.regs[0x04>>1].byte.l = 0x0b;
return cdc.wa.byte.l;
{
data = cdc.wa.byte.l;
break;
}
case 0x0b: /* WAH */
scd.regs[0x04>>1].byte.l = 0x0c;
return cdc.wa.byte.h;
{
data = cdc.wa.byte.h;
break;
}
case 0x0c: /* STAT0 */
scd.regs[0x04>>1].byte.l = 0x0d;
return cdc.stat[0];
{
data = cdc.stat[0];
break;
}
case 0x0d: /* STAT1 (always return 0) */
scd.regs[0x04>>1].byte.l = 0x0e;
return 0x00;
{
data = 0x00;
break;
}
case 0x0e: /* STAT2 */
scd.regs[0x04>>1].byte.l = 0x0f;
return cdc.stat[2];
{
data = cdc.stat[2];
break;
}
case 0x0f: /* STAT3 */
{
uint8 data = cdc.stat[3];
data = cdc.stat[3];
/* clear !VALST (note: this is not 100% correct but BIOS do not seem to care) */
cdc.stat[3] = BIT_VALST;
@ -653,13 +702,27 @@ unsigned char cdc_reg_r(void)
}
#endif
scd.regs[0x04>>1].byte.l = 0x00;
return data;
break;
}
default: /* by default, COMIN is always empty */
return 0xff;
default: /* unemulated registers */
{
data = 0xff;
break;
}
}
#ifdef LOG_CDC
error("CDC register %d read 0x%02X (%X)\n", scd.regs[0x04>>1].byte.l, data, s68k.pc);
#endif
/* increment address register (except when register #0 is selected) */
if (scd.regs[0x04>>1].byte.l)
{
scd.regs[0x04>>1].byte.l = (scd.regs[0x04>>1].byte.l + 1) & cdc.ar_mask;
}
return data;
}
unsigned short cdc_host_r(void)

View File

@ -55,6 +55,7 @@ typedef struct
int cycles;
void (*dma_w)(unsigned int length); /* DMA transfer callback */
uint8 ram[0x4000 + 2352]; /* 16K external RAM (with one block overhead to handle buffer overrun) */
uint8 ar_mask;
} cdc_t;
/* Function prototypes */

View File

@ -70,11 +70,6 @@
#define CD_TRAY 0x0E /* unused */
#define CD_TEST 0x0F /* unusec */
/* CD-DA digital filter types */
#define CD_TYPE_DEFAULT 0x00
#define CD_TYPE_WONDERMEGA 0x01
#define CD_TYPE_WONDERMEGA_M2 0x02
/* CD track */
typedef struct
{
@ -116,7 +111,6 @@ typedef struct
{
uint32 cycles;
uint32 latency;
int type;
int loaded;
int index;
int lba;

View File

@ -584,14 +584,10 @@ static unsigned int scd_read_byte(unsigned int address)
return scd.regs[0x58>>1].byte.h;
}
/* CDC register data (controlled by BIOS, byte access only ?) */
/* CDC register data */
if (address == 0x07)
{
unsigned int data = cdc_reg_r();
#ifdef LOG_CDC
error("CDC register %X read 0x%02X (%X)\n", scd.regs[0x04>>1].byte.l & 0x0F, data, s68k.pc);
#endif
return data;
return cdc_reg_r();
}
/* LED status */
@ -725,6 +721,12 @@ static unsigned int scd_read_word(unsigned int address)
return data;
}
/* CDC register data */
if (address == 0x06)
{
return cdc_reg_r();
}
/* MAIN-CPU communication words */
if ((address & 0x1f0) == 0x10)
{
@ -1036,6 +1038,12 @@ static void scd_write_byte(unsigned int address, unsigned int data)
return;
}
case 0x05: /* CDC register address */
{
scd.regs[0x04 >> 1].byte.l = data & cdc.ar_mask;
return;
}
case 0x07: /* CDC register write */
{
cdc_reg_w(data);
@ -1339,6 +1347,12 @@ static void scd_write_word(unsigned int address, unsigned int data)
return;
}
case 0x04: /* CDC mode & register address */
{
scd.regs[0x04 >> 1].w = data & (0x0700 | cdc.ar_mask);
return;
}
case 0x06: /* CDC register write */
{
cdc_reg_w(data);
@ -1406,7 +1420,7 @@ static void scd_write_word(unsigned int address, unsigned int data)
case 0x34: /* CD Fader */
{
/* Wondermega hardware (CXD2554M digital filter) */
if (cdd.type == CD_TYPE_WONDERMEGA)
if (scd.type == CD_TYPE_WONDERMEGA)
{
/* only MSB is latched by CXD2554M chip, LSB is ignored (8-bit digital filter) */
/* attenuator data is 7-bit only (bits 0-7) */
@ -1417,7 +1431,7 @@ static void scd_write_word(unsigned int address, unsigned int data)
}
/* Wondermega M2 / X'Eye hardware (SM5841A digital filter) */
else if (cdd.type == CD_TYPE_WONDERMEGA_M2)
else if (scd.type == CD_TYPE_WONDERMEGA_M2)
{
/* only MSB is latched by SM5841A chip, LSB is ignored (8-bit digital filter) */
data = data >> 8;

View File

@ -1,6 +1,6 @@
/***************************************************************************************
* Genesis Plus
* Mega CD / Sega CD hardware
* Mega-CD / Sega CD hardware
*
* Copyright (C) 2012-2023 Eke-Eke (Genesis Plus GX)
*
@ -50,6 +50,12 @@
#define scd ext.cd_hw
#endif
/* CD hardware models */
#define CD_TYPE_DEFAULT 0x00
#define CD_TYPE_WONDERMEGA 0x01
#define CD_TYPE_WONDERMEGA_M2 0x02
#define CD_TYPE_CDX 0x03
/* CD hardware Master Clock (50 MHz) */
#define SCD_CLOCK 50000000
@ -76,6 +82,7 @@ typedef struct
int32 timer; /* Timer counter */
uint8 pending; /* Pending interrupts */
uint8 dmna; /* Pending DMNA write status */
uint8 type; /* CD hardware model */
gfx_t gfx_hw; /* Graphics processor */
cdc_t cdc_hw; /* CD data controller */
cdd_t cdd_hw; /* CD drive processor */

View File

@ -3,7 +3,7 @@
* ROM Loading Support
*
* Copyright (C) 1998-2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2022 Eke-Eke (Genesis Plus GX)
* Copyright (C) 2007-2023 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
@ -422,17 +422,22 @@ int load_bios(int system)
if (!memcmp (&scd.bootrom[0x120], "WONDER-MEGA BOOT", 16))
{
/* Wondermega CD hardware */
cdd.type = CD_TYPE_WONDERMEGA;
scd.type = CD_TYPE_WONDERMEGA;
}
else if (!memcmp (&scd.bootrom[0x120], "WONDERMEGA2 BOOT", 16))
{
/* Wondermega M2 / X'Eye CD hardware */
cdd.type = CD_TYPE_WONDERMEGA_M2;
scd.type = CD_TYPE_WONDERMEGA_M2;
}
else if (!memcmp (&scd.bootrom[0x120], "CDX BOOT ROM ", 16))
{
/* CDX / Multi-Mega CD hardware */
scd.type = CD_TYPE_CDX;
}
else
{
/* default CD hardware */
cdd.type = CD_TYPE_DEFAULT;
scd.type = CD_TYPE_DEFAULT;
}
#ifdef LSB_FIRST

View File

@ -3,7 +3,7 @@
* ROM Loading Support
*
* Copyright (C) 1998-2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2022 Eke-Eke (Genesis Plus GX)
* Copyright (C) 2007-2023 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:

View File

@ -376,6 +376,12 @@ unsigned int ctrl_io_read_byte(unsigned int address)
return scd.regs[0x04>>1].byte.h & 0xc7;
}
/* CDC register address (not accessible from MAIN-CPU) */
if (index == 0x05)
{
return 0x00;
}
/* SUB-CPU communication flags */
if (index == 0x0f)
{
@ -546,6 +552,12 @@ unsigned int ctrl_io_read_word(unsigned int address)
return (scd.regs[0x0c>>1].w + ((cycles - scd.stopwatch) / TIMERS_SCYCLES_RATIO)) & 0xfff;
}
/* CDC Mode (CDC register address not accessible from MAIN-CPU) */
if (index == 0x04)
{
return (scd.regs[index >> 1].byte.h << 8);
}
/* default registers */
if (index < 0x30)
{