diff options
Diffstat (limited to 'src/devices/bus/ata/gdrom.cpp')
| -rw-r--r-- | src/devices/bus/ata/gdrom.cpp | 690 |
1 files changed, 510 insertions, 180 deletions
diff --git a/src/devices/bus/ata/gdrom.cpp b/src/devices/bus/ata/gdrom.cpp index 135cbcb66b4..a362bf5cb18 100644 --- a/src/devices/bus/ata/gdrom.cpp +++ b/src/devices/bus/ata/gdrom.cpp @@ -8,18 +8,25 @@ #include "emu.h" #include "gdrom.h" +#include "coreutil.h" + +#include <iostream> #define LOG_WARN (1U << 1) #define LOG_CMD (1U << 2) -#define LOG_XFER (1U << 3) +#define LOG_CMD_RAW (1U << 3) // bare command IDs (temp until all commands are in) +#define LOG_XFER (1U << 4) +#define LOG_TOC (1U << 5) // TOC frame offsets -#define VERBOSE (LOG_WARN | LOG_CMD) +#define VERBOSE (LOG_GENERAL | LOG_WARN | LOG_CMD | LOG_TOC) //#define LOG_OUTPUT_STREAM std::cout #include "logmacro.h" #define LOGWARN(...) LOGMASKED(LOG_WARN, __VA_ARGS__) #define LOGCMD(...) LOGMASKED(LOG_CMD, __VA_ARGS__) +#define LOGCMDRAW(...) LOGMASKED(LOG_CMD_RAW, __VA_ARGS__) #define LOGXFER(...) LOGMASKED(LOG_XFER, __VA_ARGS__) +#define LOGTOC(...) LOGMASKED(LOG_TOC, __VA_ARGS__) #define GDROM_BUSY_STATE 0x00 #define GDROM_PAUSE_STATE 0x01 @@ -32,6 +39,8 @@ #define GDROM_RETRY_STATE 0x08 #define GDROM_ERROR_STATE 0x09 +// CD status readback/subchannel Q +#define LIVE_REQ_STAT 0 /* Officially not documented security-related packet commands: @@ -161,6 +170,8 @@ void gdrom_device::device_reset() GDROM_Cmd11_Reply[i] = GDROM_Def_Cmd11_Reply[i]; atapi_cdrom_device::device_reset(); + + m_cdda->set_cdrom(m_image); } // scsicd_exec_command @@ -169,29 +180,201 @@ void gdrom_device::device_reset() void gdrom_device::ExecCommand() { + LOGCMDRAW("%02x\n", command[0]); switch ( command[0] ) { - case 0x11: // REQ_MODE + case 0x00: + // TEST_UNIT + // TODO: verify if t10mmc use is enough + // loopchk returns OK in Packet cmnd (0201) + t10mmc::ExecCommand(); + break; + + case 0x10: + { + transferOffset = command[2]; + u8 allocation_length = SCSILengthFromUINT8( &command[ 4 ] ); + // any game that enables [redbook] + LOGCMD("REQ_STAT 10h offset %02x length %02x\n", transferOffset, allocation_length); + + if (transferOffset || allocation_length != 0xa) + throw emu_fatalerror("GDROM: REQ_STAT with unsupported offset %02x length %02x", transferOffset, allocation_length); + m_phase = SCSI_PHASE_DATAIN; m_status_code = SCSI_STATUS_CODE_GOOD; - LOGCMD("REQ_MODE %02x %02x %02x %02x %02x %02x\n", - command[0], command[1], - command[2], command[3], - command[4], command[5] - ); + m_transfer_length = allocation_length; + break; + } + + case 0x11: + LOGCMD("REQ_MODE 11h %02x %02x\n", command[2], command[4]); + m_phase = SCSI_PHASE_DATAIN; + m_status_code = SCSI_STATUS_CODE_GOOD; + // if (SCSILengthFromUINT8( &command[ 4 ] ) < 32) return -1; transferOffset = command[2]; m_transfer_length = SCSILengthFromUINT8( &command[ 4 ] ); + if (transferOffset & 1) + throw emu_fatalerror("GDROM: REQ_MODE with odd offset %02x %02x", transferOffset, m_transfer_length); break; case 0x12: // SET_MODE + LOGCMD("SET_MODE 12h %02x %02x\n", command[2], command[4]); + m_phase = SCSI_PHASE_DATAOUT; m_status_code = SCSI_STATUS_CODE_GOOD; //transferOffset = command[2]; m_transfer_length = SCSILengthFromUINT8( &command[ 4 ] ); - LOGCMD("SET_MODE %02x %02x\n", command[2], command[4]); + if (command[2]) + throw emu_fatalerror("GDROM: SET_MODE with offset %02x %02x", transferOffset, m_transfer_length); + + if (m_transfer_length > 0xa) + { + LOGWARN("SET_MODE attempt to write on read only regs %02x\n", command[4]); + m_transfer_length = 0xa; + } break; + case 0x14: + { + if (!m_image->exists()) + { + m_phase = SCSI_PHASE_STATUS; + m_status_code = SCSI_STATUS_CODE_CHECK_CONDITION; + m_transfer_length = 0; + break; + } + + // TODO: it's supposed to write a single and a double density TOC request + //if (command[1]) + // throw emu_fatalerror("Double density unsupported"); + u16 allocation_length = SCSILengthFromUINT16( &command[ 3 ] ); + LOGCMD("READ_TOC 14h %02x %02x %d\n", + command[1], command[2], allocation_length + ); + + if (allocation_length != 408) + throw emu_fatalerror("TOC with allocation length != 408 (%d)", allocation_length); + + if (m_cdda != nullptr) + { + m_cdda->stop_audio(); + m_sector_number = (m_sector_number & 0xf0) | GDROM_STANDBY_STATE; + } + + m_phase = SCSI_PHASE_DATAIN; + m_status_code = SCSI_STATUS_CODE_GOOD; + m_transfer_length = allocation_length; + break; + } + + // accessed for audio CDs + // TODO: needed for Mil CD support + case 0x15: + { + m_phase = SCSI_PHASE_DATAIN; + m_status_code = SCSI_STATUS_CODE_GOOD; + m_transfer_length = SCSILengthFromUINT8( &command[ 4 ] ); + LOGCMD("REQ_SES 15h %02x %02x\n", command[2], m_transfer_length); + break; + } + + case 0x20: // CD_PLAY + { + if (!m_image->exists()) + { + m_phase = SCSI_PHASE_STATUS; + m_status_code = SCSI_STATUS_CODE_CHECK_CONDITION; + m_transfer_length = 0; + break; + } + + const u32 start_offs = (command[2]<<16 | command[3]<<8 | command[4]); + const u32 end_offs = (command[8]<<16 | command[9]<<8 | command[10]); + //(command[8] % 75) + ((command[7] * 75) % (60*75)) + (command[6] * (75*60)) - m_lba; + + const u8 play_mode = command[1] & 7; + + auto trk = m_image->get_track(start_offs); + const u8 repeat = command[6] & 0xf; + LOGCMD("CD_PLAY 20h track %d FAD %d blocks %d type %02x repeat %01x\n" + , trk + 1 + , start_offs + , end_offs - start_offs + , play_mode + , repeat + ); + + if (play_mode == 7 && m_audio_sense == SCSI_SENSE_ASC_ASCQ_AUDIO_PLAY_OPERATION_PAUSED) + { + m_cdda->pause_audio(0); + m_audio_sense = SCSI_SENSE_ASC_ASCQ_AUDIO_PLAY_OPERATION_IN_PROGRESS; + LOGCMD("\tPlayback resume\n"); + m_sector_number = (m_sector_number & 0xf0) | GDROM_PLAY_STATE; + } + else if (m_image->get_track_type(trk) == cdrom_file::CD_TRACK_AUDIO && end_offs > start_offs) + { + // TODO: check end > start assertion + m_cd_status.cdda_fad = start_offs - 150; + m_cd_status.cdda_blocks = (end_offs - start_offs); + + m_cdda->start_audio(m_cd_status.cdda_fad, m_cd_status.cdda_blocks); + m_audio_sense = SCSI_SENSE_ASC_ASCQ_AUDIO_PLAY_OPERATION_IN_PROGRESS; + m_cd_status.repeat_current = 0; + m_cd_status.repeat_count = repeat; + m_sector_number = (m_sector_number & 0xf0) | GDROM_PLAY_STATE; + + LOGCMD("\tPlayback started\n"); + } + else + { + LOGWARN("track is NOT audio!\n"); + set_sense(SCSI_SENSE_KEY_ILLEGAL_REQUEST, SCSI_SENSE_ASC_ASCQ_ILLEGAL_MODE_FOR_THIS_TRACK); + // TODO: unconfirmed state + m_sector_number = (m_sector_number & 0xf0) | GDROM_ERROR_STATE; + return; + } + + m_phase = SCSI_PHASE_STATUS; + m_status_code = SCSI_STATUS_CODE_GOOD; + m_transfer_length = 0; + break; + } + + case 0x21: + { + if (!m_image->exists()) + { + m_phase = SCSI_PHASE_STATUS; + m_status_code = SCSI_STATUS_CODE_CHECK_CONDITION; + m_transfer_length = 0; + break; + } + + const u32 start_seek = (command[2]<<16 | command[3]<<8 | command[4]); + const u8 seek_mode = command[1] & 0xf; + LOGCMD("CD_SEEK 21h FAD %d type %02x\n", start_seek + 150, seek_mode); + switch(seek_mode) + { + // TODO: implement SEEK & standby + // 1 FAD seek + // 2 MSF seek + // 3 stop audio, go to Home FAD + case 4: + // 4 pause audio + LOGCMD("\tPlayback paused\n"); + m_cdda->pause_audio(1); + m_audio_sense = SCSI_SENSE_ASC_ASCQ_AUDIO_PLAY_OPERATION_PAUSED; + m_sector_number = (m_sector_number & 0xf0) | GDROM_PAUSE_STATE; + break; + } + + m_phase = SCSI_PHASE_STATUS; + m_status_code = SCSI_STATUS_CODE_GOOD; + m_transfer_length = 0; + break; + } + case 0x30: // CD_READ if (command[1] & 1) { @@ -204,9 +387,11 @@ void gdrom_device::ExecCommand() m_blocks = command[8]<<16 | command[9]<<8 | command[10]; read_type = (command[1] >> 1) & 7; - data_select = (command[1]>>4) & 0xf; + data_select = (command[1] >> 4) & 0xf; + //m_status |= IDE_STATUS_DSC; - if (read_type != 2) // mode 1 + // TODO: any other value is a non-Mode 1 attempt basically + if (read_type != 0 && read_type != 2) { throw emu_fatalerror("GDROM: Unhandled read_type %d", read_type); } @@ -217,7 +402,7 @@ void gdrom_device::ExecCommand() } // LBA 45000 is start of double density GD-ROM - LOGCMD("CD_READ %02x %02x\n", command[2], command[4]); + LOGCMD("CD_READ 30h %02x %02x\n", command[2], command[4]); LOGCMD(" LBA %d (%x) for %d blocks (%d bytes, read type %d, data select %d)\n", m_lba + 150, m_lba, m_blocks, m_blocks * m_sector_bytes, read_type, data_select @@ -236,6 +421,8 @@ void gdrom_device::ExecCommand() if (m_cdda != nullptr) { m_cdda->stop_audio(); + m_audio_sense = SCSI_SENSE_ASC_ASCQ_NO_SENSE; + m_sector_number = (m_sector_number & 0xf0) | GDROM_STANDBY_STATE; } m_phase = SCSI_PHASE_DATAIN; @@ -244,54 +431,32 @@ void gdrom_device::ExecCommand() } break; - // READ TOC (GD-ROM ver.) - case 0x14: + case 0x40: { - if (!m_image->exists()) - { - m_phase = SCSI_PHASE_STATUS; - m_status_code = SCSI_STATUS_CODE_CHECK_CONDITION; - m_transfer_length = 0; - break; - } - - // TODO: is this correct? - int start_trk = command[2]; - int end_trk = m_image->get_last_track(); - int length; - int allocation_length = SCSILengthFromUINT16( &command[ 3 ] ); - - if( start_trk == 0 ) - { - start_trk = 1; - } - if( start_trk == 0xaa ) - { - end_trk = start_trk; - } - - length = 4 + ( 8 * ( ( end_trk - start_trk ) + 1 ) ); - if( length > allocation_length ) - { - length = allocation_length; - } - else if( length < 4 ) - { - length = 4; - } + m_transfer_length = SCSILengthFromUINT8( &command[ 4 ] ); + //LOGCMD("CD_SCD 40h %02x %d\n", command[1] & 0xf, m_transfer_length); - if (m_cdda != nullptr) + switch(command[1] & 0xf) { - m_cdda->stop_audio(); + case 0x00: + m_phase = SCSI_PHASE_DATAIN; + m_status_code = SCSI_STATUS_CODE_GOOD; + m_transfer_length = SCSILengthFromUINT8( &command[ 4 ] ); + break; + case 0x01: + m_phase = SCSI_PHASE_DATAIN; + m_status_code = SCSI_STATUS_CODE_GOOD; + m_transfer_length = 0xe; + break; + default: + LOGWARN("command 0x40: unhandled subchannel request\n"); + break; } - - m_phase = SCSI_PHASE_DATAIN; - m_status_code = SCSI_STATUS_CODE_GOOD; - m_transfer_length = length; break; } - case 0x70: // security check, return no data, always followed by cmd 0x71, command[1] parameter can be 0x1f or 0x9f + // security check, return no data, always followed by cmd 0x71, command[1] parameter can be 0x1f or 0x9f + case 0x70: m_phase = SCSI_PHASE_STATUS; m_status_code = SCSI_STATUS_CODE_GOOD; m_transfer_length = 0; @@ -303,22 +468,16 @@ void gdrom_device::ExecCommand() m_transfer_length = sizeof(GDROM_Cmd71_Reply); break; - case 0x40: - if(command[1] & 0xf) - { - m_phase = SCSI_PHASE_DATAIN; - m_status_code = SCSI_STATUS_CODE_GOOD; - m_transfer_length = 0xe; - } - else - { - LOGWARN("command 0x40: unhandled subchannel request\n"); - } - break; + // case 0x22: CD_SCAN + // case 0x31: CD_READ2 + // case 0x13: REQ_ERROR + // case 0x16: CD_OPEN [Tray] + + // case 0x08: ??? loopchk uses it in one of the Packet cmnd tests + // (check for unsupported command?) default: - t10mmc::ExecCommand(); - break; + throw emu_fatalerror("GDROM: unhandled command %02x", command[0]); } } @@ -333,14 +492,152 @@ void gdrom_device::ReadData( uint8_t *data, int dataLength ) switch ( command[0] ) { + case 0x10: // REQ_STAT + { + data[0] = m_sector_number & 0xf; // CD status + data[1] = (m_sector_number & 0xf0) | (m_cd_status.repeat_current & 0xf); + auto fad = m_cdda->get_audio_lba(); + auto trk = m_image->get_track(fad); + data[2] = m_image->get_adr_control(trk - 1); + data[3] = trk; + // TODO: index + data[4] = 1; + // FAD, in binary format + data[5] = (fad >> 16) & 0xff; + data[6] = (fad >> 8) & 0xff; + data[7] = fad & 0xff; + // Max Read Error Retry Times + data[8] = 0; + // <reserved> + data[9] = 0; + if (LIVE_REQ_STAT) + { + popmessage("REQ_STAT STATUS %02x track %d index %d adr %02x fad %06d" + , m_sector_number + , data[3] + , data[4] + , data[2] + , fad + ); + } + break; + } + case 0x11: // REQ_MODE - LOGCMD("REQ_MODE dataLength %d\n", dataLength); + //LOGCMD("REQ_MODE dataLength %d\n", dataLength); memcpy(data, &GDROM_Cmd11_Reply[transferOffset], (dataLength >= 32-transferOffset) ? 32-transferOffset : dataLength); + // TODO: reading this while playing a redbook likely hardwires CD speed to 75 Hz + //data[1] = 0x01; + break; + + case 0x13: // REQ_ERROR + // cfr. Appendix I for possible error types + data[0] = 0xf0; // fixed? + data[1] = 0; + data[2] = 6; // Sense Key + data[3] = 0; + // 4-7 "specific command details" (?) or FAD + data[4] = 0; + data[5] = 0; + data[6] = 0; + data[7] = 0; + data[8] = 0x29; // ASC + data[9] = 0; // ASCQ + break; + + case 0x14: // READ TOC (GD-ROM ver.) + /* + Track numbers are problematic here: 0 = lead-in, 0xaa = lead-out. + That makes sense in terms of how real-world CDs are referred to, but + our internal routines for tracks use "0" as track 1. That probably + should be fixed... + */ + LOGCMD("READ_TOC format %d time %d\n", + command[2] & 0xf, (command[1] >> 1) & 1 + ); + switch (command[2] & 0x0f) + { + case 0: // normal + { + int start_trk = 1; + int end_trk = m_image->get_last_track(); + int len = 408; + //int in_len; + int dptr = 0; + uint32_t tstart; + + // Non-standard, doesn't want in_len readback + //dptr = 0; + //data[dptr++] = (len>>8) & 0xff; + //data[dptr++] = (len & 0xff); + + // pre-fill with 0xffs, audio CD player counts number of tracks by checking the + // first EOF it encounters (and don't care about the 396-407 dataset below) + memset(data, 0xff, len); + dptr = 0; + LOGTOC("TOC: Start track %d end track %d\n", start_trk, end_trk); + for (i = start_trk; i <= end_trk; i++) + { + u8 adr = m_image->get_adr_control(i - 1) | 1; + data[dptr++] = adr; + + tstart = m_image->get_track_start(i - 1) + 150; + //if ((command[1]&2)>>1) + // tstart = cdrom_file::lba_to_msf(tstart); + data[dptr++] = (tstart>>16) & 0xff; + data[dptr++] = (tstart>>8) & 0xff; + data[dptr++] = (tstart & 0xff); + LOGTOC("\t%d: FAD %d %02x\n", i, tstart, adr); + } + + dptr = 396; + data[dptr++] = m_image->get_adr_control(0) | 1; + data[dptr++] = start_trk; + data[dptr++] = 0; + data[dptr++] = 0; + data[dptr++] = m_image->get_adr_control(end_trk) | 1; + data[dptr++] = end_trk; + data[dptr++] = 0; + data[dptr++] = 0; + const u32 tend = m_image->get_track_start(0xaa) + 150; + //if ((command[1]&2)>>1) + // tstart = cdrom_file::lba_to_msf(tstart); + data[dptr++] = m_image->get_adr_control(0xaa) | 1; + data[dptr++] = (tend>>16) & 0xff; + data[dptr++] = (tend>>8) & 0xff; + data[dptr++] = (tend & 0xff); + LOGTOC("\t0xaa: FAD %d\n", tstart); + break; + } + default: + LOGWARN("Unhandled READ_TOC format %d\n", command[2]&0xf); + break; + } break; + case 0x15: + { + // REQ_SES + data[0] = m_sector_number & 0xf; // CD status, stripped by type? + data[1] = 0; // <reserved>, zeroed + data[2] = 1; // number of sessions + + const u8 session_num = command[2] & 0xff; + u32 fad; + if (session_num == 0) + fad = m_image->get_track_start(0xaa) + 150; + else + fad = m_image->get_track_start(0) + 150; + LOGTOC("SESSION %d: %06d\n", session_num, fad); + data[3] = (fad >> 16) & 0xff; + data[4] = (fad >> 8) & 0xff; + data[5] = fad & 0xff; + break; + } + case 0x30: // CD_READ - LOGCMD("CD_READ read %x dataLength,\n", dataLength); - if (m_image->exists() && m_blocks) + LOGXFER("CD_READ read %x dataLength,\n", dataLength); + if ((m_image->exists()) && (m_blocks)) { while (dataLength > 0) { @@ -370,82 +667,71 @@ void gdrom_device::ReadData( uint8_t *data, int dataLength ) } break; - case 0x14: // READ TOC (GD-ROM ver.) - /* - Track numbers are problematic here: 0 = lead-in, 0xaa = lead-out. - That makes sense in terms of how real-world CDs are referred to, but - our internal routines for tracks use "0" as track 1. That probably - should be fixed... - */ - LOGCMD("READ TOC format = %d time=%d\n", - command[2] & 0xf, (command[1] >> 1) & 1 - ); + case 0x40: // Get Subchannel status switch (command[2] & 0x0f) { - case 0: // normal + case 0: // Subcode P-W { - int start_trk; - int end_trk; - int len; - int in_len; - int dptr; - uint32_t tstart; - - start_trk = command[2]; - if( start_trk == 0 ) - { - start_trk = 1; - } - - end_trk = m_image->get_last_track(); - len = (end_trk * 8) + 2; - - // the returned TOC DATA LENGTH must be the full amount, - // regardless of how much we're able to pass back due to in_len - dptr = 0; - data[dptr++] = (len>>8) & 0xff; - data[dptr++] = (len & 0xff); - data[dptr++] = 1; - data[dptr++] = end_trk; - - if( start_trk == 0xaa ) + data[0] = 0; // Reserved + data[1] = m_audio_sense; + data[2] = 0; + data[3] = m_transfer_length; // header size + auto fad = m_cdda->get_audio_lba(); + if (!m_image->read_subcode( fad, &data[4 + m_transfer_length])) { - end_trk = 0xaa; - } - - in_len = command[3]<<8 | command[4]; - - for (i = start_trk; i <= end_trk; i++) - { - int cdrom_track = i; - if( cdrom_track != 0xaa ) - { - cdrom_track--; - } - - if( dptr >= in_len ) + // attempt to provide Q channel + // TODO: audio CD player definitely reads here but never updates GFXs? + u8 subqbuf[12]{}; + const u32 msf_abs = cdrom_file::lba_to_msf_alt( fad ); + const auto trk = m_image->get_track( fad ); + const u32 msf_rel = cdrom_file::lba_to_msf_alt( fad - m_image->get_track_start( trk ) ); + + subqbuf[0] = 0x01 | ((m_image->get_track_type(m_image->get_track(trk + 1)) == cdrom_file::CD_TRACK_AUDIO) ? 0x00 : 0x40); + subqbuf[1] = dec_2_bcd(trk + 1); + subqbuf[2] = 1; + subqbuf[3] = dec_2_bcd((msf_rel >> 16) & 0xff); + subqbuf[4] = dec_2_bcd((msf_rel >> 8) & 0xff); + subqbuf[5] = dec_2_bcd((msf_rel >> 0) & 0xff); + subqbuf[6] = 0; + subqbuf[7] = dec_2_bcd((msf_abs >> 16) & 0xff); + subqbuf[8] = dec_2_bcd((msf_abs >> 8) & 0xff); + subqbuf[9] = dec_2_bcd((msf_abs >> 0) & 0xff); + subqbuf[10] = 0xff; //machine().rand(); // CRC + subqbuf[11] = 0xff; //machine().rand(); + + if (LIVE_REQ_STAT) { - break; + popmessage("Qchan STATUS %02x TRACK %d ABS %02x:%02x:%02x REL %02x:%02x:%02x" + , subqbuf[0] + , subqbuf[1] + , subqbuf[7], subqbuf[8], subqbuf[9] + , subqbuf[3], subqbuf[4], subqbuf[5] + ); } - data[dptr++] = 0; - data[dptr++] = m_image->get_adr_control(cdrom_track); - data[dptr++] = i; - data[dptr++] = 0; - - tstart = m_image->get_track_start(cdrom_track); - if ((command[1]&2)>>1) - tstart = cdrom_file::lba_to_msf(tstart); - data[dptr++] = (tstart>>24) & 0xff; - data[dptr++] = (tstart>>16) & 0xff; - data[dptr++] = (tstart>>8) & 0xff; - data[dptr++] = (tstart & 0xff); + for (int i = 0; i < m_transfer_length - 4; i++) + data[i + 4] = BIT(subqbuf[i >> 3], 7 - (i & 7)) ? 0x40 : 0x00; } break; } - default: - LOGWARN("Unhandled READ TOC format %d\n", command[2]&0xf); + case 1: // Subcode Q + // TODO: unread by audio CD player (?) + LOGWARN("CD_SCD: unhandled Subcode Q path\n"); + data[0] = 0; // Reserved + data[1] = m_audio_sense; + data[2] = 0; + data[3] = m_transfer_length; // header size + data[4] = 0; // ADR + data[5] = 1; // Track Number + data[6] = 1; // index #1 + data[7] = 0; // ? + data[8] = 0; // ? + data[9] = 0; // elapsed FAD + data[0xa] = 0; // <reserved>, zeroed + data[0xb] = 0; // FAD >> 16 + data[0xc] = 0; // FAD >> 8 + data[0xd] = 0x96; // FAD >> 0 break; } break; @@ -457,23 +743,6 @@ void gdrom_device::ReadData( uint8_t *data, int dataLength ) data[10] = 0x1f; // needed by dimm board firmware break; - case 0x40: // Get Subchannel status - data[0] = 0; // Reserved - data[1] = 0x15; // Audio Playback status (todo) - data[2] = 0; - data[3] = 0x0e; // header size - data[4] = 0; // ? - data[5] = 1; // Track Number - data[6] = 1; // gap #1 - data[7] = 0; // ? - data[8] = 0; // ? - data[9] = 0; // ? - data[0xa] = 0; // ? - data[0xb] = 0; // FAD >> 16 - data[0xc] = 0; // FAD >> 8 - data[0xd] = 0x96; // FAD >> 0 - break; - default: t10mmc::ReadData( data, dataLength ); break; @@ -490,6 +759,22 @@ void gdrom_device::WriteData( uint8_t *data, int dataLength ) { case 0x12: // SET_MODE memcpy(&GDROM_Cmd11_Reply[transferOffset], data, (dataLength >= 32-transferOffset) ? 32-transferOffset : dataLength); + if (data[2] != 0 && data[2] != 7) + throw emu_fatalerror("GDROM: Unsupported CD speed setting %02x\n", data[2]); + // ---- -000 Max speed (x12) + // ---- -xxx [x1 (75 Hz), x2, x4, x6, x8, x10, x12] + LOGCMD("\tCD speed: %02x\n", data[2]); + // Standby Time: + // - 0 = no standby + // any other value = number of seconds for pause -> standby transition + LOGCMD("\tStandby Time: %02x%02x\n", data[4], data[5]); + + LOGCMD("\tRead Continous: %s\n", BIT(data[6], 5) ? "enable" : "disable"); + LOGCMD("\tECC: %s\n", BIT(data[6], 4) ? "enable" : "disable"); + LOGCMD("\tRead Retry: %s\n", BIT(data[6], 3) ? "enable" : "disable"); + LOGCMD("\tForm 2 Read Retry: %s\n", BIT(data[6], 0) ? "enable" : "disable"); + + LOGCMD("\tRead Retry Times %d\n", data[9]); break; default: @@ -519,31 +804,33 @@ void gdrom_device::device_start() m_identify_buffer[0] = 0x8600; // ATAPI device, cmd set 6 compliant, DRQ within 3 ms of PACKET command - m_identify_buffer[23] = ('S' << 8) | 'E'; - m_identify_buffer[24] = (' ' << 8) | ' '; - m_identify_buffer[25] = (' ' << 8) | ' '; - m_identify_buffer[26] = (' ' << 8) | ' '; - - m_identify_buffer[27] = ('C' << 8) | 'D'; - m_identify_buffer[28] = ('-' << 8) | 'R'; - m_identify_buffer[29] = ('O' << 8) | 'M'; - m_identify_buffer[30] = (' ' << 8) | 'D'; - m_identify_buffer[31] = ('R' << 8) | 'I'; - m_identify_buffer[32] = ('V' << 8) | 'E'; - m_identify_buffer[33] = (' ' << 8) | ' '; - m_identify_buffer[34] = (' ' << 8) | ' '; - m_identify_buffer[35] = ('6' << 8) | '.'; - m_identify_buffer[36] = ('4' << 8) | '2'; - m_identify_buffer[37] = (' ' << 8) | ' '; - m_identify_buffer[38] = (' ' << 8) | ' '; - m_identify_buffer[39] = (' ' << 8) | ' '; - m_identify_buffer[40] = (' ' << 8) | ' '; - m_identify_buffer[41] = (' ' << 8) | ' '; - m_identify_buffer[42] = (' ' << 8) | ' '; - m_identify_buffer[43] = (' ' << 8) | ' '; - m_identify_buffer[44] = (' ' << 8) | ' '; - m_identify_buffer[45] = (' ' << 8) | ' '; - m_identify_buffer[46] = (' ' << 8) | ' '; + // non-standard identify returns + // TODO: should be centralized in command 11h + // loopchk: identify device Maker test (0104) PC=c04694a + // Was 23 + int dptr = 8; + m_identify_buffer[dptr++] = 'S' | ('E' << 8); + m_identify_buffer[dptr++] = ' ' | (' ' << 8); + m_identify_buffer[dptr++] = ' ' | (' ' << 8); + m_identify_buffer[dptr++] = ' ' | (' ' << 8); + + // loopchk: identify device Model test (0104) PC=c046acc + // Was 27 + dptr = 16; + m_identify_buffer[dptr++] = 'C' | ('D' << 8); + m_identify_buffer[dptr++] = '-' | ('R' << 8); + m_identify_buffer[dptr++] = 'O' | ('M' << 8); + m_identify_buffer[dptr++] = ' ' | ('D' << 8); + m_identify_buffer[dptr++] = 'R' | ('I' << 8); + m_identify_buffer[dptr++] = 'V' | ('E' << 8); + m_identify_buffer[dptr++] = ' ' | (' ' << 8); + m_identify_buffer[dptr++] = ' ' | (' ' << 8); + // TODO: versioning is unchecked by loopchk + m_identify_buffer[dptr++] = '6' | ('.' << 8); + m_identify_buffer[dptr++] = '4' | ('2' << 8); + // FIXME: doc mentions a "System Date" readback + for (; dptr < 47; dptr ++) + m_identify_buffer[dptr] = ' ' | (' ' << 8); m_identify_buffer[49] = 0x0400; // IORDY may be disabled @@ -555,14 +842,57 @@ void gdrom_device::process_buffer() { atapi_hle_device::process_buffer(); // HACK: find out when this should be updated - m_sector_number = 0x80 | GDROM_PAUSE_STATE; + // TODO: upper byte is CD type detection + // 0000 CD-DA + // 0001 CD-ROM + // 0010 CD-ROM XA / CD Extra + // 0011 CD-I + // 1000 GD-ROM + const u8 cd_type = m_image->is_gd() ? 0x80 : 0x00; + m_sector_number = cd_type | (m_image->exists() ? m_sector_number & 0xf : GDROM_NODISC_STATE); } void gdrom_device::signature() { atapi_hle_device::signature(); + const u8 cd_type = m_image->is_gd() ? 0x80 : 0x00; + // naomi dimm board firmware needs the upper nibble to be 8 at the beginning - if (m_image->is_gd()) - m_sector_number = 0x81; + m_sector_number = cd_type | (m_image->exists() ? m_sector_number & 0xf : GDROM_NODISC_STATE); +} + +//bool gdrom_device::set_features() +//{ + // TODO: DSC, likely tested by Check-GD programs +// m_status |= IDE_STATUS_DSC; +// return atapi_cdrom_device::set_features(); +//} + +WRITE_LINE_MEMBER(gdrom_device::cdda_end_mark_cb) +{ + if (state != ASSERT_LINE) + return; + + m_cd_status.repeat_current ++; + m_cd_status.repeat_current &= 0xf; + LOG("end marker %d %d\n", m_cd_status.repeat_current, m_cd_status.repeat_count); + + // repeat_count = 0xf: infinite, not unlike Saturn CD Block + // - ggx sound test track #20 (opening) is a good testing scenario. + // - audio cd player don't care about this: + // it manually handle repeat modes depending on GD state alone. + if (m_cd_status.repeat_current > m_cd_status.repeat_count) + { + m_cdda->pause_audio(1); + m_audio_sense = SCSI_SENSE_ASC_ASCQ_AUDIO_PLAY_OPERATION_SUCCESSFULLY_COMPLETED; + LOG("\tCompleted\n"); + m_sector_number = (m_sector_number & 0xf0) | GDROM_PAUSE_STATE; + } + else + { + m_cdda->start_audio(m_cd_status.cdda_fad, m_cd_status.cdda_blocks); + LOG("\tRestart playback\n"); + m_sector_number = (m_sector_number & 0xf0) | GDROM_PLAY_STATE; + } } |
