summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/ncr5390.cpp
blob: d84a57733d3733fc4e1a9ec34bac256d961761ba (plain) (tree)
1
2

                                     




















                                                                    
                                                                                                             
                                                                                                                                                                                                               
                                                                                                                                                                  

                             




                                   

                                     



                                 





                                        


                                
                                  
                                     



                                        

                             
 

                                     





















                                      
                           


                     





                                                                  
                           














                                            
                                                                


















                                                                                             
                                                                                                           
                                                                        















                                                                          
                                                                   


                                                               
                                                                  



















































                                                                                                                     
                                                                               

                                      
                                                                                  








                                                        
                                                                        


















































































































































































                                                                                        
                                                                                                             
















































































                                                               
                                                      
                                                                                             






                                
                                                                      


                                                                       
                                                              























































                                                                      
                                                                                                          






                                          
                                                                                                 



                                         
                                                                                                        






                                                 
                                                                                                 






































                                                       
                                                                                    




                                        
                                                                                                   
























                                                
                                                                                  



































































                                                              
                                                                                     

































                                                                                      

                                   






                                                                                                      
                                                                                             







                                                          
                                                           










                                        
                                                                                              









                                        
                                                                                             

































































                                                               
                                   






                                
                                   

         
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert

#include "emu.h"
#include "ncr5390.h"

#define DELAY_HACK

const device_type NCR5390 = &device_creator<ncr5390_device>;

DEVICE_ADDRESS_MAP_START(map, 8, ncr5390_device)
	AM_RANGE(0x0, 0x0) AM_READWRITE(tcount_lo_r, tcount_lo_w)
	AM_RANGE(0x1, 0x1) AM_READWRITE(tcount_hi_r, tcount_hi_w)
	AM_RANGE(0x2, 0x2) AM_READWRITE(fifo_r, fifo_w)
	AM_RANGE(0x3, 0x3) AM_READWRITE(command_r, command_w)
	AM_RANGE(0x4, 0x4) AM_READWRITE(status_r, bus_id_w)
	AM_RANGE(0x5, 0x5) AM_READWRITE(istatus_r, timeout_w)
	AM_RANGE(0x6, 0x6) AM_READWRITE(seq_step_r, sync_period_w)
	AM_RANGE(0x7, 0x7) AM_READWRITE(fifo_flags_r, sync_offset_w)
	AM_RANGE(0x8, 0x8) AM_READWRITE(conf_r, conf_w)
	AM_RANGE(0x9, 0x9) AM_WRITE(clock_w)
ADDRESS_MAP_END

ncr5390_device::ncr5390_device(const machine_config &mconfig, std::string tag, device_t *owner, UINT32 clock)
		: nscsi_device(mconfig, NCR5390, "5390 SCSI", tag, owner, clock, "ncr5390", __FILE__), tm(nullptr), config(0), status(0), istatus(0), clock_conv(0), sync_offset(0), sync_period(0), bus_id(0),
	select_timeout(0), seq(0), tcount(0), mode(0), fifo_pos(0), command_pos(0), state(0), xfr_phase(0), command_length(0), dma_dir(0), irq(false), drq(false),
	m_irq_handler(*this),
	m_drq_handler(*this)
{
}

void ncr5390_device::device_start()
{
	nscsi_device::device_start();

	save_item(NAME(command));
	save_item(NAME(config));
	save_item(NAME(status));
	save_item(NAME(istatus));
	save_item(NAME(clock_conv));
	save_item(NAME(sync_offset));
	save_item(NAME(sync_period));
	save_item(NAME(bus_id));
	save_item(NAME(select_timeout));
	save_item(NAME(seq));
	save_item(NAME(fifo));
	save_item(NAME(tcount));
	save_item(NAME(mode));
	save_item(NAME(fifo_pos));
	save_item(NAME(command_pos));
	save_item(NAME(state));
	save_item(NAME(xfr_phase));
	save_item(NAME(command_length));
	save_item(NAME(dma_dir));
	save_item(NAME(irq));
	save_item(NAME(drq));

	m_irq_handler.resolve_safe();
	m_drq_handler.resolve_safe();

	tcount = 0;
	config = 0;
	status = 0;
	bus_id = 0;
	select_timeout = 0;
	tm = timer_alloc(0);
}

