diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/emu/emu.mak | 3 | ||||
-rw-r--r-- | src/emu/machine/nscsi_bus.c | 656 | ||||
-rw-r--r-- | src/emu/machine/nscsi_bus.h | 353 | ||||
-rw-r--r-- | src/emu/machine/nscsi_cd.c | 146 | ||||
-rw-r--r-- | src/emu/machine/nscsi_cd.h | 34 | ||||
-rw-r--r-- | src/emu/machine/nscsi_hd.c | 155 | ||||
-rw-r--r-- | src/emu/machine/nscsi_hd.h | 34 |
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 |