From 5d9e33b786d7ef452317439359f3cbd8cc920513 Mon Sep 17 00:00:00 2001 From: Patrick Mackinlay Date: Fri, 6 Jul 2018 20:31:29 +0700 Subject: nscsi: add support for CD-ROMs with 512-byte blocks (#3727) * nscsi: add support for CD-ROMs with 512-byte blocks Older UNIX workstations used SCSI CD-ROM drives with 512-byte logical blocks instead of the now standard 2048. This change makes the block size configurable, and adds logic to translate logical blocks to/from the underlying 2048 byte sectors as needed. * add support for 512-byte logical blocks * logmacro.h logging (turned on by default to retain current behaviour) * added stub for "prevent/allow medium removal" command * removed some unnecessary state * minor fix for nscsi_hd "inquiry" command * minor changes (nw) * doh (nw) * this too (nw) --- src/devices/machine/nscsi_cd.cpp | 100 +++++++++++++++++++++++---------------- src/devices/machine/nscsi_cd.h | 10 ++-- src/devices/machine/nscsi_hd.cpp | 7 ++- 3 files changed, 71 insertions(+), 46 deletions(-) (limited to 'src/devices') diff --git a/src/devices/machine/nscsi_cd.cpp b/src/devices/machine/nscsi_cd.cpp index 4a9de7c01ac..3496a84081b 100644 --- a/src/devices/machine/nscsi_cd.cpp +++ b/src/devices/machine/nscsi_cd.cpp @@ -4,22 +4,23 @@ #include "machine/nscsi_cd.h" #include "imagedev/chd_cd.h" +#define VERBOSE 1 +#include "logmacro.h" + DEFINE_DEVICE_TYPE(NSCSI_CDROM, nscsi_cdrom_device, "scsi_cdrom", "SCSI CD-ROM") nscsi_cdrom_device::nscsi_cdrom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : - nscsi_full_device(mconfig, NSCSI_CDROM, tag, owner, clock), cdrom(nullptr), bytes_per_sector(0), lba(0), cur_lba(0), blocks(0) + nscsi_full_device(mconfig, NSCSI_CDROM, tag, owner, clock), cdrom(nullptr), bytes_per_block(bytes_per_sector), lba(0), cur_sector(0) { } void nscsi_cdrom_device::device_start() { nscsi_full_device::device_start(); - bytes_per_sector = 2048; - save_item(NAME(block)); + save_item(NAME(sector_buffer)); save_item(NAME(lba)); - save_item(NAME(cur_lba)); - save_item(NAME(blocks)); - save_item(NAME(bytes_per_sector)); + save_item(NAME(cur_sector)); + save_item(NAME(bytes_per_block)); } void nscsi_cdrom_device::device_reset() @@ -27,8 +28,7 @@ void nscsi_cdrom_device::device_reset() nscsi_full_device::device_reset(); cdrom = subdevice("image")->get_cdrom_file(); lba = 0; - blocks = 0; - cur_lba = -1; + cur_sector = -1; } MACHINE_CONFIG_START(nscsi_cdrom_device::device_add_mconfig) @@ -36,20 +36,28 @@ MACHINE_CONFIG_START(nscsi_cdrom_device::device_add_mconfig) MCFG_CDROM_INTERFACE("cdrom") MACHINE_CONFIG_END +void nscsi_cdrom_device::set_block_size(u32 block_size) +{ + assert_always(!started(), "block size should not be set after device start"); + assert_always(bytes_per_sector % block_size == 0, "block size must be a factor of sector size"); + + bytes_per_block = block_size; +}; + uint8_t nscsi_cdrom_device::scsi_get_data(int id, int pos) { if(id != 2) return nscsi_full_device::scsi_get_data(id, pos); - int clba = lba + pos / bytes_per_sector; - if(clba != cur_lba) { - cur_lba = clba; - if(!cdrom_read_data(cdrom, cur_lba, block, CD_TRACK_MODE1)) { - logerror("%s: CD READ ERROR !\n", tag()); - memset(block, 0, sizeof(block)); + int sector = (lba * bytes_per_block + pos) / bytes_per_sector; + if(sector != cur_sector) { + cur_sector = sector; + if(!cdrom_read_data(cdrom, sector, sector_buffer, CD_TRACK_MODE1)) { + LOG("CD READ ERROR sector %d!\n", sector); + std::fill_n(sector_buffer, sizeof(sector_buffer), 0); } } - return block[pos & (bytes_per_sector - 1)]; + return sector_buffer[pos & (bytes_per_sector - 1)]; } void nscsi_cdrom_device::return_no_cd() @@ -60,9 +68,11 @@ void nscsi_cdrom_device::return_no_cd() void nscsi_cdrom_device::scsi_command() { + int blocks; + switch(scsi_cmdbuf[0]) { case SC_TEST_UNIT_READY: - logerror("%s: command TEST UNIT READY\n", tag()); + LOG("command TEST UNIT READY\n"); if(cdrom) scsi_status_complete(SS_GOOD); else @@ -80,17 +90,15 @@ void nscsi_cdrom_device::scsi_command() if(!blocks) blocks = 256; - logerror("%s: command READ start=%08x blocks=%04x\n", - tag(), lba, blocks); + LOG("command READ start=%08x blocks=%04x\n", lba, blocks); - scsi_data_in(2, blocks*bytes_per_sector); + scsi_data_in(2, blocks*bytes_per_block); scsi_status_complete(SS_GOOD); break; case SC_INQUIRY: { int lun = get_lun(scsi_cmdbuf[1] >> 5); - logerror("%s: command INQUIRY lun=%d EVPD=%d page=%d alloc=%02x link=%02x\n", - tag(), + 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]); if(lun) { bad_lun(); @@ -101,16 +109,20 @@ void nscsi_cdrom_device::scsi_command() int size = scsi_cmdbuf[4]; switch(page) { case 0: - memset(scsi_cmdbuf, 0, 148); + std::fill_n(scsi_cmdbuf, 148, 0); + + // vendor and product information must be padded with spaces + std::fill_n(&scsi_cmdbuf[8], 28, 0x20); + scsi_cmdbuf[0] = 0x05; // device is present, device is CD/DVD (MMC-3) scsi_cmdbuf[1] = 0x80; // media is removable scsi_cmdbuf[2] = 0x05; // device complies with SPC-3 standard scsi_cmdbuf[3] = 0x02; // response data format = SPC-3 standard // some Konami games freak out if this isn't "Sony", so we'll lie // this is the actual drive on my Nagano '98 board - strcpy((char *)&scsi_cmdbuf[8], "Sony"); - strcpy((char *)&scsi_cmdbuf[16], "CDU-76S"); - strcpy((char *)&scsi_cmdbuf[32], "1.0"); + strncpy((char *)&scsi_cmdbuf[8], "Sony", 4); + strncpy((char *)&scsi_cmdbuf[16], "CDU-76S", 7); + strncpy((char *)&scsi_cmdbuf[32], "1.0", 3); if(size > 148) size = 148; scsi_data_in(SBUF_MAIN, size); @@ -121,7 +133,7 @@ void nscsi_cdrom_device::scsi_command() } case SC_START_STOP_UNIT: - logerror("%s: command START STOP UNIT\n", tag()); + LOG("command START STOP UNIT\n"); scsi_status_complete(SS_GOOD); break; @@ -131,10 +143,10 @@ void nscsi_cdrom_device::scsi_command() break; } - logerror("%s: command READ CAPACITY\n", tag()); + LOG("command READ CAPACITY\n"); - uint32_t temp = cdrom_get_track_start(cdrom, 0xaa); - temp--; // return the last used block on the disc + // get the last used block on the disc + const uint32_t temp = cdrom_get_track_start(cdrom, 0xaa) * (bytes_per_sector / bytes_per_block) - 1; scsi_cmdbuf[0] = (temp>>24) & 0xff; scsi_cmdbuf[1] = (temp>>16) & 0xff; @@ -142,8 +154,8 @@ void nscsi_cdrom_device::scsi_command() scsi_cmdbuf[3] = (temp & 0xff); scsi_cmdbuf[4] = 0; scsi_cmdbuf[5] = 0; - scsi_cmdbuf[6] = (bytes_per_sector>>8)&0xff; - scsi_cmdbuf[7] = (bytes_per_sector & 0xff); + scsi_cmdbuf[6] = (bytes_per_block>>8)&0xff; + scsi_cmdbuf[7] = (bytes_per_block & 0xff); scsi_data_in(SBUF_MAIN, 8); scsi_status_complete(SS_GOOD); @@ -159,17 +171,15 @@ void nscsi_cdrom_device::scsi_command() lba = (scsi_cmdbuf[2]<<24) | (scsi_cmdbuf[3]<<16) | (scsi_cmdbuf[4]<<8) | scsi_cmdbuf[5]; blocks = (scsi_cmdbuf[7] << 8) | scsi_cmdbuf[8]; - logerror("%s: command READ EXTENDED start=%08x blocks=%04x\n", - tag(), lba, blocks); + LOG("command READ EXTENDED start=%08x blocks=%04x\n", lba, blocks); - scsi_data_in(2, blocks*bytes_per_sector); + scsi_data_in(2, blocks*bytes_per_block); scsi_status_complete(SS_GOOD); break; case SC_MODE_SENSE_6: { int lun = get_lun(scsi_cmdbuf[1] >> 5); - logerror("%s: command MODE SENSE 6 lun=%d page=%02x alloc=%02x link=%02x\n", - tag(), + LOG("command MODE SENSE 6 lun=%d page=%02x alloc=%02x link=%02x\n", lun, scsi_cmdbuf[2] & 0x3f, scsi_cmdbuf[4], scsi_cmdbuf[5]); if(lun) { bad_lun(); @@ -182,8 +192,8 @@ void nscsi_cdrom_device::scsi_command() scsi_cmdbuf[pos++] = 0x00; // medium type scsi_cmdbuf[pos++] = 0x80; // WP, cache - uint32_t temp = cdrom_get_track_start(cdrom, 0xaa); - temp--; // return the last used block on the disc + // get the last used block on the disc + const uint32_t temp = cdrom_get_track_start(cdrom, 0xaa) * (bytes_per_sector / bytes_per_block) - 1; scsi_cmdbuf[pos++] = 0x08; // Block descriptor length scsi_cmdbuf[pos++] = (temp>>24) & 0xff; @@ -192,8 +202,8 @@ void nscsi_cdrom_device::scsi_command() scsi_cmdbuf[pos++] = (temp & 0xff); scsi_cmdbuf[pos++] = 0; scsi_cmdbuf[pos++] = 0; - scsi_cmdbuf[pos++] = (bytes_per_sector>>8)&0xff; - scsi_cmdbuf[pos++] = (bytes_per_sector & 0xff); + scsi_cmdbuf[pos++] = (bytes_per_block>>8)&0xff; + scsi_cmdbuf[pos++] = (bytes_per_block & 0xff); int pmax = page == 0x3f ? 0x3e : page; int pmin = page == 0x3f ? 0x00 : page; @@ -226,7 +236,7 @@ void nscsi_cdrom_device::scsi_command() break; default: - logerror("%s: mode sense page %02x unhandled\n", tag(), page); + LOG("mode sense page %02x unhandled\n", page); break; } } @@ -239,8 +249,14 @@ void nscsi_cdrom_device::scsi_command() break; } + case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: + // TODO: support eject prevention + LOG("command PREVENT ALLOW MEDIUM REMOVAL\n"); + scsi_status_complete(SS_GOOD); + break; + default: - fprintf(stderr, "scsi %02x\n", scsi_cmdbuf[0]); + logerror("unhandled command %02x\n", scsi_cmdbuf[0]); nscsi_full_device::scsi_command(); break; diff --git a/src/devices/machine/nscsi_cd.h b/src/devices/machine/nscsi_cd.h index a425a473771..95afedfdf66 100644 --- a/src/devices/machine/nscsi_cd.h +++ b/src/devices/machine/nscsi_cd.h @@ -13,6 +13,8 @@ class nscsi_cdrom_device : public nscsi_full_device public: nscsi_cdrom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + void set_block_size(u32 block_size); + protected: virtual void device_start() override; virtual void device_reset() override; @@ -22,10 +24,12 @@ protected: virtual uint8_t scsi_get_data(int id, int pos) override; private: - uint8_t block[2048]; + static constexpr uint32_t bytes_per_sector = 2048; + + uint8_t sector_buffer[bytes_per_sector]; cdrom_file *cdrom; - int bytes_per_sector; - int lba, cur_lba, blocks; + uint32_t bytes_per_block; + int lba, cur_sector; void return_no_cd(); }; diff --git a/src/devices/machine/nscsi_hd.cpp b/src/devices/machine/nscsi_hd.cpp index 7f480169715..da417cae25e 100644 --- a/src/devices/machine/nscsi_hd.cpp +++ b/src/devices/machine/nscsi_hd.cpp @@ -136,7 +136,11 @@ void nscsi_harddisk_device::scsi_command() int size = scsi_cmdbuf[4]; switch(page) { case 0: - memset(scsi_cmdbuf, 0, 148); + std::fill_n(scsi_cmdbuf, 148, 0); + + // vendor and product information must be padded with spaces + std::fill_n(&scsi_cmdbuf[8], 28, 0x20); + // From Seagate SCSI Commands Reference Manual (http://www.seagate.com/staticfiles/support/disc/manuals/scsi/100293068a.pdf), page 73: // If the SCSI target device is not capable of supporting a peripheral device connected to this logical unit, the // device server shall set these fields to 7Fh (i.e., PERIPHERAL QUALIFIER field set to 011b and PERIPHERAL DEVICE @@ -148,6 +152,7 @@ void nscsi_harddisk_device::scsi_command() scsi_cmdbuf[1] = 0x00; // media is not removable scsi_cmdbuf[2] = 0x05; // device complies with SPC-3 standard scsi_cmdbuf[3] = 0x01; // response data format = CCS + scsi_cmdbuf[4] = 52; // additional length if(m_inquiry_data.empty()) { LOG("IDNT tag not found in chd metadata, using default inquiry data\n"); -- cgit v1.2.3