[Core/CD] added support for CD-ROM Mode 2 (Form 1 & 2) image files (fixes EBXA disc support in Wonder Library)

This commit is contained in:
ekeeke 2019-11-25 19:30:07 +01:00
parent 364d186789
commit 863c7254c9
6 changed files with 168 additions and 106 deletions

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 MiB

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

After

Width:  |  Height:  |  Size: 3.9 MiB

View File

@ -51,7 +51,6 @@
/* CTRL0 register bitmasks */
#define BIT_DECEN 0x80
#define BIT_E01RQ 0x20
#define BIT_AUTORQ 0x10
#define BIT_WRRQ 0x04
@ -60,7 +59,7 @@
#define BIT_FORMRQ 0x04
#define BIT_SHDREN 0x01
/* CTRL2 register bitmask */
/* STAT3 register bitmask */
#define BIT_VALST 0x80
/* TODO: figure exact DMA transfer rate */
@ -248,7 +247,7 @@ void cdc_decoder_update(uint32 header)
/* data decoding enabled ? */
if (cdc.ctrl[0] & BIT_DECEN)
{
/* update HEAD registers */
/* update HEADx registers with current block header */
*(uint32 *)(cdc.head[0]) = header;
/* set !VALST */
@ -285,18 +284,50 @@ void cdc_decoder_update(uint32 header)
/* CDC buffer address */
offset = cdc.pt.w & 0x3fff;
/* write CDD block header (4 bytes) */
/* write current block header to RAM buffer (4 bytes) */
*(uint32 *)(cdc.ram + offset) = header;
offset += 4;
/* write CDD block data (2048 bytes) */
cdd_read_data(cdc.ram + 4 + offset);
/* check decoded block mode */
if (cdc.head[0][3] == 0x01)
{
/* write Mode 1 user data to RAM buffer (2048 bytes) */
cdd_read_data(cdc.ram + offset, NULL);
offset += 2048;
}
else
{
/* check if CD-ROM Mode 2 decoding is enabled */
if (cdc.ctrl[1] & BIT_MODRQ)
{
/* update HEADx registers with current block sub-header & write Mode 2 user data to RAM buffer (max 2328 bytes) */
cdd_read_data(cdc.ram + offset + 8, cdc.head[1]);
/* write current block sub-header to RAM buffer (4 bytes x 2) */
*(uint32 *)(cdc.ram + offset) = *(uint32 *)(cdc.head[1]);
*(uint32 *)(cdc.ram + offset + 4) = *(uint32 *)(cdc.head[1]);
offset += 2336;
}
else
{
/* update HEADx registers with current block sub-header & write Mode 2 user data to RAM buffer (max 2328 bytes) */
/* NB: when Mode 2 decoding is disabled, sub-header is apparently not written to RAM buffer (required by Wonder Library) */
cdd_read_data(cdc.ram + offset, cdc.head[1]);
offset += 2328;
}
/* set STAT2 register FORM bit according to sub-header FORM bit when CTRL0 register AUTORQ bit is set */
if (cdc.ctrl[0] & BIT_AUTORQ)
{
cdc.stat[2] = (cdc.ctrl[1] & BIT_MODRQ) | ((cdc.head[1][2] & 0x20) >> 3);
}
}
/* take care of buffer overrun */
offset = offset + 2048 + 4 - 0x4000;
if (offset > 0)
if (offset > 0x4000)
{
/* data should be written at the start of buffer */
memcpy(cdc.ram, cdc.ram + 0x4000, offset);
memcpy(cdc.ram, cdc.ram + 0x4000, offset - 0x4000);
}
}
}
@ -484,15 +515,15 @@ void cdc_reg_w(unsigned char data)
/* set CRCOK bit only if decoding is enabled */
cdc.stat[0] = data & BIT_DECEN;
/* update decoding mode */
/* update STAT2 register */
if (data & BIT_AUTORQ)
{
/* set MODE bit according to CTRL1 register & clear FORM bit */
cdc.stat[2] = cdc.ctrl[1] & BIT_MODRQ;
/* set MODE bit according to CTRL1 register MODRQ bit & set FORM bit according to sub-header FORM bit*/
cdc.stat[2] = (cdc.ctrl[1] & BIT_MODRQ) | ((cdc.head[1][2] & 0x20) >> 3);
}
else
{
/* set MODE & FORM bits according to CTRL1 register */
/* set MODE & FORM bits according to CTRL1 register MODRQ & FORMRQ bits */
cdc.stat[2] = cdc.ctrl[1] & (BIT_MODRQ | BIT_FORMRQ);
}
@ -503,15 +534,15 @@ void cdc_reg_w(unsigned char data)
case 0x0b: /* CTRL1 */
{
/* update decoding mode */
/* update STAT2 register */
if (cdc.ctrl[0] & BIT_AUTORQ)
{
/* set MODE bit according to CTRL1 register & clear FORM bit */
cdc.stat[2] = data & BIT_MODRQ;
/* set MODE bit according to CTRL1 register MODRQ bit & set FORM bit according to sub-header FORM bit*/
cdc.stat[2] = (data & BIT_MODRQ) | ((cdc.head[1][2] & 0x20) >> 3);
}
else
{
/* set MODE & FORM bits according to CTRL1 register */
/* set MODE & FORM bits according to CTRL1 register MODRQ & FORMRQ bits */
cdc.stat[2] = data & (BIT_MODRQ | BIT_FORMRQ);
}

View File

@ -47,7 +47,9 @@
#define CD_SCAN_SPEED 30
/* CD tracks type (CD-DA by default) */
#define TYPE_CDROM 0x01
#define TYPE_AUDIO 0x00
#define TYPE_MODE1 0x01
#define TYPE_MODE2 0x02
/* BCD conversion lookup tables */
static const uint8 lut_BCD_8[100] =
@ -394,19 +396,31 @@ int cdd_load(char *filename, char *header)
}
else
{
/* COOKED format (2048 bytes data blocks) */
if (!strcmp(type, "MODE1"))
cdd.sectorSize = 2048;
/* RAW format (2352 bytes data blocks) */
else if (!strcmp(type, "MODE1_RAW"))
if (!strcmp(type, "MODE1_RAW"))
{
/* Mode 1 RAW format (2352 bytes data blocks) */
cdd.sectorSize = 2352;
/* unsupported track format */
cdd.toc.tracks[0].type = TYPE_MODE1;
}
else if (!strcmp(type, "MODE1"))
{
/* Mode 1 COOKED format (2048 bytes data blocks) */
cdd.sectorSize = 2048;
cdd.toc.tracks[0].type = TYPE_MODE1;
}
else if (!strcmp(type, "MODE2_RAW"))
{
/* Mode 2 RAW format (2352 bytes data blocks) */
cdd.sectorSize = 2352;
cdd.toc.tracks[0].type = TYPE_MODE2;
}
else if (strcmp(type, "AUDIO"))
{
/* unsupported track format */
break;
/* Data track start LBA (2s pause assumed by default) */
}
/* First track start LBA (2s pause assumed by default) */
cdd.toc.tracks[0].start = 0;
}
@ -442,9 +456,6 @@ int cdd_load(char *filename, char *header)
/* copy CD image header + security code (skip RAW sector 16-byte header) */
memcpy(header, cdd.chd.hunk + (cdd.toc.tracks[0].offset % cdd.chd.hunkbytes) + ((cdd.sectorSize == 2048) ? 0 : 16), 0x210);
/* there is a valid DATA track */
cdd.toc.tracks[0].type = TYPE_CDROM;
}
/* valid CD image ? */
@ -473,40 +484,43 @@ int cdd_load(char *filename, char *header)
{
int len;
static const uint8 sync[12] = {0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00};
/* read first 16 bytes */
cdStreamRead(header, 0x10, 1, fd);
/* look for valid CD image identifier */
/* auto-detect valid Sega CD image */
if (!memcmp("SEGADISCSYSTEM", header, 14))
{
/* COOKED format (2048 bytes data blocks) */
cdd.sectorSize = 2048;
}
else
{
/* read next 16 bytes */
cdStreamRead(header, 0x10, 1, fd);
/* look for valid CD image identifier */
if (!memcmp("SEGADISCSYSTEM", header, 14))
{
/* RAW format (2352 bytes data blocks) */
cdd.sectorSize = 2352;
}
}
/* valid CD image file ? */
if (cdd.sectorSize)
{
/* read CD image header + security code */
/* COOKED CD-ROM image (2048 bytes data blocks) */
cdd.sectorSize = 2048;
/* CD-ROM Mode 1 by default */
cdd.toc.tracks[0].type = TYPE_MODE1;
}
/* auto-detect CD-ROM synchro pattern */
else if (!memcmp(sync, header, 12))
{
/* RAW CD-ROM image (2352 bytes data blocks) */
cdd.sectorSize = 2352;
/* auto-detect CD-ROM mode from block header (byte 15) */
cdd.toc.tracks[0].type = header[15];
/* read next 16 bytes (start of user data) */
cdStreamRead(header, 0x10, 1, fd);
}
/* supported CD-ROM image file ? */
if ((cdd.toc.tracks[0].type == TYPE_MODE1) || (cdd.toc.tracks[0].type == TYPE_MODE2))
{
/* read Sega CD image header + security code */
cdStreamRead(header + 0x10, 0x200, 1, fd);
/* initialize first track file descriptor */
cdd.toc.tracks[0].fd = fd;
/* this is a valid DATA track */
cdd.toc.tracks[0].type = TYPE_CDROM;
/* DATA track end LBA (based on DATA file length) */
cdStreamSeek(fd, 0, SEEK_END);
cdd.toc.tracks[0].end = cdStreamTell(fd) / cdd.sectorSize;
@ -527,7 +541,7 @@ int cdd_load(char *filename, char *header)
}
else
{
/* this is not a CD image file */
/* this is not a supported CD-ROM image file */
isCDfile = 0;
/* close file */
@ -615,7 +629,7 @@ int cdd_load(char *filename, char *header)
unsigned char head[12];
cdStreamRead(head, 12, 1, cdd.toc.tracks[cdd.toc.last].fd);
cdStreamSeek(cdd.toc.tracks[cdd.toc.last].fd, 0, SEEK_SET);
/* autodetect WAVE file */
if (!memcmp(head, "RIFF", 4) && !memcmp(head + 8, "WAVE", 4))
{
@ -673,42 +687,35 @@ int cdd_load(char *filename, char *header)
/* decode TRACK commands */
else if ((sscanf(lptr, "TRACK %02d %*s", &bb)) || (sscanf(lptr, "TRACK %d %*s", &bb)))
{
/* check track number */
if (bb != (cdd.toc.last + 1))
{
/* close any opened file */
if (cdd.toc.tracks[cdd.toc.last].fd)
{
cdStreamClose(cdd.toc.tracks[cdd.toc.last].fd);
cdd.toc.tracks[cdd.toc.last].fd = 0;
}
/* missing tracks */
break;
}
/* autodetect DATA track (first track only) */
/* autodetect DATA track type (first track only) */
if (!cdd.toc.last)
{
/* CD-ROM Mode 1 support only */
if (strstr(lptr,"MODE1/2048"))
{
/* COOKED format (2048 bytes / block) */
/* Mode 1 COOKED format (2048 bytes / block) */
cdd.sectorSize = 2048;
cdd.toc.tracks[0].type = TYPE_MODE1;
}
else if (strstr(lptr,"MODE1/2352"))
{
/* RAW format (2352 bytes / block) */
/* Mode 1 RAW format (2352 bytes / block) */
cdd.sectorSize = 2352;
/* skip 16-byte header */
cdStreamSeek(cdd.toc.tracks[0].fd, 0x10, SEEK_SET);
cdd.toc.tracks[0].type = TYPE_MODE1;
}
else if (strstr(lptr,"MODE2/2352"))
{
/* Mode 2 RAW format (2352 bytes / block) */
cdd.sectorSize = 2352;
cdd.toc.tracks[0].type = TYPE_MODE2;
}
if (cdd.sectorSize)
{
/* this is a valid DATA track */
cdd.toc.tracks[0].type = TYPE_CDROM;
if (cdd.sectorSize == 2352)
{
/* skip 16-byte header */
cdStreamSeek(cdd.toc.tracks[0].fd, 0x10, SEEK_SET);
}
/* read CD image header + security code */
cdStreamRead(header, 0x210, 1, cdd.toc.tracks[0].fd);
@ -888,7 +895,7 @@ int cdd_load(char *filename, char *header)
unsigned char head[12];
cdStreamRead(head, 12, 1, fd);
cdStreamSeek(fd, 0, SEEK_SET);
/* autodetect WAVE file */
if (!memcmp(head, "RIFF", 4) && !memcmp(head + 8, "WAVE", 4))
{
@ -1039,8 +1046,8 @@ int cdd_load(char *filename, char *header)
/* CD mounted */
cdd.loaded = 1;
/* Valid DATA track found ? */
if (cdd.toc.tracks[0].type)
/* Valid CD-ROM Mode 1 track found ? */
if (cdd.toc.tracks[0].type == TYPE_MODE1)
{
/* simulate audio tracks if none found */
if (cdd.toc.last == 1)
@ -1210,7 +1217,7 @@ void cdd_unload(void)
cdd.sectorSize = 0;
}
void cdd_read_data(uint8 *dst)
void cdd_read_data(uint8 *dst, uint8 *subheader)
{
/* only allow reading (first) CD-ROM track sectors */
if (cdd.toc.tracks[cdd.index].type && (cdd.lba >= 0))
@ -1231,36 +1238,60 @@ void cdd_read_data(uint8 *dst)
cdd.chd.hunknum = hunknum;
}
/* copy Mode 1 sector data (2048 bytes only) */
/* check sector size */
if (cdd.sectorSize == 2048)
{
/* Mode 1 COOKED data (ISO) */
/* read Mode 1 user data (2048 bytes) */
memcpy(dst, cdd.chd.hunk + (offset % cdd.chd.hunkbytes), 2048);
}
else
{
/* Mode 1 RAW data (skip 16-byte header) */
memcpy(dst, cdd.chd.hunk + (offset % cdd.chd.hunkbytes) + 16, 2048);
/* check if sub-header is required (Mode 2 sector only) */
if (!subheader)
{
/* read Mode 1 user data (2048 bytes), skipping block sync pattern (12 bytes) + block header (4 bytes)*/
memcpy(dst, cdd.chd.hunk + (offset % cdd.chd.hunkbytes) + 12 + 4, 2048);
}
else
{
/* read Mode 2 sub-header (first 4 bytes), skipping block sync pattern (12 bytes) + block header (4 bytes)*/
memcpy(subheader, cdd.chd.hunk + (offset % cdd.chd.hunkbytes) + 12 + 4, 4);
/* read Mode 2 user data (max 2328 bytes), skipping Mode 2 sub-header (8 bytes) */
memcpy(dst, cdd.chd.hunk + (offset % cdd.chd.hunkbytes) + 12 + 4 + 8, 2328);
}
}
return;
}
#endif
/* seek current track sector */
/* check sector size */
if (cdd.sectorSize == 2048)
{
/* Mode 1 COOKED data (ISO) */
/* read Mode 1 user data (2048 bytes) */
cdStreamSeek(cdd.toc.tracks[0].fd, cdd.lba * 2048, SEEK_SET);
cdStreamRead(dst, 2048, 1, cdd.toc.tracks[0].fd);
}
else
{
/* Mode 1 RAW data (skip 16-byte header) */
cdStreamSeek(cdd.toc.tracks[0].fd, cdd.lba * 2352 + 16, SEEK_SET);
}
/* check if sub-header is required (Mode 2 sector only) */
if (!subheader)
{
/* skip block sync pattern (12 bytes) + block header (4 bytes) then read Mode 1 user data (2048 bytes) */
cdStreamSeek(cdd.toc.tracks[0].fd, (cdd.lba * 2352) + 12 + 4, SEEK_SET);
cdStreamRead(dst, 2048, 1, cdd.toc.tracks[0].fd);
}
else
{
/* skip block sync pattern (12 bytes) + block header (4 bytes) + Mode 2 sub-header (first 4 bytes) then read Mode 2 sub-header (last 4 bytes) */
cdStreamSeek(cdd.toc.tracks[0].fd, (cdd.lba * 2352) + 12 + 4 + 4, SEEK_SET);
cdStreamRead(subheader, 4, 1, cdd.toc.tracks[0].fd);
/* read Mode 1 sector data (2048 bytes only) */
cdStreamRead(dst, 2048, 1, cdd.toc.tracks[0].fd);
/* read Mode 2 user data (max 2328 bytes) */
cdStreamRead(dst, 2328, 1, cdd.toc.tracks[0].fd);
}
}
}
}
@ -1580,13 +1611,13 @@ void cdd_update(void)
/* track type */
if (cdd.toc.tracks[cdd.index].type)
{
/* CD-ROM (Mode 1) sector header */
/* CD-ROM sector header */
uint8 header[4];
uint32 msf = cdd.lba + 150;
header[0] = lut_BCD_8[(msf / 75) / 60];
header[1] = lut_BCD_8[(msf / 75) % 60];
header[2] = lut_BCD_8[(msf % 75)];
header[3] = 0x01;
header[3] = cdd.toc.tracks[cdd.index].type;
/* decode CD-ROM track sector */
cdc_decoder_update(*(uint32 *)(header));
@ -1720,7 +1751,7 @@ void cdd_update(void)
}
/* AUDIO track playing ? */
scd.regs[0x36>>1].byte.h = cdd.toc.tracks[cdd.index].type;
scd.regs[0x36>>1].byte.h = cdd.toc.tracks[cdd.index].type ? 0x01 : 0x00;
/* seek to current subcode position */
if (cdd.toc.sub)
@ -1819,7 +1850,7 @@ void cdd_process(void)
scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60];
scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60];
scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)];
scd.regs[0x40>>1].byte.h = cdd.toc.tracks[cdd.index].type << 2; /* Current block flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */
scd.regs[0x40>>1].byte.h = cdd.toc.tracks[cdd.index].type ? 0x04 : 0x00; /* Current block flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */
break;
}
@ -1830,7 +1861,7 @@ void cdd_process(void)
scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60];
scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60];
scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)];
scd.regs[0x40>>1].byte.h = cdd.toc.tracks[cdd.index].type << 2; /* Current block flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */
scd.regs[0x40>>1].byte.h = cdd.toc.tracks[cdd.index].type ? 0x04 : 0x00; /* Current block flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */
break;
}
@ -1873,7 +1904,7 @@ void cdd_process(void)
scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60];
scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60];
scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)];
scd.regs[0x3e>>1].byte.h |= (cdd.toc.tracks[track-1].type << 3); /* RS6 bit 3 is set for CD-ROM track */
scd.regs[0x3e>>1].byte.h |= cdd.toc.tracks[track-1].type ? 0x08 : 0x00; /* RS6 bit 3 is set for CD-ROM track */
scd.regs[0x40>>1].byte.h = track % 10; /* Track Number (low digit) */
break;
}
@ -1973,14 +2004,14 @@ void cdd_process(void)
if (cdd.chd.file)
{
/* CHD file offset */
cdd.chd.hunkofs = cdd.toc.tracks[cdd.index].offset + (lba * CD_FRAME_SIZE);
cdd.chd.hunkofs = cdd.toc.tracks[index].offset + (lba * CD_FRAME_SIZE);
}
else
#endif
if (cdd.toc.tracks[index].type)
{
/* DATA track */
cdStreamSeek(cdd.toc.tracks[0].fd, lba * cdd.sectorSize, SEEK_SET);
cdStreamSeek(cdd.toc.tracks[index].fd, lba * cdd.sectorSize, SEEK_SET);
}
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
else if (cdd.toc.tracks[index].vf.seekable)
@ -2079,14 +2110,14 @@ void cdd_process(void)
if (cdd.chd.file)
{
/* CHD file offset */
cdd.chd.hunkofs = cdd.toc.tracks[cdd.index].offset + (lba * CD_FRAME_SIZE);
cdd.chd.hunkofs = cdd.toc.tracks[index].offset + (lba * CD_FRAME_SIZE);
}
else
#endif
if (cdd.toc.tracks[index].type)
{
/* DATA track */
cdStreamSeek(cdd.toc.tracks[0].fd, lba * cdd.sectorSize, SEEK_SET);
cdStreamSeek(cdd.toc.tracks[index].fd, lba * cdd.sectorSize, SEEK_SET);
}
#if defined(USE_LIBTREMOR) || defined(USE_LIBVORBIS)
else if (cdd.toc.tracks[index].vf.seekable)

View File

@ -136,7 +136,7 @@ extern int cdd_context_save(uint8 *state);
extern int cdd_context_load(uint8 *state);
extern int cdd_load(char *filename, char *header);
extern void cdd_unload(void);
extern void cdd_read_data(uint8 *dst);
extern void cdd_read_data(uint8 *dst, uint8 *subheader);
extern void cdd_read_audio(unsigned int samples);
extern void cdd_update(void);
extern void cdd_process(void);