From 3b9a28107711cb71fcc2abda02f9308a90cfeda0 Mon Sep 17 00:00:00 2001 From: 987123879113 <63495610+987123879113@users.noreply.github.com> Date: Wed, 23 Aug 2023 02:34:11 +0900 Subject: [PATCH] machine/t10mmc.cpp: Implemented most features of T10 MMC read CD (0xbe) command. (#11499) --- src/devices/bus/ata/atapicdr.cpp | 1 + src/devices/machine/t10mmc.cpp | 472 ++++++++++++++++++++++++++++--- src/devices/machine/t10mmc.h | 21 ++ src/devices/machine/t10spc.h | 1 + src/lib/util/cdrom.h | 2 +- 5 files changed, 461 insertions(+), 36 deletions(-) diff --git a/src/devices/bus/ata/atapicdr.cpp b/src/devices/bus/ata/atapicdr.cpp index ecf4abd8af8..510b080234c 100644 --- a/src/devices/bus/ata/atapicdr.cpp +++ b/src/devices/bus/ata/atapicdr.cpp @@ -162,6 +162,7 @@ void atapi_cdrom_device::ExecCommand() case T10MMC_CMD_PLAY_AUDIO_TRACK_INDEX: case T10MMC_CMD_PAUSE_RESUME: case T10MMC_CMD_PLAY_AUDIO_12: + case T10MMC_CMD_READ_CD: case T10SBC_CMD_READ_12: if(!m_image->exists()) { diff --git a/src/devices/machine/t10mmc.cpp b/src/devices/machine/t10mmc.cpp index b9f62352c4f..3cc206b1985 100644 --- a/src/devices/machine/t10mmc.cpp +++ b/src/devices/machine/t10mmc.cpp @@ -31,6 +31,7 @@ void t10mmc::t10_start(device_t &device) device.save_item(NAME(m_cur_subblock)); device.save_item(NAME(m_audio_sense)); device.save_item(NAME(m_sotc)); + device.save_item(NAME(m_read_cd_flags)); } void t10mmc::t10_reset() @@ -50,6 +51,7 @@ void t10mmc::t10_reset() m_cur_subblock = 0; m_audio_sense = 0; m_sotc = 0; + m_read_cd_flags = 0; } // scsicd_exec_command @@ -539,58 +541,220 @@ void t10mmc::ExecCommand() break; } - // TODO: Implement reladr bit, flag bits, test and handle other conditions besides reads to "any type" sector types m_lba = command[2]<<24 | command[3]<<16 | command[4]<<8 | command[5]; m_blocks = command[6]<<16 | command[7]<<8 | command[8]; + m_read_cd_flags = command[9]; + m_transfer_length = 0; // m_device->logerror("T10MMC: READ CD start_lba[%08x] block_len[%06x] %02x %02x %02x %02x\n", m_lba, m_blocks, command[1], command[9], command[10], command[11]); - auto expected_sector_type = BIT(command[1], 2, 3); - auto trk = m_image->get_track(m_lba); - auto track_type = m_image->get_track_type(trk); - if (expected_sector_type != 0) - { - m_device->logerror("T10MMC: READ CD requested a sector type of %d which is unhandled\n", expected_sector_type); + if (command[10] != 0) + m_device->logerror("T10MMC: READ CD requested sub-channel data which is not implemented %02x\n", command[10]); - if ((expected_sector_type == 1 && track_type != cdrom_file::CD_TRACK_AUDIO) - || (expected_sector_type == 2 && track_type != cdrom_file::CD_TRACK_MODE1 && track_type != cdrom_file::CD_TRACK_MODE1_RAW) - || (expected_sector_type == 3 && track_type != cdrom_file::CD_TRACK_MODE2 && track_type != cdrom_file::CD_TRACK_MODE2_RAW) - || (expected_sector_type == 4 && track_type != cdrom_file::CD_TRACK_MODE2_FORM1) - || (expected_sector_type == 5 && track_type != cdrom_file::CD_TRACK_MODE2_FORM2)) + const int expected_sector_type = BIT(command[1], 2, 3); + int last_track_type = -1; + uint8_t last_read_cd_flags = 0; + for (int lba = m_lba; lba < m_lba + m_blocks; lba++) + { + auto trk = m_image->get_track(lba); + auto track_type = m_image->get_track_type(trk); + + // If there's a transition between CD data and CD audio anywhere in the requested range then return an error + if ((last_track_type == cdrom_file::CD_TRACK_AUDIO && track_type != cdrom_file::CD_TRACK_AUDIO) + || (last_track_type != cdrom_file::CD_TRACK_AUDIO && track_type == cdrom_file::CD_TRACK_AUDIO)) { set_sense(SCSI_SENSE_KEY_ILLEGAL_REQUEST, SCSI_SENSE_ASC_ASCQ_ILLEGAL_MODE_FOR_THIS_TRACK); m_phase = SCSI_PHASE_STATUS; m_status_code = SCSI_STATUS_CODE_CHECK_CONDITION; m_transfer_length = 0; - break; + return; } + + // Must read the subheader to figure out what sector type it is exactly when dealing with these specific track types + int mode2_form = 0; + if (track_type == cdrom_file::CD_TRACK_MODE2_RAW || track_type == cdrom_file::CD_TRACK_MODE2_FORM_MIX) + { + uint8_t tmp_buffer[2352]; + const int submode_offset = track_type == cdrom_file::CD_TRACK_MODE2_FORM_MIX ? 2 : 0x12; + + if (!m_image->read_data(lba, tmp_buffer, cdrom_file::CD_TRACK_RAW_DONTCARE)) + { + m_device->logerror("T10MMC: CD read error! (%08x)\n", lba); + return; + } + + mode2_form = BIT(tmp_buffer[submode_offset], 5) + 1; + } + + // If the expected sector type field is set then all tracks within the specified range must be the same + if ((expected_sector_type == T10MMC_READ_CD_SECTOR_TYPE_CDDA && track_type != cdrom_file::CD_TRACK_AUDIO) + || (expected_sector_type == T10MMC_READ_CD_SECTOR_TYPE_MODE1 && track_type != cdrom_file::CD_TRACK_MODE1 && track_type != cdrom_file::CD_TRACK_MODE1_RAW) + || (expected_sector_type == T10MMC_READ_CD_SECTOR_TYPE_MODE2 && track_type != cdrom_file::CD_TRACK_MODE2) + || (expected_sector_type == T10MMC_READ_CD_SECTOR_TYPE_MODE2_FORM1 && track_type != cdrom_file::CD_TRACK_MODE2_FORM1 && mode2_form != 1) + || (expected_sector_type == T10MMC_READ_CD_SECTOR_TYPE_MODE2_FORM2 && track_type != cdrom_file::CD_TRACK_MODE2_FORM2 && mode2_form != 2)) + { + set_sense(SCSI_SENSE_KEY_ILLEGAL_REQUEST, SCSI_SENSE_ASC_ASCQ_ILLEGAL_MODE_FOR_THIS_TRACK); + + m_phase = SCSI_PHASE_STATUS; + m_status_code = SCSI_STATUS_CODE_CHECK_CONDITION; + m_transfer_length = 0; + return; + } + + // No fields selected is a valid request but none of the rest of the verification code is required in that case + if (m_read_cd_flags == 0) + { + last_track_type = track_type; + continue; + } + + // Check for illegal combinations + // t10 mmc spec gives a table which shows illegal combinations and combinations that get demoted + auto read_cd_flags = m_read_cd_flags & 0xf8; + + // CDDA tracks can only ever be user data or no data (0), requesting other fields is not illegal but will be demoted to just user data + if (track_type == cdrom_file::CD_TRACK_AUDIO) + read_cd_flags = read_cd_flags ? T10MMC_READ_CD_FIELD_USER_DATA : 0; + + // All of these combinations will be illegal for all tracks besides CDDA tracks + bool is_illegal_combo = (read_cd_flags == (T10MMC_READ_CD_FIELD_HEADER | T10MMC_READ_CD_FIELD_ECC)) + || (read_cd_flags == (T10MMC_READ_CD_FIELD_SUBHEADER | T10MMC_READ_CD_FIELD_ECC)) + || (read_cd_flags == (T10MMC_READ_CD_FIELD_HEADER | T10MMC_READ_CD_FIELD_SUBHEADER | T10MMC_READ_CD_FIELD_ECC)) + || (read_cd_flags == (T10MMC_READ_CD_FIELD_SYNC | T10MMC_READ_CD_FIELD_ECC)) + || (read_cd_flags == (T10MMC_READ_CD_FIELD_SYNC | T10MMC_READ_CD_FIELD_USER_DATA)) + || (read_cd_flags == (T10MMC_READ_CD_FIELD_SYNC | T10MMC_READ_CD_FIELD_USER_DATA | T10MMC_READ_CD_FIELD_ECC)) + || (read_cd_flags == (T10MMC_READ_CD_FIELD_SYNC | T10MMC_READ_CD_FIELD_HEADER | T10MMC_READ_CD_FIELD_ECC)) + || (read_cd_flags == (T10MMC_READ_CD_FIELD_SYNC | T10MMC_READ_CD_FIELD_SUBHEADER)) + || (read_cd_flags == (T10MMC_READ_CD_FIELD_SYNC | T10MMC_READ_CD_FIELD_SUBHEADER | T10MMC_READ_CD_FIELD_ECC)) + || (read_cd_flags == (T10MMC_READ_CD_FIELD_SYNC | T10MMC_READ_CD_FIELD_SUBHEADER | T10MMC_READ_CD_FIELD_USER_DATA)) + || (read_cd_flags == (T10MMC_READ_CD_FIELD_SYNC | T10MMC_READ_CD_FIELD_SUBHEADER | T10MMC_READ_CD_FIELD_USER_DATA | T10MMC_READ_CD_FIELD_ECC)) + || (read_cd_flags == (T10MMC_READ_CD_FIELD_SYNC | T10MMC_READ_CD_FIELD_HEADER | T10MMC_READ_CD_FIELD_SUBHEADER | T10MMC_READ_CD_FIELD_ECC)); + + // Mode 2 form 1/2 sectors have additional restrictions that CDDA, mode 1, and mode 2 formless tracks don't + if (!is_illegal_combo && (track_type == cdrom_file::CD_TRACK_MODE2_FORM1 || track_type == cdrom_file::CD_TRACK_MODE2_FORM2 || mode2_form > 0)) + { + is_illegal_combo = (read_cd_flags == (T10MMC_READ_CD_FIELD_HEADER | T10MMC_READ_CD_FIELD_USER_DATA)) + || (read_cd_flags == (T10MMC_READ_CD_FIELD_HEADER | T10MMC_READ_CD_FIELD_USER_DATA | T10MMC_READ_CD_FIELD_ECC)) + || (read_cd_flags == (T10MMC_READ_CD_FIELD_SYNC | T10MMC_READ_CD_FIELD_HEADER | T10MMC_READ_CD_FIELD_USER_DATA)) + || (read_cd_flags == (T10MMC_READ_CD_FIELD_SYNC | T10MMC_READ_CD_FIELD_HEADER | T10MMC_READ_CD_FIELD_USER_DATA | T10MMC_READ_CD_FIELD_ECC)); + } + + // Mask out flags that can't be used for specific track types + if (track_type == cdrom_file::CD_TRACK_MODE1 || track_type == cdrom_file::CD_TRACK_MODE1_RAW) + { + // Sub header only is valid but will return 0 bytes, otherwise subheader is always demoted + if (read_cd_flags != T10MMC_READ_CD_FIELD_SUBHEADER) + read_cd_flags &= ~T10MMC_READ_CD_FIELD_SUBHEADER; + } + else if (track_type == cdrom_file::CD_TRACK_MODE2) + { + // Mode 2 formless + // No EDC/ECC data + read_cd_flags &= ~T10MMC_READ_CD_FIELD_ECC; + + // Sub header only is valid but will return 0 bytes, otherwise subheader is always demoted + if (read_cd_flags != T10MMC_READ_CD_FIELD_SUBHEADER) + read_cd_flags &= ~T10MMC_READ_CD_FIELD_SUBHEADER; + } + else if (track_type == cdrom_file::CD_TRACK_MODE2_FORM2 || mode2_form == 2) + { + // No EDC/ECC data + read_cd_flags &= ~T10MMC_READ_CD_FIELD_ECC; + } + + // Requested fields must be valid for all tracks within the selected range + if (is_illegal_combo || (last_track_type != -1 && read_cd_flags != last_read_cd_flags)) + { + m_device->logerror("T10MMC: READ CD called with invalid data request for given track type %d %02x\n", track_type, command[9]); + set_sense(SCSI_SENSE_KEY_ILLEGAL_REQUEST, SCSI_SENSE_ASC_ASCQ_ILLEGAL_FIELD_IN_CDB); + m_phase = SCSI_PHASE_STATUS; + m_status_code = SCSI_STATUS_CODE_CHECK_CONDITION; + m_transfer_length = 0; + return; + } + + // The actual transfer size must be calculated for every possible valid requested data + const int c2_error_codes = BIT(m_read_cd_flags, 1, 2); + const bool requested_c2 = c2_error_codes == T10MMC_READ_CD_C2_ONLY; + const bool requested_c2_error_block = c2_error_codes == T10MMC_READ_CD_C2_BLOCK; + + const bool requested_edc_ecc = (read_cd_flags & T10MMC_READ_CD_FIELD_ECC) != 0; + const bool requested_user_data = (read_cd_flags & T10MMC_READ_CD_FIELD_USER_DATA) != 0; + const bool requested_header = (read_cd_flags & T10MMC_READ_CD_FIELD_HEADER) != 0; + const bool requested_subheader = (read_cd_flags & T10MMC_READ_CD_FIELD_SUBHEADER) != 0; + const bool requested_sync = (read_cd_flags & T10MMC_READ_CD_FIELD_SYNC) != 0; + + if (requested_c2 || requested_c2_error_block) + m_transfer_length += 294; + + if (requested_c2_error_block) + m_transfer_length += 2; + + if (track_type == cdrom_file::CD_TRACK_AUDIO) + { + if (requested_user_data) + m_transfer_length += 2352; + } + else if (track_type == cdrom_file::CD_TRACK_MODE1 || track_type == cdrom_file::CD_TRACK_MODE1_RAW) + { + if (requested_sync) + m_transfer_length += 12; + if (requested_header) + m_transfer_length += 4; + if (requested_user_data) + m_transfer_length += 2048; + if (requested_edc_ecc) + m_transfer_length += 288; + } + else if (track_type == cdrom_file::CD_TRACK_MODE2) + { + if (requested_sync) + m_transfer_length += 12; + if (requested_header) + m_transfer_length += 4; + if (requested_user_data) + m_transfer_length += 2336; + } + else if (track_type == cdrom_file::CD_TRACK_MODE2_FORM1 || mode2_form == 1) + { + if (requested_sync) + m_transfer_length += 12; + if (requested_header) + m_transfer_length += 4; + if (requested_subheader) + m_transfer_length += 8; + if (requested_user_data) + m_transfer_length += 2048; + if (requested_edc_ecc) + m_transfer_length += 280; + } + else if (track_type == cdrom_file::CD_TRACK_MODE2_FORM2 || mode2_form == 2) + { + if (requested_sync) + m_transfer_length += 12; + if (requested_header) + m_transfer_length += 4; + if (requested_subheader) + m_transfer_length += 8; + if (requested_user_data) + m_transfer_length += 2328; + } + + last_track_type = track_type; + last_read_cd_flags = read_cd_flags; } - if ((track_type != cdrom_file::CD_TRACK_MODE1 && track_type != cdrom_file::CD_TRACK_MODE1_RAW) || command[9] != 0x10) - { - // TODO: Only mode 1 user data reads are supported for now - m_device->logerror("T10MMC: READ CD called with unimplemented parameters\n"); - - m_phase = SCSI_PHASE_STATUS; - m_status_code = SCSI_STATUS_CODE_CHECK_CONDITION; - m_transfer_length = 0; - break; - } - + // Only worry about SGI block extension stuff if it ever becomes an issue if (m_num_subblocks > 1) - { - m_cur_subblock = m_lba % m_num_subblocks; - m_lba /= m_num_subblocks; - } - else - { - m_cur_subblock = 0; - } + m_device->logerror("T10MMC: READ CD does not handle sub blocks currently\n"); + // All fields were matched between all tracks, so store the simplified version + m_read_cd_flags = last_read_cd_flags | (m_read_cd_flags & ~0xf8); + + m_cur_subblock = 0; m_phase = SCSI_PHASE_DATAIN; m_status_code = SCSI_STATUS_CODE_GOOD; - m_transfer_length = m_blocks * m_sector_bytes; break; } @@ -606,7 +770,7 @@ void t10mmc::ExecCommand() void t10mmc::ReadData( uint8_t *data, int dataLength ) { uint32_t temp; - uint8_t tmp_buffer[2048]; + uint8_t tmp_buffer[2352]; switch ( command[0] ) { @@ -640,7 +804,6 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) case T10SBC_CMD_READ_10: case T10SBC_CMD_READ_12: - case T10MMC_CMD_READ_CD: // TODO: Will need its own logic once more support is implemented //m_device->logerror("T10MMC: read %x dataLength lba=%x\n", dataLength, m_lba); if ((m_image) && (m_blocks)) { @@ -672,6 +835,245 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) } break; + case T10MMC_CMD_READ_CD: + //m_device->logerror("T10MMC: read CD %x dataLength lba=%x\n", dataLength, m_lba); + if ((m_image) && (m_blocks)) + { + const int c2_error_codes = BIT(m_read_cd_flags, 1, 2); + const bool requested_c2 = c2_error_codes == T10MMC_READ_CD_C2_ONLY; + const bool requested_c2_error_block = c2_error_codes == T10MMC_READ_CD_C2_BLOCK; + + const bool requested_edc_ecc = (m_read_cd_flags & T10MMC_READ_CD_FIELD_ECC) != 0; + const bool requested_user_data = (m_read_cd_flags & T10MMC_READ_CD_FIELD_USER_DATA) != 0; + const bool requested_header = (m_read_cd_flags & T10MMC_READ_CD_FIELD_HEADER) != 0; + const bool requested_subheader = (m_read_cd_flags & T10MMC_READ_CD_FIELD_SUBHEADER) != 0; + const bool requested_sync = (m_read_cd_flags & T10MMC_READ_CD_FIELD_SYNC) != 0; + + // m_device->logerror("T10MMC: read CD flags c2[%d] c2block[%d] edc/ecc[%d] user[%d] header[%d] subheader[%d] sync[%d]\n", requested_c2, requested_c2_error_block, requested_edc_ecc, requested_user_data, requested_header, requested_subheader, requested_sync); + + if (m_read_cd_flags == 0) + { + // No data is supposed to be returned + if (dataLength > 0) + memset(data, 0, dataLength); + + data += dataLength; + dataLength = 0; + } + + while (dataLength > 0) + { + int data_idx = 0; + auto trk = m_image->get_track(m_lba); + auto track_type = m_image->get_track_type(trk); + + // Some CHDs don't have the required data so just log the error and return zeros as required + // CD_TRACK_MODE1: Only has user data + // CD_TRACK_MODE1_RAW: Has all fields required + // CD_TRACK_MODE2: Only has user data + // CD_TRACK_MODE2_FORM1: ? + // CD_TRACK_MODE2_FORM2: ? + // CD_TRACK_MODE2_FORM_MIX: Subheader data + user data + EDC/ECC data + // CD_TRACK_MODE2_RAW: Has all fields required + // CD_TRACK_AUDIO: Only returns user data + + auto read_track_type = track_type; + if (track_type == cdrom_file::CD_TRACK_MODE1) + read_track_type = cdrom_file::CD_TRACK_MODE1_RAW; // mode1 has code for partial promotion to mode1_raw so make use of it + + if (!m_image->read_data(m_lba, tmp_buffer, read_track_type)) + { + m_device->logerror("T10MMC: CD read error! (%08x)\n", m_lba); + return; + } + + int mode2_form = 0; + if (track_type == cdrom_file::CD_TRACK_MODE2_RAW) + mode2_form = BIT(tmp_buffer[0x12], 5) + 1; + else if (track_type == cdrom_file::CD_TRACK_MODE2_FORM_MIX) + mode2_form = BIT(tmp_buffer[2], 5) + 1; + + if (requested_sync) + { + if (track_type == cdrom_file::CD_TRACK_MODE2 || track_type == cdrom_file::CD_TRACK_MODE2_FORM1 || track_type == cdrom_file::CD_TRACK_MODE2_FORM2 || track_type == cdrom_file::CD_TRACK_MODE2_FORM_MIX) + { + m_device->logerror("T10MMC: sync data is not available for track type %d, inserting fake sync data\n", track_type); + constexpr uint8_t sync_field[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; + memcpy(data + data_idx, sync_field, std::size(sync_field)); + } + else + memcpy(data + data_idx, tmp_buffer, 12); + + data_idx += 12; + } + + if (requested_header) + { + if (track_type == cdrom_file::CD_TRACK_MODE2 || track_type == cdrom_file::CD_TRACK_MODE2_FORM1 || track_type == cdrom_file::CD_TRACK_MODE2_FORM2 || track_type == cdrom_file::CD_TRACK_MODE2_FORM_MIX) + { + m_device->logerror("T10MMC: header data is not available for track type %d, inserting fake header data\n", track_type); + + uint32_t msf = to_msf(m_lba); + data[data_idx] = BIT(msf, 16, 8); + data[data_idx+1] = BIT(msf, 8, 8); + data[data_idx+2] = BIT(msf, 0, 8); + data[data_idx+3] = 2; // mode 2 + } + else + memcpy(data + data_idx, tmp_buffer + 12, 4); + + data_idx += 4; + } + + if (requested_subheader) + { + if (track_type == cdrom_file::CD_TRACK_MODE2_RAW) + { + memcpy(data + data_idx, tmp_buffer + 16, 8); + } + else if (track_type == cdrom_file::CD_TRACK_MODE2_FORM_MIX) + { + // Only been able to verify 1 CHD for form mix, appears to have the 8 subheader bytes at the top of the sector + memcpy(data + data_idx, tmp_buffer, 8); + } + else if (track_type == cdrom_file::CD_TRACK_MODE2_FORM1 || track_type == cdrom_file::CD_TRACK_MODE2_FORM2) + { + // It's not possible to generate a fake subheader for mode 2 tracks because the submode byte contains detailed info + // about what actual data is inside the block + m_device->logerror("T10MMC: subheader data is not available for track type %d\n", track_type); + memset(data + data_idx, 0, 8); + + if (track_type == cdrom_file::CD_TRACK_MODE2_FORM2) + data[data_idx+2] = data[data_idx+6] = 1 << 5; // The form 2 flag can at least be set accurately + } + + // Mode 1 and mode 2 formless return 0 bytes + if (track_type != cdrom_file::CD_TRACK_MODE1 && track_type != cdrom_file::CD_TRACK_MODE1_RAW && track_type != cdrom_file::CD_TRACK_MODE2) + data_idx += 8; + } + + if (requested_user_data) + { + int buffer_offset = 0; + int data_len = 0; + + if (track_type == cdrom_file::CD_TRACK_AUDIO) + { + buffer_offset = 0; + data_len = 2352; + } + else if (track_type == cdrom_file::CD_TRACK_MODE1) + { + buffer_offset = 0; + data_len = 2048; + } + else if (track_type == cdrom_file::CD_TRACK_MODE1_RAW) + { + buffer_offset = 16; + data_len = 2048; + } + else if ((track_type == cdrom_file::CD_TRACK_MODE2_RAW && mode2_form == 1)) + { + buffer_offset = 24; + data_len = 2048; + } + else if ((track_type == cdrom_file::CD_TRACK_MODE2_RAW && mode2_form == 2)) + { + buffer_offset = 24; + data_len = 2328; + } + else if (track_type == cdrom_file::CD_TRACK_MODE2_FORM_MIX && mode2_form == 1) + { + buffer_offset = 8; + data_len = 2048; + } + else if (track_type == cdrom_file::CD_TRACK_MODE2_FORM_MIX && mode2_form == 2) + { + buffer_offset = 8; + data_len = 2328; + } + else if (track_type == cdrom_file::CD_TRACK_MODE2) + { + // Untested + buffer_offset = 0; + data_len = 2336; + } + else if (track_type == cdrom_file::CD_TRACK_MODE2_FORM1) + { + // Untested + m_device->logerror("T10MMC: reading user data from untested mode 2 form 1 track\n"); + buffer_offset = 0; + data_len = 2048; + } + else if (track_type == cdrom_file::CD_TRACK_MODE2_FORM2) + { + // Untested + m_device->logerror("T10MMC: reading user data from untested mode 2 form 2 track\n"); + buffer_offset = 0; + data_len = 2324; + } + + memcpy(data + data_idx, tmp_buffer + buffer_offset, data_len); + data_idx += data_len; + + if (track_type == cdrom_file::CD_TRACK_MODE2_FORM2) + { + // Untested, but the sector size of 2324 as noted in cdrom.h + // implies the lack of the last 4 bytes for the (optional) CRC + memset(data + data_idx, 0, 4); + data_idx += 4; + } + } + + if (requested_edc_ecc) + { + if (track_type == cdrom_file::CD_TRACK_MODE1_RAW) + { + // Includes the 8 bytes of padding + memcpy(data + data_idx, tmp_buffer + 16 + 2048, 288); + } + else if (track_type == cdrom_file::CD_TRACK_MODE2_RAW && mode2_form == 1) + { + memcpy(data + data_idx, tmp_buffer + 24 + 2048, 280); + } + else if (track_type == cdrom_file::CD_TRACK_MODE2_FORM_MIX && mode2_form == 1) + { + memcpy(data + data_idx, tmp_buffer + 8 + 2048, 280); + } + else + { + m_device->logerror("T10MMC: EDC/ECC data is not available for track type %d\n", track_type); + memset(data + data_idx, 0, 280); + } + + data_idx += 280; + } + + if (requested_c2 || requested_c2_error_block) + { + m_device->logerror("T10MMC: C2 field is not supported, returning zero data\n"); + memset(data + data_idx, 0, 294); + data_idx += 294; + } + + if (requested_c2_error_block) + { + m_device->logerror("T10MMC: error block field is not supported, returning zero data\n"); + memset(data + data_idx, 0, 2); + data_idx += 2; + } + + m_lba++; + m_blocks--; + + m_last_lba = m_lba; + dataLength -= data_idx; + data += data_idx; + data_idx = 0; + } + } + break; + case T10MMC_CMD_READ_SUB_CHANNEL: switch (command[3]) { diff --git a/src/devices/machine/t10mmc.h b/src/devices/machine/t10mmc.h index 99f7813c314..9b84e929c29 100644 --- a/src/devices/machine/t10mmc.h +++ b/src/devices/machine/t10mmc.h @@ -48,6 +48,26 @@ protected: T10MMC_CMD_READ_CD = 0xbe }; + enum + { + T10MMC_READ_CD_SECTOR_TYPE_ANY = 0, + T10MMC_READ_CD_SECTOR_TYPE_CDDA = 1, + T10MMC_READ_CD_SECTOR_TYPE_MODE1 = 2, + T10MMC_READ_CD_SECTOR_TYPE_MODE2 = 3, + T10MMC_READ_CD_SECTOR_TYPE_MODE2_FORM1 = 4, + T10MMC_READ_CD_SECTOR_TYPE_MODE2_FORM2 = 5, + + // These shouldn't be treated as masks, 3 is a reserved value + T10MMC_READ_CD_C2_ONLY = 1, // C2 error bits (294 bytes) + T10MMC_READ_CD_C2_BLOCK = 2, // C2 error bits (294 bytes) + block error byte (logical OR of all the C2 error bit bytes) + 1 padding byte + + T10MMC_READ_CD_FIELD_ECC = 0x08, // EDC/ECC + T10MMC_READ_CD_FIELD_USER_DATA = 0x10, + T10MMC_READ_CD_FIELD_HEADER = 0x20, + T10MMC_READ_CD_FIELD_SUBHEADER = 0x40, + T10MMC_READ_CD_FIELD_SYNC = 0x80, + }; + enum toc_format_t { TOC_FORMAT_TRACKS = 0, @@ -70,6 +90,7 @@ protected: uint32_t m_cur_subblock; int m_audio_sense; int m_sotc; + uint8_t m_read_cd_flags; device_t *m_device; }; diff --git a/src/devices/machine/t10spc.h b/src/devices/machine/t10spc.h index e2073eff1b5..18d764aa0b6 100644 --- a/src/devices/machine/t10spc.h +++ b/src/devices/machine/t10spc.h @@ -50,6 +50,7 @@ protected: SCSI_SENSE_ASC_ASCQ_AUDIO_PLAY_OPERATION_PAUSED = 0x0012, SCSI_SENSE_ASC_ASCQ_AUDIO_PLAY_OPERATION_SUCCESSFULLY_COMPLETED = 0x0013, SCSI_SENSE_ASC_ASCQ_AUDIO_PLAY_OPERATION_STOPPED_DUE_TO_ERROR = 0x0014, + SCSI_SENSE_ASC_ASCQ_ILLEGAL_FIELD_IN_CDB = 0x2400, SCSI_SENSE_ASC_ASCQ_ILLEGAL_MODE_FOR_THIS_TRACK = 0x6400 }; diff --git a/src/lib/util/cdrom.h b/src/lib/util/cdrom.h index f8bdb29b93b..214a7ed39d6 100644 --- a/src/lib/util/cdrom.h +++ b/src/lib/util/cdrom.h @@ -38,7 +38,7 @@ public: CD_TRACK_MODE2_FORM1, /* mode 2 2048 bytes/sector */ CD_TRACK_MODE2_FORM2, /* mode 2 2324 bytes/sector */ CD_TRACK_MODE2_FORM_MIX, /* mode 2 2336 bytes/sector */ - CD_TRACK_MODE2_RAW, /* mode 2 2352 bytes / sector */ + CD_TRACK_MODE2_RAW, /* mode 2 2352 bytes/sector */ CD_TRACK_AUDIO, /* redbook audio track 2352 bytes/sector (588 samples) */ CD_TRACK_RAW_DONTCARE /* special flag for cdrom_read_data: just return me whatever is there */