void ncr5390_device::device_reset()
{
	fifo_pos = 0;
	memset(fifo, 0, sizeof(fifo));

	clock_conv = 2;
	sync_period = 5;
	sync_offset = 0;
	seq = 0;
	config &= 7;
	status &= 0x90;
	istatus = 0;
	irq = false;
	m_irq_handler(irq);
	reset_soft();
}

void ncr5390_device::reset_soft()
{
	state = IDLE;
	scsi_bus->ctrl_wait(scsi_refid, S_SEL|S_BSY|S_RST, S_ALL);
	status &= 0xef;
	drq = false;
	m_drq_handler(drq);
	reset_disconnect();
}

void ncr5390_device::reset_disconnect()
{
	command_pos = 0;
	command_length = 0;
	memset(command, 0, sizeof(command));
	mode = MODE_D;
}

void ncr5390_device::scsi_ctrl_changed()
{
	UINT32 ctrl = scsi_bus->ctrl_r();
	if(ctrl & S_RST) {
		logerror("%s: scsi bus reset\n", tag().c_str());
		return;
	}

	step(false);
}

void ncr5390_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	step(true);
}

void ncr5390_device::step(bool timeout)
{
	UINT32 ctrl = scsi_bus->ctrl_r();
	UINT32 data = scsi_bus->data_r();
	UINT8 c     = command[0] & 0x7f;

	if(0)
		logerror("%s: state=%d.%d %s\n",
					tag().c_str(), state & STATE_MASK, (state & SUB_MASK) >> SUB_SHIFT,
					timeout ? "timeout" : "change");

	if(mode == MODE_I && !(ctrl & S_BSY)) {
		state = IDLE;
		istatus |= I_DISCONNECT;
		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--) {};
		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");
		}
		state = (state & STATE_MASK) | (ARB_ASSERT_SEL << SUB_SHIFT);
		scsi_bus->ctrl_w(scsi_refid, S_SEL, S_SEL);
		delay(6);
		break;
	}

	case ARB_ASSERT_SEL << SUB_SHIFT:
		if(!timeout)
			break;

		scsi_bus->data_w(scsi_refid, (1<<scsi_id) | (1<<bus_id));
		state = (state & STATE_MASK) | (ARB_SET_DEST << SUB_SHIFT);
		delay_cycles(4);
		break;

	case ARB_SET_DEST << SUB_SHIFT:
		if(!timeout)
			break;

		state = (state & STATE_MASK) | (ARB_RELEASE_BUSY << SUB_SHIFT);
		scsi_bus->ctrl_w(scsi_refid, c == CD_SELECT_ATN || c == CD_SELECT_ATN_STOP ? S_ATN : 0, S_ATN|S_BSY);
		delay(2);
		break;

	case ARB_RELEASE_BUSY << SUB_SHIFT:
		if(!timeout)
			break;

		if(ctrl & S_BSY) {
			state = (state & STATE_MASK) | (ARB_DESKEW_WAIT << SUB_SHIFT);
			if(c == CD_RESELECT)
				scsi_bus->ctrl_w(scsi_refid, S_BSY, S_BSY);
			delay_cycles(2);
		} else {
			state = (state & STATE_MASK) | (ARB_TIMEOUT_BUSY << SUB_SHIFT);
#ifdef DELAY_HACK
			delay(1);
#else
			delay(8192*select_timeout);
#endif
		}
		break;

	case ARB_DESKEW_WAIT << SUB_SHIFT:
		if(!timeout)
			break;

		scsi_bus->data_w(scsi_refid, 0);
		scsi_bus->ctrl_w(scsi_refid, 0, S_SEL);

		if(c == CD_RESELECT) {
			logerror("%s: mode switch to Target\n", tag().c_str());
			mode = MODE_T;
		} else {
			logerror("%s: mode switch to Initiator\n", tag().c_str());
			mode = MODE_I;
		}
		state &= STATE_MASK;
		step(true);
		break;

	case ARB_TIMEOUT_BUSY << SUB_SHIFT:
		if(timeout) {
			scsi_bus->data_w(scsi_refid, 0);
			logerror("%s: select timeout\n", tag().c_str());
			state = (state & STATE_MASK) | (ARB_TIMEOUT_ABORT << SUB_SHIFT);
			delay(1000);
		} else if(ctrl & S_BSY) {
			state = (state & STATE_MASK) | (ARB_DESKEW_WAIT << SUB_SHIFT);
			if(c == CD_RESELECT)
				scsi_bus->ctrl_w(scsi_refid, S_BSY, S_BSY);
			delay_cycles(2);
		}
		break;

	case ARB_TIMEOUT_ABORT << SUB_SHIFT:
		if(!timeout)
			break;

		if(ctrl & S_BSY) {
			state = (state & STATE_MASK) | (ARB_DESKEW_WAIT << SUB_SHIFT);
			if(c == CD_RESELECT)
				scsi_bus->ctrl_w(scsi_refid, S_BSY, S_BSY);
			delay_cycles(2);
		} else {
			scsi_bus->ctrl_w(scsi_refid, 0, S_ALL);
			state = IDLE;
			istatus |= I_DISCONNECT;
			reset_disconnect();
			check_irq();
		}
		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);
		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;

		if((state & STATE_MASK) != INIT_XFR_RECV_PAD)
			fifo_push(scsi_bus->data_r());
		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;

	case DISC_SEL_ARBITRATION:
		if(c == CD_SELECT) {
			state = DISC_SEL_WAIT_REQ;
			command_length = derive_msg_size(fifo[0]);
		} else
			state = DISC_SEL_ATN_WAIT_REQ;

		scsi_bus->ctrl_wait(scsi_refid, S_REQ, S_REQ);
		if(ctrl & S_REQ)
			step(false);
		break;

	case DISC_SEL_ATN_WAIT_REQ:
		if(!(ctrl & S_REQ))
			break;
		if((ctrl & S_PHASE_MASK) != S_PHASE_MSG_OUT) {
			function_complete();
			break;
		}
		if(c == CD_SELECT_ATN)
			scsi_bus->ctrl_w(scsi_refid, 0, S_ATN);
		state = DISC_SEL_ATN_SEND_BYTE;
		send_byte();
		break;

	case DISC_SEL_ATN_SEND_BYTE:
		if(c == CD_SELECT_ATN_STOP) {
			seq = 1;
			function_complete();
		} else {
			command_length = derive_msg_size(fifo[0]);
			state = DISC_SEL_WAIT_REQ;
		}
		break;

	case DISC_SEL_WAIT_REQ:
		if(!(ctrl & S_REQ))
			break;
		if((ctrl & S_PHASE_MASK) != S_PHASE_COMMAND) {
			if(!command_length)
				seq = 4;
			scsi_bus->ctrl_wait(scsi_refid, 0, S_REQ);
			function_bus_complete();
			break;
		}
		if(seq < 3)
			seq = 3;
		state = DISC_SEL_SEND_BYTE;
		send_byte();
		break;

	case DISC_SEL_SEND_BYTE:
		if(command_length) {
			command_length--;
			if(!command_length)
				seq = 4;
		}

		state = DISC_SEL_WAIT_REQ;
		break;

	case INIT_CPT_RECV_BYTE_ACK:
		state = INIT_CPT_RECV_WAIT_REQ;
		scsi_bus->ctrl_w(scsi_refid, 0, S_ACK);
		break;

	case INIT_CPT_RECV_WAIT_REQ:
		if(!(ctrl & S_REQ))
			break;

		if((ctrl & S_PHASE_MASK) != S_PHASE_MSG_IN) {
			command_pos = 0;
			bus_complete();
		} else {
			state = INIT_CPT_RECV_BYTE_NACK;
			recv_byte();
		}
		break;

	case INIT_CPT_RECV_BYTE_NACK:
		scsi_bus->ctrl_wait(scsi_refid, 0, S_REQ);
		function_complete();
		break;

	case INIT_MSG_WAIT_REQ:
		if((ctrl & (S_REQ|S_BSY)) == S_BSY)
			break;
		bus_complete();
		break;

	case INIT_XFR:
		switch(xfr_phase) {
		case S_PHASE_DATA_OUT:
			dma_set(DMA_OUT);
			if(tcount == 0 && fifo_pos == 1)
				scsi_bus->ctrl_w(scsi_refid, 0, S_ATN);
			state = INIT_XFR_SEND_BYTE;
			send_byte();
			break;

		case S_PHASE_DATA_IN:
			dma_set(DMA_IN);
			state = tcount == fifo_pos+1 ?
				INIT_XFR_RECV_BYTE_NACK : INIT_XFR_RECV_BYTE_ACK;
			recv_byte();
			break;

		default:
			logerror("%s: xfer on phase %d\n", tag().c_str(), scsi_bus->ctrl_r() & S_PHASE_MASK);
			function_complete();
			break;
		}
		break;

	case INIT_XFR_WAIT_REQ:
		if(!(ctrl & S_REQ))
			break;

		if((ctrl & S_PHASE_MASK) != xfr_phase) {
			command_pos = 0;
			bus_complete();
		} else {
			state = INIT_XFR;
			step(false);
		}
		break;

	case INIT_XFR_SEND_BYTE:
		if(tcount == 0 && fifo_pos == 0)
			bus_complete();
		else
			state = INIT_XFR_WAIT_REQ;
		break;

	case INIT_XFR_RECV_BYTE_ACK:
		state = INIT_XFR_WAIT_REQ;
		scsi_bus->ctrl_w(scsi_refid, 0, S_ACK);
		break;

	case INIT_XFR_RECV_BYTE_NACK:
		function_complete();
		break;

	case INIT_XFR_SEND_PAD_WAIT_REQ:
		if(!(ctrl & S_REQ))
			break;

		if((ctrl & S_PHASE_MASK) != xfr_phase) {
			command_pos = 0;
			bus_complete();
		} else {
			state = INIT_XFR_SEND_PAD;
			send_byte();
		}
		break;

	case INIT_XFR_SEND_PAD:
		tcount--;
		if(tcount) {
			state = INIT_XFR_SEND_PAD_WAIT_REQ;
			step(false);
		} else
			function_complete();
		break;

	case INIT_XFR_RECV_PAD_WAIT_REQ:
		if(!(ctrl & S_REQ))
			break;

		if((ctrl & S_PHASE_MASK) != xfr_phase) {
			command_pos = 0;
			bus_complete();
		} else {
			state = INIT_XFR_RECV_PAD;
			recv_byte();
		}
		break;

	case INIT_XFR_RECV_PAD:
		tcount--;
		if(tcount) {
			state = INIT_XFR_RECV_PAD_WAIT_REQ;
			scsi_bus->ctrl_w(scsi_refid, 0, S_ACK);
			step(false);
		} else
			function_complete();
		break;

	default:
		logerror("%s: step() unexpected state %d.%d\n",
					tag().c_str(),
					state & STATE_MASK, (state & SUB_MASK) >> SUB_SHIFT);
		exit(0);
	}
}

