diff --git a/src/devices/machine/ncr5385.cpp b/src/devices/machine/ncr5385.cpp index 76448cf620c..789a12d61bd 100644 --- a/src/devices/machine/ncr5385.cpp +++ b/src/devices/machine/ncr5385.cpp @@ -1,119 +1,783 @@ // license:BSD-3-Clause // copyright-holders:Ryan Holtz -/*********************************************************************** - NCR 5385E SCSI Controller - - TOOD: - - Everything. - -***********************************************************************/ +/* + * NCR 5385 SCSI Protocol Controller + * + * Sources: + * - NCR 5385 SCSI Protocol Controller, 1983, NCR Corporation, Dayton, Ohio, USA + * - NCR SCSI Engineering Notebook, 1984, NCR Microelectronics + * + * TODO: + * - single byte transfer + * - target mode send/receive + * - disconnect/reselection + */ #include "emu.h" #include "ncr5385.h" -DEFINE_DEVICE_TYPE(NCR5385, ncr5385_device, "ncr5385", "NCR 5385E SCSI Controller") +#define LOG_GENERAL (1U << 0) +#define LOG_REGW (1U << 1) +#define LOG_REGR (1U << 2) +#define LOG_STATE (1U << 3) +#define LOG_DMA (1U << 4) +#define LOG_COMMAND (1U << 5) -ncr5385_device::ncr5385_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, NCR5385, tag, owner, clock) +//#define VERBOSE (LOG_GENERAL|LOG_REGW|LOG_REGR|LOG_STATE|LOG_DMA|LOG_COMMAND) +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(NCR5385, ncr5385_device, "ncr5385", "NCR 5385 SCSI Protocol Controller") + +// FIXME: would be better to reuse from nscsi_full_device +static unsigned const SCSI_ARB_DELAY = 2'400; +static unsigned const SCSI_BUS_CLEAR = 800; +static unsigned const SCSI_BUS_FREE = 800; +static unsigned const SCSI_BUS_SETTLE = 400; +static unsigned const SCSI_BUS_SKEW = 10; +static unsigned const SCSI_RST_HOLD = 25'000; + +ncr5385_device::ncr5385_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) + : nscsi_device(mconfig, NCR5385, tag, owner, clock) + , nscsi_slot_card_interface(mconfig, *this, DEVICE_SELF) , m_int(*this) + , m_dreq(*this) + , m_int_state(false) + , m_dreq_state(false) { } +enum state : u32 +{ + IDLE, + DIAGNOSTIC, + + ARB_BUS_FREE, + ARB_START, + ARB_EVALUATE, + SEL_START, + SEL_DELAY, + SEL_WAIT_BSY, + SEL_COMPLETE, + SEL_WAIT_REQ, + + XFI_START, + XFI_IN_REQ, + XFI_IN_DRQ, + XFI_IN_ACK, + XFI_OUT_REQ, + XFI_OUT_DRQ, + XFI_OUT_ACK, + XFI_OUT_PAD, +}; + +enum mode : u8 +{ + DISCONNECTED, + INITIATOR, + TARGET, +}; + +enum diag_mask : u8 +{ + DIAG_SELF = 0x07, // self-diagnostic status + DIAG_CMD = 0x38, // diagnostic command status + DIAG_DONE = 0x80, // self-diagnostic complete + + DIAG_CMD_GP = 0x18, // diagnostic good parity + DIAG_CMD_BP = 0x20, // diagnostic bad parity +}; + +enum int_mask : u8 +{ + INT_FUNC_COMPLETE = 0x01, + INT_BUS_SERVICE = 0x02, + INT_DISCONNECTED = 0x04, + INT_SELECTED = 0x08, + INT_RESELECTED = 0x10, + INT_INVALID_CMD = 0x40, +}; + +enum aux_status_mask : u8 +{ + AUX_STATUS_TC_ZERO = 0x02, + AUX_STATUS_PAUSED = 0x04, + AUX_STATUS_IO = 0x08, + AUX_STATUS_CD = 0x10, + AUX_STATUS_MSG = 0x20, + AUX_STATUS_PARITY_ERR = 0x40, + AUX_STATUS_DATA_FULL = 0x80, +}; + +enum cmd_mask : u8 +{ + CMD_INT = 0x08, // interrupting + CMD_SBX = 0x40, // single byte transfer + CMD_DMA = 0x80, // DMA mode +}; + void ncr5385_device::device_start() { + save_item(NAME(m_dat)); + save_item(NAME(m_cmd)); + save_item(NAME(m_ctl)); + save_item(NAME(m_dst_id)); + save_item(NAME(m_aux_status)); + save_item(NAME(m_own_id)); + save_item(NAME(m_int_status)); + save_item(NAME(m_src_id)); + save_item(NAME(m_dia_status)); + save_item(NAME(m_cnt)); + + save_item(NAME(m_state)); + save_item(NAME(m_xfi_phase)); + save_item(NAME(m_mode)); + + save_item(NAME(m_int_state)); + save_item(NAME(m_dreq_state)); + + m_state_timer = timer_alloc(timer_expired_delegate(FUNC(ncr5385_device::state_timer), this)); + + m_dia_status = DIAG_DONE; } void ncr5385_device::device_reset() { - m_state = STATE_IDLE; - m_int_reg = 0; - m_ctrl_reg = 0; - m_aux_status_reg = AUX_STATUS_TC_ZERO; - m_diag_status_reg = DIAG_COMPLETE; + m_cmd = 0; + m_ctl = 0; + m_dst_id = 0; + m_aux_status = AUX_STATUS_TC_ZERO; + m_int_status = 0; + m_src_id = 0; + m_dia_status &= (DIAG_DONE | DIAG_SELF); + m_cnt = 0; + + m_state = IDLE; + m_mode = DISCONNECTED; + + // monitor all control lines + scsi_bus->ctrl_wait(scsi_refid, S_ALL, S_ALL); + + update_int(); } -void ncr5385_device::write(offs_t offset, uint8_t data) +void ncr5385_device::scsi_ctrl_changed() { - switch (offset) + u32 const ctrl = scsi_bus->ctrl_r(); + + static char const *const nscsi_phase[] = { "DATA OUT", "DATA IN", "COMMAND", "STATUS", "*", "*", "MESSAGE OUT", "MESSAGE IN" }; + + if (ctrl & S_RST) + LOGMASKED(LOG_STATE, "scsi_ctrl_changed 0x%03x BUS RESET\n", ctrl); + else if ((ctrl & S_BSY) && !(ctrl & S_SEL)) + LOGMASKED(LOG_STATE, "scsi_ctrl_changed 0x%03x phase %s%s%s\n", ctrl, nscsi_phase[ctrl & S_PHASE_MASK], + ctrl & S_REQ ? " REQ" : "", ctrl & S_ACK ? " ACK" : ""); + else if (ctrl & S_BSY) + LOGMASKED(LOG_STATE, "scsi_ctrl_changed 0x%03x arbitration/selection\n", ctrl); + else { - case 0x0: // Data Register - switch (m_state) - { - case STATE_DIAGNOSTIC_GOOD_PARITY: - m_aux_status_reg &= ~AUX_STATUS_PARITY_ERR; - m_aux_status_reg |= AUX_STATUS_DATA_FULL; - m_int_reg = INT_FUNC_COMPLETE; - m_diag_status_reg = DIAG_COMPLETE | DIAG_TURN_GOOD_PARITY; - m_state = STATE_IDLE; - m_int(1); - logerror("%s: ncr5385_w: data=%02x (diagnostic w/ good parity)\n", machine().describe_context(), data); - break; - case STATE_DIAGNOSTIC_BAD_PARITY: - m_aux_status_reg |= AUX_STATUS_PARITY_ERR | AUX_STATUS_DATA_FULL; - m_int_reg = INT_FUNC_COMPLETE; - m_diag_status_reg = DIAG_COMPLETE | DIAG_TURN_BAD_PARITY; - m_state = STATE_IDLE; - m_int(1); - logerror("%s: ncr5385_w: data=%02x (diagnostic w/ bad parity)\n", machine().describe_context(), data); - break; - default: - logerror("%s: ncr5385_w: data=%02x\n", machine().describe_context(), data); - break; - } - break; - case 0x1: // Command Register - switch (data & 0x3f) - { - case 0x00: // Chip Reset - logerror("%s: ncr5385_w: command: reset\n", machine().describe_context()); - m_state = STATE_IDLE; - m_int_reg = 0; - m_aux_status_reg = AUX_STATUS_TC_ZERO; - m_diag_status_reg = DIAG_COMPLETE; - m_int(0); - break; - case 0x0b: // Diagnostic - logerror("%s: ncr5385_w: command: diagnostic (%s parity)\n", machine().describe_context(), BIT(data, 6) ? "bad" : "good"); - if (BIT(data, 6)) - m_state = STATE_DIAGNOSTIC_BAD_PARITY; - else - m_state = STATE_DIAGNOSTIC_GOOD_PARITY; - break; - default: - logerror("%s: ncr5385_w: command: %02x\n", machine().describe_context(), data); - break; - } - break; - case 0x2: // Control Register - m_ctrl_reg = data & 0x07; - logerror("%s: ncr5385_w: control: parity_en=%d, reselect_en=%d, select_en=%d\n", machine().describe_context(), BIT(data, CTRL_PARITY_BIT), BIT(data, CTRL_RESELECT_BIT), BIT(data, CTRL_SELECT_BIT)); - break; - default: - logerror("%s: ncr5385_w: %x=%02x\n", machine().describe_context(), offset, data); - break; + LOGMASKED(LOG_STATE, "scsi_ctrl_changed 0x%03x BUS FREE\n", ctrl); + + if (m_mode == INITIATOR) + { + m_mode = DISCONNECTED; + m_int_status |= INT_DISCONNECTED; + update_int(); + } } } -uint8_t ncr5385_device::read(offs_t offset) +void ncr5385_device::map(address_map &map) { - switch (offset) + map(0x0, 0x0).rw(FUNC(ncr5385_device::dat_r), FUNC(ncr5385_device::dat_w)); + map(0x1, 0x1).rw(FUNC(ncr5385_device::cmd_r), FUNC(ncr5385_device::cmd_w)); + map(0x2, 0x2).rw(FUNC(ncr5385_device::ctl_r), FUNC(ncr5385_device::ctl_w)); + map(0x3, 0x3).rw(FUNC(ncr5385_device::dst_id_r), FUNC(ncr5385_device::dst_id_w)); + map(0x4, 0x4).r(FUNC(ncr5385_device::aux_status_r)); + map(0x5, 0x5).r(FUNC(ncr5385_device::own_id_r)); + map(0x6, 0x6).r(FUNC(ncr5385_device::int_status_r)); + map(0x7, 0x7).r(FUNC(ncr5385_device::src_id_r)); + map(0x9, 0x9).r(FUNC(ncr5385_device::dia_status_r)); + map(0xc, 0xc).rw(FUNC(ncr5385_device::cnt_r<2>), FUNC(ncr5385_device::cnt_w<2>)); + map(0xd, 0xd).rw(FUNC(ncr5385_device::cnt_r<1>), FUNC(ncr5385_device::cnt_w<1>)); + map(0xe, 0xe).rw(FUNC(ncr5385_device::cnt_r<0>), FUNC(ncr5385_device::cnt_w<0>)); + map(0xf, 0xf).rw(FUNC(ncr5385_device::tst_r), FUNC(ncr5385_device::tst_w)); +} + +u8 ncr5385_device::dat_r() +{ + if (m_aux_status & AUX_STATUS_DATA_FULL) + m_aux_status &= ~AUX_STATUS_DATA_FULL; + else + logerror("data register empty (%s)\n", machine().describe_context()); + + return m_dat; +} + +u8 ncr5385_device::cmd_r() +{ + return m_cmd; +} + +u8 ncr5385_device::ctl_r() +{ + return m_ctl; +} + +u8 ncr5385_device::dst_id_r() +{ + return m_dst_id; +} + +u8 ncr5385_device::aux_status_r() +{ + u8 data = m_aux_status; + + if (!m_int_status) { - case 0x2: - logerror("%s: ncr5385_r: control (%02x)\n", machine().describe_context(), m_ctrl_reg); - return m_ctrl_reg; - case 0x4: - logerror("%s: ncr5385_r: aux status (%02x)\n", machine().describe_context(), m_aux_status_reg); - return m_aux_status_reg; - case 0x6: - logerror("%s: ncr5385_r: interrupt (%02x)\n", machine().describe_context(), m_int_reg); - m_int(1); - return m_int_reg; - case 0x9: - logerror("%s: ncr5385_r: diagnostic status (%02x)\n", machine().describe_context(), m_diag_status_reg); - return m_diag_status_reg; - default: - logerror("%s: ncr5385_r: %x (%02x)\n", machine().describe_context(), offset, 0); - return 0; + // return current phase + u32 const ctrl = scsi_bus->ctrl_r(); + if (ctrl & S_MSG) + data |= AUX_STATUS_MSG; + if (ctrl & S_CTL) + data |= AUX_STATUS_CD; + if (ctrl & S_INP) + data |= AUX_STATUS_IO; + } + LOGMASKED(LOG_REGR, "aux_status_r 0x%02x (%s)\n", data, machine().describe_context()); + + return data; +} + +u8 ncr5385_device::own_id_r() +{ + return m_own_id; +} + +u8 ncr5385_device::int_status_r() +{ + u8 const data = m_int_status; + LOGMASKED(LOG_REGR, "int_status_r 0x%02x (%s)\n", data, machine().describe_context()); + m_aux_status &= ~AUX_STATUS_PARITY_ERR; + m_int_status = 0; + update_int(); + + if (m_state != IDLE) + m_state_timer->adjust(attotime::zero); + + return data; +} + +u8 ncr5385_device::src_id_r() +{ + return m_src_id; +} + +u8 ncr5385_device::dia_status_r() +{ + return m_dia_status; +} + +template u8 ncr5385_device::cnt_r() +{ + return u8(m_cnt >> (N * 8)); +} + +u8 ncr5385_device::tst_r() +{ + return 0; +} + +void ncr5385_device::dat_w(u8 data) +{ + LOGMASKED(LOG_REGW, "dat_w 0x%02x (%s)\n", data, machine().describe_context()); + + if (!(m_aux_status & AUX_STATUS_DATA_FULL)) + { + m_dat = data; + m_aux_status |= AUX_STATUS_DATA_FULL; + + if (m_state != IDLE) + m_state_timer->adjust(attotime::zero); + } + else + logerror("data register full\n"); +} + +void ncr5385_device::cmd_w(u8 data) +{ + LOGMASKED(LOG_REGW, "cmd_w 0x%02x (%s)\n", data, machine().describe_context()); + if (!(data & 0x18)) + { + // immediate commands + + switch (data & 0x1f) + { + case 0x00: + LOGMASKED(LOG_COMMAND, "reset\n"); + reset(); + break; + case 0x01: // disconnect + LOGMASKED(LOG_COMMAND, "disconnect\n"); + m_mode = DISCONNECTED; + break; + case 0x02: // pause + LOGMASKED(LOG_COMMAND, "pause\n"); + break; + case 0x03: // set atn + LOGMASKED(LOG_COMMAND, "set atn\n"); + scsi_bus->ctrl_w(scsi_refid, S_ATN, S_ATN); + break; + case 0x04: // message accepted + LOGMASKED(LOG_COMMAND, "message accepted\n"); + scsi_bus->ctrl_w(scsi_refid, 0, S_ACK); + break; + case 0x05: // chip disabled + LOGMASKED(LOG_COMMAND, "chip disabled\n"); + break; + case 0x06: case 0x07: + // reserved + break; + } + } + else + { + // interrupting commands + m_aux_status &= ~AUX_STATUS_DATA_FULL; + m_cmd = data; + + switch (data & 0x1f) + { + case 0x08: // select w/atn + LOGMASKED(LOG_COMMAND, "select %d w/atn (timeout %d)\n", m_dst_id, attotime::from_ticks(m_cnt * 1024, clock()).to_string()); + m_state = ARB_BUS_FREE; + m_state_timer->adjust(attotime::zero); + break; + case 0x09: // select w/o atn + LOGMASKED(LOG_COMMAND, "select %d w/o atn (timeout %d)\n", m_dst_id, attotime::from_ticks(m_cnt * 1024, clock()).to_string()); + m_state = ARB_BUS_FREE; + m_state_timer->adjust(attotime::zero); + break; + case 0x0a: // reselect + LOGMASKED(LOG_COMMAND, "reselect\n"); + break; + case 0x0b: // diagnostic + LOGMASKED(LOG_COMMAND, "diagnostic (%s parity)\n", BIT(data, 6) ? "bad" : "good"); + m_state = DIAGNOSTIC; + break; + case 0x0c: // receive command + LOGMASKED(LOG_COMMAND, "receive command\n"); + break; + case 0x0d: // receive data + LOGMASKED(LOG_COMMAND, "receive data\n"); + break; + case 0x0e: // receive message out + LOGMASKED(LOG_COMMAND, "receive message out\n"); + break; + case 0x0f: // receive unspecified info out + LOGMASKED(LOG_COMMAND, "receive unspecified info out\n"); + break; + case 0x10: // send status + LOGMASKED(LOG_COMMAND, "send status\n"); + break; + case 0x11: // send data + LOGMASKED(LOG_COMMAND, "send data\n"); + break; + case 0x12: // send message in + LOGMASKED(LOG_COMMAND, "send message in\n"); + break; + case 0x13: // send unspecified info in + LOGMASKED(LOG_COMMAND, "send unspecified info in\n"); + break; + case 0x14: // transfer info + LOGMASKED(LOG_COMMAND, "transfer info (count=%d)\n", m_cnt); + m_state = XFI_START; + m_state_timer->adjust(attotime::zero); + break; + case 0x15: // transfer pad + LOGMASKED(LOG_COMMAND, "transfer pad (count=%d)\n", m_cnt); + m_state = XFI_START; + m_state_timer->adjust(attotime::zero); + break; + case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + // reserved + break; + } + } +} + +void ncr5385_device::ctl_w(u8 data) +{ + LOGMASKED(LOG_REGW, "ctl_w 0x%02x (%s)\n", data, machine().describe_context()); + + m_ctl = data & 7; +} + +void ncr5385_device::dst_id_w(u8 data) +{ + LOGMASKED(LOG_REGW, "dst_id_w 0x%02x (%s)\n", data, machine().describe_context()); + m_dst_id = (data & 7); +} + +template void ncr5385_device::cnt_w(u8 data) +{ + m_cnt = (m_cnt & ~(u32(0xff) << (N * 8))) | (u32(data) << (N * 8)); +} + +void ncr5385_device::tst_w(u8 data) +{ + LOGMASKED(LOG_REGW, "tst_w 0x%02x (%s)\n", data, machine().describe_context()); +} + +u8 ncr5385_device::dma_r() +{ + u8 const data = m_dat; + m_aux_status &= ~AUX_STATUS_DATA_FULL; + + set_dreq(false); + m_state_timer->adjust(attotime::zero); + + return data; +} + +void ncr5385_device::dma_w(u8 data) +{ + m_dat = data; + m_aux_status |= AUX_STATUS_DATA_FULL; + + set_dreq(false); + m_state_timer->adjust(attotime::zero); +} + +void ncr5385_device::state_timer(s32 param) +{ + // step state machine + int const delay = state_step(); + + // check for data stall + if (delay < 0) + return; + + // repeat until idle + if (m_state != IDLE) + m_state_timer->adjust(attotime::from_nsec(delay)); +} + +int ncr5385_device::state_step() +{ + u32 const ctrl = scsi_bus->ctrl_r(); + int delay = 0; + + u8 const oid = 1 << m_own_id; + u8 const tid = 1 << m_dst_id; + + switch (m_state) + { + case DIAGNOSTIC: + m_dia_status &= DIAG_DONE | DIAG_SELF; + if (BIT(m_cmd, 6)) + { + m_aux_status |= AUX_STATUS_PARITY_ERR; + m_dia_status |= DIAG_CMD_BP; + } + else + { + m_aux_status &= ~AUX_STATUS_PARITY_ERR; + m_dia_status |= DIAG_CMD_GP; + } + + m_int_status |= INT_FUNC_COMPLETE; + m_state = IDLE; + + update_int(); + break; + + case ARB_BUS_FREE: + LOGMASKED(LOG_STATE, "arbitration: waiting for bus free\n"); + if (!(ctrl & (S_SEL | S_BSY | S_RST))) + { + m_state = ARB_START; + delay = SCSI_BUS_FREE; + } + break; + case ARB_START: + LOGMASKED(LOG_STATE, "arbitration: started\n"); + m_state = ARB_EVALUATE; + delay = SCSI_ARB_DELAY; + + // assert own ID and BSY + scsi_bus->data_w(scsi_refid, oid); + scsi_bus->ctrl_w(scsi_refid, S_BSY, S_BSY); + break; + case ARB_EVALUATE: + // check if SEL asserted, or if there's a higher ID on the bus + if ((ctrl & S_SEL) || (scsi_bus->data_r() & ~((oid - 1) | oid))) + { + LOGMASKED(LOG_STATE, "arbitration: lost\n"); + m_state = ARB_BUS_FREE; + + // clear data and BSY + scsi_bus->data_w(scsi_refid, 0); + scsi_bus->ctrl_w(scsi_refid, 0, S_BSY); + } + else + { + LOGMASKED(LOG_STATE, "arbitration: won\n"); + m_state = SEL_START; + delay = SCSI_BUS_CLEAR + SCSI_BUS_SETTLE; + } + break; + + case SEL_START: + LOGMASKED(LOG_STATE, "selection: SEL asserted\n"); + m_state = SEL_DELAY; + delay = SCSI_BUS_SKEW * 2; + + // assert own and target ID and SEL + scsi_bus->data_w(scsi_refid, oid | tid); + scsi_bus->ctrl_w(scsi_refid, S_SEL, S_SEL); + break; + case SEL_DELAY: + LOGMASKED(LOG_STATE, "selection: BSY cleared\n"); + m_state = SEL_WAIT_BSY; + delay = SCSI_BUS_SETTLE; + + // clear BSY, optionally assert ATN + if (!BIT(m_cmd, 0)) + scsi_bus->ctrl_w(scsi_refid, S_ATN, S_BSY | S_ATN); + else + scsi_bus->ctrl_w(scsi_refid, 0, S_BSY); + break; + case SEL_WAIT_BSY: + if (ctrl & S_BSY) + { + LOGMASKED(LOG_STATE, "selection: BSY asserted by target\n"); + m_state = SEL_COMPLETE; + delay = SCSI_BUS_SKEW * 2; + } + else + { + LOGMASKED(LOG_STATE, "selection: timed out\n"); + m_int_status |= INT_DISCONNECTED; + m_state = IDLE; + + scsi_bus->ctrl_w(scsi_refid, 0, S_ATN | S_SEL); + + update_int(); + } + break; + case SEL_COMPLETE: + LOGMASKED(LOG_STATE, "selection: complete\n"); + m_int_status |= INT_FUNC_COMPLETE; + m_mode = INITIATOR; + m_state = SEL_WAIT_REQ; + delay = -1; + + update_int(); + + // clear data and SEL + scsi_bus->data_w(scsi_refid, 0); + scsi_bus->ctrl_w(scsi_refid, 0, S_SEL); + break; + case SEL_WAIT_REQ: + if (ctrl & S_REQ) + { + LOGMASKED(LOG_STATE, "selection: REQ asserted by target\n"); + m_int_status |= INT_BUS_SERVICE; + m_state = IDLE; + + update_int(); + } + else + delay = -1; + break; + + case XFI_START: + m_xfi_phase = ctrl & S_PHASE_MASK; + m_state = (ctrl & S_INP) ? XFI_IN_REQ : XFI_OUT_REQ; + break; + + case XFI_IN_REQ: + // TODO: single byte transfer, disconnect + if (ctrl & S_REQ) + { + if (m_cnt && (ctrl & S_PHASE_MASK) == m_xfi_phase) + { + m_state = XFI_IN_DRQ; + + // no data transferred and no dma used by transfer pad command + if (!BIT(m_cmd, 0)) + { + m_aux_status |= AUX_STATUS_DATA_FULL; + m_dat = scsi_bus->data_r(); + + if (m_cmd & CMD_DMA) + set_dreq(true); + + delay = -1; + } + } + else + { + LOGMASKED(LOG_STATE, "xfi_in: %s\n", m_cnt ? "phase change" : "transfer complete"); + + m_int_status |= INT_BUS_SERVICE; + m_state = IDLE; + + update_int(); + } + } + break; + case XFI_IN_DRQ: + m_state = XFI_IN_ACK; + + LOGMASKED(LOG_STATE, "xfi_in: data 0x%02x\n", m_dat); + + // assert ACK + scsi_bus->ctrl_w(scsi_refid, S_ACK, S_ACK); + break; + case XFI_IN_ACK: + if (!(ctrl & S_REQ)) + { + m_state = XFI_IN_REQ; + + if ((m_cmd & CMD_DMA) && !(m_cmd & CMD_SBX)) + { + m_cnt--; + + LOGMASKED(LOG_DMA, "xfi_in: %d remaining\n", m_cnt); + + if (!m_cnt) + m_aux_status |= AUX_STATUS_TC_ZERO; + } + + // clear ACK except after last byte of message input phase + if ((m_cnt == 0) && (ctrl & S_PHASE_MASK) == S_PHASE_MSG_IN) + { + m_int_status |= INT_FUNC_COMPLETE; + m_state = IDLE; + + update_int(); + } + else + scsi_bus->ctrl_w(scsi_refid, 0, S_ACK); + } + break; + + case XFI_OUT_REQ: + if (ctrl & S_REQ) + { + // TODO: single byte transfer, disconnect + if (m_cnt && (ctrl & S_PHASE_MASK) == m_xfi_phase) + { + m_state = XFI_OUT_DRQ; + + if (m_cmd & CMD_DMA) + set_dreq(true); + + delay = -1; + } + else + { + LOGMASKED(LOG_STATE, "xfi_out: %s\n", m_cnt ? "phase change" : "transfer complete"); + m_int_status |= INT_BUS_SERVICE; + m_state = IDLE; + + update_int(); + } + } + break; + case XFI_OUT_DRQ: + m_state = XFI_OUT_ACK; + m_aux_status &= ~AUX_STATUS_DATA_FULL; + + LOGMASKED(LOG_STATE, "xfi_out: data 0x%02x\n", m_dat); + + // assert data and ACK + scsi_bus->data_w(scsi_refid, m_dat); + if ((m_cnt == 1) && (ctrl & S_PHASE_MASK) == S_PHASE_MSG_OUT) + scsi_bus->ctrl_w(scsi_refid, S_ACK, S_ACK | S_ATN); + else + scsi_bus->ctrl_w(scsi_refid, S_ACK, S_ACK); + break; + case XFI_OUT_ACK: + if (!(ctrl & S_REQ)) + { + m_state = BIT(m_cmd, 0) ? XFI_OUT_PAD : XFI_OUT_REQ; + + if ((m_cmd & CMD_DMA) && !(m_cmd & CMD_SBX)) + { + m_cnt--; + + LOGMASKED(LOG_DMA, "xfi_out: %d remaining\n", m_cnt); + + if (!m_cnt) + m_aux_status |= AUX_STATUS_TC_ZERO; + } + + // clear data and ACK + scsi_bus->data_w(scsi_refid, 0); + scsi_bus->ctrl_w(scsi_refid, 0, S_ACK); + } + break; + case XFI_OUT_PAD: + if (ctrl & S_REQ) + { + // TODO: single byte transfer, disconnect + if (m_cnt && (ctrl & S_PHASE_MASK) == m_xfi_phase) + m_state = XFI_OUT_DRQ; + else + { + LOGMASKED(LOG_STATE, "xfi_out: %s\n", m_cnt ? "phase change" : "transfer complete"); + m_int_status |= INT_BUS_SERVICE; + m_state = IDLE; + + update_int(); + } + } + break; + } + + return delay; +} + +void ncr5385_device::set_dreq(bool dreq) +{ + if (m_dreq_state != dreq) + { + LOGMASKED(LOG_DMA, "set_dreq %d\n", dreq); + + m_dreq_state = dreq; + m_dreq(m_dreq_state); + } +} + +void ncr5385_device::update_int() +{ + bool const int_state = m_int_status & 0x5f; + + if (m_int_state != int_state) + { + LOG("update_int %d\n", int_state); + + m_aux_status &= ~(AUX_STATUS_MSG | AUX_STATUS_CD | AUX_STATUS_IO); + if (int_state) + { + m_cmd = 0; + + // latch current phase + u32 const ctrl = scsi_bus->ctrl_r(); + if (ctrl & S_MSG) + m_aux_status |= AUX_STATUS_MSG; + if (ctrl & S_CTL) + m_aux_status |= AUX_STATUS_CD; + if (ctrl & S_INP) + m_aux_status |= AUX_STATUS_IO; + } + + m_int_state = int_state; + m_int(m_int_state); } } diff --git a/src/devices/machine/ncr5385.h b/src/devices/machine/ncr5385.h index a1dec052045..a25952ad7ba 100644 --- a/src/devices/machine/ncr5385.h +++ b/src/devices/machine/ncr5385.h @@ -4,11 +4,6 @@ NCR 5385 SCSI Controller emulation - TODO: - - Everything. Currently, just enough is implemented to make the - Philips VP415 CPU / Datagrabber board satisfied that the - controller has passed its internal diagnostics. - ************************************************************************ _____ _____ D2 1 |* \_/ | 48 VCC @@ -43,91 +38,87 @@ #pragma once +#include "machine/nscsi_bus.h" -//************************************************************************** -// TYPE DEFINITIONS -//************************************************************************** - -// ======================> ncr5385_device - -class ncr5385_device : public device_t +class ncr5385_device + : public nscsi_device + , public nscsi_slot_card_interface { public: - // construction/destruction - ncr5385_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - auto irq() { return m_int.bind(); } + auto dreq() { return m_dreq.bind(); } - void write(offs_t offset, uint8_t data); - uint8_t read(offs_t offset); + void set_own_id(unsigned id) { m_own_id = id; } + + ncr5385_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); + + void map(address_map &map); + + u8 dma_r(); + void dma_w(u8 data); protected: - // device-level overrides + // device_t implementation virtual void device_start() override; virtual void device_reset() override; + // ncsci_device implementation + virtual void scsi_ctrl_changed() override; + + // read handlers + u8 dat_r(); + u8 cmd_r(); + u8 ctl_r(); + u8 dst_id_r(); + u8 aux_status_r(); + u8 own_id_r(); + u8 int_status_r(); + u8 src_id_r(); + u8 dia_status_r(); + template u8 cnt_r(); + u8 tst_r(); + + // write handlers + void dat_w(u8 data); + void cmd_w(u8 data); + void ctl_w(u8 data); + void dst_id_w(u8 data); + template void cnt_w(u8 data); + void tst_w(u8 data); + + // state machine, interrupts and dma + void state_timer(s32 param); + int state_step(); + void update_int(); + void set_dreq(bool dreq); + private: - enum - { - STATE_IDLE, - STATE_DIAGNOSTIC_GOOD_PARITY, - STATE_DIAGNOSTIC_BAD_PARITY, - }; - - enum - { - DIAG_TURN_MISCOMPARE_INITIAL = 0x08, - DIAG_TURN_MISCOMPARE_FINAL = 0x10, - DIAG_TURN_GOOD_PARITY = 0x18, - DIAG_TURN_BAD_PARITY = 0x20, - DIAG_COMPLETE = 0x80, - - DIAG_COMPLETE_BIT = 7, - }; - - enum - { - INT_FUNC_COMPLETE = 0x01, - INT_INVALID_CMD = 0x40, - - INT_FUNC_COMPLETE_BIT = 0, - INT_INVALID_CMD_BIT = 6, - }; - - enum - { - AUX_STATUS_TC_ZERO = 0x02, - AUX_STATUS_PAUSED = 0x04, - AUX_STATUS_PARITY_ERR = 0x40, - AUX_STATUS_DATA_FULL = 0x80, - - AUX_STATUS_TC_ZERO_BIT = 1, - AUX_STATUS_PAUSED_BIT = 2, - AUX_STATUS_PARITY_ERR_BIT = 6, - AUX_STATUS_DATA_FULL_BIT = 7, - }; - - enum - { - CTRL_SELECT = 0x01, - CTRL_RESELECT = 0x02, - CTRL_PARITY = 0x04, - - CTRL_SELECT_BIT = 0, - CTRL_RESELECT_BIT = 1, - CTRL_PARITY_BIT = 2, - }; - devcb_write_line m_int; + devcb_write_line m_dreq; - uint32_t m_state; - uint8_t m_ctrl_reg; - uint8_t m_int_reg; - uint8_t m_aux_status_reg; - uint8_t m_diag_status_reg; + emu_timer *m_state_timer; + + // registers + u8 m_dat; + u8 m_cmd; + u8 m_ctl; + u8 m_dst_id; + u8 m_aux_status; + u8 m_own_id; + u8 m_int_status; + u8 m_src_id; + u8 m_dia_status; + u32 m_cnt; + + // other state + u32 m_state; + u8 m_xfi_phase; + u8 m_mode; + + bool m_int_state; + bool m_dreq_state; }; -// device type definition DECLARE_DEVICE_TYPE(NCR5385, ncr5385_device) #endif // MAME_MACHINE_NCR5385_H diff --git a/src/mame/skeleton/vp415.cpp b/src/mame/skeleton/vp415.cpp index 70befc79a6d..3f873fc4285 100644 --- a/src/mame/skeleton/vp415.cpp +++ b/src/mame/skeleton/vp415.cpp @@ -421,7 +421,7 @@ void vp415_state::z80_program_map(address_map &map) void vp415_state::z80_io_map(address_map &map) { map.global_mask(0xff); - map(0x00, 0x0f).rw(SCSI_TAG, FUNC(ncr5385_device::read), FUNC(ncr5385_device::write)); + map(0x00, 0x0f).m(SCSI_TAG, FUNC(ncr5385_device::map)); // 0x20, 0x21: Connected to A0 + D0..D7 of SLAVE i8041 map(0x34, 0x34).w(FUNC(vp415_state::sel34_w)); map(0x37, 0x37).r(FUNC(vp415_state::sel37_r)); diff --git a/src/mame/tektronix/tek440x.cpp b/src/mame/tektronix/tek440x.cpp index 2bb15f1fca4..26098b473d0 100644 --- a/src/mame/tektronix/tek440x.cpp +++ b/src/mame/tektronix/tek440x.cpp @@ -51,6 +51,8 @@ #include "machine/mos6551.h" // debug tty #include "machine/mc146818.h" #include "machine/mc68681.h" +#include "machine/nscsi_bus.h" +#include "bus/nscsi/hd.h" #include "machine/ncr5385.h" #include "tek410x_kbd.h" #include "sound/sn76496.h" @@ -72,6 +74,8 @@ public: m_duart(*this, "duart"), m_keyboard(*this, "keyboard"), m_snsnd(*this, "snsnd"), + m_rtc(*this, "rtc"), + m_scsi(*this, "scsi:7:ncr5385"), m_prom(*this, "maincpu"), m_mainram(*this, "mainram"), m_vram(*this, "vram"), @@ -115,6 +119,8 @@ private: required_device m_duart; required_device m_keyboard; required_device m_snsnd; + required_device m_rtc; + required_device m_scsi; required_region_ptr m_prom; required_shared_ptr m_mainram; required_shared_ptr m_vram; @@ -326,8 +332,8 @@ void tek440x_state::physical_map(address_map &map) // 7b6000-7b7fff: Mouse map(0x7b8000, 0x7b8003).mirror(0x100).rw("timer", FUNC(am9513_device::read16), FUNC(am9513_device::write16)); // 7ba000-7bbfff: MC146818 RTC - // 7bc000-7bdfff: SCSI bus address registers - map(0x7be000, 0x7be01f).mirror(0x1fe0).rw("scsic", FUNC(ncr5385_device::read), FUNC(ncr5385_device::write)).umask16(0xff00).cswidth(16); + map(0x7bc000, 0x7bc000).lw8([this](u8 data) { m_scsi->set_own_id(data & 7); }, "scsi_addr"); // 7bc000-7bdfff: SCSI bus address registers + map(0x7be000, 0x7be01f).m(m_scsi, FUNC(ncr5385_device::map)).umask16(0xff00); //.mirror(0x1fe0) .cswidth(16); } void tek440x_state::fdccpu_map(address_map &map) @@ -351,6 +357,11 @@ INPUT_PORTS_END * *************************************/ +static void scsi_devices(device_slot_interface &device) +{ + device.option_add("harddisk", NSCSI_HARDDISK); +} + void tek440x_state::tek4404(machine_config &config) { /* basic machine hardware */ @@ -391,9 +402,23 @@ void tek440x_state::tek4404(machine_config &config) AM9513(config, "timer", 40_MHz_XTAL / 4 / 10); // from CPU E output - //MC146818(config, "calendar", 32.768_MHz_XTAL); + MC146818(config, m_rtc, 32.768_MHz_XTAL); - NCR5385(config, "scsic", 40_MHz_XTAL / 4).irq().set_inputline(m_maincpu, M68K_IRQ_3); + NSCSI_BUS(config, "scsi"); + NSCSI_CONNECTOR(config, "scsi:0", scsi_devices, "harddisk"); + NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:3", scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:4", scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:5", scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5385", NCR5385).clock(40_MHz_XTAL / 4).machine_config( + [this](device_t *device) + { + ncr5385_device &adapter = downcast(*device); + + adapter.irq().set_inputline(m_maincpu, M68K_IRQ_3); + }); rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, nullptr)); rs232.rxd_handler().set("aica", FUNC(mos6551_device::write_rxd));