New modern object-oriented bus-signals-available SCSI implementation [O. Galibert]

This commit is contained in:
R. Belmont 2012-02-09 03:41:36 +00:00
parent dba51086f5
commit d37f1bb316
8 changed files with 1387 additions and 0 deletions

6
.gitattributes vendored
View File

@ -921,6 +921,12 @@ src/emu/machine/ncr539x.c svneol=native#text/plain
src/emu/machine/ncr539x.h svneol=native#text/plain
src/emu/machine/nmc9306.c svneol=native#text/plain
src/emu/machine/nmc9306.h svneol=native#text/plain
src/emu/machine/nscsi_bus.c svneol=native#text/plain
src/emu/machine/nscsi_bus.h svneol=native#text/plain
src/emu/machine/nscsi_cd.c svneol=native#text/plain
src/emu/machine/nscsi_cd.h svneol=native#text/plain
src/emu/machine/nscsi_hd.c svneol=native#text/plain
src/emu/machine/nscsi_hd.h svneol=native#text/plain
src/emu/machine/nvram.c svneol=native#text/plain
src/emu/machine/nvram.h svneol=native#text/plain
src/emu/machine/pc16552d.c svneol=native#text/plain

View File

@ -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 \

656
src/emu/machine/nscsi_bus.c Normal file
View File

@ -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);
}

353
src/emu/machine/nscsi_bus.h Normal file
View File

@ -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

146
src/emu/machine/nscsi_cd.c Normal file
View File

@ -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;
}
}

View File

@ -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

155
src/emu/machine/nscsi_hd.c Normal file
View File

@ -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;
}
}

View File

@ -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