(MESS) Reimplemented the NCR5380 SCSI controller using nscsi. [R. Belmont]

This commit is contained in:
R. Belmont 2013-03-01 01:20:43 +00:00
parent ee72cf5471
commit bff5a6cfa9
4 changed files with 827 additions and 0 deletions

2
.gitattributes vendored
View File

@ -7319,6 +7319,8 @@ src/mess/machine/nascom1.c svneol=native#text/plain
src/mess/machine/nc.c svneol=native#text/plain
src/mess/machine/ncr5380.c svneol=native#text/plain
src/mess/machine/ncr5380.h svneol=native#text/plain
src/mess/machine/ncr5380n.c svneol=native#text/plain
src/mess/machine/ncr5380n.h svneol=native#text/plain
src/mess/machine/ncr5390.c svneol=native#text/plain
src/mess/machine/ncr5390.h svneol=native#text/plain
src/mess/machine/ne1000.c svneol=native#text/plain

601
src/mess/machine/ncr5380n.c Normal file
View File

@ -0,0 +1,601 @@
/*********************************************************************
ncr5380n.c
Implementation of the NCR 5380, aka the Zilog Z5380
TODO:
- IRQs (Apple doesn't use 'em)
- Target mode
*********************************************************************/
#include "emu.h"
#include "ncr5380n.h"
const device_type NCR5380N = &device_creator<ncr5380n_device>;
DEVICE_ADDRESS_MAP_START(map, 8, ncr5380n_device)
AM_RANGE(0x0, 0x0) AM_READWRITE(scsidata_r, outdata_w)
AM_RANGE(0x1, 0x1) AM_READWRITE(icmd_r, icmd_w)
AM_RANGE(0x2, 0x2) AM_READWRITE(mode_r, mode_w)
AM_RANGE(0x3, 0x3) AM_READWRITE(command_r, command_w)
AM_RANGE(0x4, 0x4) AM_READWRITE(status_r, selenable_w)
AM_RANGE(0x5, 0x5) AM_READWRITE(busandstatus_r, startdmasend_w)
AM_RANGE(0x6, 0x6) AM_READWRITE(indata_r, startdmatargetrx_w)
AM_RANGE(0x7, 0x7) AM_READWRITE(resetparityirq_r, startdmainitrx_w)
ADDRESS_MAP_END
ncr5380n_device::ncr5380n_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: nscsi_device(mconfig, NCR5380N, "5380 SCSI (new)", tag, owner, clock)
{
}
void ncr5380n_device::device_start()
{
save_item(NAME(m_tcommand));
save_item(NAME(m_icommand));
save_item(NAME(status));
save_item(NAME(istatus));
save_item(NAME(m_busstatus));
save_item(NAME(tcount));
save_item(NAME(mode));
save_item(NAME(irq));
save_item(NAME(drq));
save_item(NAME(clock_conv));
save_item(NAME(m_dmalatch));
m_irq_func.resolve(m_irq_cb, *this);
m_drq_func.resolve(m_drq_cb, *this);
tcount = 0;
status = 0;
bus_id = 0;
select_timeout = 0;
tm = timer_alloc(0);
}
void ncr5380n_device::device_reset()
{
clock_conv = 2;
sync_period = 5;
sync_offset = 0;
seq = 0;
status = 0;
m_tcommand = 0;
m_icommand = 0;
istatus = 0;
m_busstatus = 0;
irq = false;
if(!m_irq_func.isnull())
m_irq_func(irq);
reset_soft();
}
void ncr5380n_device::device_config_complete()
{
// inherit a copy of the static data
const ncr5380n_interface *intf = reinterpret_cast<const ncr5380n_interface *>(static_config());
if (intf != NULL)
{
*static_cast<ncr5380n_interface *>(this) = *intf;
}
// or initialize to defaults if none provided
else
{
memset(&m_irq_cb, 0, sizeof(m_irq_cb));
memset(&m_drq_cb, 0, sizeof(m_drq_cb));
}
m_shortname = "ncr5380";
}
void ncr5380n_device::reset_soft()
{
state = IDLE;
scsi_bus->ctrl_w(scsi_refid, 0, S_ALL); // clear any signals we're driving
scsi_bus->ctrl_wait(scsi_refid, S_ALL, S_ALL);
status = 0;
drq = false;
if(!m_drq_func.isnull())
m_drq_func(drq);
reset_disconnect();
}
void ncr5380n_device::reset_disconnect()
{
mode = MODE_D;
}
//static int last_phase = -1;
void ncr5380n_device::scsi_ctrl_changed()
{
UINT32 ctrl = scsi_bus->ctrl_r();
// printf("scsi_ctrl_changed: lines now %x\n", ctrl);
/* if ((ctrl & (S_PHASE_MASK|S_SEL|S_BSY)) != last_phase)
{
printf("phase now %d, SEL %x BSY %x\n", ctrl & S_PHASE_MASK, ctrl & S_SEL, ctrl & S_BSY);
last_phase = (S_PHASE_MASK|S_SEL|S_BSY);
}*/
// recalculate phase match
m_busstatus &= ~BAS_PHASEMATCH;
if ((ctrl & S_PHASE_MASK) == (m_tcommand & S_PHASE_MASK))
{
m_busstatus |= BAS_PHASEMATCH;
}
if (m_mode & MODE_DMA)
{
// if BSY drops or the phase goes mismatch, that terminates the DMA
if ((!(ctrl & S_BSY)) || !(m_busstatus & BAS_PHASEMATCH))
{
// printf("BSY dropped or phase mismatch during DMA, ending DMA\n");
m_mode &= ~MODE_DMA;
m_busstatus |= BAS_ENDOFDMA;
drq_clear();
}
}
if(ctrl & S_RST) {
logerror("%s: scsi bus reset\n", tag());
return;
}
step(false);
}
void ncr5380n_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
step(true);
}
void ncr5380n_device::step(bool timeout)
{
UINT32 ctrl = scsi_bus->ctrl_r();
UINT32 data = scsi_bus->data_r();
if(0)
printf("%s: state=%d.%d %s\n",
tag(), state & STATE_MASK, (state & SUB_MASK) >> SUB_SHIFT,
timeout ? "timeout" : "change");
if(mode == MODE_I && !(ctrl & S_BSY)) {
state = IDLE;
reset_disconnect();
check_irq();
}
switch(state & SUB_MASK ? state & SUB_MASK : state & STATE_MASK) {
case IDLE:
break;
case ARB_COMPLETE << SUB_SHIFT: {
if(!timeout)
break;
int win;
for(win=7; win>=0 && !(data & (1<<win)); win--);
// printf("data %02x win %02x scsi_id %02x\n", data, win, scsi_id);
if(win != scsi_id) {
scsi_bus->data_w(scsi_refid, 0);
scsi_bus->ctrl_w(scsi_refid, 0, S_ALL);
fatalerror("need to wait for bus free\n");
break;
}
// arbitration no longer in progress
m_icommand &= ~IC_ARBITRATION;
state &= STATE_MASK;
step(true);
break;
}
case SEND_WAIT_SETTLE << SUB_SHIFT:
if(!timeout)
break;
state = (state & STATE_MASK) | (SEND_WAIT_REQ_0 << SUB_SHIFT);
step(false);
break;
case SEND_WAIT_REQ_0 << SUB_SHIFT:
if(ctrl & S_REQ)
break;
state = state & STATE_MASK;
scsi_bus->data_w(scsi_refid, 0);
scsi_bus->ctrl_w(scsi_refid, 0, S_ACK);
step(false);
// byte's done, ask for another if the target hasn't said otherwise
if (m_mode & MODE_DMA)
{
drq_set();
}
break;
case RECV_WAIT_REQ_1 << SUB_SHIFT:
if(!(ctrl & S_REQ))
break;
state = (state & STATE_MASK) | (RECV_WAIT_SETTLE << SUB_SHIFT);
delay_cycles(sync_period);
break;
case RECV_WAIT_SETTLE << SUB_SHIFT:
if(!timeout)
break;
m_dmalatch = scsi_bus->data_r();
drq_set();
scsi_bus->ctrl_w(scsi_refid, S_ACK, S_ACK);
state = (state & STATE_MASK) | (RECV_WAIT_REQ_0 << SUB_SHIFT);
step(false);
break;
case RECV_WAIT_REQ_0 << SUB_SHIFT:
if(ctrl & S_REQ)
break;
state = state & STATE_MASK;
step(false);
break;
default:
printf("%s: step() unexpected state %d.%d\n",
tag(),
state & STATE_MASK, (state & SUB_MASK) >> SUB_SHIFT);
exit(0);
}
}
void ncr5380n_device::send_byte()
{
state = (state & STATE_MASK) | (SEND_WAIT_SETTLE << SUB_SHIFT);
scsi_bus->data_w(scsi_refid, m_dmalatch);
scsi_bus->ctrl_w(scsi_refid, S_ACK, S_ACK);
scsi_bus->ctrl_wait(scsi_refid, S_REQ, S_REQ);
delay_cycles(sync_period);
}
void ncr5380n_device::recv_byte()
{
state = (state & STATE_MASK) | (RECV_WAIT_REQ_1 << SUB_SHIFT);
step(false);
}
void ncr5380n_device::function_bus_complete()
{
state = IDLE;
// istatus |= I_FUNCTION|I_BUS;
check_irq();
}
void ncr5380n_device::function_complete()
{
state = IDLE;
// istatus |= I_FUNCTION;
check_irq();
}
void ncr5380n_device::bus_complete()
{
state = IDLE;
// istatus |= I_BUS;
check_irq();
}
void ncr5380n_device::delay(int cycles)
{
if(!clock_conv)
return;
cycles *= clock_conv;
tm->adjust(clocks_to_attotime(cycles));
}
void ncr5380n_device::delay_cycles(int cycles)
{
tm->adjust(clocks_to_attotime(cycles));
}
READ8_MEMBER(ncr5380n_device::scsidata_r)
{
return scsi_bus->data_r();
}
WRITE8_MEMBER(ncr5380n_device::outdata_w)
{
m_outdata = data;
// are we driving the data bus?
if (m_icommand & IC_DBUS)
{
scsi_bus->data_w(scsi_refid, data);
}
}
READ8_MEMBER(ncr5380n_device::icmd_r)
{
return m_icommand;
}
WRITE8_MEMBER(ncr5380n_device::icmd_w)
{
// asserting to drive the data bus?
if ((data & IC_DBUS) && !(m_icommand & IC_DBUS))
{
// printf("%s: driving data bus with %02x\n", tag(), m_outdata);
scsi_bus->data_w(scsi_refid, m_outdata);
delay(2);
}
// any control lines changing?
UINT8 mask = (data & IC_PHASEMASK) ^ (m_icommand & IC_PHASEMASK);
if (mask)
{
// translate data to nscsi
UINT8 newdata = 0;
newdata = (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);
// printf("%s: changing control lines %04x\n", tag(), newdata);
scsi_bus->ctrl_w(scsi_refid, newdata, S_RST|S_ACK|S_BSY|S_SEL|S_ATN);
}
m_icommand = (data & IC_WRITEMASK);
delay(2);
}
READ8_MEMBER(ncr5380n_device::mode_r)
{
return m_mode;
}
WRITE8_MEMBER(ncr5380n_device::mode_w)
{
// printf("%s: mode_w %02x (%08x)\n", tag(), data, space.device().safe_pc());
// arbitration bit being set?
if ((data & MODE_ARBITRATE) && !(m_mode & MODE_ARBITRATE))
{
// if SEL is selected and the assert SEL bit in the initiator
// command register is clear, fail
if ((scsi_bus->ctrl_r() & S_SEL) && !(m_icommand & IC_SEL))
{
// printf("arbitration lost, ctrl = %x, m_icommand&IC_SEL = %x\n", scsi_bus->ctrl_r(), m_icommand & IC_SEL);
m_icommand &= ~IC_ARBITRATION;
m_icommand |= IC_ARBLOST;
}
else
{
seq = 0;
// state = DISC_SEL_ARBITRATION;
arbitrate();
}
}
m_mode = data;
}
READ8_MEMBER(ncr5380n_device::command_r)
{
// logerror("%s: command_r %02x (%08x)\n", tag(), m_tcommand, space.device().safe_pc());
return m_tcommand;
}
WRITE8_MEMBER(ncr5380n_device::command_w)
{
// printf("%s: command_w %02x (%08x)\n", tag(), data, space.device().safe_pc());
m_tcommand = data;
// recalculate phase match
m_busstatus &= ~BAS_PHASEMATCH;
if ((scsi_bus->ctrl_r() & S_PHASE_MASK) == (m_tcommand & S_PHASE_MASK))
{
m_busstatus |= BAS_PHASEMATCH;
}
}
void ncr5380n_device::arbitrate()
{
m_icommand &= ~IC_ARBLOST;
m_icommand |= IC_ARBITRATION;
state = (state & STATE_MASK) | (ARB_COMPLETE << SUB_SHIFT);
scsi_bus->data_w(scsi_refid, m_outdata);
scsi_bus->ctrl_w(scsi_refid, S_BSY, S_BSY);
delay(11);
}
void ncr5380n_device::check_irq()
{
#if 0
bool oldirq = irq;
irq = istatus != 0;
if(irq != oldirq && !m_irq_func.isnull())
m_irq_func(irq);
#endif
}
READ8_MEMBER(ncr5380n_device::status_r)
{
UINT32 ctrl = scsi_bus->ctrl_r();
UINT8 res = status |
(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);
// printf("%s: status_r %02x (%08x)\n", tag(), res, space.device().safe_pc());
return res;
}
WRITE8_MEMBER(ncr5380n_device::selenable_w)
{
}
READ8_MEMBER(ncr5380n_device::busandstatus_r)
{
UINT32 ctrl = scsi_bus->ctrl_r();
UINT8 res = m_busstatus |
(ctrl & S_ATN ? BAS_ATN : 0) |
(ctrl & S_ACK ? BAS_ACK : 0);
// printf("%s: busandstatus_r %02x (%08x)\n", tag(), res, space.device().safe_pc());
return res;
}
WRITE8_MEMBER(ncr5380n_device::startdmasend_w)
{
printf("%02x to start dma send\n", data);
drq_set();
}
READ8_MEMBER(ncr5380n_device::indata_r)
{
return 0;
}
WRITE8_MEMBER(ncr5380n_device::startdmatargetrx_w)
{
printf("%02x to start dma target Rx\n", data);
}
READ8_MEMBER(ncr5380n_device::resetparityirq_r)
{
return 0;
}
WRITE8_MEMBER(ncr5380n_device::startdmainitrx_w)
{
// printf("%02x to start dma initiator Rx\n", data);
recv_byte();
}
void ncr5380n_device::dma_w(UINT8 val)
{
// drop DRQ until we're ready for another byte
drq_clear();
if (m_mode & MODE_DMA)
{
m_dmalatch = val;
send_byte();
}
}
UINT8 ncr5380n_device::dma_r()
{
// drop DRQ
drq_clear();
// set up to receive our next byte if still in DMA mode
scsi_bus->ctrl_w(scsi_refid, 0, S_ACK);
if (m_mode & MODE_DMA)
{
recv_byte();
}
return m_dmalatch;
}
void ncr5380n_device::drq_set()
{
if(!drq)
{
drq = true;
m_busstatus |= BAS_DMAREQUEST;
if(!m_drq_func.isnull())
m_drq_func(drq);
}
}
void ncr5380n_device::drq_clear()
{
if(drq)
{
drq = false;
m_busstatus &= ~BAS_DMAREQUEST;
if(!m_drq_func.isnull())
m_drq_func(drq);
}
}
READ8_MEMBER(ncr5380n_device::read)
{
switch (offset & 7)
{
case 0:
return scsidata_r(space, offset);
case 1:
return icmd_r(space, offset);
case 2:
return mode_r(space, offset);
case 3:
return command_r(space, offset);
case 4:
return status_r(space, offset);
case 5:
return busandstatus_r(space, offset);
case 6:
return indata_r(space, offset);
case 7:
return resetparityirq_r(space, offset);
}
return 0xff;
}
WRITE8_MEMBER(ncr5380n_device::write)
{
switch (offset & 7)
{
case 0:
outdata_w(space, offset, data);
break;
case 1:
icmd_w(space, offset, data);
break;
case 2:
mode_w(space, offset, data);
break;
case 3:
command_w(space, offset, data);
break;
case 4:
selenable_w(space, offset, data);
break;
case 5:
startdmasend_w(space, offset, data);
break;
case 6:
startdmatargetrx_w(space, offset, data);
break;
case 7:
startdmainitrx_w(space, offset, data);
break;
}
}