void ncr5390_device::send_byte()
{
	if(!fifo_pos)
		fatalerror("ncr5390_device::send_byte - !fifo_pos\n");

	state = (state & STATE_MASK) | (SEND_WAIT_SETTLE << SUB_SHIFT);
	if((state & STATE_MASK) != INIT_XFR_SEND_PAD &&
		((state & STATE_MASK) != DISC_SEL_SEND_BYTE ||
		command_length))
		scsi_bus->data_w(scsi_refid, fifo_pop());
	else
		scsi_bus->data_w(scsi_refid, 0);

	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 ncr5390_device::recv_byte()
{
	scsi_bus->ctrl_wait(scsi_refid, S_REQ, S_REQ);
	state = (state & STATE_MASK) | (RECV_WAIT_REQ_1 << SUB_SHIFT);
	step(false);
}

void ncr5390_device::function_bus_complete()
{
	state = IDLE;
	istatus |= I_FUNCTION|I_BUS;
	dma_set(DMA_NONE);
	check_irq();
}

void ncr5390_device::function_complete()
{
	state = IDLE;
	istatus |= I_FUNCTION;
	dma_set(DMA_NONE);
	check_irq();
}

void ncr5390_device::bus_complete()
{
	state = IDLE;
	istatus |= I_BUS;
	dma_set(DMA_NONE);
	check_irq();
}

void ncr5390_device::delay(int cycles)
{
	if(!clock_conv)
		return;
	cycles *= clock_conv;
	tm->adjust(clocks_to_attotime(cycles));
}

void ncr5390_device::delay_cycles(int cycles)
{
	tm->adjust(clocks_to_attotime(cycles));
}

READ8_MEMBER(ncr5390_device::tcount_lo_r)
{
	logerror("%s: tcount_lo_r %02x (%08x)\n", tag().c_str(), tcount & 0xff, space.device().safe_pc());
	return tcount;
}

WRITE8_MEMBER(ncr5390_device::tcount_lo_w)
{
	tcount = (tcount & 0xff00) | data;
	status &= ~S_TC0;
	logerror("%s: tcount_lo_w %02x (%08x)\n", tag().c_str(), data, space.device().safe_pc());
}

READ8_MEMBER(ncr5390_device::tcount_hi_r)
{
	logerror("%s: tcount_hi_r %02x (%08x)\n", tag().c_str(), tcount >> 8, space.device().safe_pc());
	return tcount >> 8;
}

WRITE8_MEMBER(ncr5390_device::tcount_hi_w)
{
	tcount = (tcount & 0x00ff) | (data << 8);
	status &= ~S_TC0;
	logerror("%s: tcount_hi_w %02x (%08x)\n", tag().c_str(), data, space.device().safe_pc());
}

UINT8 ncr5390_device::fifo_pop()
{
	UINT8 r = fifo[0];
	fifo_pos--;
	memmove(fifo, fifo+1, fifo_pos);
	if((!fifo_pos) && tcount && dma_dir == DMA_OUT)
		drq_set();
	return r;
}

void ncr5390_device::fifo_push(UINT8 val)
{
	fifo[fifo_pos++] = val;
	if(!drq && dma_dir == DMA_IN)
		drq_set();
}

READ8_MEMBER(ncr5390_device::fifo_r)
{
	UINT8 r;
	if(fifo_pos) {
		r = fifo[0];
		fifo_pos--;
		memmove(fifo, fifo+1, fifo_pos);
	} else
		r = 0;
	return r;
}

WRITE8_MEMBER(ncr5390_device::fifo_w)
{
	if(fifo_pos != 16)
		fifo[fifo_pos++] = data;
}

READ8_MEMBER(ncr5390_device::command_r)
{
	logerror("%s: command_r (%08x)\n", tag().c_str(), space.device().safe_pc());
	return command[0];
}

WRITE8_MEMBER(ncr5390_device::command_w)
{
	//  logerror("%s: command_w %02x (%08x)\n", tag().c_str(), data, space.device().safe_pc());
	if(command_pos == 2) {
		status |= S_GROSS_ERROR;
		check_irq();
		return;
	}
	command[command_pos++] = data;
	if(command_pos == 1)
		start_command();
}

void ncr5390_device::command_pop_and_chain()
{
	if(command_pos) {
		command_pos--;
		if(command_pos) {
			command[0] = command[1];
			start_command();
		}
	}
}

void ncr5390_device::start_command()
{
	UINT8 c = command[0] & 0x7f;
	if(!check_valid_command(c)) {
		logerror("%s: invalid command %02x\n", tag().c_str(), command[0]);
		istatus |= I_ILLEGAL;
		check_irq();
		return;
	}

	switch(c) {
	case CM_NOP:
		command_pop_and_chain();
		break;

	case CM_FLUSH_FIFO:
		fifo_pos = 0;
		command_pop_and_chain();
		break;

	case CM_RESET:
		device_reset();
		break;

	case CM_RESET_BUS:
		reset_soft();
		break;

	case CD_RESELECT:
		state = DISC_REC_ARBITRATION;
		arbitrate();
		break;

	case CD_SELECT:
	case CD_SELECT_ATN:
	case CD_SELECT_ATN_STOP:
		seq = 0;
		state = DISC_SEL_ARBITRATION;
		arbitrate();
		break;

	case CD_ENABLE_SEL:
		command_pop_and_chain();
		break;

	case CI_XFER:
		state = INIT_XFR;
		xfr_phase = scsi_bus->ctrl_r() & S_PHASE_MASK;
		step(false);
		break;

	case CI_COMPLETE:
		state = INIT_CPT_RECV_BYTE_ACK;
		recv_byte();
		break;

	case CI_MSG_ACCEPT:
		state = INIT_MSG_WAIT_REQ;
		scsi_bus->ctrl_w(scsi_refid, 0, S_ACK);
		step(false);
		break;

	case CI_PAD:
		xfr_phase = scsi_bus->ctrl_r() & S_PHASE_MASK;
		if(xfr_phase & S_INP)
			state = INIT_XFR_RECV_PAD_WAIT_REQ;
		else
			state = INIT_XFR_SEND_PAD_WAIT_REQ;
		scsi_bus->ctrl_w(scsi_refid, 0, S_ACK);
		step(false);
		break;

	default:
		logerror("%s: start unimplemented command %02x\n", tag().c_str(), c);
		exit(0);
	}
}

bool ncr5390_device::check_valid_command(UINT8 cmd)
{
	int subcmd = cmd & 15;
	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 1: return mode == MODE_I && (subcmd <= 2 || subcmd == 8 || subcmd == 10);
	}
	return false;
}

