diff --git a/devices/common/ata/atapicdrom.cpp b/devices/common/ata/atapicdrom.cpp index d43750ae50..7f6b374d48 100644 --- a/devices/common/ata/atapicdrom.cpp +++ b/devices/common/ata/atapicdrom.cpp @@ -67,6 +67,11 @@ int AtapiCdrom::device_postinit() { parse_device_path(cdr_config, bus_id, dev_num); auto bus_obj = dynamic_cast(gMachineObj->get_comp_by_name(bus_id)); + if (!bus_obj) { + LOG_F(ERROR, "%s: cdr_config device path %s does not specify a known IDE bus", + this->name.c_str(), cdr_config.c_str()); + return -1; + } bus_obj->register_device(dev_num, this); std::string cdr_image_path = GET_STR_PROP("cdr_img"); @@ -100,6 +105,12 @@ void AtapiCdrom::perform_packet_command() { switch (this->cmd_pkt[0]) { case ScsiCommand::READ_CD: { + // READ_CD currently bypasses the shared CD-ROM command dispatcher. + if (!this->ready_for_command()) { + this->present_status(); + break; + } + lba = READ_DWORD_BE_U(&this->cmd_pkt[2]); xfer_len = (this->cmd_pkt[6] << 16) | READ_WORD_BE_U(&this->cmd_pkt[7]); if (this->cmd_pkt[9] == 0 && (this->cmd_pkt[10] & 7) == 0) { diff --git a/devices/common/scsi/scsiblockcmds.cpp b/devices/common/scsi/scsiblockcmds.cpp index 58186c8904..91060d1aca 100644 --- a/devices/common/scsi/scsiblockcmds.cpp +++ b/devices/common/scsi/scsiblockcmds.cpp @@ -79,6 +79,11 @@ void ScsiBlockCmds::init_block_device(uint8_t medium_type, uint8_t dev_flags, } void ScsiBlockCmds::process_command() { + if (!this->ready_for_command()) { + phy_impl->switch_phase(ScsiPhase::STATUS); + return; + } + int next_phase; // use non-disk buffer by default diff --git a/devices/common/scsi/scsibus.cpp b/devices/common/scsi/scsibus.cpp index 4e341fa0d9..fb3c5902bb 100644 --- a/devices/common/scsi/scsibus.cpp +++ b/devices/common/scsi/scsibus.cpp @@ -31,6 +31,7 @@ along with this program. If not, see . #include #include +#include ScsiBus::ScsiBus(const std::string name) { @@ -338,29 +339,65 @@ void ScsiBus::attach_scsi_devices(const std::string bus_suffix) } } + int default_cdrom_id = -1; + std::string cdr_config = GET_STR_PROP("cdr_config"); + if (!cdr_config.empty()) { + std::string bus_id; + uint32_t dev_num; + parse_device_path(cdr_config, bus_id, dev_num); + // Only consume SCSI paths; other buses handle their own CD-ROM configuration. + if (bus_id.starts_with("Scsi")) { + auto bus_obj = dynamic_cast(gMachineObj->get_comp_by_name_optional(bus_id)); + if (!bus_obj) + ABORT_F("cdr_config device path %s does not specify a known SCSI bus", cdr_config.c_str()); + if (dev_num >= SCSI_MAX_DEVS) + ABORT_F("Invalid SCSI ID in cdr_config device path %s", cdr_config.c_str()); + if (bus_obj == this) { + if (this->devices[dev_num]) + ABORT_F("%s: cdr_config SCSI ID %u is already occupied", bus_id.c_str(), dev_num); + default_cdrom_id = dev_num; + } + } + } + image_path = GET_STR_PROP("cdr_img" + bus_suffix); + std::vector cdrom_paths; if (!image_path.empty()) { - std::istringstream image_stream(image_path); - while (std::getline(image_stream, path, ':')) { - // do two passes because we start at ID 3. + // Keep empty entries, including the last one, as empty CD-ROM drives. + size_t start = 0; + size_t end; + do { + end = image_path.find(':', start); + cdrom_paths.push_back(image_path.substr(start, end - start)); + start = end + 1; + } while (end != std::string::npos); + } else if (default_cdrom_id >= 0) { + cdrom_paths.push_back(""); + } + for (const auto& path : cdrom_paths) { + if (default_cdrom_id >= 0) { + scsi_id = default_cdrom_id; + default_cdrom_id = -1; + } else { + // do two passes because we start at ID 3. for (scsi_id = 3; scsi_id < SCSI_MAX_DEVS * 2 && this->devices[scsi_id % SCSI_MAX_DEVS]; scsi_id++) {} + } - if (scsi_id < SCSI_MAX_DEVS * 2) { - scsi_id = scsi_id % SCSI_MAX_DEVS; - std::string scsi_device_name = "ScsiCdrom" + bus_suffix + "," + - std::to_string(scsi_id); - ScsiCdrom *scsi_device = new ScsiCdrom(scsi_device_name, scsi_id); - gMachineObj->add_device(scsi_device_name, - std::unique_ptr(scsi_device)); - this->register_device(scsi_id, scsi_device); - if (!scsi_device->insert_image(path)) - ABORT_F("Could not insert CD-ROM image, %s", path.c_str()); - } - else { - LOG_F(ERROR, "%s: Too many devices. CD-ROM \"%s\" was not added.", - this->get_name().c_str(), path.c_str()); - } + if (scsi_id < SCSI_MAX_DEVS * 2) { + scsi_id = scsi_id % SCSI_MAX_DEVS; + std::string scsi_device_name = "ScsiCdrom" + bus_suffix + "," + + std::to_string(scsi_id); + ScsiCdrom *scsi_device = new ScsiCdrom(scsi_device_name, scsi_id); + gMachineObj->add_device(scsi_device_name, + std::unique_ptr(scsi_device)); + this->register_device(scsi_id, scsi_device); + if (!path.empty() && !scsi_device->insert_image(path)) + ABORT_F("Could not insert CD-ROM image, %s", path.c_str()); + } + else { + LOG_F(ERROR, "%s: Too many devices. CD-ROM \"%s\" was not added.", + this->get_name().c_str(), path.c_str()); } } } diff --git a/devices/common/scsi/scsicdromcmds.cpp b/devices/common/scsi/scsicdromcmds.cpp index 67946c8f40..2399994fc8 100644 --- a/devices/common/scsi/scsicdromcmds.cpp +++ b/devices/common/scsi/scsicdromcmds.cpp @@ -42,6 +42,11 @@ ScsiCdromCmds::ScsiCdromCmds() { } void ScsiCdromCmds::process_command() { + if (!this->ready_for_command()) { + phy_impl->switch_phase(ScsiPhase::STATUS); + return; + } + int next_phase; // use non-disk buffer by default diff --git a/devices/common/scsi/scsicommoncmds.cpp b/devices/common/scsi/scsicommoncmds.cpp index 1795e0cfc2..8a70c7f528 100644 --- a/devices/common/scsi/scsicommoncmds.cpp +++ b/devices/common/scsi/scsicommoncmds.cpp @@ -109,16 +109,33 @@ int ScsiCommonCmds::verify_cdb() { } int ScsiCommonCmds::test_unit_ready() { - // Assume that LUN is okay and the status has been set to GOOD + this->ready_for_command(true); + return ScsiPhase::STATUS; +} + +bool ScsiCommonCmds::ready_for_command(bool force_check) { + // TUR forces a check from its handler; dispatch lets it and media-independent + // commands through to ScsiCommonCmds. + if (!force_check) { + switch (this->cdb_ptr[0]) { + case ScsiCommand::INQUIRY: + case ScsiCommand::REQ_SENSE: + case ScsiCommand::TEST_UNIT_READY: + case ScsiCommand::MODE_SENSE_6: + case ScsiCommand::MODE_SENSE_10: + return true; + } + } if (!phy_impl->is_device_ready()) { this->sense_key = ScsiSense::NOT_READY; this->asc = phy_impl->not_ready_reason(); this->ascq = 0; phy_impl->set_status(ScsiStatus::CHECK_CONDITION, this->sense_key); + return false; } - return ScsiPhase::STATUS; + return true; } int ScsiCommonCmds::inquiry() { diff --git a/devices/common/scsi/scsicommoncmds.h b/devices/common/scsi/scsicommoncmds.h index 9c02cff1d8..7e63700a8d 100644 --- a/devices/common/scsi/scsicommoncmds.h +++ b/devices/common/scsi/scsicommoncmds.h @@ -98,6 +98,8 @@ class ScsiCommonCmds { virtual void invalid_command(); virtual void illegal_request(uint8_t asc, uint8_t ascq, bool is_cdb = true); + bool ready_for_command(bool force_check = false); + int get_one_page(uint8_t ctrl, uint8_t page, uint8_t subpage, uint8_t* out_ptr, int avail_len); diff --git a/machines/machinealchemy.cpp b/machines/machinealchemy.cpp index d7c0b6d84d..a62c14ab4d 100644 --- a/machines/machinealchemy.cpp +++ b/machines/machinealchemy.cpp @@ -126,6 +126,8 @@ static const PropMap pm6400_settings = { new IntProperty( 0, std::vector({0, 4, 8, 16, 32}))}, {"hdd_config", new StrProperty("Ide0:0")}, + {"cdr_config", + new StrProperty("ScsiMesh:3")}, {"emmo", new BinProperty(0)}, }; diff --git a/machines/machinecatalyst.cpp b/machines/machinecatalyst.cpp index 67fbc09933..18279e6d8f 100644 --- a/machines/machinecatalyst.cpp +++ b/machines/machinecatalyst.cpp @@ -123,6 +123,8 @@ static const PropMap pm7200_settings = { new IntProperty( 0, std::vector({0, 4, 8, 16, 32, 64, 128}))}, {"rambank4_size", new IntProperty( 0, std::vector({0, 4, 8, 16, 32, 64, 128}))}, + {"cdr_config", + new StrProperty("ScsiCurio:3")}, {"emmo", new BinProperty(0)}, {"cpu", diff --git a/machines/machinegazelle.cpp b/machines/machinegazelle.cpp index 894a9349a2..633893d263 100644 --- a/machines/machinegazelle.cpp +++ b/machines/machinegazelle.cpp @@ -145,6 +145,8 @@ static const PropMap pm6500_settings = { new IntProperty(32, std::vector({ 4, 8, 16, 32, 64}))}, {"rambank2_size", new IntProperty( 0, std::vector({0, 4, 8, 16, 32, 64}))}, + {"cdr_config", + new StrProperty("ScsiMesh:3")}, {"emmo", new BinProperty(0)}, {"hdd_config", diff --git a/machines/machinepdm.cpp b/machines/machinepdm.cpp index 443a7752d0..94278da290 100644 --- a/machines/machinepdm.cpp +++ b/machines/machinepdm.cpp @@ -158,6 +158,8 @@ static const PropMap pm6100_settings = { new StrProperty("HiRes12-14in", PDMBuiltinMonitorIDs)}, {"pds", new StrProperty("")}, + {"cdr_config", + new StrProperty("ScsiCurio:3")}, {"emmo", new BinProperty(0)}, }; diff --git a/machines/machinepippin.cpp b/machines/machinepippin.cpp index 8ced27d21b..cc509569b3 100644 --- a/machines/machinepippin.cpp +++ b/machines/machinepippin.cpp @@ -88,6 +88,8 @@ static const PropMap Pippin_settings = { new IntProperty(0, std::vector({0, 1, 4, 8, 16}))}, {"rambank4_size", new IntProperty(0, std::vector({0, 1, 4, 8, 16}))}, + {"cdr_config", + new StrProperty("ScsiCurio:3")}, {"emmo", new BinProperty(0)}, {"adb_devices", diff --git a/machines/machineproperties.cpp b/machines/machineproperties.cpp index 0eaa89a506..60b50ed548 100644 --- a/machines/machineproperties.cpp +++ b/machines/machineproperties.cpp @@ -23,6 +23,7 @@ along with this program. If not, see . #include #include +#include #include #include #include @@ -151,15 +152,16 @@ void parse_device_path(std::string dev_path, std::string& bus_id, uint32_t& dev_ dev_num = -1; size_t delimiter_pos = dev_path.find(":"); - if (delimiter_pos == std::string::npos) + if (delimiter_pos == std::string::npos || delimiter_pos == 0) ABORT_F("Invalid device path %s", dev_path.c_str()); bus_id = dev_path.substr(0, delimiter_pos); - try { - dev_num = (uint32_t)strtoul(dev_path.substr(delimiter_pos+1).c_str(), 0, 0); - } catch (std::string bad_string) { - ABORT_F("Invalid device number %s in device path %s", bad_string.c_str(), - dev_path.c_str()); - } + std::string number = dev_path.substr(delimiter_pos + 1); + char* end; + errno = 0; + unsigned long value = strtoul(number.c_str(), &end, 0); + if (end == number.c_str() || *end || errno == ERANGE || value > UINT32_MAX) + ABORT_F("Invalid device number %s in device path %s", number.c_str(), dev_path.c_str()); + dev_num = (uint32_t)value; } diff --git a/machines/machinetnt.cpp b/machines/machinetnt.cpp index 349ce232e5..041e44c5b5 100644 --- a/machines/machinetnt.cpp +++ b/machines/machinetnt.cpp @@ -172,6 +172,7 @@ static const PropMap pm7500_settings_ ## cpu = { \ {"rambank10_size", new IntProperty( 0, std::vector({0, 4, 8, 16, 32, 64, 128}))}, \ {"rambank11_size", new IntProperty( 0, std::vector({0, 4, 8, 16, 32, 64, 128}))}, \ {"rambank12_size", new IntProperty( 0, std::vector({0, 4, 8, 16, 32, 64, 128}))}, \ + {"cdr_config", new StrProperty("ScsiCurio:3")}, \ {"emmo", new BinProperty(0)}, \ {"cpu", new StrProperty(# cpu, std::vector({"601", "604", "604e", "750"}))}, \ };