summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/ncr5380.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/ncr5380.cpp')
-rw-r--r--src/devices/machine/ncr5380.cpp925
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;
}
}