// license:BSD-3-Clause
// copyright-holders:Philip Bennett
/*********************************************************************
53c7xx.c
NCR 53C700 SCSI I/O Processor
TODO:
* Low-level register accesses
* Remove arbitrary delays
* Add unimplemented SCRIPTS opcodes
*********************************************************************/
#include "emu.h"
#include "53c7xx.h"
//**************************************************************************
// DEBUGGERY
//**************************************************************************
#define DEBUG_LOG 1
#define DEBUG_LEVEL 0
#if DEBUG_LOG
#define VERBOSE_LOG(machine, level, ...) verbose_log(machine, level, __VA_ARGS__)
#else
#define VERBOSE_LOG(machine, level, ...)
#endif
static void ATTR_PRINTF(3,4) verbose_log(running_machine &machine, int level, const char* format, ...)
{
if (level <= DEBUG_LEVEL)
{
char buffer[32768];
va_list v;
va_start(v, format);
vsprintf(buffer, format, v);
va_end(v);
printf("53C7XX (%s): %s", machine.describe_context(), buffer);
}
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
const device_type NCR53C7XX = &device_creator<ncr53c7xx_device>;
//-------------------------------------------------
// ncr53c7xx_device - constructor/destructor
//-------------------------------------------------
ncr53c7xx_device::ncr53c7xx_device(const machine_config &mconfig, std::string tag, device_t *owner, UINT32 clock)
: nscsi_device(mconfig, NCR53C7XX, "53C7xx SCSI", tag, owner, clock, "ncr537xx", __FILE__),
device_execute_interface(mconfig, *this),
m_icount(0),
m_irq_handler(*this),
m_host_write(*this),
m_host_read(*this)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void ncr53c7xx_device::device_start()
{
// set our instruction counter
m_icountptr = &m_icount;
// resolve line callbacks
m_irq_handler.resolve_safe();
m_host_read.resolve_safe(0);
m_host_write.resolve_safe();
m_tm = timer_alloc(0);
// The SCRIPTS processor runs at ~2 MIPS so approximate this
set_unscaled_clock(2000000);
// savestate support
save_item(NAME(m_scntl));
save_item(NAME(m_sdid));
save_item(NAME(m_sien));
save_item(NAME(m_scid));
save_item(NAME(m_sxfer));
save_item(NAME(m_sodl));
save_item(NAME(m_socl));
save_item(NAME(m_sfbr));
save_item(NAME(m_sidl));
save_item(NAME(m_sbdl));
save_item(NAME(m_sbcl));
save_item(NAME(m_dstat));
save_item(NAME(m_sstat));
save_item(NAME(m_ctest));
save_item(NAME(m_temp));
save_item(NAME(m_dfifo));
save_item(NAME(m_istat));
save_item(NAME(m_dbc));
save_item(NAME(m_dcmd));
save_item(NAME(m_dnad));
save_item(NAME(m_dsp));
save_item(NAME(m_dsps));
save_item(NAME(m_dmode));
save_item(NAME(m_dien));
save_item(NAME(m_dwt));
save_item(NAME(m_dcntl));
// other state
save_item(NAME(m_scsi_state));
save_item(NAME(m_connected));
save_item(NAME(m_finished));
save_item(NAME(m_last_data));
save_item(NAME(m_xfr_phase));
save_item(NAME(m_scripts_state));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void ncr53c7xx_device::device_reset()
{
// Reset registers to defaults
m_scntl[0] = 3 << SCNTL0_ARB_SHIFT;
m_scntl[1] = 0;
m_sdid = 0;
m_sien = 0;
m_scid = 0;
m_sxfer = 0;
m_sodl = 0;
m_socl = 0;
m_sfbr = 0;
m_sidl = 0;
m_sbdl = 0;
m_sbcl = 0;
m_dstat = DSTAT_DFE;
m_sstat[0] = 0;
m_sstat[1] = 0;
m_sstat[2] = 0;
m_ctest[0] = 0;
m_ctest[1] = 0xf0;
m_ctest[2] = 0x21;
m_ctest[3] = 0;
m_ctest[4] = 0;
m_ctest[5] = 0;
m_ctest[6] = 0;
m_ctest[7] = 0;
m_dfifo = 0;
m_istat = 0;//ISTAT_PRE;
m_dmode = 0;
m_dien = 0;
m_dcntl = 0;
m_finished = false;
m_connected = false;
scsi_bus->ctrl_wait(scsi_refid, S_SEL | S_BSY | S_RST, S_ALL);
set_scripts_state(SCRIPTS_IDLE);
set_scsi_state(IDLE);
m_irq_handler(CLEAR_LINE);
}
//**************************************************************************
// MEMORY HANDLERS
//**************************************************************************
//-------------------------------------------------
// read - Host read handler
//-------------------------------------------------
READ32_MEMBER( ncr53c7xx_device::read )
{
VERBOSE_LOG(machine(), 1, "REG R: [%x] (%08X)\n", offset, mem_mask);
UINT32 ret = 0;
switch (offset)
{
case 0x0:
{
if (ACCESSING_BITS_0_7)
{
ret = m_scntl[0];
}
if (ACCESSING_BITS_8_15)
{
ret |= m_scntl[1] << 8;
}
if (ACCESSING_BITS_16_23)
{
ret |= m_sdid << 16;
}
if (ACCESSING_BITS_24_31)
{
ret |= m_sien << 24;
}
break;
}
case 0x1:
{
if (ACCESSING_BITS_0_7)
{
ret = m_scid;
}
if (ACCESSING_BITS_8_15)
{
ret |= m_sxfer << 8;
}
if (ACCESSING_BITS_16_23)
{
ret |= m_sodl << 16;
}
if (ACCESSING_BITS_24_31)
{
ret |= m_socl << 24;
}
break;
}
case 0x2:
{
if (ACCESSING_BITS_0_7)
{
ret = m_sfbr;
}
if (ACCESSING_BITS_8_15)
{
ret |= m_sidl << 8;
}
if (ACCESSING_BITS_16_23)
{
ret |= m_sbdl << 16;
}
if (ACCESSING_BITS_24_31)
{
ret |= m_sbcl << 24;
}
break;
}
case 0x3:
{
if (ACCESSING_BITS_0_7)
{
ret = m_dstat;
m_dstat = 0;
update_irqs();
}
if (ACCESSING_BITS_8_15)
{
ret |= m_sstat[0] << 8;
m_sstat[0] = 0;
update_irqs();
}
if (ACCESSING_BITS_16_23)
{
ret |= m_sstat[1] << 16;
}
if (ACCESSING_BITS_24_31)
{
ret |= m_sstat[2] << 24;
}
break;
}
case 0x5:
{
if (ACCESSING_BITS_0_7)
{
ret = m_ctest[0];
}
if (ACCESSING_BITS_8_15)
{
ret |= m_ctest[1] << 8;
}
if (ACCESSING_BITS_16_23)
{
ret |= m_ctest[2] << 16;
}
if (ACCESSING_BITS_24_31)
{
ret |= m_ctest[3] << 24;
}
break;
}
case 0x6:
{
if (ACCESSING_BITS_0_7)
{
ret = m_ctest[4];
}
if (ACCESSING_BITS_8_15)
{
ret |= m_ctest[5] << 8;
}
if (ACCESSING_BITS_16_23)
{
ret |= m_ctest[6] << 16;
}
if (ACCESSING_BITS_24_31)
{
ret |= m_ctest[7] << 24;
}
break;
}
case 0x7:
{
ret = m_temp;
break;
}
case 0x8:
{
if (ACCESSING_BITS_0_7)
{
ret = m_dfifo;
}
if (ACCESSING_BITS_8_15)
{
ret |= m_istat << 8;
}
break;
}
case 0x9:
{
if (ACCESSING_BITS_0_7 || ACCESSING_BITS_8_15 || ACCESSING_BITS_16_23)
{
ret = m_dbc;
}
if (ACCESSING_BITS_24_31)
{
ret |= m_dcmd << 24;
}
break;
}
case 0xa:
{
ret = m_dnad;
break;
}
case 0xb:
{
ret = m_dsp;
break;
}
case 0xc:
{
ret = m_dsps;
break;
}
case 0xd:
{
if (ACCESSING_BITS_0_7)
{
ret = m_dmode;
}
break;
}
case 0xe:
{
if (ACCESSING_BITS_8_15)
{
ret = m_dien << 8;
}
if (ACCESSING_BITS_16_23)
{
ret |= m_dwt << 16;
}
if (ACCESSING_BITS_24_31)
{
ret |= m_dcntl << 24;
}
break;
}
default:
{
VERBOSE_LOG(machine(), 0, "Unhandled register access");
}
}
return ret;
}
//-------------------------------------------------
// write - Host write handler
//-------------------------------------------------
WRITE32_MEMBER( ncr53c7xx_device::write )
{
VERBOSE_LOG(machine(), 1, "REG W: [%x] (%08X) %x\n", offset, mem_mask, data);
switch (offset)
{
case 0x0:
{
if (ACCESSING_BITS_0_7)
{
m_scntl[0] = data;
if (data & SCNTL0_TRG)
fatalerror("53c7xx: Target mode unsupported!");
if (data & SCNTL0_START)
{
// Start arbitration
set_scsi_state(ARBITRATE_WAIT_FREE);
step(true);
}
}
if (ACCESSING_BITS_8_15)
{
m_scntl[1] = data >> 8;
}
if (ACCESSING_BITS_16_23)
{
m_sdid = data >> 16;
}
if (ACCESSING_BITS_24_31)
{
m_sien = data >> 24;
}
break;
}
case 0x1:
{
if (ACCESSING_BITS_0_7)
{
m_scid = data;
}
if (ACCESSING_BITS_8_15)
{
m_sxfer = data >> 8;
}
if (ACCESSING_BITS_16_23)
{
m_sodl = data >> 16;
}
if (ACCESSING_BITS_24_31)
{
m_socl = data >> 24;
}
break;
}
case 0x6:
{
if (ACCESSING_BITS_0_7)
{
m_ctest[4] = data;
}
if (ACCESSING_BITS_8_15)
{
m_ctest[5] = data >> 8;
}
if (ACCESSING_BITS_16_23)
{
m_ctest[6] = data >> 16;
}
if (ACCESSING_BITS_24_31)
{
m_ctest[7] = data >> 24;
}
break;
}
case 0x7:
{
m_temp = data;
break;
}
case 0x8:
{
if (ACCESSING_BITS_0_7)
{
m_dfifo = data;
}
if (ACCESSING_BITS_8_15)
{
m_istat = data >> 8;
}
break;
}
case 0x9:
{
if (ACCESSING_BITS_0_7 || ACCESSING_BITS_8_15 || ACCESSING_BITS_16_23)
{
m_dbc = data & 0xffffff;
}
if (ACCESSING_BITS_24_31)
{
m_dcmd = data >> 24;
}
break;
}
case 0xa:
{
m_dnad = data;
break;
}
case 0xb:
{
// Write to the upper byte starts the fetch
m_dsp = data;
if (m_dmode & 1)
{
set_scripts_state(SCRIPTS_WAIT_MANUAL_START);
}
else
{
set_scripts_state(SCRIPTS_FETCH);
}
break;
}
case 0xc:
{
m_dsps = data;
break;
}
case 0xd:
{
if (ACCESSING_BITS_0_7)
{
m_dmode = data;
}
break;
}
case 0xe:
{
if (ACCESSING_BITS_8_15)
{
m_dien = data >> 8;
}
if (ACCESSING_BITS_16_23)
{
m_dwt = data >> 16;
if (m_dwt)
fatalerror("53c7xx: DMA Watchdog Timer enabled!");
}
if (ACCESSING_BITS_24_31)
{
m_dcntl = data >> 24;
// Note: not self-clearing
if (m_dcntl & 1) // RST
{
device_reset();
}
else if (m_dcntl & 2) // STD
{
// Only applies to these modes:
// * Manual Start
// * Single Step
// * Pipeline
fatalerror("53c7xx: Start DMA");
}
else if (m_dcntl & 4)
{
fatalerror("53c7xx: SCSI Low-Level Mode not supported!");
}
// TODO: Update clocking
}
break;
}
default:
{
VERBOSE_LOG(machine(), 0, "Unhandled register access");
}
}
}
//**************************************************************************
// SCSI STATE MACHINE
//**************************************************************************
//-------------------------------------------------
// update_irqs -
//-------------------------------------------------
void ncr53c7xx_device::update_irqs()
{
if (m_sstat[0] & m_sien)
m_istat |= ISTAT_SIP;
else
m_istat &= ~ISTAT_SIP;
if (m_dstat & m_dien)
m_istat |= ISTAT_DIP;
else
m_istat &= ~ISTAT_DIP;
m_irq_handler(m_istat ? ASSERT_LINE : CLEAR_LINE);
}
//-------------------------------------------------
// set_scsi_state - change SCSI state
//-------------------------------------------------
void ncr53c7xx_device::set_scsi_state(int state)
{
VERBOSE_LOG(machine(), 2, "SCSI state change: %x to %x\n", m_scsi_state, state);
m_scsi_state = state;
}
//-------------------------------------------------
// delay - step the SCSI state machine following
// a time delay
//-------------------------------------------------
void ncr53c7xx_device::delay(const attotime &delay)
{
m_tm->adjust(delay);
}
//-------------------------------------------------
// scsi_ctrl_changed - callback from nscsi_device
//-------------------------------------------------
void ncr53c7xx_device::scsi_ctrl_changed()
{
step(false);
}
//-------------------------------------------------
// send_byte - send data to a SCSI device
//-------------------------------------------------
void ncr53c7xx_device::send_byte()
{
if (m_dbc == 0)
fatalerror("53C7XX: send_byte() - DBC should not be 0\n");
set_scsi_state( (m_scsi_state & STATE_MASK) | (SEND_WAIT_SETTLE << SUB_SHIFT) );
UINT32 data = m_host_read(m_dnad & ~3, 0xffffffff);
data = data >> ((m_dnad & 3) * 8) & 0xff;
++m_dnad;
--m_dbc;
scsi_bus->data_w(scsi_refid, data);
scsi_bus->ctrl_w(scsi_refid, S_ACK, S_ACK);
scsi_bus->ctrl_wait(scsi_refid, S_REQ, S_REQ);
delay(attotime::from_nsec(5));
}
//-------------------------------------------------
// recv_byte - receive data from a SCSI device
//-------------------------------------------------
void ncr53c7xx_device::recv_byte()
{
scsi_bus->ctrl_wait(scsi_refid, S_REQ, S_REQ);
set_scsi_state( (m_scsi_state & STATE_MASK) | (RECV_WAIT_REQ_1 << SUB_SHIFT) );
step(false);
}
//-------------------------------------------------
// device_timer - callback to step the SCSI
// state machine
//-------------------------------------------------
void ncr53c7xx_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
step(true);
}
//-------------------------------------------------
// step - advance the SCSI state machine
//-------------------------------------------------
void ncr53c7xx_device::step(bool timeout)
{
UINT32 ctrl = scsi_bus->ctrl_r();
UINT32 data = scsi_bus->data_r();
VERBOSE_LOG(machine(), 2, "Step: CTRL:%x DATA:%x (%d.%d) Timeout:%d\n", ctrl, data, m_scsi_state & STATE_MASK, m_scsi_state >> SUB_SHIFT, timeout);
// Check for disconnect from target
if (!(m_scntl[0] & SCNTL0_TRG) && m_connected && !(ctrl & S_BSY))
{
set_scsi_state(FINISHED);
m_connected = false;
// istatus |= I_DISCONNECT;
// reset_disconnect();
// check_irq();
}
switch (m_scsi_state & SUB_MASK ? m_scsi_state & SUB_MASK : m_scsi_state & STATE_MASK)
{
case IDLE:
{
break;
}
case FINISHED:
{
m_finished = true;
set_scsi_state(IDLE);
step(true);
break;
}
case ARBITRATE_WAIT_FREE:
{
if (!timeout)
break;
// Is the bus free?
if (ctrl & (S_BSY | S_SEL))
{
// Keep trying until it is
delay(attotime::from_nsec(800));
}
else
{
// Bus is free; next phase
delay(attotime::from_nsec(800));
set_scsi_state(ARBITRATE_CHECK_FREE);
}
break;
}
case ARBITRATE_CHECK_FREE:
{
if ((ctrl & (S_BSY | S_SEL)) == 0)
{
// Bus is free - assert the controller SCSI ID and BUSY
scsi_bus->ctrl_w(scsi_refid, S_BSY, S_BSY);
if (((m_scntl[0] >> SCNTL0_ARB_SHIFT) & SCNTL0_ARB_MASK) == 3)
{
// Full arbitration
scsi_bus->data_w(scsi_refid, m_scid);
}
else
{
// Simple arbitration
scsi_bus->data_w(scsi_refid, m_sodl);
}
set_scsi_state(ARBITRATE_EXAMINE_BUS);
delay(attotime::from_nsec(2400));
}
break;
}
case ARBITRATE_EXAMINE_BUS:
{
if (!timeout)
break;
if (ctrl & S_SEL)
{
scsi_bus->ctrl_w(scsi_refid, 0, S_BSY);
scsi_bus->data_w(scsi_refid, 0);
if (((m_scntl[0] >> SCNTL0_ARB_SHIFT) & SCNTL0_ARB_MASK) == 3)
{
// Try again
set_scsi_state(ARBITRATE_WAIT_FREE);
delay(attotime::from_nsec(2400));
}
else
{
// TODO: Is this right?
m_sstat[1] |= SSTAT1_LOA;
m_scntl[0] &= ~SCNTL0_START;
m_sstat[0] |= SSTAT0_CMP;
update_irqs();
set_scsi_state(FINISHED);
step(true);
}
break;
}
// Full arbitration?
if (((m_scntl[0] >> SCNTL0_ARB_SHIFT) & SCNTL0_ARB_MASK) == 3)
{
int win;
for (win = 7; win >=0 && !(data & (1 << win)); win--) {};
if ((1 << win) != m_scid)
{
scsi_bus->data_w(scsi_refid, 0);
scsi_bus->ctrl_w(scsi_refid, 0, S_ALL);
delay(attotime::from_nsec(2400));
break;
}
// Begin the select phase; assert SEL
m_sstat[1] |= SSTAT1_WOA;
scsi_bus->ctrl_w(scsi_refid, S_SEL, S_SEL);
set_scsi_state(ARBITRATE_ASSERT_SEL);
delay(attotime::from_nsec(1200));
}
else
{
// TODO: Worth adding another state here?
m_sstat[0] |= SSTAT0_CMP;
m_scntl[0] &= ~SCNTL0_START;
update_irqs();
set_scsi_state(FINISHED);
step(true);
}
break;
}
case ARBITRATE_ASSERT_SEL:
{
if (!timeout)
break;
// Activate data line of the thing
scsi_bus->data_w(scsi_refid, m_sdid);
set_scsi_state(ARBITRATE_SELECT_DEST);
delay(attotime::from_nsec(2));
break;
}
case ARBITRATE_SELECT_DEST:
{
if (!timeout)
break;
scsi_bus->ctrl_w(scsi_refid, m_scntl[0] & SCNTL0_WATN ? S_ATN : 0, S_ATN | S_BSY);
set_scsi_state(ARBITRATE_RELEASE_BSY);
delay(attotime::from_nsec(20));
break;
}
case ARBITRATE_RELEASE_BSY:
{
if (!timeout)
break;
set_scsi_state(ARBITRATE_DESKEW_WAIT);
delay(attotime::from_nsec(500));
break;
}
case ARBITRATE_DESKEW_WAIT:
{
if (!timeout)
break;
// Clear everything
scsi_bus->data_w(scsi_refid, 0);
scsi_bus->ctrl_w(scsi_refid, 0, S_SEL);
// Done?
m_sstat[0] |= SSTAT0_CMP;
m_scntl[0] &= ~SCNTL0_START;
//update_irqs();
set_scsi_state(FINISHED);
m_connected = true;
step(true);
break;
}
// Note this is actually block transfers
case INIT_XFER:
{
if (ctrl & S_INP)
{
set_scsi_state(m_dbc ? INIT_XFER_RECV_BYTE_ACK : INIT_XFER_RECV_BYTE_NACK);
recv_byte();
}
else
{
if (m_dbc == 1)
scsi_bus->ctrl_w(scsi_refid, 0, S_ATN);
set_scsi_state(INIT_XFER_SEND_BYTE);
send_byte();
}
break;
}
case INIT_XFER_SEND_BYTE:
{
if (m_dbc == 0)
{
set_scsi_state(FINISHED);
step(true);
}
else
{
set_scsi_state(INIT_XFER_WAIT_REQ);
}
break;
}
case INIT_XFER_RECV_BYTE_ACK:
{
set_scsi_state(INIT_XFER_WAIT_REQ);
scsi_bus->ctrl_w(scsi_refid, 0, S_ACK);
break;
}
case INIT_XFER_RECV_BYTE_NACK:
{
set_scsi_state(FINISHED);
step(true);
break;
}
case INIT_XFER_WAIT_REQ:
{
if (!(ctrl & S_REQ))
break;
if ((ctrl & S_PHASE_MASK) != m_xfr_phase)
{
set_scsi_state(FINISHED);
step(true);
}
else
{
set_scsi_state(INIT_XFER);
step(false);
}
break;
}
case SEND_WAIT_SETTLE << SUB_SHIFT:
{
if (!timeout)
break;
set_scsi_state( (m_scsi_state & STATE_MASK) | (SEND_WAIT_REQ_0 << SUB_SHIFT) );
step(false);
break;
}
case SEND_WAIT_REQ_0 << SUB_SHIFT:
{
if (ctrl & S_REQ)
break;
set_scsi_state(m_scsi_state & STATE_MASK);
scsi_bus->data_w(scsi_refid, 0);
scsi_bus->ctrl_w(scsi_refid, 0, S_ACK);
step(false);
break;
}
case RECV_WAIT_REQ_1 << SUB_SHIFT:
{
if (!(ctrl & S_REQ))
break;
set_scsi_state( (m_scsi_state & STATE_MASK) | (RECV_WAIT_SETTLE << SUB_SHIFT) );
delay(attotime::from_nsec(5));
break;
}
case RECV_WAIT_SETTLE << SUB_SHIFT:
{
if (!timeout)
break;
if ((m_scsi_state & STATE_MASK) != INIT_XFER_RECV_PAD)
{
m_last_data = scsi_bus->data_r();
UINT32 shift = (8 * (m_dnad & 3));
UINT32 mem_mask = 0xff << shift;
m_host_write(m_dnad & ~3, data << shift, mem_mask);
++m_dnad;
--m_dbc;
}
scsi_bus->ctrl_w(scsi_refid, S_ACK, S_ACK);
set_scsi_state( (m_scsi_state & STATE_MASK) | (RECV_WAIT_REQ_0 << SUB_SHIFT) );
step(false);
break;
}
case RECV_WAIT_REQ_0 << SUB_SHIFT:
{
if (ctrl & S_REQ)
break;
set_scsi_state(m_scsi_state & STATE_MASK);
step(false);
break;
}
default:
fatalerror("Unknown state! (%x)\n", m_scsi_state);
}
}
//**************************************************************************
// SCSI SCRIPTS
//**************************************************************************
#define UNIMPLEMENTED fatalerror("%s is unimplemented\n", __FUNCTION__)
//-------------------------------------------------
// set_scripts_state -
//-------------------------------------------------
void ncr53c7xx_device::set_scripts_state(scripts_state state)
{
m_scripts_state = state;
}
//-------------------------------------------------
// scripts_yield - suspend execution
//-------------------------------------------------
void ncr53c7xx_device::scripts_yield()
{
m_icount = 0;
}
//-------------------------------------------------
// execute_run - SCRIPTS execution loop
//-------------------------------------------------
void ncr53c7xx_device::execute_run()
{
// Not processing anything so bail
if (m_scripts_state < SCRIPTS_FETCH)
{
m_icount = 0;
return;
}
do
{
switch (m_scripts_state)
{
case SCRIPTS_FETCH:
{
m_finished = false;
// Fetch the instruction
UINT32 inst = m_host_read(m_dsp, 0xffffffff);
m_dcmd = inst >> 24;
m_dbc = inst & 0xffffff;
// Unless we encounter an illegal instruction...
set_scripts_state(SCRIPTS_EXECUTE);
// Decode the relevant group
switch ((m_dcmd >> 6) & 3)
{
case 0:
scripts_decode_bm();
break;
case 1:
scripts_decode_io();
break;
case 2:
scripts_decode_tc();
break;
case 3:
illegal();
}
VERBOSE_LOG(machine(), 3, "%s", disassemble_scripts());
break;
}
case SCRIPTS_EXECUTE:
{
(*this.*m_scripts_op)();
break;
}
}
m_icount--;
} while (m_icount > 0);
}
//-------------------------------------------------
// scripts_decode_bm - decode block move
//-------------------------------------------------
void ncr53c7xx_device::scripts_decode_bm(void)
{
// Decode our instruction
if (m_scntl[0] & SCNTL0_TRG)
{
// Target mode
switch ((m_dcmd >> 3) & 3)
{
case 0:
m_scripts_op = &ncr53c7xx_device::bm_t_move;
break;
default:
illegal();
return;
}
}
else
{
// Initiator mode
switch ((m_dcmd >> 3) & 3)
{
case 0:
m_scripts_op = &ncr53c7xx_device::bm_i_move;
break;
case 1:
m_scripts_op = &ncr53c7xx_device::bm_i_wmov;
break;
default:
illegal();
return;
}
}
m_dnad = m_host_read(m_dsp + 4, 0xffffffff);
m_dsp += 8;
}
//-------------------------------------------------
// scripts_decode_io - decode IO
//-------------------------------------------------
void ncr53c7xx_device::scripts_decode_io(void)
{
// Set Target Mode?
if (m_dbc & (1 << 9))
m_scntl[0] |= SCNTL0_TRG;
// Decode our instruction
if (m_scntl[0] & SCNTL0_TRG)
{
// Initiator mode
switch ((m_dcmd >> 3) & 7)
{
case 0:
m_scripts_op = &ncr53c7xx_device::io_t_reselect;
break;
case 1:
m_scripts_op = &ncr53c7xx_device::io_t_disconnect;
break;
case 2:
m_scripts_op = &ncr53c7xx_device::io_t_waitselect;
break;
case 3:
m_scripts_op = &ncr53c7xx_device::io_t_set;
break;
case 4:
m_scripts_op = &ncr53c7xx_device::io_t_clear;
break;
default:
illegal();
return;
}
}
else
{
// Initiator mode
switch ((m_dcmd >> 3) & 7)
{
case 0:
m_scripts_op = &ncr53c7xx_device::io_i_select;
break;
case 1:
m_scripts_op = &ncr53c7xx_device::io_i_waitdisconnect;
break;
case 2:
m_scripts_op = &ncr53c7xx_device::io_i_waitreselect;
break;
case 3:
m_scripts_op = &ncr53c7xx_device::io_i_set;
break;
case 4:
m_scripts_op = &ncr53c7xx_device::io_i_clear;
break;
default:
illegal();
return;
}
}
// Set some additional registers
m_dnad = m_dsps = m_host_read(m_dsp + 4, 0xffffffff);
m_dsp += 8;
}
//-------------------------------------------------
// scripts_decode_tc - decode transfer control
//-------------------------------------------------
void ncr53c7xx_device::scripts_decode_tc(void)
{
// Decode our instruction
switch ((m_dcmd >> 3) & 7)
{
case 0:
m_scripts_op = &ncr53c7xx_device::tc_jump;
break;
case 1:
m_scripts_op = &ncr53c7xx_device::tc_call;
break;
case 2:
m_scripts_op = &ncr53c7xx_device::tc_return;
break;
case 3:
m_scripts_op = &ncr53c7xx_device::tc_int;
break;
default:
illegal();
break;
}
m_dnad = m_dsps = m_host_read(m_dsp + 4, 0xffffffff);
m_dsp += 8;
}
//**************************************************************************
// SCSI SCRIPTS INSTRUCTIONS
//**************************************************************************
//-------------------------------------------------
// illegal - illegal instruction
//-------------------------------------------------
void ncr53c7xx_device::illegal()
{
m_dstat |= DSTAT_OPC;
update_irqs();
set_scripts_state(SCRIPTS_IDLE);
}
//-------------------------------------------------
// bm_t_move - block move (target)
//-------------------------------------------------
void ncr53c7xx_device::bm_t_move()
{
UNIMPLEMENTED;
}
//-------------------------------------------------
// bm_i_move - block move (initiator)
//-------------------------------------------------
void ncr53c7xx_device::bm_i_move()
{
UNIMPLEMENTED;
}
//-------------------------------------------------
// bm_i_wmov - wait block move (initiator)
//-------------------------------------------------
void ncr53c7xx_device::bm_i_wmov()
{
if (!m_finished)
{
if (m_scsi_state == IDLE)
{
if (m_dbc == 0)
{
VERBOSE_LOG(machine(), 0, "DBC should not be 0\n");
illegal();
}
// Indirect addressing
if (m_dcmd & (1 << 5))
m_dnad = m_host_read(m_dnad, 0xffffffff);
// Compare the phase bits
if ((scsi_bus->ctrl_r() & 7) == (m_dcmd & 7))
{
// Transfer bytes
set_scsi_state(INIT_XFER);
m_xfr_phase = m_dcmd & 7;
step(false);
}
else
{
fatalerror("Phase mismatch\n");
}
}
else
{
scripts_yield();
}
}
else
{
// TODO: We should see what happened here; different behaviour
// depending on whether or not we won arbitration
set_scripts_state(SCRIPTS_FETCH);
}
}
//-------------------------------------------------
// io_t_reselect -
//-------------------------------------------------
void ncr53c7xx_device::io_t_reselect()
{
UNIMPLEMENTED;
}
//-------------------------------------------------
// io_t_disconnect -
//-------------------------------------------------
void ncr53c7xx_device::io_t_disconnect()
{
UNIMPLEMENTED;
}
//-------------------------------------------------
// io_t_waitselect -
//-------------------------------------------------
void ncr53c7xx_device::io_t_waitselect()
{
UNIMPLEMENTED;
}
//-------------------------------------------------
// io_t_set -
//-------------------------------------------------
void ncr53c7xx_device::io_t_set()
{
UNIMPLEMENTED;
}
//-------------------------------------------------
// io_t_clear -
//-------------------------------------------------
void ncr53c7xx_device::io_t_clear()
{
UNIMPLEMENTED;
}
//-------------------------------------------------
// io_i_select -
//-------------------------------------------------
void ncr53c7xx_device::io_i_select()
{
if (!m_finished)
{
if (m_scsi_state == IDLE)
{
m_sdid = m_dbc >> 16;
m_scntl[0] |= (3 << SCNTL0_ARB_SHIFT) | SCNTL0_START;
// Set select with ATN bit
if (m_dcmd & 1)
m_scntl[0] |= SCNTL0_WATN;
// Start the arbitration
set_scsi_state(ARBITRATE_WAIT_FREE);
step(true);
}
scripts_yield();
}
else
{
// TODO: We should see what happened here; different behaviour
// depending on whether or not we won arbitration
set_scripts_state(SCRIPTS_FETCH);
}
}
//-------------------------------------------------
// io_i_waitdisconnect -
//-------------------------------------------------
void ncr53c7xx_device::io_i_waitdisconnect()
{
if (scsi_bus->ctrl_r() & (S_BSY | S_SEL))
scripts_yield();
else
set_scripts_state(SCRIPTS_FETCH);
}
//-------------------------------------------------
// io_i_waitreselect -
//-------------------------------------------------
void ncr53c7xx_device::io_i_waitreselect()
{
UNIMPLEMENTED;
}
//-------------------------------------------------
// io_i_set -
//-------------------------------------------------
void ncr53c7xx_device::io_i_set()
{
UINT32 mask = 0;
if (m_dbc & (1 << 3))
mask |= S_ATN;
if (m_dbc & (1 << 6))
mask |= S_ACK;
scsi_bus->ctrl_w(scsi_refid, mask, mask);
set_scripts_state(SCRIPTS_FETCH);
}
//-------------------------------------------------
// io_i_clear -
//-------------------------------------------------
void ncr53c7xx_device::io_i_clear()
{
UINT32 mask = 0;
if (m_dbc & (1 << 3))
mask |= S_ATN;
if (m_dbc & (1 << 6))
mask |= S_ACK;
scsi_bus->ctrl_w(scsi_refid, 0, mask);
set_scripts_state(SCRIPTS_FETCH);
}
//-------------------------------------------------
// tc_jump -
//-------------------------------------------------
void ncr53c7xx_device::tc_jump()
{
// if (m_dbc & (1 << 16))
// printf("Must wait for valid phase?\n");
bool jump = true;
if (m_dbc & (1 << 17))
{
// Phase
jump &= (m_dcmd & 7) == (scsi_bus->ctrl_r() & 7);
}
if (m_dbc & (1 << 18))
{
// Data
jump &= (m_dbc & 0xff) == m_last_data;
}
if (!(m_dbc & (1 << 19)))
jump = !jump;
if (jump)
{
m_dsp = m_dsps;
}
set_scripts_state(SCRIPTS_FETCH);
}
//-------------------------------------------------
// tc_call -
//-------------------------------------------------
void ncr53c7xx_device::tc_call()
{
bool jump = true;
if (m_dbc & (1 << 17))
{
// Phase
jump &= (m_dcmd & 7) == (scsi_bus->ctrl_r() & 7);
}
if (m_dbc & (1 << 18))
{
// Data
jump &= (m_dbc & 0xff) == m_last_data;
}
if (!(m_dbc & (1 << 19)))
jump = !jump;
if (jump)
{
m_temp = m_dsp;
m_dsp = m_dsps;
}
set_scripts_state(SCRIPTS_FETCH);
}
//-------------------------------------------------
// tc_return -
//-------------------------------------------------
void ncr53c7xx_device::tc_return()
{
bool jump = true;
if (m_dbc & (1 << 17))
{
// Phase
jump &= (m_dcmd & 7) == (scsi_bus->ctrl_r() & 7);
}
if (m_dbc & (1 << 18))
{
// Data
jump &= (m_dbc & 0xff) == m_last_data;
}
if (!(m_dbc & (1 << 19)))
jump = !jump;
if (jump)
{
m_dsp = m_temp;
}
set_scripts_state(SCRIPTS_FETCH);
}
//-------------------------------------------------
// tc_int -
//-------------------------------------------------
void ncr53c7xx_device::tc_int()
{
bool jump = true;
if (m_dbc & (1 << 17))
{
// Phase
jump &= (m_dcmd & 7) == (scsi_bus->ctrl_r() & 7);
}
if (m_dbc & (1 << 18))
{
// Data
jump &= (m_dbc & 0xff) == m_last_data;
}
if (!(m_dbc & (1 << 19)))
jump = !jump;
if (jump)
{
m_dstat |= DSTAT_SIR;
update_irqs();
set_scripts_state(SCRIPTS_IDLE);
}
else
{
set_scripts_state(SCRIPTS_FETCH);
}
}
//**************************************************************************
// SCSI SCRIPTS DISASSEMBLY
//**************************************************************************
//-------------------------------------------------
// disassemble_scripts -
//-------------------------------------------------
const char* ncr53c7xx_device::disassemble_scripts()
{
static const char* phases[] =
{
"Data Out",
"Data In",
"Command",
"Status",
"Reserved",
"Reserved",
"Message Out",
"Message In"
};
static char buffer[64];
char opstring[64];
switch ((m_dcmd >> 6) & 3)
{
case 0:
{
sprintf(opstring, "BMOV: %s [%x] %d bytes\n", phases[m_dcmd & 7], m_dnad, m_dbc);
break;
}
case 1:
{
static const char* ops[] =
{
"SELECT",
"DISCONNECT",
"RESELECT",
"SET",
"CLEAR",
"ILLEGAL",
"ILLEGAL",
"ILLEGAL",
};
sprintf(opstring, "IO: %s (%x)\n", ops[(m_dcmd >> 3) & 7], m_dnad);
break;
}
case 2:
{
static const char* ops[] =
{
"JUMP",
"CALL",
"RETURN",
"INT",
"ILLEGAL",
"ILLEGAL",
"ILLEGAL",
"ILLEGAL",
};
sprintf(opstring, "TC: %s %c (%s) (%x)\n", ops[(m_dcmd >> 3) & 7], m_dbc & (1 << 19) ? 'T' : 'F', phases[m_dcmd & 7], m_dnad);
break;
}
case 3:
{
sprintf(opstring, "ILLEGAL");
break;
}
}
sprintf(buffer, "SCRIPTS [%08x]: %s", m_dsp - 8, opstring);
return buffer;
}