223
src/mess/machine/ncr5380n.h Normal file
View File

@ -0,0 +1,223 @@
/*********************************************************************
ncr5380n.c
Implementation of the NCR 5380
*********************************************************************/
#ifndef NCR5380_H
#define NCR5380_H
#include "machine/nscsi_bus.h"
struct ncr5380n_interface
{
devcb_write_line m_irq_cb;
devcb_write_line m_drq_cb;
};
class ncr5380n_device : public nscsi_device,
public ncr5380n_interface
{
public:
ncr5380n_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
DECLARE_ADDRESS_MAP(map, 8);
DECLARE_READ8_MEMBER(scsidata_r);
DECLARE_WRITE8_MEMBER(outdata_w);
DECLARE_READ8_MEMBER(icmd_r);
DECLARE_WRITE8_MEMBER(icmd_w);
DECLARE_READ8_MEMBER(mode_r);
DECLARE_WRITE8_MEMBER(mode_w);
DECLARE_READ8_MEMBER(command_r);
DECLARE_WRITE8_MEMBER(command_w);
DECLARE_READ8_MEMBER(status_r);
DECLARE_WRITE8_MEMBER(selenable_w);
DECLARE_READ8_MEMBER(busandstatus_r);
DECLARE_WRITE8_MEMBER(startdmasend_w);
DECLARE_READ8_MEMBER(indata_r);
DECLARE_WRITE8_MEMBER(startdmatargetrx_w);
DECLARE_READ8_MEMBER(resetparityirq_r);
DECLARE_WRITE8_MEMBER(startdmainitrx_w);
DECLARE_READ8_MEMBER(read);
DECLARE_WRITE8_MEMBER(write);
virtual void scsi_ctrl_changed();
UINT8 dma_r();
void dma_w(UINT8 val);
protected:
virtual void device_start();
virtual void device_reset();
virtual void device_config_complete();
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
private:
enum { MODE_D, MODE_T, MODE_I };
enum { IDLE };
enum {
// Bus initiated sequences
BUSINIT_SETTLE_DELAY = 1,
BUSINIT_ASSERT_BUS_SEL,
BUSINIT_MSG_OUT,
BUSINIT_RECV_BYTE,
BUSINIT_ASSERT_BUS_RESEL,
BUSINIT_WAIT_REQ,
BUSINIT_RECV_BYTE_NACK,
// Bus SCSI Reset
BUSRESET_WAIT_INT,
BUSRESET_RESET_BOARD,
// Disconnected state commands
DISC_SEL_ARBITRATION,
DISC_SEL_ATN_WAIT_REQ,
DISC_SEL_ATN_SEND_BYTE,
DISC_SEL_WAIT_REQ,
DISC_SEL_SEND_BYTE,
DISC_REC_ARBITRATION,
DISC_REC_MSG_IN,
DISC_REC_SEND_BYTE,
DISC_RESET,
// Command sequence
CMDSEQ_CMD_PHASE,
CMDSEQ_RECV_BYTE,
// Target commands
TARGET_SEND_BYTE,
TARGET_CMD_RECV_BYTE,
TARGET_MSG_RECV_BYTE,
TARGET_MSG_RECV_PAD,
TARGET_DISC_SEND_BYTE,
TARGET_DISC_MSG_IN,
TARGET_DISC_SEND_BYTE_2,
// Initiator commands
INIT_MSG_WAIT_REQ,
INIT_XFR,
INIT_XFR_SEND_BYTE,
INIT_XFR_SEND_PAD_WAIT_REQ,
INIT_XFR_SEND_PAD,
INIT_XFR_RECV_PAD_WAIT_REQ,
INIT_XFR_RECV_PAD,
INIT_XFR_RECV_BYTE_ACK,
INIT_XFR_RECV_BYTE_NACK,
INIT_XFR_WAIT_REQ,
INIT_CPT_RECV_BYTE_ACK,
INIT_CPT_RECV_WAIT_REQ,
INIT_CPT_RECV_BYTE_NACK,
};
enum {
// Arbitration
ARB_WAIT_BUS_FREE = 1,
ARB_COMPLETE,
ARB_ASSERT_SEL,
ARB_SET_DEST,
ARB_RELEASE_BUSY,
ARB_TIMEOUT_BUSY,
ARB_TIMEOUT_ABORT,
ARB_DESKEW_WAIT,
// Send/recieve byte
SEND_WAIT_SETTLE,
SEND_WAIT_REQ_0,
RECV_WAIT_REQ_1,
RECV_WAIT_SETTLE,
RECV_WAIT_REQ_0,
};
enum {
STATE_MASK = 0x00ff,
SUB_SHIFT = 8,
SUB_MASK = 0xff00,
};
enum { BUS_BUSY, BUS_FREE_WAIT, BUS_FREE };
enum {
ST_RST = 0x80,
ST_BSY = 0x40,
ST_REQ = 0x20,
ST_MSG = 0x10,
ST_CD = 0x08,
ST_IO = 0x04,
ST_SEL = 0x02,
ST_DBP = 0x01,
BAS_ENDOFDMA = 0x80,
BAS_DMAREQUEST = 0x40,
BAS_PARITYERROR = 0x20,
BAS_IRQACTIVE = 0x10,
BAS_PHASEMATCH = 0x08,
BAS_BUSYERROR = 0x04,
BAS_ATN = 0x02,
BAS_ACK = 0x01,
IC_RST = 0x80,
IC_ARBITRATION = 0x40,
IC_ARBLOST = 0x20,
IC_ACK = 0x10,
IC_BSY = 0x08,
IC_SEL = 0x04,
IC_ATN = 0x02,
IC_DBUS = 0x01,
IC_PHASEMASK = 0x9e,
IC_WRITEMASK = 0x9f,
MODE_BLOCKDMA = 0x80,
MODE_TARGET = 0x40,
MODE_PARITYCHK = 0x20,
MODE_PARITYIRQ = 0x10,
MODE_EOPIRQ = 0x08,
MODE_BSYIRQ = 0x04,
MODE_DMA = 0x02,
MODE_ARBITRATE = 0x01,
};
enum { DMA_NONE, DMA_IN, DMA_OUT };
emu_timer *tm;
UINT8 status, istatus, m_mode, m_outdata, m_busstatus, m_dmalatch;
UINT8 m_icommand, m_tcommand;
UINT8 clock_conv, sync_offset, sync_period, bus_id, select_timeout, seq;
UINT16 tcount;
int mode;
int state, xfr_phase;
bool irq, drq;
devcb_resolved_write_line m_irq_func;
devcb_resolved_write_line m_drq_func;
void drq_set();
void drq_clear();
void step(bool timeout);
void function_complete();
void function_bus_complete();
void bus_complete();
void arbitrate();
void check_irq();
void reset_soft();
void reset_disconnect();
void send_byte();
void recv_byte();
void delay(int cycles);
void delay_cycles(int cycles);
};
extern const device_type NCR5380N;
#endif

View File

@ -529,6 +529,7 @@ $(MESSOBJ)/shared.a: \
$(MESS_MACHINE)/msm6222b.o \
$(MESS_MACHINE)/serial.o \
$(MESS_MACHINE)/ncr5380.o \
$(MESS_MACHINE)/ncr5380n.o \
$(MESS_MACHINE)/ncr5390.o \
$(MESS_MACHINE)/pc_kbdc.o \
$(MESS_MACHINE)/pc_lpt.o \