int ncr5390_device::derive_msg_size(UINT8 msg_id)
{
	const static int sizes[8] = { 6, 10, 6, 6, 6, 12, 6, 10 };
	return sizes[msg_id >> 5];
}

void ncr5390_device::arbitrate()
{
	state = (state & STATE_MASK) | (ARB_COMPLETE << SUB_SHIFT);
	scsi_bus->data_w(scsi_refid, 1 << scsi_id);
	scsi_bus->ctrl_w(scsi_refid, S_BSY, S_BSY);
	delay(11);
}

void ncr5390_device::check_irq()
{
	bool oldirq = irq;
	irq = istatus != 0;
	if(irq != oldirq)
		m_irq_handler(irq);

}

READ8_MEMBER(ncr5390_device::status_r)
{
	UINT32 ctrl = scsi_bus->ctrl_r();
	UINT8 res = status | (ctrl & S_MSG ? 4 : 0) | (ctrl & S_CTL ? 2 : 0) | (ctrl & S_INP ? 1 : 0);
	logerror("%s: status_r %02x (%08x)\n", tag().c_str(), res, space.device().safe_pc());
	if(irq)
		status &= ~(S_GROSS_ERROR|S_PARITY|S_TCC);
	return res;
}

WRITE8_MEMBER(ncr5390_device::bus_id_w)
{
	bus_id = data & 7;
	logerror("%s: bus_id=%d\n", tag().c_str(), bus_id);
}

