mirror of
https://github.com/holub/mame
synced 2025-04-22 16:31:49 +03:00
ncr5390: several small but important changes (nw) (#3619)
These changes work with InterPro, but haven't been tested on other machines. Despite this, I'm reasonably confident they are all correct according to the observed behaviour and the documentation. * don't wait for REQ after initiator complete with NACK * wait until fifo empty during dma out before command complete * make sure drq is always cleared on bus/function complete * reset happens immediately * always clear TC0 when counter reloaded * check valid commands separately for 5390 and 5390a * handle 5390a initiator set attention command * use logmacro for logging
This commit is contained in:
parent
c4fac5bfdc
commit
93a797cb34
@ -4,6 +4,14 @@
|
||||
#include "emu.h"
|
||||
#include "ncr5390.h"
|
||||
|
||||
#define LOG_GENERAL (1U << 0)
|
||||
#define LOG_STATE (1U << 1)
|
||||
#define LOG_FIFO (1U << 2)
|
||||
#define LOG_COMMAND (1U << 3)
|
||||
|
||||
#define VERBOSE (0)
|
||||
#include "logmacro.h"
|
||||
|
||||
#define DELAY_HACK
|
||||
|
||||
DEFINE_DEVICE_TYPE(NCR5390, ncr5390_device, "ncr5390", "NCR 5390 SCSI")
|
||||
@ -151,7 +159,7 @@ void ncr5390_device::scsi_ctrl_changed()
|
||||
{
|
||||
uint32_t ctrl = scsi_bus->ctrl_r();
|
||||
if(ctrl & S_RST) {
|
||||
logerror("%s: scsi bus reset\n", tag());
|
||||
LOG("scsi bus reset\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -169,10 +177,9 @@ void ncr5390_device::step(bool timeout)
|
||||
uint32_t data = scsi_bus->data_r();
|
||||
uint8_t c = command[0] & 0x7f;
|
||||
|
||||
if(0)
|
||||
logerror("%s: state=%d.%d %s\n",
|
||||
tag(), state & STATE_MASK, (state & SUB_MASK) >> SUB_SHIFT,
|
||||
timeout ? "timeout" : "change");
|
||||
LOGMASKED(LOG_STATE, "state=%d.%d %s\n",
|
||||
state & STATE_MASK, (state & SUB_MASK) >> SUB_SHIFT,
|
||||
timeout ? "timeout" : "change");
|
||||
|
||||
if(mode == MODE_I && !(ctrl & S_BSY)) {
|
||||
state = IDLE;
|
||||
@ -246,10 +253,10 @@ void ncr5390_device::step(bool timeout)
|
||||
scsi_bus->ctrl_w(scsi_refid, 0, S_SEL);
|
||||
|
||||
if(c == CD_RESELECT) {
|
||||
logerror("%s: mode switch to Target\n", tag());
|
||||
LOG("mode switch to Target\n");
|
||||
mode = MODE_T;
|
||||
} else {
|
||||
logerror("%s: mode switch to Initiator\n", tag());
|
||||
LOG("mode switch to Initiator\n");
|
||||
mode = MODE_I;
|
||||
}
|
||||
state &= STATE_MASK;
|
||||
@ -259,7 +266,7 @@ void ncr5390_device::step(bool timeout)
|
||||
case ARB_TIMEOUT_BUSY << SUB_SHIFT:
|
||||
if(timeout) {
|
||||
scsi_bus->data_w(scsi_refid, 0);
|
||||
logerror("%s: select timeout\n", tag());
|
||||
LOG("select timeout\n");
|
||||
state = (state & STATE_MASK) | (ARB_TIMEOUT_ABORT << SUB_SHIFT);
|
||||
delay(1000);
|
||||
} else if(ctrl & S_BSY) {
|
||||
@ -411,7 +418,6 @@ void ncr5390_device::step(bool timeout)
|
||||
break;
|
||||
|
||||
case INIT_CPT_RECV_BYTE_NACK:
|
||||
scsi_bus->ctrl_wait(scsi_refid, 0, S_REQ);
|
||||
function_complete();
|
||||
break;
|
||||
|
||||
@ -456,7 +462,7 @@ void ncr5390_device::step(bool timeout)
|
||||
break;
|
||||
|
||||
default:
|
||||
logerror("%s: xfer on phase %d\n", tag(), scsi_bus->ctrl_r() & S_PHASE_MASK);
|
||||
LOG("xfer on phase %d\n", scsi_bus->ctrl_r() & S_PHASE_MASK);
|
||||
function_complete();
|
||||
break;
|
||||
}
|
||||
@ -467,7 +473,7 @@ void ncr5390_device::step(bool timeout)
|
||||
break;
|
||||
|
||||
// check for command complete
|
||||
if ((dma_command && tcounter == 0) // dma in/out: transfer counter == 0
|
||||
if ((dma_command && tcounter == 0 && (dma_dir == DMA_IN || fifo_pos == 0)) // dma in/out: transfer counter == 0
|
||||
|| (!dma_command && (xfr_phase & S_INP) == 0 && fifo_pos == 0) // non-dma out: fifo empty
|
||||
|| (!dma_command && (xfr_phase & S_INP) == S_INP && fifo_pos == 1)) // non-dma in: every byte
|
||||
state = INIT_XFR_BUS_COMPLETE;
|
||||
@ -562,9 +568,8 @@ void ncr5390_device::step(bool timeout)
|
||||
break;
|
||||
|
||||
default:
|
||||
logerror("%s: step() unexpected state %d.%d\n",
|
||||
tag(),
|
||||
state & STATE_MASK, (state & SUB_MASK) >> SUB_SHIFT);
|
||||
LOG("step() unexpected state %d.%d\n",
|
||||
state & STATE_MASK, (state & SUB_MASK) >> SUB_SHIFT);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
@ -596,25 +601,31 @@ void ncr5390_device::recv_byte()
|
||||
|
||||
void ncr5390_device::function_bus_complete()
|
||||
{
|
||||
LOG("function_bus_complete\n");
|
||||
state = IDLE;
|
||||
istatus |= I_FUNCTION|I_BUS;
|
||||
dma_set(DMA_NONE);
|
||||
drq_clear();
|
||||
check_irq();
|
||||
}
|
||||
|
||||
void ncr5390_device::function_complete()
|
||||
{
|
||||
LOG("function_complete\n");
|
||||
state = IDLE;
|
||||
istatus |= I_FUNCTION;
|
||||
dma_set(DMA_NONE);
|
||||
drq_clear();
|
||||
check_irq();
|
||||
}
|
||||
|
||||
void ncr5390_device::bus_complete()
|
||||
{
|
||||
LOG("bus_complete\n");
|
||||
state = IDLE;
|
||||
istatus |= I_BUS;
|
||||
dma_set(DMA_NONE);
|
||||
drq_clear();
|
||||
check_irq();
|
||||
}
|
||||
|
||||
@ -633,26 +644,26 @@ void ncr5390_device::delay_cycles(int cycles)
|
||||
|
||||
READ8_MEMBER(ncr5390_device::tcounter_lo_r)
|
||||
{
|
||||
logerror("%s: tcounter_lo_r %02x (%s)\n", tag(), tcounter & 0xff, machine().describe_context());
|
||||
LOG("tcounter_lo_r %02x (%s)\n", tcounter & 0xff, machine().describe_context());
|
||||
return tcounter;
|
||||
}
|
||||
|
||||
WRITE8_MEMBER(ncr5390_device::tcount_lo_w)
|
||||
{
|
||||
tcount = (tcount & 0xff00) | data;
|
||||
logerror("%s: tcount_lo_w %02x (%s)\n", tag(), data, machine().describe_context());
|
||||
LOG("tcount_lo_w %02x (%s)\n", data, machine().describe_context());
|
||||
}
|
||||
|
||||
READ8_MEMBER(ncr5390_device::tcounter_hi_r)
|
||||
{
|
||||
logerror("%s: tcounter_hi_r %02x (%s)\n", tag(), tcounter >> 8, machine().describe_context());
|
||||
LOG("tcounter_hi_r %02x (%s)\n", tcounter >> 8, machine().describe_context());
|
||||
return tcounter >> 8;
|
||||
}
|
||||
|
||||
WRITE8_MEMBER(ncr5390_device::tcount_hi_w)
|
||||
{
|
||||
tcount = (tcount & 0x00ff) | (data << 8);
|
||||
logerror("%s: tcount_hi_w %02x (%s)\n", tag(), data, machine().describe_context());
|
||||
LOG("tcount_hi_w %02x (%s)\n", data, machine().describe_context());
|
||||
}
|
||||
|
||||
uint8_t ncr5390_device::fifo_pop()
|
||||
@ -681,29 +692,38 @@ READ8_MEMBER(ncr5390_device::fifo_r)
|
||||
memmove(fifo, fifo+1, fifo_pos);
|
||||
} else
|
||||
r = 0;
|
||||
LOGMASKED(LOG_FIFO, "fifo_r 0x%02x fifo_pos %d (%s)\n", r, fifo_pos, machine().describe_context());
|
||||
return r;
|
||||
}
|
||||
|
||||
WRITE8_MEMBER(ncr5390_device::fifo_w)
|
||||
{
|
||||
LOGMASKED(LOG_FIFO, "fifo_w 0x%02x fifo_pos %d (%s)\n", data, fifo_pos, machine().describe_context());
|
||||
if(fifo_pos != 16)
|
||||
fifo[fifo_pos++] = data;
|
||||
}
|
||||
|
||||
READ8_MEMBER(ncr5390_device::command_r)
|
||||
{
|
||||
logerror("%s: command_r (%s)\n", tag(), machine().describe_context());
|
||||
LOG("command_r (%s)\n", machine().describe_context());
|
||||
return command[0];
|
||||
}
|
||||
|
||||
WRITE8_MEMBER(ncr5390_device::command_w)
|
||||
{
|
||||
logerror("%s: command_w %02x (%s)\n", tag(), data, machine().describe_context());
|
||||
LOG("command_w %02x command_pos %d (%s)\n", data, command_pos, machine().describe_context());
|
||||
if(command_pos == 2) {
|
||||
status |= S_GROSS_ERROR;
|
||||
check_irq();
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Note the RESET chip and RESET SCSI Bus commands execute as soon as they are loaded into
|
||||
* the top of the Command Register.
|
||||
*/
|
||||
if((data & 0x7f) == CM_RESET || (data & 0x7f) == CM_RESET_BUS)
|
||||
command_pos = 0;
|
||||
|
||||
command[command_pos++] = data;
|
||||
if(command_pos == 1)
|
||||
start_command();
|
||||
@ -724,7 +744,7 @@ void ncr5390_device::start_command()
|
||||
{
|
||||
uint8_t c = command[0] & 0x7f;
|
||||
if(!check_valid_command(c)) {
|
||||
logerror("%s: invalid command %02x\n", tag(), command[0]);
|
||||
LOG("invalid command %02x\n", command[0]);
|
||||
istatus |= I_ILLEGAL;
|
||||
check_irq();
|
||||
return;
|
||||
@ -737,29 +757,33 @@ void ncr5390_device::start_command()
|
||||
tcounter = tcount;
|
||||
|
||||
// clear transfer count zero flag when counter is reloaded
|
||||
if (tcounter)
|
||||
status &= ~S_TC0;
|
||||
status &= ~S_TC0;
|
||||
}
|
||||
|
||||
switch(c) {
|
||||
case CM_NOP:
|
||||
LOGMASKED(LOG_COMMAND, "NOP\n");
|
||||
command_pop_and_chain();
|
||||
break;
|
||||
|
||||
case CM_FLUSH_FIFO:
|
||||
LOGMASKED(LOG_COMMAND, "Flush FIFO\n");
|
||||
fifo_pos = 0;
|
||||
command_pop_and_chain();
|
||||
break;
|
||||
|
||||
case CM_RESET:
|
||||
LOGMASKED(LOG_COMMAND, "Reset chip\n");
|
||||
device_reset();
|
||||
break;
|
||||
|
||||
case CM_RESET_BUS:
|
||||
LOGMASKED(LOG_COMMAND, "Reset SCSI bus\n");
|
||||
reset_soft();
|
||||
break;
|
||||
|
||||
case CD_RESELECT:
|
||||
LOGMASKED(LOG_COMMAND, "Reselect sequence\n");
|
||||
state = DISC_REC_ARBITRATION;
|
||||
arbitrate();
|
||||
break;
|
||||
@ -767,31 +791,40 @@ void ncr5390_device::start_command()
|
||||
case CD_SELECT:
|
||||
case CD_SELECT_ATN:
|
||||
case CD_SELECT_ATN_STOP:
|
||||
LOGMASKED(LOG_COMMAND,
|
||||
(c == CD_SELECT) ? "Select without ATN sequence\n" :
|
||||
(c == CD_SELECT_ATN) ? "Select with ATN sequence\n" :
|
||||
"Select with ATN and stop sequence\n");
|
||||
seq = 0;
|
||||
state = DISC_SEL_ARBITRATION;
|
||||
arbitrate();
|
||||
break;
|
||||
|
||||
case CD_ENABLE_SEL:
|
||||
LOGMASKED(LOG_COMMAND, "Enable selection/reselection\n");
|
||||
command_pop_and_chain();
|
||||
break;
|
||||
|
||||
case CD_DISABLE_SEL:
|
||||
LOGMASKED(LOG_COMMAND, "Disable selection/reselection\n");
|
||||
command_pop_and_chain();
|
||||
break;
|
||||
|
||||
case CI_XFER:
|
||||
LOGMASKED(LOG_COMMAND, "Transfer information\n");
|
||||
state = INIT_XFR;
|
||||
xfr_phase = scsi_bus->ctrl_r() & S_PHASE_MASK;
|
||||
step(false);
|
||||
break;
|
||||
|
||||
case CI_COMPLETE:
|
||||
LOGMASKED(LOG_COMMAND, "Initiator command complete sequence\n");
|
||||
state = INIT_CPT_RECV_BYTE_ACK;
|
||||
recv_byte();
|
||||
break;
|
||||
|
||||
case CI_MSG_ACCEPT:
|
||||
LOGMASKED(LOG_COMMAND, "Message accepted\n");
|
||||
state = INIT_MSG_WAIT_REQ;
|
||||
// It's undocumented what the sequence register should contain after a message accept
|
||||
// command, but the InterPro boot code expects it to be non-zero; setting it to an
|
||||
@ -804,6 +837,7 @@ void ncr5390_device::start_command()
|
||||
break;
|
||||
|
||||
case CI_PAD:
|
||||
LOGMASKED(LOG_COMMAND, "Transfer pad\n");
|
||||
xfr_phase = scsi_bus->ctrl_r() & S_PHASE_MASK;
|
||||
if(xfr_phase & S_INP)
|
||||
state = INIT_XFR_RECV_PAD_WAIT_REQ;
|
||||
@ -813,9 +847,14 @@ void ncr5390_device::start_command()
|
||||
step(false);
|
||||
break;
|
||||
|
||||
case CI_SET_ATN:
|
||||
LOGMASKED(LOG_COMMAND, "Set ATN\n");
|
||||
scsi_bus->ctrl_w(scsi_refid, S_ATN, S_ATN);
|
||||
command_pop_and_chain();
|
||||
break;
|
||||
|
||||
default:
|
||||
logerror("%s: start unimplemented command %02x\n", tag(), c);
|
||||
exit(0);
|
||||
fatalerror("start unimplemented command %02x\n", c);
|
||||
}
|
||||
}
|
||||
|
||||
@ -825,7 +864,7 @@ bool ncr5390_device::check_valid_command(uint8_t cmd)
|
||||
switch((cmd >> 4) & 7) {
|
||||
case 0: return subcmd <= 3;
|
||||
case 4: return mode == MODE_D && subcmd <= 5;
|
||||
case 2: return mode == MODE_T && subcmd <= 13 && subcmd != 6;
|
||||
case 2: return mode == MODE_T && subcmd <= 11 && subcmd != 6;
|
||||
case 1: return mode == MODE_I && (subcmd <= 2 || subcmd == 8 || subcmd == 10);
|
||||
}
|
||||
return false;
|
||||
@ -858,7 +897,7 @@ READ8_MEMBER(ncr5390_device::status_r)
|
||||
{
|
||||
uint32_t ctrl = scsi_bus->ctrl_r();
|
||||
uint8_t res = status | (ctrl & S_MSG ? 4 : 0) | (ctrl & S_CTL ? 2 : 0) | (ctrl & S_INP ? 1 : 0);
|
||||
logerror("%s: status_r %02x (%s)\n", tag(), res, machine().describe_context());
|
||||
LOG("status_r %02x (%s)\n", res, machine().describe_context());
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -866,7 +905,7 @@ READ8_MEMBER(ncr5390_device::status_r)
|
||||
WRITE8_MEMBER(ncr5390_device::bus_id_w)
|
||||
{
|
||||
bus_id = data & 7;
|
||||
logerror("%s: bus_id=%d\n", tag(), bus_id);
|
||||
LOG("bus_id=%d\n", bus_id);
|
||||
}
|
||||
|
||||
READ8_MEMBER(ncr5390_device::istatus_r)
|
||||
@ -883,18 +922,19 @@ READ8_MEMBER(ncr5390_device::istatus_r)
|
||||
if(res)
|
||||
command_pop_and_chain();
|
||||
|
||||
logerror("%s: istatus_r %02x (%s)\n", tag(), res, machine().describe_context());
|
||||
LOG("istatus_r %02x (%s)\n", res, machine().describe_context());
|
||||
return res;
|
||||
}
|
||||
|
||||
WRITE8_MEMBER(ncr5390_device::timeout_w)
|
||||
{
|
||||
LOG("timeout_w 0x%02x\n", data);
|
||||
select_timeout = data;
|
||||
}
|
||||
|
||||
READ8_MEMBER(ncr5390_device::seq_step_r)
|
||||
{
|
||||
logerror("%s: seq_step_r %d (%s)\n", tag(), seq, machine().describe_context());
|
||||
LOG("seq_step_r %d (%s)\n", seq, machine().describe_context());
|
||||
return seq;
|
||||
}
|
||||
|
||||
@ -931,7 +971,7 @@ WRITE8_MEMBER(ncr5390_device::conf_w)
|
||||
WRITE8_MEMBER(ncr5390_device::test_w)
|
||||
{
|
||||
if (test_mode)
|
||||
logerror("%s: test_w %d (%s) - test mode not implemented\n", tag(), data, machine().describe_context());
|
||||
logerror("test_w %d (%s) - test mode not implemented\n", data, machine().describe_context());
|
||||
}
|
||||
|
||||
WRITE8_MEMBER(ncr5390_device::clock_w)
|
||||
@ -990,7 +1030,7 @@ void ncr5390_device::decrement_tcounter()
|
||||
}
|
||||
|
||||
/*
|
||||
* According to the NCR 53C90A, 53C90B data book (http://bitsavers.informatik.uni-stuttgart.de/pdf/ncr/scsi/NCR53C90ab.pdf),
|
||||
* According to the NCR 53C90A, 53C90B data book (http://bitsavers.org/pdf/ncr/scsi/NCR53C90ab.pdf),
|
||||
* the following are the differences from the 53C90:
|
||||
*
|
||||
* - Supports three-byte message exchange SCSI-2 tagged queueing
|
||||
@ -1028,12 +1068,23 @@ READ8_MEMBER(ncr53c90a_device::status_r)
|
||||
{
|
||||
uint32_t ctrl = scsi_bus->ctrl_r();
|
||||
uint8_t res = (irq ? S_INTERRUPT : 0) | status | (ctrl & S_MSG ? 4 : 0) | (ctrl & S_CTL ? 2 : 0) | (ctrl & S_INP ? 1 : 0);
|
||||
logerror("%s: status_r %02x (%s)\n", tag(), res, machine().describe_context());
|
||||
LOG("status_r %02x (%s)\n", res, machine().describe_context());
|
||||
if (irq)
|
||||
status &= ~(S_GROSS_ERROR | S_PARITY | S_TCC);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool ncr53c90a_device::check_valid_command(uint8_t cmd)
|
||||
{
|
||||
int subcmd = cmd & 15;
|
||||
switch ((cmd >> 4) & 7) {
|
||||
case 0: return subcmd <= 3 || (mode == MODE_T && subcmd == 4);
|
||||
case 4: return mode == MODE_D && subcmd <= 6;
|
||||
case 2: return mode == MODE_T && subcmd <= 11 && subcmd != 6;
|
||||
case 1: return mode == MODE_I && (subcmd <= 2 || subcmd == 8 || subcmd == 10 || subcmd == 11);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ncr53c94_device::device_start()
|
||||
{
|
||||
|
@ -172,6 +172,7 @@ protected:
|
||||
CD_SELECT_ATN_STOP = 0x43,
|
||||
CD_ENABLE_SEL = 0x44,
|
||||
CD_DISABLE_SEL = 0x45,
|
||||
CD_SELECT_ATN3 = 0x46, // 53c90a
|
||||
CT_SEND_MSG = 0x20,
|
||||
CT_SEND_STATUS = 0x21,
|
||||
CT_SEND_DATA = 0x22,
|
||||
@ -183,11 +184,13 @@ protected:
|
||||
CT_RECV_CMD = 0x29,
|
||||
CT_RECV_DATA = 0x2a,
|
||||
CT_RECV_CMD_SEQ = 0x2b,
|
||||
CT_ABORT_DMA = 0x04, // 53c90a
|
||||
CI_XFER = 0x10,
|
||||
CI_COMPLETE = 0x11,
|
||||
CI_MSG_ACCEPT = 0x12,
|
||||
CI_PAD = 0x18,
|
||||
CI_SET_ATN = 0x1a
|
||||
CI_SET_ATN = 0x1a,
|
||||
CI_RESET_ATN = 0x1b, // 53c90a
|
||||
};
|
||||
|
||||
enum { DMA_NONE, DMA_IN, DMA_OUT };
|
||||
@ -215,7 +218,7 @@ protected:
|
||||
|
||||
void start_command();
|
||||
void step(bool timeout);
|
||||
bool check_valid_command(uint8_t cmd);
|
||||
virtual bool check_valid_command(uint8_t cmd);
|
||||
int derive_msg_size(uint8_t msg_id);
|
||||
void function_complete();
|
||||
void function_bus_complete();
|
||||
@ -263,6 +266,8 @@ protected:
|
||||
virtual void device_start() override;
|
||||
void reset_soft();
|
||||
|
||||
virtual bool check_valid_command(uint8_t cmd) override;
|
||||
|
||||
// 53c90a uses a previously reserved bit as an interrupt flag
|
||||
enum {
|
||||
S_INTERRUPT = 0x80,
|
||||
|
Loading…
Reference in New Issue
Block a user