diff options
Diffstat (limited to 'src/devices/machine/t10mmc.cpp')
-rw-r--r-- | src/devices/machine/t10mmc.cpp | 532 |
1 files changed, 410 insertions, 122 deletions
diff --git a/src/devices/machine/t10mmc.cpp b/src/devices/machine/t10mmc.cpp index 7797156f823..423d7b7147c 100644 --- a/src/devices/machine/t10mmc.cpp +++ b/src/devices/machine/t10mmc.cpp @@ -5,15 +5,30 @@ #include "multibyte.h" -static int to_msf(int frame) +static int32_t to_msf_raw(int32_t frame) { - int m = frame / (75 * 60); - int s = (frame / 75) % 60; - int f = frame % 75; - + const int m = (frame / (75 * 60)) % 100; + const int s = (frame / 75) % 60; + const int f = frame % 75; return (m << 16) | (s << 8) | f; } +static int32_t to_msf(int32_t frame) +{ + int32_t adjusted_frame = frame + 150; + if (adjusted_frame < 0) + adjusted_frame += 450000; + return to_msf_raw(adjusted_frame); +} + +static int32_t to_lba(int32_t msf) +{ + int32_t lba = cdrom_file::msf_to_lba(msf) - 150; + if (BIT(msf, 16, 8) >= 90) // 90:00:00 and later + lba -= 450000; + return lba; +} + void t10mmc::set_model(std::string model_name) { m_model_name = model_name; @@ -240,6 +255,20 @@ void t10mmc::ExecCommand() length = 4 + (8 * 1); break; + case TOC_FORMAT_FULL_TOC: + { + const int total_tracks = m_image->get_last_track(); + const int total_sessions = m_image->get_last_session(); + const int total_a0s = total_sessions * 3; // every session has a set of a0/a1/a2 + const int total_b0s = (total_sessions - 1) * 2; // every additional session starts with a b0/c0 set + length = 4 + (11 * (total_tracks + total_a0s + total_b0s)); + break; + } + + case TOC_FORMAT_CDTEXT: + length = 4; + break; + default: m_device->logerror("T10MMC: Unhandled READ TOC format %d\n", toc_format()); length = 0; @@ -272,6 +301,12 @@ void t10mmc::ExecCommand() m_lba = get_u32be(&command[2]); m_blocks = SCSILengthFromUINT16( &command[7] ); + if (m_lba == 0xffffffff) + { + m_device->logerror("T10MMC: play audio from current not fully implemented!\n"); + m_lba = m_cdda->get_audio_lba(); + } + if (m_lba == 0) { // A request for LBA 0 will return something different depending on the type of media being played. @@ -282,10 +317,6 @@ void t10mmc::ExecCommand() else m_lba = 150; } - else if (m_lba == 0xffffffff) - { - m_device->logerror("T10MMC: play audio from current not implemented!\n"); - } //m_device->logerror("T10MMC: PLAY AUDIO(10) at LBA %x for %x blocks\n", m_lba, m_blocks); @@ -308,16 +339,29 @@ void t10mmc::ExecCommand() break; case T10MMC_CMD_PLAY_AUDIO_MSF: + { + m_phase = SCSI_PHASE_STATUS; + m_transfer_length = 0; + if (!m_image->exists()) { - m_phase = SCSI_PHASE_STATUS; m_status_code = SCSI_STATUS_CODE_CHECK_CONDITION; - m_transfer_length = 0; break; } - m_lba = (command[5] % 75) + ((command[4] * 75) % (60*75)) + (command[3] * (75*60)); - m_blocks = (command[8] % 75) + ((command[7] * 75) % (60*75)) + (command[6] * (75*60)) - m_lba; + uint32_t msf_start = get_u24be(&command[3]); + const uint32_t msf_end = get_u24be(&command[6]); + + if (msf_start == 0xffffff) + { + // when start MSF is set to all FFs, the starting address becomes the current optical head location + m_lba = m_cdda->get_audio_lba(); + msf_start = to_msf(m_lba); + } + + // note: BeOS's CD player sends the start MSF + a large end MSF (99:59:71) when a scan is ended and it wants to resume playback + m_lba = to_lba(msf_start); + m_blocks = cdrom_file::msf_to_lba(msf_end - msf_start); if (m_lba == 0) { @@ -326,31 +370,40 @@ void t10mmc::ExecCommand() else m_lba = 150; } - else if (m_lba == 0xffffffff) - { - m_device->logerror("T10MMC: play audio from current not implemented!\n"); - } - - //m_device->logerror("T10MMC: PLAY AUDIO MSF at LBA %x for %x blocks (MSF %i:%i:%i - %i:%i:%i)\n", - //m_lba, m_blocks, command[3], command[4], command[5], command[6], command[7], command[8]); trk = m_image->get_track(m_lba); - if (m_image->get_track_type(trk) == cdrom_file::CD_TRACK_AUDIO) + //m_device->logerror("T10MMC: PLAY AUDIO MSF at LBA %x (track %d) for %x blocks (MSF %02d:%02d:%02d - %02d:%02d:%02d)\n", + //m_lba, trk + 1, m_blocks, command[3], command[4], command[5], command[6], command[7], command[8]); + + if (msf_start == msf_end) { - m_cdda->start_audio(m_lba, m_blocks); - m_audio_sense = SCSI_SENSE_ASC_ASCQ_AUDIO_PLAY_OPERATION_IN_PROGRESS; + // audio start operation does not happen but also isn't an error + m_device->logerror("T10MMC: track is not played\n"); + m_status_code = SCSI_STATUS_CODE_GOOD; + m_audio_sense = SCSI_SENSE_ASC_ASCQ_NO_SENSE; } - else + else if (msf_start > msf_end) + { + m_device->logerror("T10MMC: track starts after requested end time!\n"); + m_status_code = SCSI_STATUS_CODE_CHECK_CONDITION; + set_sense(SCSI_SENSE_KEY_ILLEGAL_REQUEST, SCSI_SENSE_ASC_ASCQ_ILLEGAL_FIELD_IN_CDB); + } + else if (m_image->get_track_type(trk) != cdrom_file::CD_TRACK_AUDIO) { m_device->logerror("T10MMC: track is NOT audio!\n"); + m_status_code = SCSI_STATUS_CODE_CHECK_CONDITION; set_sense(SCSI_SENSE_KEY_ILLEGAL_REQUEST, SCSI_SENSE_ASC_ASCQ_ILLEGAL_MODE_FOR_THIS_TRACK); } + else + { + m_cdda->start_audio(m_lba, m_blocks); + m_status_code = SCSI_STATUS_CODE_GOOD; + m_audio_sense = SCSI_SENSE_ASC_ASCQ_AUDIO_PLAY_OPERATION_IN_PROGRESS; + } - m_phase = SCSI_PHASE_STATUS; - m_status_code = SCSI_STATUS_CODE_GOOD; - m_transfer_length = 0; break; + } case T10MMC_CMD_PLAY_AUDIO_TRACK_INDEX: if (!m_image->exists()) @@ -430,6 +483,8 @@ void t10mmc::ExecCommand() break; case T10MMC_CMD_STOP_PLAY_SCAN: + m_last_lba = m_cdda->get_audio_lba(); + abort_audio(); //m_device->logerror("T10MMC: STOP_PLAY_SCAN\n"); @@ -463,6 +518,12 @@ void t10mmc::ExecCommand() m_lba = get_u32be(&command[2]); m_blocks = get_u32be(&command[6]); + if (m_lba == 0xffffffff) + { + m_device->logerror("T10MMC: play audio from current not fully implemented!\n"); + m_lba = m_cdda->get_audio_lba(); + } + if (m_lba == 0) { if (m_image->get_track_type(0) == cdrom_file::CD_TRACK_AUDIO) @@ -470,10 +531,6 @@ void t10mmc::ExecCommand() else m_lba = 150; } - else if (m_lba == 0xffffffff) - { - m_device->logerror("T10MMC: play audio from current not implemented!\n"); - } //m_device->logerror("T10MMC: PLAY AUDIO(12) at LBA %x for %x blocks\n", m_lba, m_blocks); @@ -533,6 +590,13 @@ void t10mmc::ExecCommand() m_transfer_length = 0; break; + case T10MMC_CMD_MECHANISM_STATUS: + m_phase = SCSI_PHASE_DATAIN; + m_status_code = SCSI_STATUS_CODE_GOOD; + m_transfer_length = get_u16be(&command[8]); + m_device->logerror("T10MMC: MECHANISM STATUS requested %d bytes\n", m_transfer_length); + break; + case T10MMC_CMD_READ_CD: { if (!m_image->exists()) @@ -562,8 +626,8 @@ void t10mmc::ExecCommand() 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)) + if (last_track_type != -1 && ((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); @@ -843,6 +907,32 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) } break; + case T10MMC_CMD_MECHANISM_STATUS: + { + uint8_t status_header[8]; + size_t transfer_len = get_u16be(&command[8]); + + if (transfer_len == 0) + break; + + std::fill_n(&data[0], transfer_len, 0); + std::fill(std::begin(status_header), std::end(status_header), 0); + + put_u24be(&status_header[2], m_last_lba); + + if (m_cdda->audio_active()) + { + // cdrdao checks this flag after starting an audio track to play for 1 block to + // determine when it can read the sub q channel data when finding tracks and indexes + m_last_lba = m_cdda->get_audio_lba(); + status_header[1] |= 1 << 5; // playing (audio or data) flag + } + + std::copy_n(std::begin(status_header), std::min(std::size(status_header), transfer_len), &data[0]); + + 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)) @@ -922,9 +1012,7 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) 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); + put_u24be(&data[data_idx], msf); data[data_idx+3] = 2; // mode 2 } else @@ -1087,13 +1175,11 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) return; } - m_device->logerror("T10MMC: READ SUB-CHANNEL Time = %x, SUBQ = %x\n", command[1], command[2]); - bool msf = (command[1] & 0x2) != 0; data[0]= 0x00; - int audio_active = m_cdda->audio_active(); + const int audio_active = m_cdda->audio_active(); if (audio_active) { // if audio is playing, get the latest LBA from the CDROM layer @@ -1121,14 +1207,18 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) } } + m_device->logerror("T10MMC: READ SUB-CHANNEL Time = %x, SUBQ = %x, LBA = %d)\n", msf, command[2], m_last_lba); + if (command[2] & 0x40) { + int track = m_image->get_track(m_last_lba); + data[2] = 0; - data[3] = 12; // data length + data[3] = 12; // data length data[4] = 0x01; // sub-channel format code - data[5] = 0x10 | (audio_active ? 0 : 4); - data[6] = m_image->get_track(m_last_lba) + 1; // track - data[7] = 0; // index + data[5] = m_image->get_adr_control(track); + data[6] = track + 1; // track + data[7] = m_image->get_track_index(m_last_lba); // index uint32_t frame = m_last_lba; @@ -1143,7 +1233,8 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) if (msf) { - frame = to_msf(frame); + // this is relative so don't adjust the LBA when converting to MSF + frame = to_msf_raw(frame); } put_u32be(&data[12], frame); @@ -1155,8 +1246,11 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) } break; } + default: m_device->logerror("T10MMC: Unknown subchannel type %d requested\n", command[3]); + std::fill_n(data, dataLength, 0); + break; } break; @@ -1216,7 +1310,7 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) if (msf) { - tstart = to_msf(tstart+150); + tstart = to_msf(tstart); } put_u32be(&data[dptr], tstart); @@ -1227,33 +1321,166 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) case TOC_FORMAT_SESSIONS: { + auto toc = m_image->get_toc(); + const int tracks = toc_tracks(); + const int last_session = m_image->get_last_session(); + uint32_t first_track_last_session = 0; + + for (int i = 0; i < tracks - 1; i++) + { + if (toc.tracks[i].session == last_session - 1) + first_track_last_session = i; + } + int len = 2 + (8 * 1); int dptr = 0; put_u16be(&data[dptr], len); dptr += 2; data[dptr++] = 1; - data[dptr++] = 1; + data[dptr++] = last_session; data[dptr++] = 0; - data[dptr++] = m_image->get_adr_control(0); - data[dptr++] = 1; + data[dptr++] = m_image->get_adr_control(first_track_last_session); + data[dptr++] = first_track_last_session + 1; // First Track Number In Last Complete Session data[dptr++] = 0; - uint32_t tstart = m_image->get_track_start(0); + uint32_t tstart = m_image->get_track_start(first_track_last_session); if (msf) { - tstart = to_msf(tstart+150); + tstart = to_msf(tstart); } - put_u32be(&data[dptr], tstart); + put_u32be(&data[dptr], tstart); // Start Address of First Track in Last Session dptr += 4; } break; + case TOC_FORMAT_FULL_TOC: + { + auto toc = m_image->get_toc(); + + const uint32_t tracks = m_image->get_last_track(); + const uint32_t total_sessions = m_image->get_last_session(); + const int total_a0s = total_sessions * 3; // every session has a set of a0/a1/a2 + const int total_b0s = (total_sessions - 1) * 2; // every additional session starts with a b0/c0 set + const int len = 2 + (11 * (tracks + total_a0s + total_b0s)); + + int dptr = 0; + put_u16be(&data[dptr], len); + dptr += 2; + data[dptr++] = 1; + data[dptr++] = total_sessions; + + int cur_track = 0; + for (uint32_t cur_session = 0; cur_session < total_sessions; cur_session++) + { + if (toc.tracks[cur_track].session != cur_session) + continue; + + int last_track_in_session = cur_track; + for (int i = cur_track; i < tracks && toc.tracks[i].session == cur_session; i++) + last_track_in_session = i; + + // point 0xa0, first track number in session + data[dptr++] = toc.tracks[cur_track].session + 1; + data[dptr++] = m_image->get_adr_control(cur_track); + data[dptr++] = 0; + data[dptr++] = 0xa0; + put_u24be(&data[dptr], 0); + dptr += 3; + data[dptr++] = 0; + data[dptr++] = cur_track + 1; // first track number in session + data[dptr++] = 0; + data[dptr++] = 0; + + // point 0xa1, last track number in session + data[dptr++] = toc.tracks[cur_track].session + 1; + data[dptr++] = m_image->get_adr_control(last_track_in_session); + data[dptr++] = 0; + data[dptr++] = 0xa1; + put_u24be(&data[dptr], 0); + dptr += 3; + data[dptr++] = 0; + data[dptr++] = last_track_in_session + 1; // last track number in session + data[dptr++] = 0; + data[dptr++] = 0; + + // point 0xa2, start of lead-out + uint32_t leadout_addr = m_image->get_track_start(last_track_in_session) + toc.tracks[last_track_in_session].frames; + if (toc.tracks[last_track_in_session].pgdatasize != 0) + leadout_addr -= toc.tracks[last_track_in_session].pregap; // pregap is already included in frame count if pgdatasize is set + + data[dptr++] = toc.tracks[cur_track].session + 1; + data[dptr++] = m_image->get_adr_control(last_track_in_session); + data[dptr++] = 0; + data[dptr++] = 0xa2; + put_u24be(&data[dptr], 0); + dptr += 3; + data[dptr++] = 0; + put_u24be(&data[dptr], to_msf(leadout_addr)); // Track start time + dptr += 3; + + while (cur_track < tracks && toc.tracks[cur_track].session == cur_session) + { + data[dptr++] = toc.tracks[cur_track].session + 1; + data[dptr++] = m_image->get_adr_control(cur_track); + data[dptr++] = 0; + data[dptr++] = cur_track + 1; + put_u24be(&data[dptr], 0); + dptr += 3; + data[dptr++] = 0; + put_u24be(&data[dptr], to_msf(m_image->get_track_start(cur_track))); // Track start time + dptr += 3; + + cur_track++; + } + + if (cur_session + 1 < total_sessions) + { + const uint32_t leadout_addr = m_image->get_track_start(cur_track) + toc.tracks[cur_track].frames; + const uint32_t next_session_program_area = m_image->get_track_start(cur_track) - 150; // fixed 2s pregap + + // point 0xb0, info about next session + data[dptr++] = cur_session + 1; + data[dptr++] = (m_image->get_adr_control(cur_track - 1) & 0x0f) | 0x50; + data[dptr++] = 0; + data[dptr++] = 0xb0; + put_u24be(&data[dptr], to_msf(next_session_program_area)); // start time for the next possible session's program area + dptr += 3; + data[dptr++] = (total_sessions - (cur_session + 1)) * 2; // the number of different Mode-5 pointers present from this point, inclusive + put_u24be(&data[dptr], to_msf(leadout_addr)); // the maximum possible start time of the outermost Lead-out + dptr += 3; + + // point 0xc0, start time of first lead-in of the disc + data[dptr++] = cur_session + 1; + data[dptr++] = (m_image->get_adr_control(0) & 0x0f) | 0x50; + data[dptr++] = 0; + data[dptr++] = 0xc0; + put_u24be(&data[dptr], 0); + dptr += 3; + data[dptr++] = 0; + // Hardcode a value of 95:00:00 because that's the most common value I've seen on various multisession CD dumps + put_u24be(&data[dptr], 0x5f0000); // Start time of the first Lead-in area of the disc + dptr += 3; + } + } + + break; + } + + case TOC_FORMAT_CDTEXT: + { + put_u16be(&data[0], 0); // length + data[2] = 0; // first track/session/reserved + data[3] = 0; // last track/session/reserved + break; + } + default: m_device->logerror("T10MMC: Unhandled READ TOC format %d\n", toc_format()); + std::fill_n(data, dataLength, 0); break; } } @@ -1265,62 +1492,98 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) { memset(data, 0, SCSILengthFromUINT16( &command[ 7 ] )); + const int dbd = BIT(command[1], 3); const uint8_t page = command[2] & 0x3f; - int ptr = 0; + int ptr = dbd ? 0 : (command[0] == T10SPC_CMD_MODE_SENSE_6) ? 4 : 8; if ((page == 0xe) || (page == 0x3f)) - { // CD Audio control page - data[ptr++] = 0x8e; // page E, parameter is savable - data[ptr++] = 0x0e; // page length - data[ptr++] = (1 << 2) | (m_sotc << 1); // IMMED = 1 - ptr += 5; // skip reserved bytes - // connect each audio channel to 1 output port and indicate max volume - data[ptr++] = 1; - data[ptr++] = 0xff; - data[ptr++] = 2; - data[ptr++] = 0xff; - data[ptr++] = 4; - data[ptr++] = 0xff; - data[ptr++] = 8; - data[ptr++] = 0xff; + { + // CD Audio control page + data[ptr++] = 0x8e; // page E, parameter is savable + data[ptr++] = 0x0e; // page length + data[ptr++] = (1 << 2) | (m_sotc << 1); // IMMED = 1 + // reserved + data[ptr++] = 0; + data[ptr++] = 0; + data[ptr++] = 0; + data[ptr++] = 0; + data[ptr++] = 0; + // connect each audio channel to 1 output port and indicate max volume + data[ptr++] = 1; + data[ptr++] = 0xff; + data[ptr++] = 2; + data[ptr++] = 0xff; + data[ptr++] = 4; + data[ptr++] = 0xff; + data[ptr++] = 8; + data[ptr++] = 0xff; } if ((page == 0x0d) || (page == 0x3f)) - { // CD page - data[ptr++] = 0x0d; - data[ptr++] = 6; // page length - data[ptr++] = 0; - data[ptr++] = 0; - data[ptr++] = 0; - data[ptr++] = 60; - data[ptr++] = 0; - data[ptr++] = 75; + { + // CD page + data[ptr++] = 0x0d; + data[ptr++] = 6; // page length + data[ptr++] = 0; + data[ptr++] = 0; + data[ptr++] = 0; + data[ptr++] = 60; + data[ptr++] = 0; + data[ptr++] = 75; } if ((page == 0x2a) || (page == 0x3f)) - { // Page capabilities - data[ptr++] = 0x2a; - data[ptr++] = 0x14; // page length - data[ptr++] = 0x00; - data[ptr++] = 0x00; // CD-R only - data[ptr++] = 0x01; // can play audio - data[ptr++] = 0; - data[ptr++] = 0; - data[ptr++] = 0; - data[ptr++] = 0x02; - data[ptr++] = 0xc0; // 4x speed - data[ptr++] = 0x01; - data[ptr++] = 0x00; // 256 volume levels supported - data[ptr++] = 0x00; - data[ptr++] = 0x00; // buffer - data[ptr++] = 0x02; - data[ptr++] = 0xc0; // 4x read speed - data[ptr++] = 0; - data[ptr++] = 0; - data[ptr++] = 0; - data[ptr++] = 0; - data[ptr++] = 0; - data[ptr++] = 0; + { + // Page capabilities + data[ptr++] = 0x2a; + data[ptr++] = 0x14; // page length + data[ptr++] = 0x00; + data[ptr++] = 0x00; // CD-R only + data[ptr++] = + (1 << 4) | // Mode 2 Form 1 + (1 << 1) | // XA Cmds Supported + (1 << 0); // AudioPlay + data[ptr++] = 0; + data[ptr++] = 0; + data[ptr++] = 0; + data[ptr++] = 0x02; + data[ptr++] = 0xc0; // 4x speed + data[ptr++] = 0x01; + data[ptr++] = 0x00; // 256 volume levels supported + data[ptr++] = 0x00; + data[ptr++] = 0x00; // buffer + data[ptr++] = 0x02; + data[ptr++] = 0xc0; // 4x read speed + data[ptr++] = 0; + data[ptr++] = 0; + data[ptr++] = 0; + data[ptr++] = 0; + data[ptr++] = 0; + data[ptr++] = 0; + } + + if (!dbd) + { + int const medium_type = + m_image->is_dvd() ? 0x41 : + (m_image->get_last_track() >= 1 && m_image->get_track_type(0) == cdrom_file::CD_TRACK_AUDIO) ? 0x02 : + m_image->get_last_track() > 1 ? 0x03 : 0x01; + + if (command[0] == T10SPC_CMD_MODE_SENSE_6) + { + data[0] = ptr - 1; // Mode Data Length + data[1] = medium_type; + data[2] = 0x00; // Device-Specific Parameter + data[3] = 0x00; // Block Descriptor Length + } + else + { + put_u16be(&data[0], ptr - 2); // Mode Data Length + data[2] = medium_type; + data[3] = 0x00; // Device-Specific Parameter + put_u16be(&data[4], 0); // Reserved + put_u16be(&data[6], 0); // Block Descriptor Length + } } } break; @@ -1333,7 +1596,7 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) if((command[1] & 0x0f) == 0 && command[7] == 0x04) // DVD / DVD disc manufacturing information { data[1] = 0xe; - for(int i=4; i != 0xe; i++) + for (int i = 4; i != 0xe; i++) data[i] = 0; } break; @@ -1354,42 +1617,67 @@ void t10mmc::WriteData( uint8_t *data, int dataLength ) { case T10SPC_CMD_MODE_SELECT_6: case T10SPC_CMD_MODE_SELECT_10: - m_device->logerror("T10MMC: MODE SELECT page %x\n", data[0] & 0x3f); + { + int len = dataLength; + int reserved = (command[0] == T10SPC_CMD_MODE_SELECT_6) ? data[0] + 1 : get_u16be(&data[0]) + 2; + if (len != reserved && reserved) + m_device->logerror("T10MMC: reserved length=%d (%d)", reserved, dataLength); + + int bdlen = (command[0] == T10SPC_CMD_MODE_SELECT_6) ? data[3] : get_u16be(&data[6]); + int ptr = (command[0] == T10SPC_CMD_MODE_SELECT_6) ? 4 : 8; - switch (data[0] & 0x3f) + if (bdlen == 8) { - case 0x0: // vendor-specific - // check for SGI extension to force 512-byte blocks - if ((data[3] == 8) && (data[10] == 2)) - { - m_device->logerror("T10MMC: Experimental SGI 512-byte block extension enabled\n"); + int blen = get_u24be(&data[ptr + 5]); + if (blen == 512) + { + m_device->logerror("T10MMC: Experimental SGI 512-byte block extension enabled\n"); - m_sector_bytes = 512; - m_num_subblocks = 4; - } - else - { - m_device->logerror("T10MMC: Unknown vendor-specific page!\n"); - } - break; + m_sector_bytes = 512; + m_num_subblocks = 4; + } + else if (blen == 2048) + { + m_sector_bytes = 2048; + m_num_subblocks = 1; + } + else + { + m_device->logerror("T10MMC: Unsupported block length %d\n", blen); + } + } + else if (bdlen) + m_device->logerror("T10MMC: MODE SELECT Invalid Block Descriptor Length %d\n", bdlen); + + ptr += bdlen; + while (ptr < len) + { + m_device->logerror("T10MMC: MODE SELECT page %x\n", data[ptr + 0] & 0x3f); + + switch (data[ptr + 0] & 0x3f) + { case 0xe: // audio page - m_sotc = (data[2] >> 1) & 1; - m_device->logerror("Ch 0 route: %x vol: %x\n", data[8], data[9]); - m_device->logerror("Ch 1 route: %x vol: %x\n", data[10], data[11]); - m_device->logerror("Ch 2 route: %x vol: %x\n", data[12], data[13]); - m_device->logerror("Ch 3 route: %x vol: %x\n", data[14], data[15]); + m_sotc = (data[ptr + 2] >> 1) & 1; + m_device->logerror("Ch 0 route: %x vol: %x\n", data[ptr + 8], data[ptr + 9]); + m_device->logerror("Ch 1 route: %x vol: %x\n", data[ptr + 10], data[ptr + 11]); + m_device->logerror("Ch 2 route: %x vol: %x\n", data[ptr + 12], data[ptr + 13]); + m_device->logerror("Ch 3 route: %x vol: %x\n", data[ptr + 14], data[ptr + 15]); // TODO: CDDA audio channels and output port should be separate // The actual audio channel that gets sent to the output port is configurable and more than one audio channel can go to an output port - m_cdda->set_output_gain(0, data[9] / 255.0f); - m_cdda->set_output_gain(1, data[11] / 255.0f); + m_cdda->set_output_gain(0, data[ptr + 9] / 255.0f); + m_cdda->set_output_gain(1, data[ptr + 11] / 255.0f); break; + } + + ptr += data[ptr + 1] + 2; } break; + } default: t10spc::WriteData( data, dataLength ); break; -} + } } |