diff options
Diffstat (limited to 'src/devices/bus/nscsi/cd.cpp')
-rw-r--r-- | src/devices/bus/nscsi/cd.cpp | 824 |
1 files changed, 759 insertions, 65 deletions
diff --git a/src/devices/bus/nscsi/cd.cpp b/src/devices/bus/nscsi/cd.cpp index 4d81d3dc9a2..1aea67056fd 100644 --- a/src/devices/bus/nscsi/cd.cpp +++ b/src/devices/bus/nscsi/cd.cpp @@ -3,14 +3,19 @@ #include "emu.h" #include "bus/nscsi/cd.h" +#include "coreutil.h" +#include "multibyte.h" + #define VERBOSE 0 #include "logmacro.h" DEFINE_DEVICE_TYPE(NSCSI_CDROM, nscsi_cdrom_device, "scsi_cdrom", "SCSI CD-ROM") DEFINE_DEVICE_TYPE(NSCSI_CDROM_SGI, nscsi_cdrom_sgi_device, "scsi_cdrom_sgi", "SCSI CD-ROM SGI") +DEFINE_DEVICE_TYPE(NSCSI_CDROM_NEWS, nscsi_cdrom_news_device, "scsi_cdrom_news", "SCSI CD-ROM NEWS") DEFINE_DEVICE_TYPE(NSCSI_RRD45, nscsi_dec_rrd45_device, "nrrd45", "RRD45 CD-ROM (New)") DEFINE_DEVICE_TYPE(NSCSI_XM3301, nscsi_toshiba_xm3301_device, "nxm3301", "XM-3301TA CD-ROM (New)") DEFINE_DEVICE_TYPE(NSCSI_XM5301SUN, nscsi_toshiba_xm5301_sun_device, "nxm5301sun", "XM-5301B Sun 4x CD-ROM (New)") +DEFINE_DEVICE_TYPE(NSCSI_XM5401, nscsi_toshiba_xm5401_device, "nxm5401", "XM-5401B 4x CD-ROM (New)") DEFINE_DEVICE_TYPE(NSCSI_XM5401SUN, nscsi_toshiba_xm5401_sun_device, "nxm5401sun", "XM-5401B Sun 4x CD-ROM (New)") DEFINE_DEVICE_TYPE(NSCSI_XM5701, nscsi_toshiba_xm5701_device, "nxm5701", "XM-5701B 12x CD-ROM (New)") DEFINE_DEVICE_TYPE(NSCSI_XM5701SUN, nscsi_toshiba_xm5701_sun_device, "nxm5701sun", "XM-5701B Sun 12x CD-ROM (New)") @@ -26,6 +31,11 @@ nscsi_cdrom_sgi_device::nscsi_cdrom_sgi_device(const machine_config &mconfig, co { } +nscsi_cdrom_news_device::nscsi_cdrom_news_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + nscsi_cdrom_device(mconfig, NSCSI_CDROM_NEWS, tag, owner, "Sony", "CD-ROM CDU-541", "1.0A", 0x00, 0x05) +{ +} + nscsi_dec_rrd45_device::nscsi_dec_rrd45_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : nscsi_cdrom_device(mconfig, NSCSI_RRD45, tag, owner, "DEC ", "RRD45 (C) DEC ", "0436", 0x98, 0x02) { @@ -41,6 +51,11 @@ nscsi_toshiba_xm5301_sun_device::nscsi_toshiba_xm5301_sun_device(const machine_c { } +nscsi_toshiba_xm5401_device::nscsi_toshiba_xm5401_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + nscsi_cdrom_device(mconfig, NSCSI_XM5401, tag, owner, "TOSHIBA ", "CD-ROM XM-5401TA", "3605", 0x98, 0x02) +{ +} + nscsi_toshiba_xm5401_sun_device::nscsi_toshiba_xm5401_sun_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : nscsi_cdrom_device(mconfig, NSCSI_XM5401SUN, tag, owner, "TOSHIBA ", "XM-5401TASUN4XCD", "1036", 0x98, 0x02) { @@ -56,41 +71,49 @@ nscsi_toshiba_xm5701_sun_device::nscsi_toshiba_xm5701_sun_device(const machine_c { } +// drive identifies as an original Apple CDSC (Sony CDU-8001 with custom firmware) nscsi_cdrom_apple_device::nscsi_cdrom_apple_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : - nscsi_cdrom_device(mconfig, NSCSI_CDROM_APPLE, tag, owner, "Sony", "CDU-76S", "1.0", 0x00, 0x05) + nscsi_cdrom_device(mconfig, NSCSI_CDROM_APPLE, tag, owner, "SONY ", "CD-ROM CDU-8002 ", "1.8g", 0x00, 0x05), + m_stopped(true), + m_stop_position(0) { } nscsi_cdrom_device::nscsi_cdrom_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : nscsi_full_device(mconfig, type, tag, owner, clock) - , cdrom(nullptr) + , image(*this, "image") + , cdda(*this, "cdda") , bytes_per_block(bytes_per_sector) , lba(0) , cur_sector(0) - , image(*this, "image") { } void nscsi_cdrom_device::device_start() { nscsi_full_device::device_start(); + save_item(NAME(sector_buffer)); save_item(NAME(lba)); save_item(NAME(cur_sector)); save_item(NAME(bytes_per_block)); + save_item(NAME(mode_data_size)); } void nscsi_cdrom_device::device_reset() { nscsi_full_device::device_reset(); - cdrom = image->get_cdrom_file(); + sequence_counter = image->sequence_counter(); lba = 0; cur_sector = -1; + mode_data_size = 12; } void nscsi_cdrom_device::device_add_mconfig(machine_config &config) { CDROM(config, image).set_interface("cdrom"); + + CDDA(config, cdda).set_cdrom_tag(image); } int nscsi_cdrom_device::to_msf(int frame) @@ -105,7 +128,10 @@ int nscsi_cdrom_device::to_msf(int frame) void nscsi_cdrom_device::set_block_size(u32 block_size) { if (bytes_per_sector % block_size) - throw emu_fatalerror("nscsi_cdrom_device(%s): block size must be a factor of sector size", tag()); + { + logerror("nscsi_cdrom_device(%s): block size %d must be a factor of sector size %d", tag(), block_size, bytes_per_sector); + return; + } bytes_per_block = block_size; }; @@ -119,7 +145,7 @@ uint8_t nscsi_cdrom_device::scsi_get_data(int id, int pos) const int extra_pos = (lba * bytes_per_block) % bytes_per_sector; if(sector != cur_sector) { cur_sector = sector; - if(!cdrom_read_data(cdrom, sector, sector_buffer, CD_TRACK_MODE1)) { + if(!image->read_data(sector, sector_buffer, cdrom_file::CD_TRACK_MODE1)) { LOG("CD READ ERROR sector %d!\n", sector); std::fill_n(sector_buffer, sizeof(sector_buffer), 0); } @@ -135,14 +161,30 @@ void nscsi_cdrom_device::scsi_put_data(int id, int pos, uint8_t data) } // process mode parameter header and one block descriptor - if(pos < sizeof(mode_data)) { + if(pos < mode_data_size) { mode_data[pos] = data; // is this the last byte of the mode parameter block descriptor? - if(pos == sizeof(mode_data) - 1) + if(pos == mode_data_size - 1) { // is there exactly one block descriptor? if(mode_data[3] == 8) - set_block_size((mode_data[9] << 16) | (mode_data[10] << 8) | (mode_data[11] << 0)); + set_block_size(get_u24be(&mode_data[9])); + + // Size of block descriptor should change based on if it's mode select (6) or (10) + // TODO: Find a usage of mode select (10) for testing + int pagedata_offset = 4 + mode_data[3]; + switch(mode_data[pagedata_offset] & 0x3f) { + case 0x0e: // CD Audio Control Mode Page + cdda_sotc = BIT(mode_data[pagedata_offset+2], 1); + LOG("Ch 0 route: %x vol: %x\n", mode_data[pagedata_offset+8], mode_data[pagedata_offset+9]); + LOG("Ch 1 route: %x vol: %x\n", mode_data[pagedata_offset+10], mode_data[pagedata_offset+11]); + LOG("Ch 2 route: %x vol: %x\n", mode_data[pagedata_offset+12], mode_data[pagedata_offset+13]); + LOG("Ch 3 route: %x vol: %x\n", mode_data[pagedata_offset+14], mode_data[pagedata_offset+15]); + cdda->set_output_gain(0, mode_data[pagedata_offset+9] / 255.0f); + cdda->set_output_gain(1, mode_data[pagedata_offset+11] / 255.0f); + break; + } + } } } @@ -157,11 +199,11 @@ void nscsi_cdrom_device::scsi_command() int blocks; // check for media change - if((cdrom != image->get_cdrom_file()) && (scsi_cmdbuf[0] != SC_INQUIRY)) + if( sequence_counter != image->sequence_counter() && (scsi_cmdbuf[0] != SC_INQUIRY)) { // clear media change condition - cdrom = image->get_cdrom_file(); cur_sector = -1; + sequence_counter = image->sequence_counter(); // report unit attention condition sense(false, SK_UNIT_ATTENTION); @@ -172,19 +214,19 @@ void nscsi_cdrom_device::scsi_command() switch(scsi_cmdbuf[0]) { case SC_TEST_UNIT_READY: LOG("command TEST UNIT READY\n"); - if(cdrom) + if(image->exists()) scsi_status_complete(SS_GOOD); else return_no_cd(); break; case SC_READ_6: - if(!cdrom) { + if(!image->exists()) { return_no_cd(); break; } - lba = ((scsi_cmdbuf[1] & 0x1f)<<16) | (scsi_cmdbuf[2]<<8) | scsi_cmdbuf[3]; + lba = get_u24be(&scsi_cmdbuf[1]) & 0x1fffff; blocks = scsi_cmdbuf[4]; if(!blocks) blocks = 256; @@ -219,7 +261,7 @@ void nscsi_cdrom_device::scsi_command() int size = scsi_cmdbuf[4]; switch(page) { case 0: - std::fill_n(scsi_cmdbuf, 36, 0); + std::fill_n(scsi_cmdbuf, size, 0); if (lun != 0) scsi_cmdbuf[0] = 0x7f; @@ -239,8 +281,6 @@ void nscsi_cdrom_device::scsi_command() if(scsi_cmdbuf[i] == 0) scsi_cmdbuf[i] = 0x20; - if(size > 36) - size = 36; scsi_data_in(SBUF_MAIN, size); break; } @@ -252,8 +292,9 @@ void nscsi_cdrom_device::scsi_command() LOG("command MODE SELECT 6 length %d\n", scsi_cmdbuf[4]); // accept mode select parameter data - if(scsi_cmdbuf[4]) - scsi_data_out(2, scsi_cmdbuf[4]); + mode_data_size = scsi_cmdbuf[4]; + if(mode_data_size) + scsi_data_out(2, mode_data_size); scsi_status_complete(SS_GOOD); break; @@ -264,9 +305,9 @@ void nscsi_cdrom_device::scsi_command() scsi_status_complete(SS_GOOD); break; - case SC_RECIEVE_DIAG_RES: { - LOG("command RECIEVE DIAGNOSTICS RESULTS"); - int size = (scsi_cmdbuf[3] << 8) | scsi_cmdbuf[4]; + case SC_RECEIVE_DIAGNOSTIC_RESULTS: { + LOG("command RECEIVE DIAGNOSTIC RESULTS"); + int size = get_u16be(&scsi_cmdbuf[3]); int pos = 0; scsi_cmdbuf[pos++] = 0; scsi_cmdbuf[pos++] = 6; @@ -282,9 +323,9 @@ void nscsi_cdrom_device::scsi_command() break; } - case SC_SEND_DIAGNOSTICS: { - LOG("command SEND DIAGNOSTICS"); - int size = (scsi_cmdbuf[3] << 8) | scsi_cmdbuf[4]; + case SC_SEND_DIAGNOSTIC: { + LOG("command SEND DIAGNOSTIC"); + int size = get_u16be(&scsi_cmdbuf[3]); if(scsi_cmdbuf[1] & 4) { // Self-test scsi_status_complete(SS_GOOD); @@ -307,24 +348,20 @@ void nscsi_cdrom_device::scsi_command() } case SC_READ_CAPACITY: { - if(!cdrom) { + LOG("command READ CAPACITY\n"); + + if(!image->exists()) { return_no_cd(); break; } - LOG("command READ CAPACITY\n"); - // get the last used block on the disc - const u32 temp = cdrom_get_track_start(cdrom, 0xaa) * (bytes_per_sector / bytes_per_block) - 1; + const u32 temp = image->get_track_start(0xaa) * (bytes_per_sector / bytes_per_block) - 1; - scsi_cmdbuf[0] = (temp>>24) & 0xff; - scsi_cmdbuf[1] = (temp>>16) & 0xff; - scsi_cmdbuf[2] = (temp>>8) & 0xff; - scsi_cmdbuf[3] = (temp & 0xff); + put_u32be(&scsi_cmdbuf[0], temp); scsi_cmdbuf[4] = 0; scsi_cmdbuf[5] = 0; - scsi_cmdbuf[6] = (bytes_per_block>>8)&0xff; - scsi_cmdbuf[7] = (bytes_per_block & 0xff); + put_u16be(&scsi_cmdbuf[6], bytes_per_block); scsi_data_in(SBUF_MAIN, 8); scsi_status_complete(SS_GOOD); @@ -332,11 +369,11 @@ void nscsi_cdrom_device::scsi_command() } case SC_READ_10: - lba = (scsi_cmdbuf[2]<<24) | (scsi_cmdbuf[3]<<16) | (scsi_cmdbuf[4]<<8) | scsi_cmdbuf[5]; - blocks = (scsi_cmdbuf[7] << 8) | scsi_cmdbuf[8]; + lba = get_u32be(&scsi_cmdbuf[2]); + blocks = get_u16be(&scsi_cmdbuf[7]); LOG("command READ EXTENDED start=%08x blocks=%04x\n", lba, blocks); - if(!cdrom) { + if(!image->exists()) { return_no_cd(); break; } @@ -361,17 +398,16 @@ void nscsi_cdrom_device::scsi_command() scsi_cmdbuf[pos++] = 0x80; // WP, cache // get the last used block on the disc - const u32 temp = cdrom_get_track_start(cdrom, 0xaa) * (bytes_per_sector / bytes_per_block) - 1; + const u32 temp = image->exists() ? image->get_track_start(0xaa) * (bytes_per_sector / bytes_per_block) - 1 : 0; scsi_cmdbuf[pos++] = 0x08; // Block descriptor length scsi_cmdbuf[pos++] = 0x00; // density code - scsi_cmdbuf[pos++] = (temp>>16) & 0xff; - scsi_cmdbuf[pos++] = (temp>>8) & 0xff; - scsi_cmdbuf[pos++] = (temp & 0xff); + put_u24be(&scsi_cmdbuf[pos], temp); + pos += 3; scsi_cmdbuf[pos++] = 0; scsi_cmdbuf[pos++] = 0; - scsi_cmdbuf[pos++] = (bytes_per_block>>8)&0xff; - scsi_cmdbuf[pos++] = (bytes_per_block & 0xff); + put_u16be(&scsi_cmdbuf[pos], bytes_per_block); + pos += 2; bool fail = false; int pmax = page == 0x3f ? 0x3e : page; @@ -420,6 +456,40 @@ void nscsi_cdrom_device::scsi_command() scsi_cmdbuf[pos++] = 0x00; // Reserved break; + case 0x0e: // CD Audio control page + scsi_cmdbuf[pos++] = 0x8e; // PS, page id + scsi_cmdbuf[pos++] = 0x0e; // Page length + scsi_cmdbuf[pos++] = (1 << 2) | (cdda_sotc << 1); // IMMED = 1 + scsi_cmdbuf[pos++] = 0x00; // Reserved + scsi_cmdbuf[pos++] = 0x00; + scsi_cmdbuf[pos++] = 0x00; + scsi_cmdbuf[pos++] = 0x00; + scsi_cmdbuf[pos++] = 0x00; + scsi_cmdbuf[pos++] = 0x01; // Ch 1 connected + scsi_cmdbuf[pos++] = 0xff; // Ch 1 max volume + scsi_cmdbuf[pos++] = 0x02; // Ch 2 connected + scsi_cmdbuf[pos++] = 0xff; // Ch 2 max volume + scsi_cmdbuf[pos++] = 0x04; // Ch 3 connected + scsi_cmdbuf[pos++] = 0xff; // Ch 3 max volume + scsi_cmdbuf[pos++] = 0x08; // Ch 4 connected + scsi_cmdbuf[pos++] = 0xff; // Ch 4 max volume + break; + + case 0x30: // magic Apple page + { + static const u8 apple_magic[0x17] = + { + 0x00, 0x41, 0x50, 0x50, 0x4C, 0x45, 0x20, 0x43, 0x4F, 0x4D, 0x50, 0x55, + 0x54, 0x45, 0x52, 0x2C, 0x20, 0x49, 0x4E, 0x43, 0x20, 0x20, 0x20 + }; + + LOG("Apple special MODE SENSE page\n"); + scsi_cmdbuf[pos++] = 0x30; // PS, page id + memcpy(&scsi_cmdbuf[pos], apple_magic, 0x17); + pos += 0x17; + } + break; + default: if (page != 0x3f) { LOG("mode sense page %02x unhandled\n", p); @@ -442,12 +512,117 @@ void nscsi_cdrom_device::scsi_command() break; } + case SC_READ_DISC_INFORMATION: + LOG("command READ DISC INFORMATION\n"); + if(!image->exists()) { + return_no_cd(); + break; + } + + std::fill_n(scsi_cmdbuf, 34, 0); + scsi_cmdbuf[1] = 32; + scsi_cmdbuf[2] = 2; // disc is complete + scsi_cmdbuf[3] = 1; // first track + scsi_cmdbuf[4] = 1; // number of sessions (TODO: session support for CHDv6) + scsi_cmdbuf[5] = 1; // first track in last session + scsi_cmdbuf[6] = image->get_last_track(); // last track in last session + scsi_cmdbuf[8] = 0; // CD-ROM, not XA + + // lead in start time in MSF + { + u32 tstart = image->get_track_start(0); + tstart = to_msf(tstart + 150); + + put_u32be(&scsi_cmdbuf[16], tstart); + + // lead-out start time in MSF + tstart = image->get_track_start(0xaa); + tstart = to_msf(tstart + 150); + + put_u32be(&scsi_cmdbuf[20], tstart); + } + + scsi_data_in(0, 34); + scsi_status_complete(SS_GOOD); + break; + case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: // TODO: support eject prevention LOG("command %s MEDIUM REMOVAL\n", (scsi_cmdbuf[4] & 0x1) ? "PREVENT" : "ALLOW"); scsi_status_complete(SS_GOOD); break; + case SC_READ_SUB_CHANNEL: { + const bool msf = BIT(scsi_cmdbuf[1], 1) != 0; + const bool subq = BIT(scsi_cmdbuf[2], 6); + const int param = scsi_cmdbuf[3]; + const int track = scsi_cmdbuf[6]; + const int alloc_length = get_u16be(&scsi_cmdbuf[7]); + + LOG("command READ SUB CHANNEL Param = %d, Track = %d, MSF = %d, SUBQ = %d\n", param, track, msf, subq); + + if(!image->exists()) { + return_no_cd(); + break; + } + + std::fill_n(scsi_cmdbuf, alloc_length, 0); + + scsi_cmdbuf[0] = 0x00; // Reserved + + int audio_active = cdda->audio_active(); + if(audio_active) { + if(cdda->audio_paused()) + scsi_cmdbuf[1] = 0x12; // Audio play operation paused + else + scsi_cmdbuf[1] = 0x11; // Audio play operation in progress + } else { + if(cdda->audio_ended()) + scsi_cmdbuf[1] = 0x13; // Audio play operation successfully completed + else + scsi_cmdbuf[1] = 0x15; // No current audio status to return + } + + if(subq) { + switch(param) { + case 1: { // CD current position + int m_last_lba = audio_active ? cdda->get_audio_lba() : 0; + + scsi_cmdbuf[2] = 0; // Sub-channel Data Length + scsi_cmdbuf[3] = 12; + + scsi_cmdbuf[4] = 0x01; // Sub-channel Data Format Code + scsi_cmdbuf[5] = (audio_active ? 0 : 4) | 0x10; // Q Sub-channel encodes current position data + scsi_cmdbuf[6] = image->get_track(m_last_lba) + 1; // Track Number + scsi_cmdbuf[7] = 0x00; // Index Number + + uint32_t frame = m_last_lba; + if(msf) + frame = to_msf(frame); + + put_u32be(&scsi_cmdbuf[8], frame); // Absolute CD Address + + frame = m_last_lba - image->get_track_start(scsi_cmdbuf[6] - 1); + if(msf) + frame = to_msf(frame); + + put_u32be(&scsi_cmdbuf[12], frame); // Track Relative CD Address + break; + } + + case 0x02: // Media Catalog number (UPC/bar code) + case 0x03: // Track International standard recording code (ISRC) + default: + LOG("Unknown subchannel type %d requested\n", scsi_cmdbuf[3]); + break; + } + } + + scsi_data_in(0, alloc_length); + scsi_status_complete(SS_GOOD); + break; + } + case SC_READ_TOC_PMA_ATIP: { /* Track numbers are problematic here: 0 = lead-in, 0xaa = lead-out. @@ -460,7 +635,7 @@ void nscsi_cdrom_device::scsi_command() "Session info", "Full TOC", "PMA", - "ATIP" + "ATIP", "Reserved 5", "Reserved 6", "Reserved 7", @@ -475,7 +650,7 @@ void nscsi_cdrom_device::scsi_command() }; bool msf = (scsi_cmdbuf[1] & 0x2) != 0; - u16 size = (scsi_cmdbuf[7] << 7) | scsi_cmdbuf[8]; + u16 size = get_u16be(&scsi_cmdbuf[7]); u8 format = scsi_cmdbuf[2] & 15; /// SFF8020 legacy format field (see T10/1836-D Revision 2g page 643) @@ -484,11 +659,16 @@ void nscsi_cdrom_device::scsi_command() LOG("command READ TOC PMA ATIP, format %s msf=%d size=%d\n", format_names[format], msf, size); + if(!image->exists()) { + return_no_cd(); + break; + } + int pos = 0; switch (format) { case 0: { int start_track = scsi_cmdbuf[6]; - int end_track = cdrom_get_last_track(cdrom); + int end_track = image->get_last_track(); int tracks; if(start_track == 0) @@ -504,10 +684,10 @@ void nscsi_cdrom_device::scsi_command() // the returned TOC DATA LENGTH must be the full amount, // regardless of how much we're able to pass back due to size - scsi_cmdbuf[pos++] = (len>>8) & 0xff; - scsi_cmdbuf[pos++] = (len & 0xff); + put_u16be(&scsi_cmdbuf[pos], len); + pos += 2; scsi_cmdbuf[pos++] = 1; - scsi_cmdbuf[pos++] = cdrom_get_last_track(cdrom); + scsi_cmdbuf[pos++] = image->get_last_track(); if (start_track == 0) start_track = 1; @@ -521,19 +701,17 @@ void nscsi_cdrom_device::scsi_command() } scsi_cmdbuf[pos++] = 0; - scsi_cmdbuf[pos++] = cdrom_get_adr_control(cdrom, cdrom_track); + scsi_cmdbuf[pos++] = image->get_adr_control(cdrom_track); scsi_cmdbuf[pos++] = track; scsi_cmdbuf[pos++] = 0; - u32 tstart = cdrom_get_track_start(cdrom, cdrom_track); + u32 tstart = image->get_track_start(cdrom_track); if(msf) tstart = to_msf(tstart+150); - scsi_cmdbuf[pos++] = (tstart>>24) & 0xff; - scsi_cmdbuf[pos++] = (tstart>>16) & 0xff; - scsi_cmdbuf[pos++] = (tstart>>8) & 0xff; - scsi_cmdbuf[pos++] = (tstart & 0xff); + put_u32be(&scsi_cmdbuf[pos], tstart); + pos += 4; } break; } @@ -541,25 +719,23 @@ void nscsi_cdrom_device::scsi_command() case 1: { int len = 2 + (8 * 1); - scsi_cmdbuf[pos++] = (len>>8) & 0xff; - scsi_cmdbuf[pos++] = (len & 0xff); + put_u16be(&scsi_cmdbuf[pos], len); + pos += 2; scsi_cmdbuf[pos++] = 1; scsi_cmdbuf[pos++] = 1; scsi_cmdbuf[pos++] = 0; - scsi_cmdbuf[pos++] = cdrom_get_adr_control(cdrom, 0); + scsi_cmdbuf[pos++] = image->get_adr_control(0); scsi_cmdbuf[pos++] = 1; scsi_cmdbuf[pos++] = 0; - u32 tstart = cdrom_get_track_start(cdrom, 0); + u32 tstart = image->get_track_start(0); if (msf) tstart = to_msf(tstart+150); - scsi_cmdbuf[pos++] = (tstart>>24) & 0xff; - scsi_cmdbuf[pos++] = (tstart>>16) & 0xff; - scsi_cmdbuf[pos++] = (tstart>>8) & 0xff; - scsi_cmdbuf[pos++] = (tstart & 0xff); + put_u32be(&scsi_cmdbuf[pos], tstart); + pos += 4; break; } @@ -583,6 +759,44 @@ void nscsi_cdrom_device::scsi_command() break; } + case SC_PLAY_AUDIO_TRACK_INDEX: { + LOG("command PLAY AUDIO TRACK INDEX tr %d/idx %d to tr %d/idx %d\n", scsi_cmdbuf[4], scsi_cmdbuf[5], scsi_cmdbuf[7], scsi_cmdbuf[8]); + + if(scsi_cmdbuf[4] > scsi_cmdbuf[7]) { + scsi_status_complete(SS_CHECK_CONDITION); + sense(false, SK_ILLEGAL_REQUEST, 0x00, 0x14); + break; + } + + // TODO: Index isn't accounted for at all + const uint32_t start_track = scsi_cmdbuf[4]; + const uint32_t end_track = cdda_sotc ? start_track : std::min(image->get_last_track(), (int)scsi_cmdbuf[7]); + const uint32_t lba = image->get_track_start(start_track - 1); + const uint32_t blocks = image->get_track_start(end_track) - lba; + const uint32_t track = image->get_track(lba); + + if(image->get_track_type(track) == cdrom_file::CD_TRACK_AUDIO) { + LOG("Playing %d blocks from track %d (lba %d)\n", blocks, start_track, lba); + + cdda->start_audio(lba, blocks); + scsi_status_complete(SS_GOOD); + sense(false, SK_NO_SENSE, 0x00, 0x11); + } else { + LOG("Track %d is not audio\n", start_track); + + scsi_status_complete(SS_CHECK_CONDITION); + sense(false, SK_ILLEGAL_REQUEST, 0x64, 0x00); + } + break; + } + + case SC_PAUSE_RESUME: + if (image->exists()) + cdda->pause_audio(BIT(scsi_cmdbuf[8], 0) ^ 1); + + scsi_status_complete(SS_GOOD); + break; + default: nscsi_full_device::scsi_command(); break; @@ -644,6 +858,30 @@ bool nscsi_cdrom_sgi_device::scsi_command_done(uint8_t command, uint8_t length) return nscsi_full_device::scsi_command_done(command, length); } } + +// Apple CDSC commands, from AppleCDSC.aii in IIgs System 6.0.1. +// These are related to the Sony CDU-541 commands but customized by Apple. +enum apple_scsi_command_e : uint8_t { + APPLE_EJECT = 0xc0, + APPLE_READ_TOC = 0xc1, + APPLE_READ_SUB_CHANNEL = 0xc2, + APPLE_AUDIO_PLAY = 0xc8, + APPLE_AUDIO_PLAY_TRACK = 0xc9, + APPLE_AUDIO_PAUSE = 0xca, + APPLE_AUDIO_STOP = 0xcb, + APPLE_AUDIO_STATUS = 0xcc, + APPLE_AUDIO_SCAN = 0xcd, + APPLE_AUDIO_CONTROL = 0xce +}; + +void nscsi_cdrom_apple_device::device_start() +{ + nscsi_cdrom_device::device_start(); + + save_item(NAME(m_stopped)); + save_item(NAME(m_stop_position)); +} + /* The Apple II SCSI Card firmware demands that ASC on a failing TEST_UNIT_READY be either 0x28 or 0xb0. 0x28 is MEDIA_CHANGED, 0xb0 is vendor-specific. If the drive returns the normal 0x3A for disc-not-present, @@ -652,10 +890,19 @@ bool nscsi_cdrom_sgi_device::scsi_command_done(uint8_t command, uint8_t length) */ void nscsi_cdrom_apple_device::scsi_command() { + if (scsi_cmdbuf[0] != 8 && scsi_cmdbuf[0] != 0x28 && scsi_cmdbuf[0] != 0 && scsi_cmdbuf[0] != 0x03) + { + LOG("CD command: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + scsi_cmdbuf[0], scsi_cmdbuf[1], scsi_cmdbuf[2], + scsi_cmdbuf[3], scsi_cmdbuf[4], scsi_cmdbuf[5], + scsi_cmdbuf[6], scsi_cmdbuf[7], scsi_cmdbuf[8], + scsi_cmdbuf[9]); + } + switch (scsi_cmdbuf[0]) { case SC_TEST_UNIT_READY: LOG("command TEST UNIT READY (AppleCD)\n"); - if(cdrom) + if(image->exists()) { scsi_status_complete(SS_GOOD); } @@ -666,8 +913,455 @@ void nscsi_cdrom_apple_device::scsi_command() } break; + case SC_INQUIRY: + { + int lun = get_lun(scsi_cmdbuf[1] >> 5); + LOG("command INQUIRY lun=%d EVPD=%d page=%d alloc=%02x link=%02x\n", + lun, scsi_cmdbuf[1] & 1, scsi_cmdbuf[2], scsi_cmdbuf[4], scsi_cmdbuf[5]); + + int page = scsi_cmdbuf[2]; + int size = scsi_cmdbuf[4]; + switch (page) + { + case 0: + std::fill_n(scsi_cmdbuf, size, 0); + + // data taken from the ROM of an AppleCD 150 + scsi_cmdbuf[0] = 0x05; // device is present, device is CD/DVD (MMC-3) + scsi_cmdbuf[1] = 0x80; // media is removable + scsi_cmdbuf[2] = 0x01; + scsi_cmdbuf[3] = 0x01; + scsi_cmdbuf[4] = 0x31; // additional length + memcpy((char *)&scsi_cmdbuf[8], "SONY ", 8); + memcpy((char *)&scsi_cmdbuf[16], "CD-ROM CDU-8002 ", 16); + memcpy((char *)&scsi_cmdbuf[32], "1.8g", 4); + scsi_cmdbuf[39] = 0xd0; + scsi_cmdbuf[40] = 0x90; + scsi_cmdbuf[41] = 0x27; + scsi_cmdbuf[42] = 0x3e; + scsi_cmdbuf[43] = 0x01; + scsi_cmdbuf[44] = 0x04; + scsi_cmdbuf[45] = 0x91; + scsi_cmdbuf[47] = 0x18; + scsi_cmdbuf[48] = 0x06; + scsi_cmdbuf[49] = 0xf0; + scsi_cmdbuf[50] = 0xfe; + + scsi_data_in(SBUF_MAIN, size); + break; + } + scsi_status_complete(SS_GOOD); + } + break; + + case APPLE_READ_TOC: + { + if (!image->exists()) + { + return_no_cd(); + break; + } + + u16 size = get_u16be(&scsi_cmdbuf[7]); + LOG("command READ TOC (AppleCD), size=%d, track=%d, type1=%02x type2=%02x\n", size, scsi_cmdbuf[2], scsi_cmdbuf[9], scsi_cmdbuf[5]); + + if (scsi_cmdbuf[9] == 0x80) + { + // get TOC info for 1 or more tracks from a specified starting track + assert(scsi_cmdbuf[5] > 0); + + const int start_track = scsi_cmdbuf[5] - 1; + const int num_trks = size / 4; + int pos = 0; + + for (int trk = 0; trk < num_trks; trk++) + { + // Coverity: The emulated program could conceivably request a buffer size for + // more tracks than exist, but we must still return the requested amount of data. + if ((trk + start_track) < image->get_last_track()) + { + const uint32_t start_lba = image->get_track_start(trk + start_track); + const uint32_t start_frame = to_msf(start_lba); + + scsi_cmdbuf[pos++] = image->get_adr_control(trk + start_track); + scsi_cmdbuf[pos++] = dec_2_bcd(BIT(start_frame, 16, 8)); // minutes + scsi_cmdbuf[pos++] = dec_2_bcd(BIT(start_frame, 8, 8)); // seconds + scsi_cmdbuf[pos++] = dec_2_bcd(BIT(start_frame, 0, 8)); // frames + } + else // keep returning the last track if necessary + { + const uint32_t start_lba = image->get_track_start(image->get_last_track()); + const uint32_t start_frame = to_msf(start_lba); + + scsi_cmdbuf[pos++] = image->get_adr_control(image->get_last_track()); + scsi_cmdbuf[pos++] = dec_2_bcd(BIT(start_frame, 16, 8)); // minutes + scsi_cmdbuf[pos++] = dec_2_bcd(BIT(start_frame, 8, 8)); // seconds + scsi_cmdbuf[pos++] = dec_2_bcd(BIT(start_frame, 0, 8)); // frames + } + } + + scsi_data_in(SBUF_MAIN, pos); + scsi_status_complete(SS_GOOD); + } + else if (scsi_cmdbuf[9] == 0x40) // get MSF of the start of the leadout + { + const int track = image->get_last_track(); + const uint32_t frame = to_msf(image->get_track_start(track)); + scsi_cmdbuf[0] = dec_2_bcd(BIT(frame, 16, 8)); // minutes + scsi_cmdbuf[1] = dec_2_bcd(BIT(frame, 8, 8)); // seconds + scsi_cmdbuf[2] = dec_2_bcd(BIT(frame, 0, 8)); // frames + scsi_cmdbuf[3] = 0; + + scsi_data_in(SBUF_MAIN, 4); + scsi_status_complete(SS_GOOD); + } + else + { + switch (scsi_cmdbuf[5]) + { + case 0x00: // get start and end track numbers + scsi_cmdbuf[0] = 1; + scsi_cmdbuf[1] = dec_2_bcd(image->get_last_track()); + scsi_cmdbuf[2] = 0; + scsi_cmdbuf[3] = 0; + + scsi_data_in(SBUF_MAIN, 4); + scsi_status_complete(SS_GOOD); + break; + + case 0x40: // get first frame of lead-out (one past end of last track) + { + const uint32_t leadout_lba = image->get_track_start(0xaa); + const uint32_t leadout_frame = to_msf(leadout_lba); + scsi_cmdbuf[0] = dec_2_bcd(BIT(leadout_frame, 16, 8)); // minutes + scsi_cmdbuf[1] = dec_2_bcd(BIT(leadout_frame, 8, 8)); // seconds + scsi_cmdbuf[2] = dec_2_bcd(BIT(leadout_frame, 0, 8)); // frames + scsi_cmdbuf[3] = 0; + + scsi_data_in(SBUF_MAIN, 4); + scsi_status_complete(SS_GOOD); + } + break; + + case 0x80: // get track info + { + const uint32_t start_lba = image->get_track_start(scsi_cmdbuf[2]); + const uint32_t start_frame = to_msf(start_lba); + + scsi_cmdbuf[0] = image->get_adr_control(scsi_cmdbuf[2]); + scsi_cmdbuf[1] = dec_2_bcd(BIT(start_frame, 16, 8)); // minutes + scsi_cmdbuf[2] = dec_2_bcd(BIT(start_frame, 8, 8)); // seconds + scsi_cmdbuf[3] = dec_2_bcd(BIT(start_frame, 0, 8)); // frames + scsi_data_in(SBUF_MAIN, 4); + scsi_status_complete(SS_GOOD); + } + break; + + default: + logerror("APPLE READ TOC: Unhandled type %02x\n", scsi_cmdbuf[5]); + break; + } + } + } + break; + + case APPLE_AUDIO_STATUS: + LOG("command APPLE AUDIO STATUS, type %02x, want %d bytes\n", scsi_cmdbuf[3], scsi_cmdbuf[8]); + + if (!image->exists()) + { + return_no_cd(); + break; + } + + // Audio status codes, adapted from CDRemote.equ in GS/OS 6.0.1 (matches CDU-541 as well) + enum cd_status_t: u8 + { + PLAYING = 0, // Audio is playing + PAUSED, // Audio is paused + PLAYING_MUTED, // Playing, but muted + REACHED_END, // Playback reached the end successfully + ERROR, // an error occured + VOID // really "idle" + }; + + { + const int type = scsi_cmdbuf[3]; + + scsi_cmdbuf[2] = image->get_adr_control(image->get_track(cdda->get_audio_lba())); + + const uint32_t frame = to_msf(cdda->get_audio_lba()); + scsi_cmdbuf[3] = dec_2_bcd(BIT(frame, 16, 8)); // minute + scsi_cmdbuf[4] = dec_2_bcd(BIT(frame, 8, 8)); // second + scsi_cmdbuf[5] = dec_2_bcd(BIT(frame, 0, 8)); // frame + + // TODO: When you pause, both 7.1's CD Remote and 7.6+'s more modern player still see the + // status as "playing" even though it's definitely returning "paused". I've tried returning + // every code and nothing works. + if (type == 0) + { + scsi_cmdbuf[1] = 0; + if (m_stopped) + { + scsi_cmdbuf[0] = VOID; + } + else + { + if (cdda->audio_ended()) + { + scsi_cmdbuf[0] = REACHED_END; + } + else + { + if (cdda->audio_active()) + { + if (cdda->audio_paused()) + { + scsi_cmdbuf[0] = PAUSED; + } + else + { + scsi_cmdbuf[0] = PLAYING; + } + } + else + { + scsi_cmdbuf[0] = VOID; + } + } + } + } + else if (type == 1) // "volume status" + { + scsi_cmdbuf[0] = (u8)(cdda->output_gain(0) * 255.0f); + scsi_cmdbuf[1] = (u8)(cdda->output_gain(1) * 255.0f); + + // Other 4 bytes unknown in this mode, going with the same as type 0 for now + } + } + //printf("status => %02x %02x %02x %02x %02x %02x\n", scsi_cmdbuf[0], scsi_cmdbuf[1], scsi_cmdbuf[2], scsi_cmdbuf[3], scsi_cmdbuf[4], scsi_cmdbuf[5]); + scsi_cmdbuf[0] = 0; + scsi_data_in(SBUF_MAIN, 6); + scsi_status_complete(SS_GOOD); + break; + + case APPLE_READ_SUB_CHANNEL: + LOG("command READ SUB CHANNEL\n"); + + scsi_cmdbuf[0] = 0; // Control nibble + scsi_cmdbuf[1] = 0; // Track + scsi_cmdbuf[2] = 0; // Index + scsi_cmdbuf[3] = 0; // Relative Minute + scsi_cmdbuf[4] = 0; // Relative Second + scsi_cmdbuf[5] = 0; // Relative Frame + scsi_cmdbuf[6] = 0; // AMinute + scsi_cmdbuf[7] = 0; // ASecond + scsi_cmdbuf[8] = 0; // AFrame + + if (cdda->audio_active()) + { + int track = image->get_track(cdda->get_audio_lba()); + scsi_cmdbuf[1] = dec_2_bcd(track+1); + scsi_cmdbuf[2] = 1; // Index + + const uint32_t track_start = image->get_track_start(track); + uint32_t cur_lba = cdda->get_audio_lba(); + + if (cur_lba < track_start) + { + cur_lba = track_start; + } + + const uint32_t relframe = to_msf(cur_lba - track_start); + scsi_cmdbuf[3] = dec_2_bcd(BIT(relframe, 16, 8)); // minute + scsi_cmdbuf[4] = dec_2_bcd(BIT(relframe, 8, 8)); // second + scsi_cmdbuf[5] = dec_2_bcd(BIT(relframe, 0, 8)); // frame + + const uint32_t frame = to_msf(cur_lba); + scsi_cmdbuf[6] = dec_2_bcd(BIT(frame, 16, 8)); // minute + scsi_cmdbuf[7] = dec_2_bcd(BIT(frame, 8, 8)); // second + scsi_cmdbuf[8] = dec_2_bcd(BIT(frame, 0, 8)); // frame + } + + scsi_data_in(SBUF_MAIN, 9); + scsi_status_complete(SS_GOOD); + break; + + case APPLE_AUDIO_PLAY: + case APPLE_AUDIO_PLAY_TRACK: + { + const uint8_t val = scsi_cmdbuf[5]; + const uint8_t start_track = (((val & 0xf0) >> 4) * 10) + (val & 0x0f); + + uint32_t start_lba = 0; + + if (scsi_cmdbuf[0] == APPLE_AUDIO_PLAY) + { + if (scsi_cmdbuf[9] == 0x40) + { + LOG("command APPLE AUDIO PLAY MSF = %02x:%02x:%02x\n", scsi_cmdbuf[5], scsi_cmdbuf[6], scsi_cmdbuf[7]); + const uint32_t msf = (bcd_2_dec(scsi_cmdbuf[5]) << 16) | (bcd_2_dec(scsi_cmdbuf[6]) << 8) | bcd_2_dec(scsi_cmdbuf[7]); + start_lba = cdrom_file::msf_to_lba(msf); + } + else if (scsi_cmdbuf[9] == 0x80) + { + LOG("command APPLE AUDIO PLAY track = %d\n", start_track); + if (start_track == 0) + { + cdda->stop_audio(); + scsi_status_complete(SS_GOOD); + return; + } + else + { + start_lba = image->get_track_start(start_track - 1); + } + } + else + { + logerror("nscsi_cdrom_apple_device: Unknown APPLE AUDIO PLAY address mode %02x\n", scsi_cmdbuf[9]); + } + } + else // APPLE_AUDIO_PLAY_TRACK + { + LOG("command APPLE AUDIO PLAY TRACK track = %d\n", start_track); + + // The Mac version of Apple CD-ROM Explorer issues PLAY on track 0 to stop + if (start_track == 0) + { + cdda->stop_audio(); + m_stopped = true; + scsi_status_complete(SS_GOOD); + return; + } + else + { + start_lba = image->get_track_start(start_track - 1); + m_stop_position = image->get_track_start(start_track); + } + } + + m_stopped = false; + cdda->start_audio(start_lba, m_stop_position - start_lba); + scsi_status_complete(SS_GOOD); + } + break; + + case APPLE_AUDIO_SCAN: + LOG("command APPLE_AUDIO_SCAN: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + scsi_cmdbuf[0], scsi_cmdbuf[1], scsi_cmdbuf[2], + scsi_cmdbuf[3], scsi_cmdbuf[4], scsi_cmdbuf[5], + scsi_cmdbuf[6], scsi_cmdbuf[7], scsi_cmdbuf[8], + scsi_cmdbuf[9]); + scsi_status_complete(SS_GOOD); + break; + + case APPLE_AUDIO_PAUSE: + LOG("command APPLE AUDIO PAUSE, on/off=%02x\n", scsi_cmdbuf[1]); + if (scsi_cmdbuf[1] == 0x10) + { + // set pause + if (!cdda->audio_paused()) + { + cdda->pause_audio(1); + } + } + else + { + // release pause + if (cdda->audio_paused()) + { + cdda->pause_audio(0); + } + } + scsi_status_complete(SS_GOOD); + break; + + case APPLE_AUDIO_STOP: + LOG("command APPLE AUDIO STOP\n"); + switch (scsi_cmdbuf[9]) + { + case 0x00: // stop immediately + m_stop_position = 0; + m_stopped = true; + cdda->stop_audio(); + break; + + case 0x40: // stop at MSF + { + const uint32_t msf = (bcd_2_dec(scsi_cmdbuf[5]) << 16) | (bcd_2_dec(scsi_cmdbuf[6]) << 8) | bcd_2_dec(scsi_cmdbuf[7]); + m_stop_position = cdrom_file::msf_to_lba(msf); + } + break; + + case 0x80: // stop at track number + { + const uint8_t val = scsi_cmdbuf[5]; + const uint8_t start_track = (((val & 0xf0) >> 4) * 10) + (val & 0x0f); + m_stop_position = image->get_track_start(start_track); + } + break; + + default: + logerror("nscsi_cdrom_apple_device: Unknown APPLE AUDIO STOPO address mode %02x\n", scsi_cmdbuf[9]); + break; + } + scsi_status_complete(SS_GOOD); + break; + + case APPLE_AUDIO_CONTROL: + LOG("command APPLE AUDIO CONTROL, size %d\n", scsi_cmdbuf[8]); + + scsi_data_out(3, scsi_cmdbuf[8]); + scsi_status_complete(SS_GOOD); + break; + + case APPLE_EJECT: + LOG("command APPLE EJECT\n"); + cdda->stop_audio(); + m_stopped = true; + scsi_status_complete(SS_GOOD); + break; + + case SC_READ_6: + case SC_READ_10: + case SC_READ_12: + cdda->stop_audio(); + [[fallthrough]]; + default: nscsi_cdrom_device::scsi_command(); break; } } + +bool nscsi_cdrom_apple_device::scsi_command_done(uint8_t command, uint8_t length) +{ + switch (command & 0xf0) + { + case 0xc0: + return length == 10; + + default: + return nscsi_full_device::scsi_command_done(command, length); + } +} + +void nscsi_cdrom_apple_device::scsi_put_data(int id, int pos, uint8_t data) +{ + if (id != 3) + { + nscsi_cdrom_device::scsi_put_data(id, pos, data); + return; + } + + if (pos == 0) + { + cdda->set_output_gain(0, data / 255.0f); + } + else if (pos == 1) + { + cdda->set_output_gain(1, data / 255.0f); + } + +} |