diff options
Diffstat (limited to 'src/devices/machine/ncr5380.cpp')
-rw-r--r-- | src/devices/machine/ncr5380.cpp | 925 |
1 files changed, 591 insertions, 334 deletions
diff --git a/src/devices/machine/ncr5380.cpp b/src/devices/machine/ncr5380.cpp index adc309f6b60..f0f5a73aa6c 100644 --- a/src/devices/machine/ncr5380.cpp +++ b/src/devices/machine/ncr5380.cpp @@ -1,420 +1,677 @@ // license:BSD-3-Clause -// copyright-holders:R. Belmont +// copyright-holders:Patrick Mackinlay + /* - * ncr5380.c - * - * NCR 5380 SCSI controller, as seen in many 680x0 Macs, - * official Apple add-on cards for the Apple II series, - * and probably some PC and Amiga cards as well. - * - * Emulation by R. Belmont. + * NCR 5380 and 53C80, aka Zilog Z5380, AMD Am5380, Sony CXD1180, National Semiconductor DP8490, Logic Devices L5380 and others. * - * References: - * Zilog 5380 manual - * "Inside Macintosh: Devices" (formerly online at http://www.manolium.org/dev/techsupport/insidemac/Devices/Devices-2.html ) - * - * NOTES: - * This implementation is tied closely to the drivers found in the Mac Plus ROM and the routines in Mac - * System 6 and 7 that it patches out the ROM traps with. While attempts have been made to - * have the behavior work according to the manual and not the specific Apple driver code, - * there are almost certainly areas where that is true. + * Sources: + * - http://bitsavers.org/components/ncr/scsi/SP-1051_NCR_5380-53C80_SCSI_Interface_Chip_Design_Manual_Mar86.pdf * + * TODO: + * - target mode + * - CXD1180 enhancements + * - DP8490 enhancements */ #include "emu.h" #include "ncr5380.h" -//#define VERBOSE 1 +#define LOG_REGW (1U << 1) +#define LOG_REGR (1U << 2) +#define LOG_SCSI (1U << 3) +#define LOG_ARB (1U << 4) +#define LOG_DMA (1U << 5) + +//#define VERBOSE (LOG_GENERAL|LOG_REGW|LOG_REGR|LOG_SCSI|LOG_ARB|LOG_DMA) #include "logmacro.h" +DEFINE_DEVICE_TYPE(NCR5380, ncr5380_device, "ncr5380", "NCR 5380 SCSI") +DEFINE_DEVICE_TYPE(NCR53C80, ncr53c80_device, "ncr53c80", "NCR 53C80 SCSI") +DEFINE_DEVICE_TYPE(CXD1180, cxd1180_device, "cxd1180", "Sony CXD1180") +DEFINE_DEVICE_TYPE(DP8490, dp8490_device, "dp8490", "National Semiconductor DP8490 EASI") -static const char *const rnames[] = -{ - "Current data", - "Initiator cmd", - "Mode", - "Target cmd", - "Bus status", - "Bus and status", - "Input data", - "Reset parity" -}; +ALLOW_SAVE_TYPE(ncr5380_device::state); -static const char *const wnames[] = +ncr5380_device::ncr5380_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock, bool has_lbs) + : nscsi_device(mconfig, type, tag, owner, clock) + , nscsi_slot_card_interface(mconfig, *this, DEVICE_SELF) + , m_irq_handler(*this) + , m_drq_handler(*this) + , m_has_lbs(has_lbs) { - "Output data", - "Initiator cmd", - "Mode", - "Target cmd", - "Select enable", - "Start DMA", - "DMA target", - "DMA initiator rec" -}; +} -// get the length of a SCSI command based on it's command byte type -static int get_cmd_len(int cbyte) +ncr5380_device::ncr5380_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) + : ncr5380_device(mconfig, NCR5380, tag, owner, clock) { - int group; +} - group = (cbyte>>5) & 7; +ncr53c80_device::ncr53c80_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) + : ncr5380_device(mconfig, NCR53C80, tag, owner, clock, true) +{ +} - if (group == 0) return 6; - if (group == 1 || group == 2) return 10; - if (group == 5) return 12; +cxd1180_device::cxd1180_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) + : ncr5380_device(mconfig, CXD1180, tag, owner, clock, true) +{ +} - fatalerror("NCR5380: Unknown SCSI command group %d\n", group); +dp8490_device::dp8490_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) + : ncr5380_device(mconfig, DP8490, tag, owner, clock, true) +{ +} - // never executed - //return 6; +void ncr5380_device::map(address_map &map) +{ + map(0x0, 0x0).rw(FUNC(ncr5380_device::csdata_r), FUNC(ncr5380_device::odata_w)); + map(0x1, 0x1).rw(FUNC(ncr5380_device::icmd_r), FUNC(ncr5380_device::icmd_w)); + map(0x2, 0x2).rw(FUNC(ncr5380_device::mode_r), FUNC(ncr5380_device::mode_w)); + map(0x3, 0x3).rw(FUNC(ncr5380_device::tcmd_r), FUNC(ncr5380_device::tcmd_w)); + map(0x4, 0x4).rw(FUNC(ncr5380_device::csstat_r), FUNC(ncr5380_device::selen_w)); + map(0x5, 0x5).rw(FUNC(ncr5380_device::bas_r), FUNC(ncr5380_device::sds_w)); + map(0x6, 0x6).rw(FUNC(ncr5380_device::idata_r), FUNC(ncr5380_device::sdtr_w)); + map(0x7, 0x7).rw(FUNC(ncr5380_device::rpi_r), FUNC(ncr5380_device::sdir_w)); } +void ncr5380_device::device_start() +{ + // Need to be cleared here so that set_irq/drq called from reset + // does not compare with uninitialized + m_irq_state = false; + m_drq_state = false; -//************************************************************************** -// LIVE DEVICE -//************************************************************************** + m_state_timer = timer_alloc(FUNC(ncr5380_device::state_timer), this); -DEFINE_DEVICE_TYPE(NCR5380, ncr5380_device, "ncr5380", "NCR 5380 SCSI") + save_item(NAME(m_state)); -//------------------------------------------------- -// ncr5380_device - constructor/destructor -//------------------------------------------------- + save_item(NAME(m_odata)); + save_item(NAME(m_icmd)); + save_item(NAME(m_mode)); + save_item(NAME(m_tcmd)); + save_item(NAME(m_bas)); + save_item(NAME(m_idata)); -ncr5380_device::ncr5380_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : - legacy_scsi_host_adapter(mconfig, NCR5380, tag, owner, clock), - m_irq_cb(*this) + save_item(NAME(m_scsi_ctrl)); + save_item(NAME(m_irq_state)); + save_item(NAME(m_drq_state)); +} + +void ncr5380_device::device_reset() { + m_state = IDLE; + + // clear registers + m_odata = 0; + m_icmd = 0; + m_mode = 0; + m_tcmd = 0; + m_bas = 0; + m_idata = 0; + + // clear scsi bus + scsi_bus->data_w(scsi_refid, 0); + scsi_bus->ctrl_w(scsi_refid, 0, S_ALL); + + // monitor all control lines + m_scsi_ctrl = 0; + scsi_bus->ctrl_wait(scsi_refid, S_ALL, S_ALL); + + // clear output lines + set_irq(false); + set_drq(false); } -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- +void ncr5380_device::scsi_ctrl_changed() +{ + u32 const ctrl = scsi_bus->ctrl_r(); -void ncr5380_device::device_start() + if (VERBOSE & LOG_SCSI) + { + static char const *const nscsi_phase[] = { "DATA OUT", "DATA IN", "COMMAND", "STATUS", "*", "*", "MESSAGE OUT", "MESSAGE IN" }; + + if (ctrl & S_RST) + LOGMASKED(LOG_SCSI, "scsi_ctrl_changed 0x%x BUS RESET\n", ctrl); + else if ((ctrl & S_BSY) && !(ctrl & S_SEL)) + LOGMASKED(LOG_SCSI, "scsi_ctrl_changed 0x%x 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_SCSI, "scsi_ctrl_changed 0x%x arbitration/selection\n", ctrl); + else + LOGMASKED(LOG_SCSI, "scsi_ctrl_changed 0x%x BUS FREE\n", ctrl); + } + + m_bas &= ~BAS_BUSYERROR; + + if (ctrl & S_RST) + { + LOG("scsi reset received\n"); + device_reset(); + + set_irq(true); + } + else if (!(m_mode & MODE_TARGET) && (m_scsi_ctrl & S_BSY) && !(ctrl & S_BSY)) + { + LOG("target disconnected\n"); + m_icmd &= (IC_RST | IC_AIP); + + if (m_mode & MODE_DMA) + { + // stop dma + m_mode &= ~MODE_DMA; + m_bas &= ~BAS_ENDOFDMA; + + set_drq(false); + } + + if (m_mode & MODE_BSYIRQ) + { + m_bas |= BAS_BUSYERROR; + + set_irq(true); + } + + m_state = IDLE; + m_state_timer->enable(false); + + // clear scsi bus + scsi_bus->data_w(scsi_refid, 0); + scsi_bus->ctrl_w(scsi_refid, 0, S_ALL); + } + else if (!(m_scsi_ctrl & S_REQ) && (ctrl & S_REQ)) + { + // target asserted REQ + if (m_mode & MODE_DMA) + { + if ((ctrl & S_PHASE_MASK) == (m_tcmd & TC_PHASE)) + { + // transfer cycle + if (m_state != IDLE && !m_state_timer->enabled()) + m_state_timer->adjust(attotime::zero); + } + else + { + LOG("phase mismatch %d != %d\n", (ctrl & S_PHASE_MASK), (m_tcmd & TC_PHASE)); + + m_state = IDLE; + m_state_timer->enable(false); + + set_drq(true); + set_irq(true); + } + } + } + + m_scsi_ctrl = ctrl; +} + +u8 ncr5380_device::csdata_r() { - legacy_scsi_host_adapter::device_start(); + u8 const data = scsi_bus->data_r(); + LOGMASKED(LOG_REGR, "csdata_r 0x%02x (%s)\n", data, machine().describe_context()); - memset(m_5380_Registers, 0, sizeof(m_5380_Registers)); - memset(m_5380_Data, 0, sizeof(m_5380_Data)); + return data; +} - m_next_req_flag = 0; - m_irq_cb.resolve_safe(); +void ncr5380_device::odata_w(u8 data) +{ + LOGMASKED(LOG_REGW, "odata_w 0x%02x (%s)\n", data, machine().describe_context()); + + // drive scsi data + if (m_icmd & IC_DBUS) + scsi_data_w(data); - save_item(NAME(m_5380_Registers)); - save_item(NAME(m_5380_Command)); - save_item(NAME(m_5380_Data)); - save_item(NAME(m_last_id)); - save_item(NAME(m_cmd_ptr)); - save_item(NAME(m_d_ptr)); - save_item(NAME(m_d_limit)); - save_item(NAME(m_next_req_flag)); + m_odata = data; } -//------------------------------------------------- -// device_reset - device-specific reset -//------------------------------------------------- +u8 ncr5380_device::icmd_r() +{ + LOGMASKED(LOG_REGR, "icmd_r 0x%02x (%s)\n", m_icmd, machine().describe_context()); -void ncr5380_device::device_reset() + return m_icmd; +} + +void ncr5380_device::icmd_w(u8 data) { - memset(m_5380_Registers, 0, sizeof(m_5380_Registers)); - memset(m_5380_Data, 0, sizeof(m_5380_Data)); + LOGMASKED(LOG_REGW, "icmd_w 0x%02x (%s)\n", data, machine().describe_context()); + + if (!(data & IC_RST)) + { + // drive scsi data + if ((data ^ m_icmd) & IC_DBUS) + scsi_data_w((data & IC_DBUS) ? m_odata : 0); + + // check for control line changes + if ((data & IC_PHASE) ^ (m_icmd & IC_PHASE)) + { + u32 const mask = (m_mode & MODE_TARGET) ? + (S_RST | S_BSY | S_SEL) : + (S_RST | S_ACK | S_BSY | S_SEL | S_ATN); + + // translate to nscsi + u32 const ctrl = + (data & IC_RST ? S_RST : 0) | + (data & IC_ACK ? S_ACK : 0) | + (data & IC_BSY ? S_BSY : 0) | + (data & IC_SEL ? S_SEL : 0) | + (data & IC_ATN ? S_ATN : 0); + + LOGMASKED(LOG_SCSI, "changing control lines 0x%04x\n", ctrl); + scsi_bus->ctrl_w(scsi_refid, ctrl, mask); + } + } + else + { + LOG("scsi reset issued\n"); + device_reset(); + scsi_bus->ctrl_w(scsi_refid, S_RST, S_RST); - m_next_req_flag = 0; - m_cmd_ptr = 0; - m_d_ptr = 0; - m_d_limit = 0; - m_last_id = 0; + set_irq(true); + } + + m_icmd = (m_icmd & ~IC_WRITE) | (data & IC_WRITE); } -//------------------------------------------------- -// device_stop - device-specific stop/shutdown -//------------------------------------------------- -void ncr5380_device::device_stop() +u8 ncr5380_device::mode_r() { + LOGMASKED(LOG_REGR, "mode_r 0x%02x (%s)\n", m_mode, machine().describe_context()); + + return m_mode; } -//------------------------------------------------- -// Public API -//------------------------------------------------- -uint8_t ncr5380_device::ncr5380_read_reg(uint32_t offset) +void ncr5380_device::mode_w(u8 data) { - int reg = offset & 7; - uint8_t rv; + LOGMASKED(LOG_REGW, "mode_w 0x%02x (%s)\n", data, machine().describe_context()); + + if (!(data & MODE_BSYIRQ)) + m_bas &= ~BAS_BUSYERROR; - switch( reg ) + // disable dma + if ((m_mode & MODE_DMA) && !(data & MODE_DMA)) { - case R5380_CURDATA: - case R5380_INPUTDATA: - rv = m_5380_Registers[reg]; + m_state = IDLE; + m_state_timer->enable(false); - // if we're in the data transfer phase or DMA, readback device data instead - if (((m_5380_Registers[R5380_BUSSTATUS] & 0x1c) == 0x04) || (m_5380_Registers[R5380_BUSSTATUS] & 0x40)) - { - rv = m_5380_Data[m_d_ptr]; - - // if the limit's less than 512, only read "limit" bytes - if (m_d_limit < 512) - { - if (m_d_ptr < (m_d_limit-1)) - { - m_d_ptr++; - } - else - { - m_next_req_flag = 1; - } - } - else - { - if (m_d_ptr < 511) - { - m_d_ptr++; - } - else - { - m_d_limit -= 512; - m_d_ptr = 0; - - m_next_req_flag = 1; - - // don't issue a "false" read - if (m_d_limit > 0) - { - read_data(m_5380_Data, (m_d_limit < 512) ? m_d_limit : 512); - } - else - { - // if this is DMA, signal DMA end - if (m_5380_Registers[R5380_BUSSTATUS] & 0x40) - { - m_5380_Registers[R5380_BUSSTATUS] |= 0x80; - } - - // drop /REQ - m_5380_Registers[R5380_BUSSTATUS] &= ~0x20; - - // clear phase match - m_5380_Registers[R5380_BUSANDSTAT] &= ~0x08; - } - } - } + m_bas &= ~BAS_ENDOFDMA; - } - break; + if (m_has_lbs) + m_tcmd &= ~TC_LBS; - default: - rv = m_5380_Registers[reg]; + set_drq(false); - // temporarily drop /REQ - if ((reg == R5380_BUSSTATUS) && (m_next_req_flag)) - { - rv &= ~0x20; - m_next_req_flag = 0; - } - break; + // clear ACK + scsi_bus->ctrl_w(scsi_refid, 0, S_ACK); + } + + // start/stop arbitration + if ((m_mode ^ data) & MODE_ARBITRATE) + { + if (data & MODE_ARBITRATE) + { + // start arbitration + m_icmd &= ~IC_LA; + m_state = ARB_BUS_FREE; + m_state_timer->adjust(attotime::zero); + } + else + { + // stop arbitration + m_state = IDLE; + m_icmd &= ~(IC_AIP | IC_LA); + } + } + + m_mode = data; +} + +u8 ncr5380_device::tcmd_r() +{ + LOGMASKED(LOG_REGR, "tcmd_r 0x%02x (%s)\n", m_tcmd, machine().describe_context()); + + return m_tcmd; +} + +void ncr5380_device::tcmd_w(u8 data) +{ + LOGMASKED(LOG_REGW, "tcmd_w 0x%02x (%s)\n", data, machine().describe_context()); + + if (m_has_lbs) + m_tcmd = (m_tcmd & TC_LBS) | (data & ~TC_LBS); + else + m_tcmd = data; +} + +u8 ncr5380_device::csstat_r() +{ + u32 const ctrl = scsi_bus->ctrl_r(); + u8 const data = + (ctrl & S_RST ? ST_RST : 0) | + (ctrl & S_BSY ? ST_BSY : 0) | + (ctrl & S_REQ ? ST_REQ : 0) | + (ctrl & S_MSG ? ST_MSG : 0) | + (ctrl & S_CTL ? ST_CD : 0) | + (ctrl & S_INP ? ST_IO : 0) | + (ctrl & S_SEL ? ST_SEL : 0); + + LOGMASKED(LOG_REGR, "csstat_r 0x%02x (%s)\n", data, machine().describe_context()); + return data; +} + +void ncr5380_device::selen_w(u8 data) +{ + LOGMASKED(LOG_REGW, "selen_w 0x%02x (%s)\n", data, machine().describe_context()); +} + +u8 ncr5380_device::bas_r() +{ + u32 const ctrl = scsi_bus->ctrl_r(); + u8 const data = m_bas | + (((ctrl & S_PHASE_MASK) == (m_tcmd & TC_PHASE)) ? BAS_PHASEMATCH : 0) | + (ctrl & S_ATN ? BAS_ATN : 0) | + (ctrl & S_ACK ? BAS_ACK : 0); + + LOGMASKED(LOG_REGR, "bas_r 0x%02x (%s)\n", data, machine().describe_context()); + + return data; +} + +void ncr5380_device::sds_w(u8 data) +{ + LOGMASKED(LOG_REGW, "sds_w 0x%02x (%s)\n", data, machine().describe_context()); + + if (m_mode & MODE_DMA) + { + m_state = DMA_OUT_DRQ; + m_state_timer->adjust(attotime::zero); } +} - LOG("%s NCR5380: read %s (reg %d) = %02x\n", machine().describe_context(), rnames[reg], reg, rv); +u8 ncr5380_device::idata_r() +{ + LOGMASKED(LOG_REGR, "idata_r 0x%02x (%s)\n", m_idata, machine().describe_context()); + + return m_idata; +} - return rv; +void ncr5380_device::sdtr_w(u8 data) +{ + LOGMASKED(LOG_REGW, "sdtr_w 0x%02x (%s)\n", data, machine().describe_context()); } -void ncr5380_device::ncr5380_write_reg(uint32_t offset, uint8_t data) +u8 ncr5380_device::rpi_r() { - int reg = offset & 7; + LOGMASKED(LOG_REGR, "rpi_r (%s)\n", machine().describe_context()); + + m_bas &= ~(BAS_PARITYERROR | BAS_BUSYERROR); + set_irq(false); - LOG("%s NCR5380: %02x to %s (reg %d)\n", machine().describe_context(), data, wnames[reg], reg); + return 0; +} - switch( reg ) +void ncr5380_device::sdir_w(u8 data) +{ + LOGMASKED(LOG_REGW, "sdir_w 0x%02x (%s)\n", data, machine().describe_context()); + + if ((m_mode & MODE_DMA) && !(m_mode & MODE_TARGET)) { - case R5380_OUTDATA: - // if we're in the command phase, collect the command bytes - if ((m_5380_Registers[R5380_BUSSTATUS] & 0x1c) == 0x08) - { - m_5380_Command[m_cmd_ptr++] = data; - } + m_state = DMA_IN_REQ; + m_state_timer->adjust(attotime::zero); + } +} - // if we're in the select phase, this is the target id - if (m_5380_Registers[R5380_INICOMMAND] == 0x04) - { - data &= 0x7f; // clear the high bit - if (data == 0x40) - { - m_last_id = 6; - } - else if (data == 0x20) - { - m_last_id = 5; - } - else if (data == 0x10) - { - m_last_id = 4; - } - else if (data == 0x08) - { - m_last_id = 3; - } - else if (data == 0x04) - { - m_last_id = 2; - } - else if (data == 0x02) - { - m_last_id = 1; - } - else if (data == 0x01) - { - m_last_id = 0; - } - } +void ncr5380_device::state_timer(s32 param) +{ + // step state machine + int const delay = state_step(); - // if this is a write, accumulate accordingly - if (((m_5380_Registers[R5380_BUSSTATUS] & 0x1c) == 0x00) && (m_5380_Registers[R5380_INICOMMAND] == 1)) - { - m_5380_Data[m_d_ptr] = data; - - // if we've hit a sector, flush - if (m_d_ptr == 511) - { - write_data(&m_5380_Data[0], 512); - - m_d_limit -= 512; - m_d_ptr = 0; - - // no more data? set DMA END flag - if (m_d_limit <= 0) - { - m_5380_Registers[R5380_BUSANDSTAT] = 0xc8; - } - } - else - { - m_d_ptr++; - } - - // make sure we don't upset the status readback - data = 0; - } - break; + // check for data stall + if (delay < 0) + return; - case R5380_INICOMMAND: - if (data == 0) // dropping the bus - { - // make sure it's not busy - m_5380_Registers[R5380_BUSSTATUS] &= ~0x40; - - // are we in the command phase? - if ((m_5380_Registers[R5380_BUSSTATUS] & 0x1c) == 0x08) - { - // is the current command complete? - if (get_cmd_len(m_5380_Command[0]) == m_cmd_ptr) - { - LOG("%s NCR5380: Command (to ID %d): %x %x %x %x %x %x %x %x %x %x\n", machine().describe_context(), m_last_id, m_5380_Command[0], m_5380_Command[1], m_5380_Command[2], m_5380_Command[3], m_5380_Command[4], m_5380_Command[5], m_5380_Command[6], m_5380_Command[7], m_5380_Command[8], m_5380_Command[9]); - - send_command(&m_5380_Command[0], 16); - m_d_limit = get_length(); - - LOG("NCR5380: Command returned %d bytes\n", m_d_limit); - - m_d_ptr = 0; - - // is data available? - if (m_d_limit > 0) - { - // make sure for transfers under 512 bytes that we always pad with a zero - if (m_d_limit < 512) - { - m_5380_Data[m_d_limit] = 0; - } - - // read back the amount available, or 512 bytes, whichever is smaller - read_data(m_5380_Data, (m_d_limit < 512) ? m_d_limit : 512); - - // raise REQ to indicate data is available - m_5380_Registers[R5380_BUSSTATUS] |= 0x20; - } - } - } + // repeat until idle + if (m_state != IDLE) + m_state_timer->adjust(attotime::from_nsec(delay)); +} - } +int ncr5380_device::state_step() +{ + u32 const ctrl = scsi_bus->ctrl_r(); + int delay = 0; - if (data == 5) // want the bus? + switch (m_state) + { + case IDLE: + break; + + case ARB_BUS_FREE: + if (!(ctrl & (S_SEL | S_BSY | S_RST))) + { + LOGMASKED(LOG_ARB, "arbitration: bus free\n"); + // FIXME: AIP should only be set when arbitration begins + m_icmd |= IC_AIP; + m_state = ARB_START; + delay = 1700; + } + else + { + LOGMASKED(LOG_ARB, "arbitration: bus not free\n"); + m_state = IDLE; + } + break; + case ARB_START: + LOGMASKED(LOG_ARB, "arbitration: started\n"); + m_icmd |= IC_BSY; + m_state = ARB_EVALUATE; + delay = 2200; + + // assert own ID and BSY + scsi_bus->data_w(scsi_refid, m_odata); + 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() & ~((m_odata - 1) | m_odata))) + { + LOGMASKED(LOG_ARB, "arbitration: lost\n"); + m_icmd &= ~IC_BSY; + m_icmd |= IC_LA; + + m_state = IDLE; + + // clear data and BSY + scsi_bus->data_w(scsi_refid, 0); + scsi_bus->ctrl_w(scsi_refid, 0, S_BSY); + } + else + { + LOGMASKED(LOG_ARB, "arbitration: won\n"); + m_state = IDLE; + } + break; + + case DMA_IN_REQ: + if (ctrl & S_REQ) + { + if ((ctrl & S_PHASE_MASK) == (m_tcmd & TC_PHASE)) { - // if the device exists, make the bus busy. - // otherwise don't. - - if (select(m_last_id)) - { - LOG("NCR5380: Giving the bus for ID %d\n", m_last_id); - m_5380_Registers[R5380_BUSSTATUS] |= 0x40; - } - else - { - LOG("NCR5380: Rejecting the bus for ID %d\n", m_last_id); - m_5380_Registers[R5380_BUSSTATUS] &= ~0x40; - } - } + m_idata = scsi_bus->data_r(); + LOGMASKED(LOG_DMA, "dma in: 0x%02x\n", m_idata); - if (data == 1) // data bus (prelude to command?) - { - // raise REQ - m_5380_Registers[R5380_BUSSTATUS] |= 0x20; - } + m_state = DMA_IN_ACK; + set_drq(true); - if (data & 0x10) // ACK drops REQ - { - // drop REQ - m_5380_Registers[R5380_BUSSTATUS] &= ~0x20; + // assert ACK + scsi_bus->ctrl_w(scsi_refid, S_ACK, S_ACK); } - break; - case R5380_MODE: - if (data == 2) // DMA + delay = -1; + } + break; + case DMA_IN_ACK: + if (!(ctrl & S_REQ)) + { + m_state = (m_bas & BAS_ENDOFDMA) ? IDLE : DMA_IN_REQ; + + // clear ACK + scsi_bus->ctrl_w(scsi_refid, 0, S_ACK); + } + break; + + case DMA_OUT_DRQ: + m_state = DMA_OUT_REQ; + set_drq(true); + + delay = -1; + break; + case DMA_OUT_REQ: + if (ctrl & S_REQ) + { + if ((ctrl & S_PHASE_MASK) == (m_tcmd & TC_PHASE)) { - // put us in DMA mode - m_5380_Registers[R5380_BUSANDSTAT] |= 0x40; - } + LOGMASKED(LOG_DMA, "dma out: 0x%02x\n", m_odata); - if (data == 1) // arbitrate? - { - m_5380_Registers[R5380_INICOMMAND] |= 0x40; // set arbitration in progress - m_5380_Registers[R5380_INICOMMAND] &= ~0x20; // clear "lost arbitration" - } + m_state = DMA_OUT_ACK; - if (data == 0) + // assert data and ACK + scsi_bus->data_w(scsi_refid, m_odata); + scsi_bus->ctrl_w(scsi_refid, S_ACK, S_ACK); + } + } + break; + case DMA_OUT_ACK: + if (!(ctrl & S_REQ)) + { + if (m_bas & BAS_ENDOFDMA) { - // drop DMA mode - m_5380_Registers[R5380_BUSANDSTAT] &= ~0x40; + m_state = IDLE; + + if (m_has_lbs) + m_tcmd |= TC_LBS; } - break; + else + m_state = DMA_OUT_DRQ; + + // clear data and ACK + scsi_bus->data_w(scsi_refid, 0); + scsi_bus->ctrl_w(scsi_refid, 0, S_ACK); + } + break; + } - case R5380_TARGETCMD: - // sync the bus phase with what was just written - m_5380_Registers[R5380_BUSSTATUS] &= ~0x1c; - m_5380_Registers[R5380_BUSSTATUS] |= (data & 7)<<2; + return delay; +} - // and set the "phase match" flag - m_5380_Registers[R5380_BUSANDSTAT] |= 0x08; +void ncr5380_device::eop_w(int state) +{ + LOGMASKED(LOG_DMA, "eop_w %d\n", state); + if (state && (m_mode & MODE_DMA)) + { + m_bas |= BAS_ENDOFDMA; - // and set /REQ - m_5380_Registers[R5380_BUSSTATUS] |= 0x20; + if (m_mode & MODE_EOPIRQ) + { + // FIXME: should only trigger when combined with dma_r/dma_w + LOG("eop irq asserted\n"); - // if we're entering the command phase, start accumulating the data - if ((m_5380_Registers[R5380_BUSSTATUS] & 0x1c) == 0x08) - { - m_cmd_ptr = 0; - } - break; + set_irq(true); + } + } +} + +void ncr5380_device::dma_w(u8 data) +{ + set_drq(false); + + if (m_mode & MODE_DMA) + { + m_odata = data; - default: - break; + m_state_timer->adjust(attotime::zero); } +} + +u8 ncr5380_device::dma_r() +{ + set_drq(false); + + if (m_mode & MODE_DMA) + m_state_timer->adjust(attotime::zero); + + return m_idata; +} - m_5380_Registers[reg] = data; +void ncr5380_device::scsi_data_w(u8 data) +{ + // TODO: release data bus when any of the prerequisite conditions expire + u32 const ctrl = scsi_bus->ctrl_r(); + + if ((m_mode & MODE_TARGET) || (!(ctrl & S_INP) && (ctrl & S_PHASE_MASK) == (m_tcmd & S_PHASE_MASK))) + { + LOGMASKED(LOG_SCSI, "scsi data 0x%02x\n", data); + scsi_bus->data_w(scsi_refid, data); + } +} - // note: busandstat overlaps startdma, so we need to do this here! - if (reg == R5380_STARTDMA) +void ncr5380_device::set_irq(bool irq_state) +{ + if (irq_state != m_irq_state) + { + LOG("set_irq %d\n", irq_state); + + if (irq_state) + m_bas |= BAS_IRQACTIVE; + else + m_bas &= ~BAS_IRQACTIVE; + + m_irq_state = irq_state; + m_irq_handler(m_irq_state); + } +} + +void ncr5380_device::set_drq(bool drq_state) +{ + if (drq_state != m_drq_state) + { + LOGMASKED(LOG_DMA, "set_drq %d\n", drq_state); + + if (drq_state) + m_bas |= BAS_DMAREQUEST; + else + m_bas &= ~BAS_DMAREQUEST; + + m_drq_state = drq_state; + m_drq_handler(m_drq_state); + } +} + +u8 ncr5380_device::read(offs_t offset) +{ + switch (offset & 7) + { + case 0: return csdata_r(); + case 1: return icmd_r(); + case 2: return mode_r(); + case 3: return tcmd_r(); + case 4: return csstat_r(); + case 5: return bas_r(); + case 6: return idata_r(); + case 7: return rpi_r(); + } + + // can't happen + return 0; +} + +void ncr5380_device::write(offs_t offset, u8 data) +{ + switch (offset & 7) { - m_5380_Registers[R5380_BUSANDSTAT] = 0x48; + case 0: odata_w(data); break; + case 1: icmd_w(data); break; + case 2: mode_w(data); break; + case 3: tcmd_w(data); break; + case 4: selen_w(data); break; + case 5: sds_w(data); break; + case 6: sdtr_w(data); break; + case 7: sdir_w(data); break; } } |