mirror of
https://github.com/holub/mame
synced 2025-10-07 01:16:22 +03:00
743 lines
19 KiB
C++
743 lines
19 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Olivier Galibert
|
|
#include "emu.h"
|
|
#include "nscsi_bus.h"
|
|
|
|
#define LOG_GENERAL (1U << 0)
|
|
#define LOG_STATE (1U << 1)
|
|
#define LOG_CONTROL (1U << 2)
|
|
|
|
//#define VERBOSE (LOG_GENERAL | LOG_STATE | LOG_CONTROL)
|
|
#define VERBOSE (LOG_GENERAL)
|
|
|
|
#include "logmacro.h"
|
|
|
|
DEFINE_DEVICE_TYPE(NSCSI_BUS, nscsi_bus_device, "nscsi_bus", "SCSI Bus (new)")
|
|
DEFINE_DEVICE_TYPE(NSCSI_CONNECTOR, nscsi_connector, "nscsi_connector", "SCSI Connector Abstraction (new)")
|
|
|
|
|
|
nscsi_bus_device::nscsi_bus_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
|
|
device_t(mconfig, NSCSI_BUS, tag, owner, clock), data(0), ctrl(0)
|
|
{
|
|
devcnt = 0;
|
|
std::fill(std::begin(dev), std::end(dev), dev_t{ nullptr, 0, 0, 0 });
|
|
}
|
|
|
|
void nscsi_bus_device::device_start()
|
|
{
|
|
data = 0;
|
|
ctrl = 0;
|
|
save_item(NAME(data));
|
|
save_item(NAME(ctrl));
|
|
for(int i=0; i<devcnt; i++) {
|
|
save_item(NAME(dev[i].data), i);
|
|
save_item(NAME(dev[i].ctrl), i);
|
|
save_item(NAME(dev[i].wait_ctrl), i);
|
|
}
|
|
}
|
|
|
|
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_t octrl = ctrl;
|
|
ctrl = 0;
|
|
for(int i=0; i<devcnt; i++)
|
|
ctrl |= dev[i].ctrl;
|
|
|
|
if(VERBOSE & LOG_CONTROL) {
|
|
LOGMASKED(LOG_CONTROL, "ctrl %c%c%c%c%c%c%c%c%c %s %04x\n",
|
|
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) {
|
|
LOGMASKED(LOG_CONTROL, "%d=%s%s%s%s%s%s%s%s%s\n", i,
|
|
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" : "");
|
|
}
|
|
}
|
|
|
|
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_t nscsi_bus_device::data_r() const
|
|
{
|
|
return data;
|
|
}
|
|
|
|
uint32_t nscsi_bus_device::ctrl_r() const
|
|
{
|
|
return ctrl;
|
|
}
|
|
|
|
void nscsi_bus_device::ctrl_w(int refid, uint32_t lines, uint32_t mask)
|
|
{
|
|
uint32_t c = dev[refid].ctrl;
|
|
dev[refid].ctrl = (c & ~mask) | (lines & mask);
|
|
regen_ctrl(refid);
|
|
}
|
|
|
|
void nscsi_bus_device::data_w(int refid, uint32_t lines)
|
|
{
|
|
dev[refid].data = lines;
|
|
regen_data();
|
|
}
|
|
|
|
void nscsi_bus_device::ctrl_wait(int refid, uint32_t lines, uint32_t mask)
|
|
{
|
|
uint32_t 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_t clock) :
|
|
device_t(mconfig, NSCSI_CONNECTOR, tag, owner, clock),
|
|
device_slot_interface(mconfig, *this)
|
|
{
|
|
}
|
|
|
|
nscsi_connector::~nscsi_connector()
|
|
{
|
|
}
|
|
|
|
void nscsi_connector::device_start()
|
|
{
|
|
}
|
|
|
|
nscsi_device *nscsi_connector::get_device()
|
|
{
|
|
return dynamic_cast<nscsi_device *>(get_card_device());
|
|
}
|
|
|
|
nscsi_device::nscsi_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
|
|
device_t(mconfig, type, tag, owner, clock),
|
|
device_slot_card_interface(mconfig, *this)
|
|
{
|
|
scsi_id = scsi_refid = -1;
|
|
scsi_bus = nullptr;
|
|
}
|
|
|
|
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()
|
|
{
|
|
}
|
|
|
|
void nscsi_device::device_start()
|
|
{
|
|
save_item(NAME(scsi_id));
|
|
}
|
|
|
|
|
|
const char *const nscsi_full_device::command_names[256] = {
|
|
/* 00 */ "TEST_UNIT_READY", "REZERO", "?", "REQUEST_SENSE", "FORMAT_UNIT", "?", "?", "REASSIGN_BLOCKS",
|
|
/* 08 */ "READ_6/RECIEVE", "?", "WRITE_6/SEND", "SEEK", "?", "?", "?", "?",
|
|
/* 10 */ "?", "?", "INQUIRY", "?", "?", "MODE_SELECT_6", "RESERVE_6", "RELEASE_6",
|
|
/* 18 */ "?", "?", "MODE_SENSE_6", "START_STOP_UNIT", "RECIEVE_DIAG_RES", "SEND_DIAGNOSTICS", "PREVENT_ALLOW_MEDIUM_REMOVAL", "?",
|
|
/* 20 */ "?", "?", "?", "READ_FORMAT_CAPACITIES", "?", "READ_CAPACITY", "?", "?",
|
|
|
|
/* 28 */ "READ_10", "READ_GENERATION", "WRITE_10", "SEEK_10", "ERASE_10", "READ_UPDATED_BLOCK_10", "WRITE_VERIFY", "VERIFY",
|
|
/* 30 */ "SEARCH_DATA_HIGH_10", "SEARCH_DATA_EQUAL_10", "SEARCH_DATA_LOW_10", "SET_LIMITS_10", "PREFETCH", "SYNC_CACHE", "LOCK_UNLOCK_CACHE", "READ_DEFECT_DATA",
|
|
/* 38 */ "MEDIUM_SCAN", "COMPARE", "COPY_AND_VERIFY", "WRITE_BUFFER", "READ_DATA_BUFFER", "UPDATE_BLOCK", "READ_LONG", "WRITE_LONG",
|
|
/* 40 */ "CHANGE_DEFINITION", "WRITE_SAME", "READ_SUB_CHANNEL", "READ_TOC_PMA_ATIP", "READ_HEADER", "PLAY_AUDIO_10", "GET_CONFIGURATION", "PLAY_AUDIO_MSF",
|
|
/* 48 */ "PLAY_AUDIO_TRACK_INDEX", "PLAY_RELATIVE_10", "GET_EVENT_STATUS_NOTIFICATION", "PAUSE_RESUME", "LOG_SELECT", "LOG_SENSE", "STOP_PLAY_SCAN", "?",
|
|
/* 50 */ "XDWRITE", "READ_DISC_INFORMATION/XPWRITE", "READ_TRACK_INFORMATION/XDREAD", "RESERVE_TRACK", "SEND_OPC_INFORMATION", "MODE_SELECT_10", "RESERVE_10", "RELEASE_10",
|
|
/* 58 */ "REPAIR_TRACK", "READ_MASTER_CUE", "MODE_SENSE_10", "CLOSE_TRACK_SESSION", "READ_BUFFER_CAPACITY", "SEND_CUE_SHEET", "PERSISTENT_RESERVE_IN", "PERSISTENT_RESERVE_OUT",
|
|
/* 80 */ "XDWRITE_EXTENDED", "REBUILD", "REGENERATE", "EXTENDED_COPY", "RECEIVE_COPY_RESULTS", "?", "?", "?",
|
|
/* 88 */ "?", "?", "?", "?", "?", "?", "?", "?",
|
|
/* 90 */ "?", "?", "?", "?", "?", "?", "?", "?",
|
|
/* 98 */ "?", "?", "?", "?", "?", "?", "?", "?",
|
|
/* a0 */ "REPORT_LUNS", "BLANK", "SEND_EVENT", "REPORT_DEVICE_IDENTIFIER/SEND_KEY", "SET_DEVICE_IDENTIFIER/REPORT_KEY", "PLAY_AUDIO_12", "LOAD_UNLOAD_MEDIUM", "MOVE_MEDIUM_ATTACHED/SET_READ_AHEAD",
|
|
/* a8 */ "READ_12", "PLAY_RELATIVE_12", "WRITE_12", "?", "ERASE_12/GET_PERFORMANCE", "READ_DVD_STRUCTURE", "WRITE_AND_VERIFY_12", "VERIFY_12",
|
|
/* b0 */ "SEARCH_DATA_HIGH_12", "SEARCH_DATA_EQUAL_12", "SEARCH_DATA_LOW_12", "SET_LIMITS_12", "READ_ELEMENT_STATUS_ATTACHED", "?", "SET_STREAMING", "READ_DEFECT_DATA_12",
|
|
/* b8 */ "?", "READ_CD_MSF", "SCAN_MMC", "SET_CD_SPEED", "PLAY_CD", "MECHANISM_STATUS", "READ_CD", "SEND_DVD_STRUCTURE",
|
|
/* c0 */ "?", "?", "?", "?", "?", "?", "?", "?",
|
|
/* c8 */ "?", "?", "?", "?", "?", "?", "?", "?",
|
|
/* d0 */ "?", "?", "?", "?", "?", "?", "?", "?",
|
|
/* d8 */ "?", "?", "?", "?", "?", "?", "?", "?",
|
|
/* e0 */ "?", "?", "?", "?", "?", "?", "?", "?",
|
|
/* e8 */ "?", "?", "?", "?", "?", "?", "?", "?",
|
|
/* f0 */ "?", "?", "?", "?", "?", "?", "?", "?",
|
|
/* f8 */ "?", "?", "?", "?", "?", "?", "?", "?",
|
|
};
|
|
|
|
void nscsi_full_device::device_start()
|
|
{
|
|
nscsi_device::device_start();
|
|
scsi_timer = timer_alloc(SCSI_TIMER);
|
|
save_item(NAME(scsi_cmdbuf));
|
|
save_item(NAME(scsi_sense_buffer));
|
|
save_item(NAME(scsi_cmdsize));
|
|
save_item(NAME(scsi_identify));
|
|
save_item(NAME(scsi_state));
|
|
save_item(NAME(scsi_substate));
|
|
save_item(NAME(scsi_initiator_id));
|
|
save_item(NAME(data_buffer_id));
|
|
save_item(NAME(data_buffer_size));
|
|
save_item(NAME(data_buffer_pos));
|
|
save_item(NAME(buf_control_rpos));
|
|
save_item(NAME(buf_control_wpos));
|
|
for(int i=0; i<32; i++) {
|
|
save_item(NAME(buf_control[i].action), i);
|
|
save_item(NAME(buf_control[i].param1), i);
|
|
save_item(NAME(buf_control[i].param2), i);
|
|
}
|
|
}
|
|
|
|
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_t ctrl = scsi_bus->ctrl_r();
|
|
uint32_t 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;
|
|
LOG("scsi bus reset\n");
|
|
return;
|
|
}
|
|
|
|
LOGMASKED(LOG_STATE, "state=%d.%d %s\n",
|
|
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) {
|
|
scsi_put_data(data_buffer_id, data_buffer_pos++, 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:
|
|
data_buffer_id = SBUF_MAIN;
|
|
data_buffer_pos = 0;
|
|
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);
|
|
}
|
|
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_DATA_OUT:
|
|
scsi_bus->ctrl_w(scsi_refid, S_PHASE_DATA_OUT, S_PHASE_MASK);
|
|
data_buffer_id = ctl->param1;
|
|
data_buffer_size = ctl->param2;
|
|
data_buffer_pos = 0;
|
|
scsi_state = TARGET_WAIT_DATA_OUT_BYTE;
|
|
target_recv_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_DATA_OUT_BYTE:
|
|
if(data_buffer_pos == data_buffer_size-1)
|
|
scsi_state = TARGET_NEXT_CONTROL;
|
|
target_recv_byte();
|
|
break;
|
|
|
|
case TARGET_WAIT_MSG_BYTE:
|
|
if(ctrl & S_SEL)
|
|
return;
|
|
if(!(ctrl & S_ATN)) {
|
|
scsi_cmdsize = data_buffer_pos;
|
|
scsi_message();
|
|
data_buffer_id = SBUF_MAIN;
|
|
data_buffer_pos = 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) {
|
|
LOG("Parity error? Say what?\n");
|
|
scsi_state = IDLE;
|
|
break;
|
|
}
|
|
|
|
if(command_done()) {
|
|
scsi_cmdsize = data_buffer_pos;
|
|
scsi_bus->ctrl_wait(scsi_refid, 0, S_ACK);
|
|
scsi_command();
|
|
scsi_state = TARGET_NEXT_CONTROL;
|
|
step(false);
|
|
} else
|
|
target_recv_byte();
|
|
break;
|
|
|
|
default:
|
|
LOG("step() unexpected state %d.%d\n",
|
|
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_t 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);
|
|
}
|
|
|
|
uint8_t 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:
|
|
fatalerror("nscsi_full_device::scsi_get_data - unknown id\n");
|
|
}
|
|
}
|
|
|
|
void nscsi_full_device::scsi_put_data(int id, int pos, uint8_t data)
|
|
{
|
|
switch(id) {
|
|
case SBUF_MAIN:
|
|
scsi_cmdbuf[pos] = data;
|
|
break;
|
|
case SBUF_SENSE:
|
|
scsi_sense_buffer[pos] = data;
|
|
break;
|
|
default:
|
|
fatalerror("nscsi_full_device::scsi_put_data - unknown id\n");
|
|
}
|
|
}
|
|
|
|
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(!data_buffer_pos)
|
|
return false;
|
|
uint8_t h = scsi_cmdbuf[0];
|
|
switch(h >> 5) {
|
|
case 0: return data_buffer_pos == 6;
|
|
case 1: return data_buffer_pos == 10;
|
|
case 2: return data_buffer_pos == 10;
|
|
case 3: return true;
|
|
case 4: return true;
|
|
case 5: return data_buffer_pos == 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(ARRAY_LENGTH(buf_control)))
|
|
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_t 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_t 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()
|
|
{
|
|
LOG("Unhandled command %s", command_names[scsi_cmdbuf[0]]);
|
|
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:
|
|
LOG("command REQUEST SENSE\n");
|
|
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;
|
|
}
|
|
|
|
LOG("Unknown message");
|
|
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);
|
|
}
|