summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/emu/emu.mak3
-rw-r--r--src/emu/machine/nscsi_bus.c656
-rw-r--r--src/emu/machine/nscsi_bus.h353
-rw-r--r--src/emu/machine/nscsi_cd.c146
-rw-r--r--src/emu/machine/nscsi_cd.h34
-rw-r--r--src/emu/machine/nscsi_hd.c155
-rw-r--r--src/emu/machine/nscsi_hd.h34
7 files changed, 1381 insertions, 0 deletions
diff --git a/src/emu/emu.mak b/src/emu/emu.mak
index 3cfe6a21167..83fa4397683 100644
--- a/src/emu/emu.mak
+++ b/src/emu/emu.mak
@@ -220,6 +220,9 @@ EMUMACHINEOBJS = \
$(EMUMACHINE)/msm6242.o \
$(EMUMACHINE)/ncr539x.o \
$(EMUMACHINE)/nmc9306.o \
+ $(EMUMACHINE)/nscsi_bus.o \
+ $(EMUMACHINE)/nscsi_cd.o \
+ $(EMUMACHINE)/nscsi_hd.o \
$(EMUMACHINE)/nvram.o \
$(EMUMACHINE)/pc16552d.o \
$(EMUMACHINE)/pci.o \
diff --git a/src/emu/machine/nscsi_bus.c b/src/emu/machine/nscsi_bus.c
new file mode 100644
index 00000000000..dc11575af82
--- /dev/null
+++ b/src/emu/machine/nscsi_bus.c
@@ -0,0 +1,656 @@
+#include "nscsi_bus.h"
+
+const device_type NSCSI_BUS = &device_creator<nscsi_bus_device>;
+const device_type NSCSI_CONNECTOR = &device_creator<nscsi_connector>;
+
+nscsi_bus_device::nscsi_bus_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, NSCSI_BUS, "SCSI Bus", tag, owner, clock)
+{
+ devcnt = 0;
+ memset(dev, 0, sizeof(dev));
+}
+
+void nscsi_bus_device::device_start()
+{
+ data = 0;
+ ctrl = 0;
+}
+
+void nscsi_bus_device::device_reset()
+{
+}
+
+void nscsi_bus_device::regen_data()
+{
+ data = 0;
+ for(int i=0; i<devcnt; i++)
+ data |= dev[i].data;
+}
+
+void nscsi_bus_device::regen_ctrl(int refid)
+{
+ static const char *phase[8] = {
+ "dout", "din ", "cmd ", "stat", "4 ", "5 ", "mout", "min "
+ };
+
+ UINT32 octrl = ctrl;
+ ctrl = 0;
+ for(int i=0; i<devcnt; i++)
+ ctrl |= dev[i].ctrl;
+
+ if(0) {
+ logerror("%s: ctrl %c%c%c%c%c%c%c%c%c %s %04x -",
+ tag(),
+ ctrl & nscsi_device::S_RST ? 'R' : '.',
+ ctrl & nscsi_device::S_ATN ? 'A' : '.',
+ ctrl & nscsi_device::S_ACK ? 'K' : '.',
+ ctrl & nscsi_device::S_REQ ? 'Q' : '.',
+ ctrl & nscsi_device::S_SEL ? 'S' : '.',
+ ctrl & nscsi_device::S_BSY ? 'B' : '.',
+ ctrl & nscsi_device::S_MSG ? 'M' : '.',
+ ctrl & nscsi_device::S_CTL ? 'C' : '.',
+ ctrl & nscsi_device::S_INP ? 'I' : '.',
+ phase[ctrl & 7],
+ data);
+ for(int i=0; i<devcnt; i++)
+ if(dev[i].ctrl) {
+ logerror(" %d=", i);
+ logerror("%s%s%s%s%s%s%s%s%s",
+ dev[i].ctrl & nscsi_device::S_RST ? "R" : "",
+ dev[i].ctrl & nscsi_device::S_ATN ? "A" : "",
+ dev[i].ctrl & nscsi_device::S_ACK ? "K" : "",
+ dev[i].ctrl & nscsi_device::S_REQ ? "Q" : "",
+ dev[i].ctrl & nscsi_device::S_MSG ? "M" : "",
+ dev[i].ctrl & nscsi_device::S_INP ? "I" : "",
+ dev[i].ctrl & nscsi_device::S_CTL ? "C" : "",
+ dev[i].ctrl & nscsi_device::S_SEL ? "S" : "",
+ dev[i].ctrl & nscsi_device::S_BSY ? "B" : "");
+ }
+ logerror("\n");
+ }
+
+ octrl = octrl ^ ctrl;
+ if(octrl)
+ for(int i=0; i<devcnt; i++)
+ if(i != refid && (dev[i].wait_ctrl & octrl))
+ dev[i].dev->scsi_ctrl_changed();
+}
+
+UINT32 nscsi_bus_device::data_r() const
+{
+ return data;
+}
+
+UINT32 nscsi_bus_device::ctrl_r() const
+{
+ return ctrl;
+}
+
+void nscsi_bus_device::ctrl_w(int refid, UINT32 lines, UINT32 mask)
+{
+ UINT32 c = dev[refid].ctrl;
+ dev[refid].ctrl = (c & ~mask) | (lines & mask);
+ regen_ctrl(refid);
+}
+
+void nscsi_bus_device::data_w(int refid, UINT32 lines)
+{
+ dev[refid].data = lines;
+ regen_data();
+}
+
+void nscsi_bus_device::ctrl_wait(int refid, UINT32 lines, UINT32 mask)
+{
+ UINT32 w = dev[refid].wait_ctrl;
+ dev[refid].wait_ctrl = (w & ~mask) | (lines & mask);
+}
+
+void nscsi_bus_device::device_config_complete()
+{
+ char id[3];
+ for(int i=0; i<16; i++) {
+ sprintf(id, "%d", i);
+ nscsi_connector *conn = downcast<nscsi_connector *>(subdevice(id));
+ if(conn) {
+ nscsi_device *sdev = conn->get_device();
+ if(sdev) {
+ int rid = devcnt++;
+ dev[rid].dev = sdev;
+ sdev->connect_to_bus(this, rid, i);
+ }
+ }
+ }
+}
+
+
+nscsi_connector::nscsi_connector(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, NSCSI_CONNECTOR, "NSCSI device connector abstraction", tag, owner, clock),
+ device_slot_interface(mconfig, *this)
+{
+ fixed_subtag = 0;
+}
+
+nscsi_connector::~nscsi_connector()
+{
+}
+
+void nscsi_connector::device_start()
+{
+}
+
+nscsi_device *nscsi_connector::get_device()
+{
+ return dynamic_cast<nscsi_device *>(fixed_subtag ? subdevice(fixed_subtag) : get_card_device());
+}
+
+void nscsi_connector::set_fixed_device(const char *subtag)
+{
+ fixed_subtag = subtag;
+}
+
+
+nscsi_device::nscsi_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, type, name, tag, owner, clock),
+ device_slot_card_interface(mconfig, *this)
+{
+ scsi_id = scsi_refid = -1;
+ scsi_bus = 0;
+}
+
+void nscsi_device::connect_to_bus(nscsi_bus_device *bus, int refid, int default_scsi_id)
+{
+ scsi_bus = bus;
+ scsi_refid = refid;
+ scsi_id = default_scsi_id;
+}
+
+void nscsi_device::scsi_ctrl_changed()
+{
+}
+
+nscsi_full_device::nscsi_full_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) :
+ nscsi_device(mconfig, type, name, tag, owner, clock)
+{
+}
+
+void nscsi_full_device::device_start()
+{
+ scsi_timer = timer_alloc(SCSI_TIMER);
+}
+
+void nscsi_full_device::device_reset()
+{
+ scsi_state = scsi_substate = IDLE;
+ buf_control_rpos = buf_control_wpos = 0;
+ scsi_identify = 0;
+ scsi_bus->data_w(scsi_refid, 0);
+ scsi_bus->ctrl_w(scsi_refid, 0, S_ALL);
+ scsi_bus->ctrl_wait(scsi_refid, S_SEL|S_BSY|S_RST, S_ALL);
+}
+
+void nscsi_full_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ if(id != SCSI_TIMER)
+ return;
+
+ step(true);
+
+}
+
+void nscsi_full_device::scsi_ctrl_changed()
+{
+ step(false);
+}
+
+void nscsi_full_device::step(bool timeout)
+{
+ UINT32 ctrl = scsi_bus->ctrl_r();
+ UINT32 data = scsi_bus->data_r();
+ if(ctrl & S_RST) {
+ scsi_bus->data_w(scsi_refid, 0);
+ scsi_bus->ctrl_w(scsi_refid, 0, S_ALL);
+ scsi_state = IDLE;
+ logerror("%s: scsi bus reset\n", tag());
+ return;
+ }
+
+ if(0)
+ logerror("%s: state=%d.%d %s\n",
+ tag(), scsi_state & STATE_MASK, (scsi_state & SUB_MASK) >> SUB_SHIFT,
+ timeout ? "timeout" : "change");
+
+ switch(scsi_state & SUB_MASK ? scsi_state & SUB_MASK : scsi_state & STATE_MASK) {
+ case IDLE:
+ if(((ctrl & (S_SEL|S_BSY)) == S_SEL) && (scsi_id != -1) && ((data & (1 << scsi_id)) != 0)) {
+ for(scsi_initiator_id = 0; scsi_initiator_id != 16 && (scsi_initiator_id == scsi_id || (data & (1 << scsi_initiator_id))); scsi_initiator_id++);
+ if(scsi_initiator_id == 16)
+ scsi_initiator_id = -1;
+ scsi_state = TARGET_SELECT_WAIT_BUS_SETTLE;
+ scsi_timer->adjust(scsi_bus_settle_delay());
+ }
+ break;
+
+ case TARGET_SELECT_WAIT_BUS_SETTLE:
+ if((ctrl & (S_SEL|S_BSY)) == S_SEL) {
+ scsi_state = TARGET_SELECT_WAIT_SEL_0;
+ scsi_bus->ctrl_w(scsi_refid, S_BSY, S_BSY);
+ } else
+ scsi_state = IDLE;
+ break;
+
+ case TARGET_SELECT_WAIT_SEL_0:
+ if(ctrl & S_SEL)
+ break;
+ buf_control_push()->action = BC_MSG_OR_COMMAND;
+ scsi_state = TARGET_NEXT_CONTROL;
+ step(false);
+ break;
+
+ case RECV_BYTE_T_WAIT_ACK_1 << SUB_SHIFT:
+ if(ctrl & S_ACK) {
+ received(scsi_bus->data_r());
+ scsi_state = (scsi_state & STATE_MASK) | (RECV_BYTE_T_WAIT_ACK_0 << SUB_SHIFT);
+ scsi_bus->ctrl_w(scsi_refid, 0, S_REQ);
+ }
+ break;
+
+ case RECV_BYTE_T_WAIT_ACK_0 << SUB_SHIFT:
+ if(!(ctrl & S_ACK)) {
+ scsi_state &= STATE_MASK;
+ scsi_bus->ctrl_wait(scsi_refid, 0, S_ACK);
+ step(false);
+ }
+ break;
+
+ case SEND_BYTE_T_WAIT_ACK_1 << SUB_SHIFT:
+ if(ctrl & S_ACK) {
+ scsi_state = (scsi_state & STATE_MASK) | (SEND_BYTE_T_WAIT_ACK_0 << SUB_SHIFT);
+ scsi_bus->data_w(scsi_refid, 0);
+ scsi_bus->ctrl_w(scsi_refid, 0, S_REQ);
+ }
+ break;
+
+ case SEND_BYTE_T_WAIT_ACK_0 << SUB_SHIFT:
+ if(!(ctrl & S_ACK)) {
+ scsi_state &= STATE_MASK;
+ scsi_bus->ctrl_wait(scsi_refid, 0, S_ACK);
+ step(false);
+ }
+ break;
+
+ case TARGET_NEXT_CONTROL: {
+ control *ctl = buf_control_pop();
+ switch(ctl->action) {
+ case BC_MSG_OR_COMMAND:
+ if(ctrl & S_ATN) {
+ scsi_state = TARGET_WAIT_MSG_BYTE;
+ scsi_bus->ctrl_w(scsi_refid, S_PHASE_MSG_OUT, S_PHASE_MASK);
+ } else {
+ scsi_state = TARGET_WAIT_CMD_BYTE;
+ scsi_bus->ctrl_w(scsi_refid, S_PHASE_COMMAND, S_PHASE_MASK);
+ }
+ scsi_cmdsize = 0;
+ target_recv_byte();
+ break;
+
+ case BC_STATUS:
+ scsi_bus->ctrl_w(scsi_refid, S_PHASE_STATUS, S_PHASE_MASK);
+ target_send_byte(ctl->param1);
+ break;
+
+ case BC_DATA_IN:
+ scsi_bus->ctrl_w(scsi_refid, S_PHASE_DATA_IN, S_PHASE_MASK);
+ data_buffer_id = ctl->param1;
+ data_buffer_size = ctl->param2;
+ data_buffer_pos = 0;
+ scsi_state = TARGET_WAIT_DATA_IN_BYTE;
+ target_send_buffer_byte();
+ break;
+
+ case BC_MESSAGE_1:
+ scsi_bus->ctrl_w(scsi_refid, S_PHASE_MSG_IN, S_PHASE_MASK);
+ target_send_byte(ctl->param1);
+ break;
+
+ case BC_BUS_FREE:
+ scsi_bus->data_w(scsi_refid, 0);
+ scsi_bus->ctrl_wait(scsi_refid, S_BSY|S_SEL|S_RST, S_ALL);
+ scsi_bus->ctrl_w(scsi_refid, 0, S_ALL);
+ scsi_state = IDLE;
+ break;
+ };
+ break;
+ }
+
+ case TARGET_WAIT_DATA_IN_BYTE:
+ if(data_buffer_pos == data_buffer_size-1)
+ scsi_state = TARGET_NEXT_CONTROL;
+ target_send_buffer_byte();
+ break;
+
+ case TARGET_WAIT_MSG_BYTE:
+ if(ctrl & S_SEL)
+ return;
+ if(!(ctrl & S_ATN)) {
+ scsi_message();
+ scsi_cmdsize = 0;
+ scsi_state = TARGET_WAIT_CMD_BYTE;
+ scsi_bus->ctrl_w(scsi_refid, S_PHASE_COMMAND, S_PHASE_MASK);
+ }
+ target_recv_byte();
+ break;
+
+ case TARGET_WAIT_CMD_BYTE:
+ if(ctrl & S_SEL)
+ return;
+ if(ctrl & S_ATN) {
+ logerror("%s: Parity error? Say what?\n", tag());
+ scsi_state = IDLE;
+ break;
+ }
+
+ if(command_done()) {
+ scsi_bus->ctrl_wait(scsi_refid, 0, S_ACK);
+ scsi_command();
+ scsi_state = TARGET_NEXT_CONTROL;
+ step(false);
+ } else
+ target_recv_byte();
+ break;
+
+ default:
+ logerror("%s: step() unexpected state %d.%d\n",
+ tag(),
+ scsi_state & STATE_MASK, (scsi_state & SUB_MASK) >> SUB_SHIFT);
+ exit(0);
+ }
+}
+
+void nscsi_full_device::target_recv_byte()
+{
+ scsi_bus->ctrl_wait(scsi_refid, S_ACK, S_ACK);
+ scsi_state = (scsi_state & STATE_MASK) | (RECV_BYTE_T_WAIT_ACK_1 << SUB_SHIFT);
+ scsi_bus->ctrl_w(scsi_refid, S_REQ, S_REQ);
+ step(false);
+}
+
+void nscsi_full_device::target_send_byte(UINT8 val)
+{
+ scsi_bus->ctrl_wait(scsi_refid, S_ACK, S_ACK);
+ scsi_state = (scsi_state & STATE_MASK) | (SEND_BYTE_T_WAIT_ACK_1 << SUB_SHIFT);
+ scsi_bus->data_w(scsi_refid, val);
+ scsi_bus->ctrl_w(scsi_refid, S_REQ, S_REQ);
+ step(false);
+}
+
+void nscsi_full_device::received(UINT8 val)
+{
+ scsi_cmdbuf[scsi_cmdsize++] = val;
+}
+
+UINT8 nscsi_full_device::scsi_get_data(int id, int pos)
+{
+ switch(id) {
+ case SBUF_MAIN:
+ return scsi_cmdbuf[pos];
+ case SBUF_SENSE:
+ return scsi_sense_buffer[pos];
+ default:
+ abort();
+ }
+}
+
+void nscsi_full_device::target_send_buffer_byte()
+{
+ target_send_byte(scsi_get_data(data_buffer_id, data_buffer_pos++));
+}
+
+bool nscsi_full_device::command_done()
+{
+ if(!scsi_cmdsize)
+ return false;
+ UINT8 h = scsi_cmdbuf[0];
+ switch(h >> 5) {
+ case 0: return scsi_cmdsize == 6;
+ case 1: return scsi_cmdsize == 10;
+ case 2: return scsi_cmdsize == 10;
+ case 3: return true;
+ case 4: return true;
+ case 5: return scsi_cmdsize == 12;
+ case 6: return true;
+ case 7: return true;
+ }
+ return true;
+}
+
+nscsi_full_device::control *nscsi_full_device::buf_control_push()
+{
+ if(buf_control_wpos == int(sizeof(buf_control)/sizeof(buf_control[0])))
+ throw emu_fatalerror("%s: buf_control overflow\n", tag());
+
+ control *c = buf_control + buf_control_wpos;
+ buf_control_wpos++;
+ return c;
+}
+
+nscsi_full_device::control *nscsi_full_device::buf_control_pop()
+{
+ if(buf_control_rpos == buf_control_wpos)
+ throw emu_fatalerror("%s: buf_control underflow\n", tag());
+
+ control *c = buf_control + buf_control_rpos;
+ buf_control_rpos++;
+ if(buf_control_rpos == buf_control_wpos)
+ buf_control_rpos = buf_control_wpos = 0;
+ return c;
+}
+
+void nscsi_full_device::scsi_status_complete(UINT8 st)
+{
+ control *c;
+ c = buf_control_push();
+ c->action = BC_STATUS;
+ c->param1 = st;
+ c = buf_control_push();
+ c->action = BC_MESSAGE_1;
+ c->param1 = SM_COMMAND_COMPLETE;
+ c = buf_control_push();
+ c->action = BC_BUS_FREE;
+}
+
+void nscsi_full_device::scsi_data_in(int buf, int size)
+{
+ control *c;
+ c = buf_control_push();
+ c->action = BC_DATA_IN;
+ c->param1 = buf;
+ c->param2 = size;
+}
+
+void nscsi_full_device::scsi_data_out(int buf, int size)
+{
+ control *c;
+ c = buf_control_push();
+ c->action = BC_DATA_OUT;
+ c->param1 = buf;
+ c->param2 = size;
+}
+
+void nscsi_full_device::sense(bool deferred, UINT8 key)
+{
+ memset(scsi_sense_buffer, 0, sizeof(scsi_sense_buffer));
+ scsi_sense_buffer[0] = deferred ? 0x71 : 0x70;
+ scsi_sense_buffer[2] = key;
+}
+
+void nscsi_full_device::scsi_unknown_command()
+{
+ logerror("%s: Unknown command", tag());
+ for(int i=0; i != scsi_cmdsize; i++)
+ logerror(" %02x", scsi_cmdbuf[i]);
+ logerror("\n");
+
+ scsi_status_complete(SS_CHECK_CONDITION);
+ sense(false, 5);
+}
+
+void nscsi_full_device::scsi_command()
+{
+ switch(scsi_cmdbuf[0]) {
+ case SC_REQUEST_SENSE:
+ logerror("%s: command REQUEST SENSE\n", tag());
+ scsi_data_in(SBUF_SENSE, 8);
+ scsi_status_complete(SS_GOOD);
+ break;
+ default:
+ scsi_unknown_command();
+ break;
+ }
+}
+
+void nscsi_full_device::scsi_message()
+{
+ if(scsi_cmdbuf[0] & 0x80) {
+ scsi_identify = scsi_cmdbuf[0];
+ return;
+ }
+
+ logerror("%s: Unknown message", tag());
+ for(int i=0; i != scsi_cmdsize; i++)
+ logerror(" %02x", scsi_cmdbuf[i]);
+ logerror("\n");
+}
+
+int nscsi_full_device::get_lun(int def)
+{
+ if(scsi_identify & 0x80)
+ return scsi_identify & 0x7f;
+ return def;
+}
+
+void nscsi_full_device::bad_lun()
+{
+ scsi_status_complete(SS_CHECK_CONDITION);
+ sense(false, 2);
+}
+
+// Arbitration delay (2.4us)
+attotime nscsi_full_device::scsi_arbitation_delay()
+{
+ return attotime::from_nsec(2400);
+}
+
+// Assertion period (90ns)
+attotime nscsi_full_device::scsi_assertion_period()
+{
+ return attotime::from_nsec(90);
+}
+
+// Bus clear delay (800ns)
+attotime nscsi_full_device::scsi_bus_clear_delay()
+{
+ return attotime::from_nsec(800);
+}
+
+// Bus free delay (800ns)
+attotime nscsi_full_device::scsi_bus_free_delay()
+{
+ return attotime::from_nsec(800);
+}
+
+// Bus set delay (1.8us)
+attotime nscsi_full_device::scsi_bus_set_delay()
+{
+ return attotime::from_nsec(1800);
+}
+
+// Bus settle delay (400ns)
+attotime nscsi_full_device::scsi_bus_settle_delay()
+{
+ return attotime::from_nsec(400);
+}
+
+// Cable skew delay (10ns)
+attotime nscsi_full_device::scsi_cable_skew_delay()
+{
+ return attotime::from_nsec(10);
+}
+
+// Data release delay (400ns)
+attotime nscsi_full_device::scsi_data_release_delay()
+{
+ return attotime::from_nsec(40);
+}
+
+// Deskew delay (45ns)
+attotime nscsi_full_device::scsi_deskew_delay()
+{
+ return attotime::from_nsec(45);
+}
+
+// Disconnection delay (200us)
+attotime nscsi_full_device::scsi_disconnection_delay()
+{
+ return attotime::from_usec(200);
+}
+
+// Hold time (45ns)
+attotime nscsi_full_device::scsi_hold_time()
+{
+ return attotime::from_nsec(45);
+}
+
+// Negation period (90ns)
+attotime nscsi_full_device::scsi_negation_period()
+{
+ return attotime::from_nsec(90);
+}
+
+// Reset hold time (25us)
+attotime nscsi_full_device::scsi_reset_hold_time()
+{
+ return attotime::from_usec(25);
+}
+
+// Selection abort time (200us)
+attotime nscsi_full_device::scsi_selection_abort_time()
+{
+ return attotime::from_usec(200);
+}
+
+// Selection timeout delay (250ms)
+attotime nscsi_full_device::scsi_selection_timeout_delay()
+{
+ return attotime::from_msec(250);
+}
+
+// Fast assertion period (30ns)
+attotime nscsi_full_device::scsi_fast_assertion_period()
+{
+ return attotime::from_nsec(30);
+}
+
+// Fast cable skew delay (5ns)
+attotime nscsi_full_device::scsi_fast_cable_skew_delay()
+{
+ return attotime::from_nsec(5);
+}
+
+// Fast deskew delay (20ns)
+attotime nscsi_full_device::scsi_fast_deskew_delay()
+{
+ return attotime::from_nsec(20);
+}
+
+// Fast hold time (10ns)
+attotime nscsi_full_device::scsi_fast_hold_time()
+{
+ return attotime::from_nsec(10);
+}
+
+// Fast negation period (30ns)
+attotime nscsi_full_device::scsi_fast_negation_period()
+{
+ return attotime::from_nsec(30);
+}
+
diff --git a/src/emu/machine/nscsi_bus.h b/src/emu/machine/nscsi_bus.h
new file mode 100644
index 00000000000..ea34ad0cbe9
--- /dev/null
+++ b/src/emu/machine/nscsi_bus.h
@@ -0,0 +1,353 @@
+#ifndef __NSCSI_BUS_H__
+#define __NSCSI_BUS_H__
+
+#include "emu.h"
+
+#define MCFG_NSCSI_BUS_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, NSCSI_BUS, 0)
+
+#define MCFG_NSCSI_DEVICE_ADD(_tag, _subtag, _type, _clock) \
+ MCFG_DEVICE_ADD(_tag, NSCSI_CONNECTOR, 0) \
+ downcast<nscsi_connector *>(device)->set_fixed_device(_subtag); \
+ MCFG_DEVICE_ADD(_tag ":" _subtag, _type, _clock)
+
+#define MCFG_NSCSI_FULL_DEVICE_ADD(_tag, _subtag, _type, _clock) \
+ MCFG_NSCSI_DEVICE_ADD(_tag, _subtag, _type, _clock)
+
+#define MCFG_NSCSI_ADD(_tag, _slot_intf, _def_slot, _def_inp) \
+ MCFG_DEVICE_ADD(_tag, NSCSI_CONNECTOR, 0) \
+ MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, _def_inp)
+
+class nscsi_device;
+
+class nscsi_bus_device : public device_t
+{
+public:
+ nscsi_bus_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ void ctrl_w(int refid, UINT32 lines, UINT32 mask);
+ void data_w(int refid, UINT32 lines);
+ void ctrl_wait(int refid, UINT32 lines, UINT32 mask);
+
+ UINT32 ctrl_r() const;
+ UINT32 data_r() const;
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_config_complete();
+
+private:
+ struct dev_t {
+ nscsi_device *dev;
+ UINT32 ctrl, wait_ctrl;
+ UINT32 data;
+ };
+
+ dev_t dev[16];
+ int devcnt;
+
+ UINT32 data, ctrl;
+
+ void regen_data();
+ void regen_ctrl(int refid);
+};
+
+class nscsi_connector: public device_t,
+ public device_slot_interface
+{
+public:
+ nscsi_connector(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ virtual ~nscsi_connector();
+
+ nscsi_device *get_device();
+ void set_fixed_device(const char *subtag);
+
+protected:
+ virtual void device_start();
+
+private:
+ const char *fixed_subtag;
+};
+
+class nscsi_device : public device_t,
+ public device_slot_card_interface
+{
+public:
+ // Here because the biggest users are the devices, not the bus
+ enum {
+ S_INP = 0x0001,
+ S_CTL = 0x0002,
+ S_MSG = 0x0004,
+ S_BSY = 0x0008,
+ S_SEL = 0x0010,
+ S_REQ = 0x0020,
+ S_ACK = 0x0040,
+ S_ATN = 0x0080,
+ S_RST = 0x0100,
+ S_ALL = 0x01ff,
+
+ S_PHASE_DATA_OUT = 0,
+ S_PHASE_DATA_IN = S_INP,
+ S_PHASE_COMMAND = S_CTL,
+ S_PHASE_STATUS = S_CTL|S_INP,
+ S_PHASE_MSG_OUT = S_MSG|S_CTL,
+ S_PHASE_MSG_IN = S_MSG|S_CTL|S_INP,
+ S_PHASE_MASK = S_MSG|S_CTL|S_INP,
+ };
+
+ nscsi_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock);
+
+ void connect_to_bus(nscsi_bus_device *bus, int refid, int default_scsi_id);
+ virtual void scsi_ctrl_changed();
+protected:
+ int scsi_id;
+ int scsi_refid;
+ nscsi_bus_device *scsi_bus;
+};
+
+class nscsi_full_device : public nscsi_device
+{
+public:
+ nscsi_full_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock);
+
+ virtual void scsi_ctrl_changed();
+protected:
+ enum { SCSI_TIMER = 100 };
+
+ // SCSI status returns
+ enum {
+ SS_GOOD = 0x00,
+ SS_CHECK_CONDITION = 0x02,
+ SS_CONDITION_MET = 0x04,
+ SS_BUSY = 0x08,
+ SS_INT_GOOD = 0x10,
+ SS_INT_CONDITION_MET = 0x14,
+ SS_RESV_CONFLICT = 0x18,
+ SS_TERMINATED = 0x22,
+ SS_QUEUE_FULL = 0x28,
+ };
+
+ // SCSI commands
+ enum {
+ SC_TEST_UNIT_READY = 0x00,
+ SC_REZERO = 0x01,
+ SC_REQUEST_SENSE = 0x03,
+ SC_FORMAT_UNIT = 0x04,
+ SC_REASSIGN_BLOCKS = 0x07,
+ SC_READ = 0x08,
+ SC_WRITE = 0x0a,
+ SC_SEEK = 0x0b,
+ SC_INQUIRY = 0x12,
+ SC_MODE_SELECT_6 = 0x15,
+ SC_RESERVE_6 = 0x16,
+ SC_RELEASE_6 = 0x17,
+ SC_MODE_SENSE_6 = 0x1a,
+ SC_START_STOP_UNIT = 0x1b,
+ SC_RECIEVE_DIAG_RES = 0x1c,
+ SC_SEND_DIAGNOSTICS = 0x1d,
+ SC_READ_CAPACITY = 0x25,
+ SC_READ_EXTENDED = 0x28,
+ SC_WRITE_EXTENDED = 0x2a,
+ SC_SEEK_EXTENDED = 0x2b,
+ SC_WRITE_VERIFY = 0x2e,
+ SC_VERIFY = 0x2f,
+ SC_SYNC_CACHE = 0x35,
+ SC_READ_DEFECT_DATA = 0x37,
+ SC_READ_DATA_BUFFER = 0x3c,
+ SC_READ_LONG = 0x3e,
+ SC_WRITE_LONG = 0x3f,
+ SC_CHANGE_DEFINITION = 0x40,
+ SC_LOG_SELECT = 0x4c,
+ SC_LOG_SENSE = 0x4d,
+ SC_MODE_SELECT_10 = 0x55,
+ SC_RESERVE_10 = 0x56,
+ SC_RELEASE_10 = 0x57,
+ SC_MODE_SENSE_10 = 0x5a,
+ };
+
+ // SCSI Messages
+ enum {
+ SM_COMMAND_COMPLETE = 0x00,
+ SM_EXTENDED_MSG = 0x01,
+ SM_SAVE_DATA_PTR = 0x02,
+ SM_RESTORE_PTR = 0x03,
+ SM_DISCONNECT = 0x04,
+ SM_INITIATOR_ERROR = 0x05,
+ SM_ABORT = 0x06,
+ SM_MSG_REJECT = 0x07,
+ SM_NOP = 0x08,
+ SM_MSG_PARITY = 0x09,
+ SM_LCMD_COMPLETE = 0x0a,
+ SM_LCMD_COMPLETE_F = 0x0b,
+ SM_BUS_DEVICE_RESET = 0x0c,
+ SM_ABORT_TAG = 0x0d,
+ SM_CLEAR_QUEUE = 0x0e,
+ SM_INIT_RECOVERY = 0x0f,
+ SM_RELEASE_RECOVERY = 0x10,
+ SM_TERMINATE_IO = 0x11,
+ SM_SIMPLE_QUEUE = 0x20,
+ SM_HEAD_QUEUE = 0x21,
+ SM_ORDERED_QUEUE = 0x22,
+ SM_IGNORE_WIDE_RES = 0x23,
+ };
+
+ enum {
+ SBUF_MAIN,
+ SBUF_SENSE,
+ };
+
+ UINT8 scsi_cmdbuf[4096], scsi_sense_buffer[8];
+ int scsi_cmdsize;
+ UINT8 scsi_identify;
+
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ virtual void scsi_message();
+ virtual void scsi_command();
+
+ void scsi_unknown_command();
+ void scsi_status_complete(UINT8 st);
+ void scsi_data_in(int buf, int size);
+ void scsi_data_out(int buf, int size);
+
+ void sense(bool deferred, UINT8 key);
+ int get_lun(int def = 0);
+ void bad_lun();
+
+ virtual UINT8 scsi_get_data(int buf, int offset);
+
+ // Default delays:
+
+ // Arbitration delay (2.4us)
+ virtual attotime scsi_arbitation_delay();
+
+ // Assertion period (90ns)
+ virtual attotime scsi_assertion_period();
+
+ // Bus clear delay (800ns)
+ virtual attotime scsi_bus_clear_delay();
+
+ // Bus free delay (800ns)
+ virtual attotime scsi_bus_free_delay();
+
+ // Bus set delay (1.8us)
+ virtual attotime scsi_bus_set_delay();
+
+ // Bus settle delay (400ns)
+ virtual attotime scsi_bus_settle_delay();
+
+ // Cable skew delay (10ns)
+ virtual attotime scsi_cable_skew_delay();
+
+ // Data release delay (400ns)
+ virtual attotime scsi_data_release_delay();
+
+ // Deskew delay (45ns)
+ virtual attotime scsi_deskew_delay();
+
+ // Disconnection delay (200us)
+ virtual attotime scsi_disconnection_delay();
+
+ // Hold time (45ns)
+ virtual attotime scsi_hold_time();
+
+ // Negation period (90ns)
+ virtual attotime scsi_negation_period();
+
+ // Reset hold time (25us)
+ virtual attotime scsi_reset_hold_time();
+
+ // Selection abort time (200us)
+ virtual attotime scsi_selection_abort_time();
+
+ // Selection timeout delay (250ms)
+ virtual attotime scsi_selection_timeout_delay();
+
+ // Fast assertion period (30ns)
+ virtual attotime scsi_fast_assertion_period();
+
+ // Fast cable skew delay (5ns)
+ virtual attotime scsi_fast_cable_skew_delay();
+
+ // Fast deskew delay (20ns)
+ virtual attotime scsi_fast_deskew_delay();
+
+ // Fast hold time (10ns)
+ virtual attotime scsi_fast_hold_time();
+
+ // Fast negation period (30ns)
+ virtual attotime scsi_fast_negation_period();
+
+private:
+ enum {
+ IDLE,
+ };
+
+ enum {
+ TARGET_SELECT_WAIT_BUS_SETTLE = 1,
+ TARGET_SELECT_WAIT_SEL_0,
+
+ TARGET_NEXT_CONTROL,
+ TARGET_WAIT_MSG_BYTE,
+ TARGET_WAIT_CMD_BYTE,
+ TARGET_WAIT_DATA_IN_BYTE,
+ };
+
+ enum {
+ RECV_BYTE_T_WAIT_ACK_0 = 1,
+ RECV_BYTE_T_WAIT_ACK_1,
+ SEND_BYTE_T_WAIT_ACK_0,
+ SEND_BYTE_T_WAIT_ACK_1,
+ };
+
+ enum {
+ STATE_MASK = 0x00ff,
+ SUB_SHIFT = 8,
+ SUB_MASK = 0xff00,
+ };
+
+ enum {
+ BC_MSG_OR_COMMAND,
+ BC_STATUS,
+ BC_MESSAGE_1,
+ BC_MESSAGE_2,
+ BC_DATA_IN,
+ BC_DATA_OUT,
+ BC_BUS_FREE,
+ };
+
+ struct control {
+ int action;
+ int param1, param2;
+ };
+
+ emu_timer *scsi_timer;
+
+ int scsi_state, scsi_substate;
+ int scsi_initiator_id;
+ int data_buffer_id, data_buffer_size, data_buffer_pos;
+
+ control buf_control[32];
+ int buf_control_rpos;
+ int buf_control_wpos;
+
+ control *buf_control_push();
+ control *buf_control_pop();
+
+ void step(bool timeout);
+ void target_recv_byte();
+ void target_send_byte(UINT8 val);
+ void received(UINT8 val);
+ void target_send_buffer_byte();
+ bool command_done();
+};
+
+
+extern const device_type NSCSI_BUS;
+extern const device_type NSCSI_CONNECTOR;
+
+#endif
+
diff --git a/src/emu/machine/nscsi_cd.c b/src/emu/machine/nscsi_cd.c
new file mode 100644
index 00000000000..048928555d7
--- /dev/null
+++ b/src/emu/machine/nscsi_cd.c
@@ -0,0 +1,146 @@
+#include "machine/nscsi_cd.h"
+#include "imagedev/chd_cd.h"
+
+const device_type NSCSI_CDROM = &device_creator<nscsi_cdrom_device>;
+
+nscsi_cdrom_device::nscsi_cdrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ nscsi_full_device(mconfig, NSCSI_CDROM, "SCSI CDROM", tag, owner, clock)
+{
+}
+
+void nscsi_cdrom_device::device_start()
+{
+ nscsi_full_device::device_start();
+ bytes_per_sector = 2048;
+}
+
+void nscsi_cdrom_device::device_reset()
+{
+ nscsi_full_device::device_reset();
+ cdrom = subdevice<cdrom_image_device>("image")->get_cdrom_file();
+ lba = 0;
+ blocks = 0;
+ cur_lba = -1;
+}
+
+cdrom_interface nscsi_cdrom_device::cd_intf = { 0, 0 };
+
+static MACHINE_CONFIG_FRAGMENT(scsi_cdrom)
+ MCFG_CDROM_ADD("image", nscsi_cdrom_device::cd_intf)
+MACHINE_CONFIG_END
+
+machine_config_constructor nscsi_cdrom_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME(scsi_cdrom);
+}
+
+UINT8 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));
+ }
+ }
+ return block[pos & (bytes_per_sector - 1)];
+}
+
+void nscsi_cdrom_device::scsi_command()
+{
+ switch(scsi_cmdbuf[0]) {
+ case SC_TEST_UNIT_READY:
+ logerror("%s: command TEST UNIT READY\n", tag());
+ scsi_status_complete(SS_GOOD);
+ break;
+
+ case SC_READ:
+ lba = ((scsi_cmdbuf[1] & 0x1f)<<16) | (scsi_cmdbuf[2]<<8) | scsi_cmdbuf[3];
+ blocks = scsi_cmdbuf[4];
+ if(!blocks)
+ blocks = 256;
+
+ logerror("%s: command READ start=%08x blocks=%04x\n",
+ tag(), lba, blocks);
+
+ scsi_data_in(2, blocks*bytes_per_sector);
+ 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(),
+ lun, scsi_cmdbuf[1] & 1, scsi_cmdbuf[2], scsi_cmdbuf[4], scsi_cmdbuf[5]);
+ if(lun) {
+ bad_lun();
+ return;
+ }
+
+ int page = scsi_cmdbuf[2];
+ int size = scsi_cmdbuf[4];
+ switch(page) {
+ case 0:
+ memset(scsi_cmdbuf, 0, 148);
+ 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");
+ if(size > 148)
+ size = 148;
+ scsi_data_in(SBUF_MAIN, size);
+ break;
+ }
+ scsi_status_complete(SS_GOOD);
+ break;
+ }
+
+ case SC_START_STOP_UNIT:
+ logerror("%s: command START STOP UNIT\n", tag());
+ scsi_status_complete(SS_GOOD);
+ break;
+
+ case SC_READ_CAPACITY: {
+ logerror("%s: command READ CAPACITY\n", tag());
+
+ UINT32 temp = cdrom_get_track_start(cdrom, 0xaa);
+ temp--; // return the last used block on the disc
+
+ scsi_cmdbuf[0] = (temp>>24) & 0xff;
+ scsi_cmdbuf[1] = (temp>>16) & 0xff;
+ scsi_cmdbuf[2] = (temp>>8) & 0xff;
+ 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_data_in(SBUF_MAIN, 8);
+ scsi_status_complete(SS_GOOD);
+ break;
+ }
+
+ case SC_READ_EXTENDED:
+ 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);
+
+ scsi_data_in(2, blocks*bytes_per_sector);
+ scsi_status_complete(SS_GOOD);
+ break;
+
+ default:
+ nscsi_full_device::scsi_command();
+ break;
+ }
+}
diff --git a/src/emu/machine/nscsi_cd.h b/src/emu/machine/nscsi_cd.h
new file mode 100644
index 00000000000..4231a5c8599
--- /dev/null
+++ b/src/emu/machine/nscsi_cd.h
@@ -0,0 +1,34 @@
+#ifndef __NSCSI_CD_H__
+#define __NSCSI_CD_H__
+
+#include "machine/nscsi_bus.h"
+#include "cdrom.h"
+
+#define MCFG_NSCSI_CDROM_ADD(_tag, _subtag) \
+ MCFG_NSCSI_FULL_DEVICE_ADD(_tag, _subtag, NSCSI_CDROM, 0)
+
+class nscsi_cdrom_device : public nscsi_full_device
+{
+public:
+ nscsi_cdrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ virtual machine_config_constructor device_mconfig_additions() const;
+
+ static struct cdrom_interface cd_intf;
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+
+ virtual void scsi_command();
+ virtual UINT8 scsi_get_data(int id, int pos);
+
+private:
+ UINT8 block[2048];
+ cdrom_file *cdrom;
+ int bytes_per_sector;
+ int lba, cur_lba, blocks;
+};
+
+extern const device_type NSCSI_CDROM;
+
+#endif
diff --git a/src/emu/machine/nscsi_hd.c b/src/emu/machine/nscsi_hd.c
new file mode 100644
index 00000000000..61642ed0468
--- /dev/null
+++ b/src/emu/machine/nscsi_hd.c
@@ -0,0 +1,155 @@
+#include "machine/nscsi_hd.h"
+#include "imagedev/harddriv.h"
+
+const device_type NSCSI_HARDDISK = &device_creator<nscsi_harddisk_device>;
+
+nscsi_harddisk_device::nscsi_harddisk_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ nscsi_full_device(mconfig, NSCSI_HARDDISK, "SCSI HARDDISK", tag, owner, clock)
+{
+}
+
+void nscsi_harddisk_device::device_start()
+{
+ nscsi_full_device::device_start();
+}
+
+void nscsi_harddisk_device::device_reset()
+{
+ nscsi_full_device::device_reset();
+ harddisk_image_device *hd = subdevice<harddisk_image_device>("image");
+ harddisk = hd->get_hard_disk_file();
+ if(!harddisk) {
+ scsi_id = -1;
+ bytes_per_sector = 0;
+ } else {
+ const hard_disk_info *hdinfo = hard_disk_get_info(harddisk);
+ bytes_per_sector = hdinfo->sectorbytes;
+ }
+}
+
+harddisk_interface nscsi_harddisk_device::hd_intf = { 0, 0 };
+
+static MACHINE_CONFIG_FRAGMENT(scsi_harddisk)
+ MCFG_HARDDISK_CONFIG_ADD("image", nscsi_harddisk_device::hd_intf)
+MACHINE_CONFIG_END
+
+machine_config_constructor nscsi_harddisk_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME(scsi_harddisk);
+}
+
+UINT8 nscsi_harddisk_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(!hard_disk_read(harddisk, cur_lba, block)) {
+ logerror("%s: HD READ ERROR !\n", tag());
+ memset(block, 0, sizeof(block));
+ }
+ }
+ return block[pos & (bytes_per_sector - 1)];
+}
+
+void nscsi_harddisk_device::scsi_command()
+{
+ switch(scsi_cmdbuf[0]) {
+ case SC_TEST_UNIT_READY:
+ logerror("%s: command TEST UNIT READY\n", tag());
+ scsi_status_complete(SS_GOOD);
+ break;
+
+ case SC_READ:
+ lba = ((scsi_cmdbuf[1] & 0x1f)<<16) | (scsi_cmdbuf[2]<<8) | scsi_cmdbuf[3];
+ blocks = scsi_cmdbuf[4];
+ if(!blocks)
+ blocks = 256;
+
+ logerror("%s: command READ start=%08x blocks=%04x\n",
+ tag(), lba, blocks);
+
+ scsi_data_in(2, blocks*bytes_per_sector);
+ scsi_status_complete(SS_GOOD);
+ break;
+
+ case SC_INQUIRY: {
+ int lun = get_lun(scsi_cmdbuf[1] >> 5);
+ logerror("%s: INQUIRY lun=%d EVPD=%d page=%d alloc=%02x link=%02x\n",
+ tag(),
+ lun, scsi_cmdbuf[1] & 1, scsi_cmdbuf[2], scsi_cmdbuf[4], scsi_cmdbuf[5]);
+ if(lun) {
+ bad_lun();
+ return;
+ }
+
+ int page = scsi_cmdbuf[2];
+ int size = scsi_cmdbuf[4];
+ switch(page) {
+ case 0:
+ memset(scsi_cmdbuf, 0, 148);
+ scsi_cmdbuf[0] = 0x00; // device is direct-access (e.g. hard disk)
+ scsi_cmdbuf[1] = 0x00; // media is not removable
+ scsi_cmdbuf[2] = 0x05; // device complies with SPC-3 standard
+ scsi_cmdbuf[3] = 0x02; // response data format = SPC-3 standard
+ // Apple HD SC setup utility needs to see this
+ strcpy((char *)&scsi_cmdbuf[8], " SEAGATE");
+ strcpy((char *)&scsi_cmdbuf[16], " ST225N");
+ strcpy((char *)&scsi_cmdbuf[32], "1.0");
+ if(size > 148)
+ size = 148;
+ scsi_data_in(0, size);
+ break;
+ }
+ scsi_status_complete(SS_GOOD);
+ break;
+ }
+
+ case SC_START_STOP_UNIT:
+ logerror("%s: command START STOP UNIT\n", tag());
+ scsi_status_complete(SS_GOOD);
+ break;
+
+ case SC_READ_CAPACITY: {
+ logerror("%s: command READ CAPACITY\n", tag());
+
+ hard_disk_info *info;
+ UINT32 temp;
+
+ info = hard_disk_get_info(harddisk);
+
+ // get # of sectors
+ temp = info->cylinders * info->heads * info->sectors;
+ temp--;
+
+ scsi_cmdbuf[0] = (temp>>24) & 0xff;
+ scsi_cmdbuf[1] = (temp>>16) & 0xff;
+ scsi_cmdbuf[2] = (temp>>8) & 0xff;
+ scsi_cmdbuf[3] = (temp & 0xff);
+ scsi_cmdbuf[4] = (info->sectorbytes>>24)&0xff;
+ scsi_cmdbuf[5] = (info->sectorbytes>>16)&0xff;
+ scsi_cmdbuf[6] = (info->sectorbytes>>8)&0xff;
+ scsi_cmdbuf[7] = (info->sectorbytes & 0xff);
+
+ scsi_data_in(0, 8);
+ scsi_status_complete(SS_GOOD);
+ break;
+ }
+
+ case SC_READ_EXTENDED:
+ 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);
+
+ scsi_data_in(2, blocks*bytes_per_sector);
+ scsi_status_complete(SS_GOOD);
+ break;
+
+ default:
+ nscsi_full_device::scsi_command();
+ break;
+ }
+}
diff --git a/src/emu/machine/nscsi_hd.h b/src/emu/machine/nscsi_hd.h
new file mode 100644
index 00000000000..d49f6109d8a
--- /dev/null
+++ b/src/emu/machine/nscsi_hd.h
@@ -0,0 +1,34 @@
+#ifndef __NSCSI_HD_H__
+#define __NSCSI_HD_H__
+
+#include "machine/nscsi_bus.h"
+#include "harddisk.h"
+
+#define MCFG_NSCSI_HARDDISK_ADD(_tag, _subtag) \
+ MCFG_NSCSI_FULL_DEVICE_ADD(_tag, _subtag, NSCSI_HARDDISK, 0)
+
+class nscsi_harddisk_device : public nscsi_full_device
+{
+public:
+ nscsi_harddisk_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ virtual machine_config_constructor device_mconfig_additions() const;
+
+ static struct harddisk_interface hd_intf;
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+
+ virtual void scsi_command();
+ virtual UINT8 scsi_get_data(int id, int pos);
+
+private:
+ UINT8 block[512];
+ hard_disk_file *harddisk;
+ int lba, cur_lba, blocks;
+ int bytes_per_sector;
+};
+
+extern const device_type NSCSI_HARDDISK;
+
+#endif