READ8_MEMBER(ncr5390_device::istatus_r)
{
	UINT8 res = istatus;
	istatus = 0;
	seq = 0;
	check_irq();
	if(res)
		command_pop_and_chain();

	logerror("%s: istatus_r %02x (%08x)\n", tag().c_str(), res, space.device().safe_pc());
	return res;
}

WRITE8_MEMBER(ncr5390_device::timeout_w)
{
	select_timeout = data;
}

READ8_MEMBER(ncr5390_device::seq_step_r)
{
	logerror("%s: seq_step_r %d (%08x)\n", tag().c_str(), seq, space.device().safe_pc());
	return seq;
}

WRITE8_MEMBER(ncr5390_device::sync_period_w)
{
	sync_period = data & 0x1f;
}

READ8_MEMBER(ncr5390_device::fifo_flags_r)
{
	return fifo_pos;
}

WRITE8_MEMBER(ncr5390_device::sync_offset_w)
{
	sync_offset = data & 0x0f;
}

READ8_MEMBER(ncr5390_device::conf_r)
{
	return config;
}

WRITE8_MEMBER(ncr5390_device::conf_w)
{
	config = data;
	scsi_id = data & 7;
}

WRITE8_MEMBER(ncr5390_device::clock_w)
{
	clock_conv = data & 0x07;
}

void ncr5390_device::dma_set(int dir)
{
	dma_dir = dir;
	if(dma_dir == DMA_OUT && fifo_pos != 16 && tcount != 0)
		drq_set();
}

void ncr5390_device::dma_w(UINT8 val)
{
	fifo_push(val);
	tcount--;
	if(fifo_pos == 16 || tcount == 0)
		drq_clear();
}

UINT8 ncr5390_device::dma_r()
{
	UINT8 r = fifo_pop();
	if(!fifo_pos)
		drq_clear();
	tcount--;
	if(tcount == 0) {
		status |= S_TC0;
		step(false);
	}
	return r;
}

void ncr5390_device::drq_set()
{
	if(!drq) {
		drq = true;
		m_drq_handler(drq);
	}
}

void ncr5390_device::drq_clear()
{
	if(drq) {
		drq = false;
		m_drq_handler(drq);
	}
}