diff --git a/builds/genesis_plus_gx_libretro.dll b/builds/genesis_plus_gx_libretro.dll index 0d2dd3f..a65abd2 100644 Binary files a/builds/genesis_plus_gx_libretro.dll and b/builds/genesis_plus_gx_libretro.dll differ diff --git a/builds/genplus_cube.dol b/builds/genplus_cube.dol index 002e7d1..8aaa54e 100644 Binary files a/builds/genplus_cube.dol and b/builds/genplus_cube.dol differ diff --git a/builds/genplus_wii.dol b/builds/genplus_wii.dol index 450ce2c..39526e7 100644 Binary files a/builds/genplus_wii.dol and b/builds/genplus_wii.dol differ diff --git a/core/cd_hw/cdc.c b/core/cd_hw/cdc.c index 6945186..4036c49 100644 --- a/core/cd_hw/cdc.c +++ b/core/cd_hw/cdc.c @@ -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); } diff --git a/core/cd_hw/cdd.c b/core/cd_hw/cdd.c index d64c79a..fa9f004 100644 --- a/core/cd_hw/cdd.c +++ b/core/cd_hw/cdd.c @@ -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) diff --git a/core/cd_hw/cdd.h b/core/cd_hw/cdd.h index f07a75c..101aab4 100644 --- a/core/cd_hw/cdd.h +++ b/core/cd_hw/cdd.h @@ -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);