/******************************************************** * * * PC Engine CD Command 0xD9 - SAPEP * * * ********************************************************/ static void DoNEC_PCE_SAPEP(const uint8 *cdb) { uint32 new_read_sec_end; //printf("Set audio end: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]); switch (cdb[9] & 0xc0) { default: SCSIDBG("Unknown SAPEP 9: %02x\n", cdb[9]); case 0x00: new_read_sec_end = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; break; case 0x40: new_read_sec_end = BCD_to_U8(cdb[4]) + 75 * (BCD_to_U8(cdb[3]) + 60 * BCD_to_U8(cdb[2])); new_read_sec_end -= 150; break; case 0x80: { int track = BCD_to_U8(cdb[2]); if(!track) track = 1; else if(track >= toc.last_track + 1) track = 100; new_read_sec_end = toc.tracks[track].lba; } break; } read_sec_end = new_read_sec_end; switch(cdb[1]) // PCE CD(TODO: Confirm these, and check the mode mask): { default: case 0x03: cdda.PlayMode = PLAYMODE_NORMAL; cdda.CDDAStatus = CDDASTATUS_PLAYING; break; case 0x02: cdda.PlayMode = PLAYMODE_INTERRUPT; cdda.CDDAStatus = CDDASTATUS_PLAYING; break; case 0x01: cdda.PlayMode = PLAYMODE_LOOP; cdda.CDDAStatus = CDDASTATUS_PLAYING; break; case 0x00: cdda.PlayMode = PLAYMODE_SILENT; cdda.CDDAStatus = CDDASTATUS_STOPPED; break; } SendStatusAndMessage(STATUS_GOOD, 0x00); }
/******************************************************** * * * PC Engine CD Command 0xDE - Get Directory Info * * * ********************************************************/ static void DoNEC_PCE_GETDIRINFO(const uint8 *cdb) { // Problems: // Returned data lengths on real PCE are not confirmed. // Mode 0x03 behavior not tested on real PCE uint8 data_in[2048]; uint32 data_in_size = 0; memset(data_in, 0, sizeof(data_in)); switch(cdb[1]) { default: MDFN_DispMessage("Unknown GETDIRINFO Mode: %02x", cdb[1]); printf("Unknown GETDIRINFO Mode: %02x", cdb[1]); case 0x0: data_in[0] = U8_to_BCD(toc.first_track); data_in[1] = U8_to_BCD(toc.last_track); data_in_size = 2; break; case 0x1: { uint8 m, s, f; LBA_to_AMSF(toc.tracks[100].lba, &m, &s, &f); data_in[0] = U8_to_BCD(m); data_in[1] = U8_to_BCD(s); data_in[2] = U8_to_BCD(f); data_in_size = 3; } break; case 0x2: { uint8 m, s, f; int track = BCD_to_U8(cdb[2]); if(!track) track = 1; else if(cdb[2] == 0xAA) { track = 100; } else if(track > 99) { CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); return; } LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f); data_in[0] = U8_to_BCD(m); data_in[1] = U8_to_BCD(s); data_in[2] = U8_to_BCD(f); data_in[3] = toc.tracks[track].control; data_in_size = 4; } break; } DoSimpleDataIn(data_in, data_in_size); }
/******************************************************** * * * PC Engine CD Command 0xD8 - SAPSP * * * ********************************************************/ static void DoNEC_PCE_SAPSP(const uint8 *cdb) { uint32 new_read_sec_start; //printf("Set audio start: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]); switch (cdb[9] & 0xc0) { default: SCSIDBG("Unknown SAPSP 9: %02x\n", cdb[9]); case 0x00: new_read_sec_start = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; break; case 0x40: new_read_sec_start = AMSF_to_LBA(BCD_to_U8(cdb[2]), BCD_to_U8(cdb[3]), BCD_to_U8(cdb[4])); break; case 0x80: { int track = BCD_to_U8(cdb[2]); if(!track) track = 1; else if(track >= toc.last_track + 1) track = 100; new_read_sec_start = toc.tracks[track].lba; } break; } //printf("%lld\n", (long long)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock); if(cdda.CDDAStatus == CDDASTATUS_PLAYING && new_read_sec_start == read_sec_start && ((int64)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock) < 190) { pce_lastsapsp_timestamp = monotonic_timestamp; SendStatusAndMessage(STATUS_GOOD, 0x00); CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_DONE); return; } pce_lastsapsp_timestamp = monotonic_timestamp; read_sec = read_sec_start = new_read_sec_start; read_sec_end = toc.tracks[100].lba; cdda.CDDAReadPos = 588; cdda.CDDAStatus = CDDASTATUS_PAUSED; cdda.PlayMode = PLAYMODE_SILENT; if(cdb[1]) { cdda.PlayMode = PLAYMODE_NORMAL; cdda.CDDAStatus = CDDASTATUS_PLAYING; } if(read_sec < toc.tracks[100].lba) Cur_CDIF->HintReadSector(read_sec); SendStatusAndMessage(STATUS_GOOD, 0x00); CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_DONE); }
// // Checks for Q subchannel mode 1(current time) data that has a correct checksum, but the data is nonsensical or corrupted nonetheless; this is the // case for some bad rips floating around on the Internet. Allowing these bad rips to be used will cause all sorts of problems during emulation, so we // error out here if a bad rip is detected. // // This check is not as aggressive or exhaustive as it could be, and will not detect all potential Q subchannel rip errors; as such, it should definitely NOT be // used in an effort to "repair" a broken rip. // void CDAccess_CCD::CheckSubQSanity(void) { size_t checksum_pass_counter = 0; int prev_lba = INT_MAX; uint8 prev_track = 0; for(size_t s = 0; s < img_numsectors; s++) { union { uint8 full[96]; struct { uint8 pbuf[12]; uint8 qbuf[12]; }; } buf; sub_stream->seek(s * 96, SEEK_SET); sub_stream->read(buf.full, 96); if(subq_check_checksum(buf.qbuf)) { uint8 adr = buf.qbuf[0] & 0xF; if(adr == 0x01) { uint8 track_bcd = buf.qbuf[1]; uint8 index_bcd = buf.qbuf[2]; uint8 rm_bcd = buf.qbuf[3]; uint8 rs_bcd = buf.qbuf[4]; uint8 rf_bcd = buf.qbuf[5]; uint8 am_bcd = buf.qbuf[7]; uint8 as_bcd = buf.qbuf[8]; uint8 af_bcd = buf.qbuf[9]; //printf("%2x %2x %2x\n", am_bcd, as_bcd, af_bcd); if(!BCD_is_valid(track_bcd) || !BCD_is_valid(index_bcd) || !BCD_is_valid(rm_bcd) || !BCD_is_valid(rs_bcd) || !BCD_is_valid(rf_bcd) || !BCD_is_valid(am_bcd) || !BCD_is_valid(as_bcd) || !BCD_is_valid(af_bcd) || rs_bcd > 0x59 || rf_bcd > 0x74 || as_bcd > 0x59 || af_bcd > 0x74) { throw MDFN_Error(0, _("Garbage subchannel Q data detected(bad BCD/out of range): %02x:%02x:%02x %02x:%02x:%02x"), rm_bcd, rs_bcd, rf_bcd, am_bcd, as_bcd, af_bcd); } else { int lba = ((BCD_to_U8(am_bcd) * 60 + BCD_to_U8(as_bcd)) * 75 + BCD_to_U8(af_bcd)) - 150; uint8 track = BCD_to_U8(track_bcd); prev_lba = lba; if(track < prev_track) throw MDFN_Error(0, _("Garbage subchannel Q data detected(bad track number)")); //else if(prev_track && track - pre prev_track = track; } checksum_pass_counter++; } } } //printf("%u/%u\n", checksum_pass_counter, img_numsectors); }