/*-
 * Copyright (c) 1995, 1996, 1997, 1999 Berkeley Software Design, Inc.
 * All rights reserved.
 * The Berkeley Software Design Inc. software License Agreement specifies
 * the terms and conditions for redistribution.
 *
 *	BSDI aic_code,v 2.15 1999/12/16 02:10:04 cp Exp
 */

#include aic_reg.def
#include aic_sram.def
#include aic_scb.def


SEQUENCE_OK		= 0x10
SEQUENCE_HELP		= 0x20
SEQUENCE_TRACE		= 0x30
SEQUENCE_MSGIN		= 0x40
SEQUENCE_CHECK		= 0x50	/* unit returned check condition */
SEQUENCE_PARITY		= 0x60

/*
 * following turn on and off tracing
 */
#ld	SEQCTL, STEP;
#ld	SEQCTL, 0;

/*
 * following generate TRACE interrupt
 */
#ld	INTSTAT, SEQINT | SEQUENCE_TRACE;
#ld INTSTAT, SEQINT | SEQUENCE_TRACE; ld	NONE, ALLZEROS; ld NONE, ALLZEROS;

	ld	AC, 0x66;
	ld	INTSTAT, SEQINT | SEQUENCE_OK;
	ld	NONE, ALLZEROS;
	ld	NONE, ALLZEROS;
	ld	NONE, ALLZEROS;

idle:
	cmp	iocount, 0			jne	. + 2;
	clr	SBLKCTL, DIAGLEDON;
	clr	SXFRCTL1, ENSPCHK;
	ld	SCSISEQ, ENRESELI | ENAUTOATNP;
	tst	gen_flags, G_ULTRA2		js	Idleloop;
idleloop:
	tst	SSTAT0, SELDI			js	reconnect;
	cmp	QINCNT, 0			je	idleloop;
	jmp	old_look4work;

look4work:
	tst	gen_flags, G_ULTRA2		jns	old_look4work;
	tst	QOFF_CTLSTA, SCB_AVAIL		jns	look4work_doit;
	ld	NONE, SNSCB_QOFF;
	jmp	. - 2;

old_look4work:
	ld	NONE, QINFIFO;
look4work_doit:
	clr	gen_flags, G_SELECTDONE | G_RESELECT;
	ld	cio_iolen, q_copysize;
	ld	DSTPTR, &cio_ioaddr;
	ldsi	&startq				call	copy4;

	ld	DSTPTR, &queue;
	ldsi	&cio_ioaddr			call	copyin;
	tst	q_flags, Q_READY		jns	idle;
	/*
	 * On some boards the PCI bus appears to be unmapped
	 * when on reboot. The firmware gets all ones from
	 * memory, and proceeds to wedge the SCSI bus
	 * solid. The flags word should only have the
	 * low order bit on, if more bits are on something
	 * is very wrong; stop now.
	 */
	tst	q_flags, 0xfe			js	.;
	/*
	 * Reload the queue info. The flag is not the first
	 * thing in the packet so we can get the flag set
	 * but data that the driver put in the q before setting
	 * the flag.
	 */
	ld	DSTPTR, &queue;
	ldsi	&cio_ioaddr			call	copyin;
	tst	q_flags, Q_READY		jns	.;
	tst	q_flags, 0xfe			js	.;
	jmp	foundit;

Idleloop:
	tst	QOFF_CTLSTA, SCB_AVAIL		js	look4work;
	tst	SSTAT0, SELDI			jns	Idleloop;

reconnect:
	clr	gen_flags, G_SELECTDONE | G_RESELECT;

	/*
	 * Load the resel info for this target. We do this always
	 * first because we need the sync info to be loaded by
	 * the time we start looking for a tag.
	 */
	ld	DSTPTR, &cio_ioaddr;
	ldsi	&reselect			call	copy4;
	sr	AC, 1, SELID;	
	and	AC, 0x78;
	add	cio_ioaddr, AC;
	ld	DSTPTR, &resel_info;
	ld	cio_iolen, ri_copysize;
	ldsi	&cio_ioaddr			call	copyin;

	ldsi	ri_scsiid			call	loadtid;
	ld	SCSISEQ, ENAUTOATNP;
	tst	ri_flags, RI_F_DO_PARITY	jns	. + 2;
	set	SXFRCTL1, ENSPCHK;

	ld	CLRSINT1, BUSFREE;
	ld	SCSIRATE, ri_scsirate;
	tst	gen_flags, G_ULTRA2		jns	. + 2;
	and	SCSIOFFSET, ~RI_F_DO_PARITY, ri_flags;
	or	SXFRCTL0, CLRCHIN, ri_sxfrctl0;
tag_loop:
	tst	SSTAT1, REQINIT			jns	.;	
	and	AC, PHASE_MASK, SCSISIGI;
	cmp	AC, MSG_IN			jne	target_reload;

	/*
	 * The target has reconnected. Watch for a tag queue. If
	 * this comes first use it to reselect scb. If we get an
	 * identify toss it, according to the spec this shouldn't
	 * happen.
	 */

	ld	DSTPTR, &last_msgin;
	ld	DSTDATA,SCSIBUSL;
	cmp	last_msgin, 0x20		je	gottag;
	tst	last_msgin, IDENTIFY		jns	target_reload;
	ld	NONE, SCSIDATAL;
	jmp	tag_loop;
gottag:
	call	more_msg;
	ld	NONE, SCSIDATAL;
	ld	AC, last_msgin + 1;
	add	AC, AC, last_msgin + 1;
	addc	cio_ioaddr + 1, 1;
	add	cio_ioaddr + 1, -1;
	ld	cio_ioaddr, AC;
	ld	DSTPTR, &resel_info;
	ldsi	&cio_ioaddr			call	copyin;

target_reload:
	tst	gen_flags, G_SCB_LOADED		jns	reloadscb;
	ld	AC, cio_ioaddr;
	sr	AC, 1, cio_ioaddr;	
	tst	cio_ioaddr + 1, 1		jns	. + 2;
	set	AC, 0x80;
	cmp	s_tag, AC			je	rightscb;
	call	copyout_scb;

reloadscb:
	ld	DSTPTR, &cio_scbaddr;
	ldsi	&q_scb				call	copy4;
	ld	DSTPTR, &scb;
	ldsi	&cio_scbaddr			call	copyin;
	set	gen_flags, G_SCB_LOADED;
rightscb:
	ld	DSTPTR, &cur_sg;
	ldsi	&s_sg				call	copysg;
	ldsi	s_scsiid			call	loadtid;
	set	gen_flags, G_RESELECT;
	jmp	phaselock1;
	
noauto:
	clr	SXFRCTL1, ENSPCHK;
	ld	SCSISEQ, ENSELO | ENRESELI | ENAUTOATNP;
	jmp	select_loop;

loadtid:
	tst	gen_flags, G_ULTRA2		js	. +2;
	ld	SCSIID, SRCPTR			ret;
	ld	SCSIID_U2, SRCPTR		ret;

foundit:
	set	SBLKCTL, DIAGLEDON;

	#	If there is an active SCB copy it back to main memory
	#
	tst	gen_flags,  G_SCB_LOADED	jns	. + 2;
	call	copyout_scb;


	#	Load in the new SCB	
	#
	ld	DSTPTR, &cio_scbaddr;
	ldsi	&q_scb				call	copy4;
	ld	DSTPTR, &scb;
	ldsi	&cio_scbaddr			call	copyin;
	set	gen_flags, G_SCB_LOADED;

	/*
	 *	make cur scatter gather reflect the scb
	 *	which we just loaded
	 */
	ld	DSTPTR, &cur_sg;
	ldsi	&s_sg				call	copysg;

	ld	CLRSINT1, SELTIMO;

	ldsi	s_scsiid			call	loadtid;

	cmp	s_msgolen, 0			je	noauto;
	clr	SXFRCTL1, ENSPCHK;
	ld	SCSISEQ, ENSELO | ENRESELI | ENAUTOATNO | ENAUTOATNP;
select_loop:
	tst	SSTAT0, SELDO			js	seloutdone;
	tst	SSTAT0, SELDI			js	reconnect;
	tst	SSTAT1, SELTIMO			jns	select_loop;

	#	The device selection timed out.
	call	next_iqueue;
	ld	s_status, 0x80;
	ld	SCSISEQ, ENAUTOATNP;
done_scb:
	call	copyout_scb;
	clr	gen_flags, G_SCB_LOADED;

	/*
	 * read in next slot one done q. The read
	 * is really just to pick up the next pointer
	 */
	ld	cio_iolen, q_copysize;
	ld	DSTPTR, &cio_ioaddr;
	ldsi	&doneq				call	copy4;
	ld	DSTPTR, &queue;
	ldsi	&cio_ioaddr			call	copyin;
	/*
	 * Copy the virtual address of the scb, from the scb,
	 * to the doneq entry. Also set the ready bit.
	 */
	ld	DSTPTR, &q_scb;
	ldsi	&s_vaddr			call	copy4;
	set	q_flags, Q_READY;
	/*
	 * Copy out the done q entry and update the doneq
	 * pointer to the next element.
	 */
	ld	AC, &queue;
	ldsi	&cio_ioaddr			call	copyout;
	ld	DSTPTR, &doneq;
	ldsi	&q_next				call	copy4;

	/*
	 * Tell the host to come and get it
	 */
	ld	INTSTAT, CMDCMPLT;
	jmp	look4work;

next_iqueue:
	/*
	 * Update the startq pointer to the next element.
	 * Copy out the startq element telling the host
	 * the the firmware has accepted it.
	 */
	ld	DSTPTR, &startq;
	ldsi	&q_next				call	copy4;
	clr	q_flags, Q_READY;
	ld	AC, &queue;
	ldsi	&cio_ioaddr			jmp	copyout;

seloutdone:
	/*
	 *	Tell driver we have address of SCB
	 *	Wait until now because we need to leave the
	 *	scb on the queue if we get reselected so we
	 *	can pick it up latter and try starting it again.
	 */
	add	iocount, 1;
	set	gen_flags, G_SELECTDONE;
	tst	s_flags, S_F_DO_PARITY		jns	. + 2;
	set	SXFRCTL1, ENSPCHK;
	call	next_iqueue;

	ld	SCSISEQ, ENAUTOATNP;
	ld	CLRSINT1, BUSFREE;
	ld	SCSIRATE, s_scsirate;
	tst	gen_flags, G_ULTRA2		jns	. + 2;
	ld	SCSIOFFSET, s_scsioffset;
	or	SXFRCTL0, CLRCHIN, s_sxfrctl0;
	jmp	phaselock1;

phaselock:
	clr	gen_flags, G_SELECTDONE | G_RESELECT;
phaselock1:
	tst	SSTAT1, REQINIT			jns	.;	
	and	AC, PHASE_MASK, SCSISIGI;
	ld	SCSISIGO, AC;


	cmp	AC, MSG_OUT			je 	p_msgout;
	cmp	AC, DATA_OUT			je	p_dataout;
	cmp	AC, DT_DATA_OUT			je	p_dataout;
	cmp	AC, DATA_IN			je	p_datain;
	cmp	AC, DT_DATA_IN			je	p_datain;
	cmp	AC, MSG_IN			je	p_msgin;
	cmp	AC, COMMAND			je	p_command;

	/*
	 * At this point has to be status message
	 */
	clr	gen_flags, G_MSGIN;
	ld	s_status, SCSIDATAL;
	ld	AC, s_status;
	and	AC, 0x3e;
	cmp	AC, 2				jne	phaselock;
	ld	INTSTAT, SEQINT | SEQUENCE_CHECK;
	jmp	phaselock;

	/*
	 * initiator detected error
	 */
ide:	
	ld	CLRSINT1, ATNO;
	ld	SCSIDATAL, INITIATOR_DETECTED_ERROR;
	jmp	phaselock;

sendnop:
	ld	SCSIDATAL, MSG_NOOP;
	jmp	phaselock;

perror:
	ld	INTSTAT, SEQINT | SEQUENCE_PARITY;
	ld	CLRSINT1, SCSIPERR;
	tst	gen_flags, G_MSGIN		jns	ide;
	ld	CLRSINT1, ATNO;
	ld	SCSIDATAL, MESSAGE_PARITY_ERROR;
	jmp	phaselock;
	
p_msgout:
	tst	SSTAT1, SCSIPERR		js	perror;
	tst	gen_flags, G_RESELECT		js	sendnop;
	tst	gen_flags, G_SELECTDONE		jns	ide;
	tst	gen_flags, G_ULTRA2		js	P_msgout;
	/*
	 * load fifo with message
	 */
	ldsi	s_msgolen			call	scb_dma;
	or	HADDR0, S_MSGO_OFFSET, s_selfptr;
	ld	DFCNTRL, HDMAEN | DIRECTION | FIFORESET; 
	tst	DFSTATUS, HDONE			jns	.;
	ld	counter, s_msgolen;

msgout_tloop:
	/*
	 * when only 1 byte left drop attention
	 * drop attentin and make sure at least two instructions
	 * before sending data. 
	 */
	tst	SSTAT0, SPIORDY			jns	.;	/* bus free?? */
	cmp	counter, 1			jne	. + 2;
	ld	CLRSINT1, ATNO;
	tst	SSTAT1, PHASEMIS		js 	msgout_phasechange;
	add	counter, 0xff;
	ld	AC, DFDAT;
	ld	SCSIDATAL, AC;
	cmp	counter, 0			jne	msgout_tloop;
	ld	DFCNTRL, 0;
	jmp	phaselock;

msgout_phasechange:
	ld	CLRSINT1, ATNO;			/* drop attention */
	ld	DFCNTRL, 0;
	jmp	phaselock;

p_dataout:
	tst	gen_flags, G_ULTRA2		js	P_dataout;
	call	fetch_sg;
	ldsi	&cur_sgaddr			call	long_dma;
	call	setstcnt;

	ld	DFCNTRL, SCSIEN | SDMAEN | HDMAEN | DIRECTION | FIFORESET;
	tst	SSTAT1, PHASEMIS		js 	. + 2;
	tst	SSTAT0, DMADONE			jns	. - 1;
	tst	DFSTATUS, MREQPEND		js	.;
	ld	DFCNTRL, 0;
	tst	DFCNTRL, SCSIEN | SDMAEN | HDMAEN	js .;
	ldsi	&cur_sgaddr			call	update_sg;
	jmp	phaselock;

p_datain:
	clr	gen_flags, G_MSGIN;
	tst	gen_flags, G_ULTRA2		js	P_datain;
	call	fetch_sg;
	ldsi	&cur_sgaddr			call	long_dma;
	call	setstcnt;
	tst	cur_sgflags, S_SGF_WIDEODD	js	. + 3;
	ld	DFCNTRL, SCSIEN | SDMAEN | HDMAEN | FIFORESET;
	jmp	. + 2;
	ld	DFCNTRL, SCSIEN | SDMAEN | HDMAEN | FIFORESET | WIDEODD;
	tst	SSTAT1, PHASEMIS		js 	. + 2;
	tst	SSTAT0, DMADONE			jns	. - 1;
	tst	DFCNTRL,  FIFOFLUSH		js	.;
	tst	DFSTATUS, FIFOEMPTY		jns	.;
	tst	DFSTATUS, MREQPEND		js	.;

	and	DFCNTRL, WIDEODD;

	tst	DFCNTRL, SCSIEN | SDMAEN | HDMAEN | FIFOFLUSH	js .;
	ldsi	&cur_sgaddr			call	update_sg;
	jmp	phaselock;

p_msgin:
	set	gen_flags, G_MSGIN;
	ld	DSTPTR, &last_msgin;
	ld	DSTDATA,SCSIBUSL;
	
	cmp	last_msgin, COMMAND_COMPLETE	je	msg_command_complete;
	cmp	last_msgin, SAVE_DATA_PTR	je	save_data_ptr;
	cmp	last_msgin, RESTORE_DATA_PTR	je	restore_data_ptr;
	cmp	last_msgin, DISCONNECT		je	disconnect;
	tst	last_msgin, IDENTIFY		js	msg_ignore;
	cmp	last_msgin, EXTENEDED_MESSAGE	je	extmsg;
	ld	AC, last_msgin;
	and	AC, 0xf0;
	cmp	AC, 0x20			jne	msg2driver;
	call	more_msg;
msg2driver:
	tst	gen_flags, G_ULTRA2		js	Msg2driver;
	ldsi	8				call	scb_dma;
	or	HADDR0, S_MSGI_OFFSET, s_selfptr;
	ldsi	&last_msgin			call	copyout1;
	ld	INTSTAT, SEQINT | SEQUENCE_MSGIN;
	jmp	phaselock;

extmsg:
	call	more_msg;
	ld	AC, 0;
	cmp	last_msgin + 1, AC		je	msg2driver;
	call	more_msg;
	add	AC, 1;
	jmp	. - 3;
	

msg_ignore:
	ld	NONE, SCSIDATAL;
	jmp	phaselock;

disconnect:
	ld	NONE, SCSIDATAL;
	tst	SSTAT1, BUSFREE			jns	.;
	tst	s_flags, S_F_BROKE_DISCONNECT	jns	look4work;
	ld	DSTPTR, &s_sg;
	ldsi	&cur_sg				call	copysg;
	jmp	look4work;

restore_data_ptr:
	ld	NONE, SCSIDATAL;
	ld	DSTPTR, &cur_sg;
	ldsi	&s_sg				call	copysg;
	jmp	phaselock;

save_data_ptr:
	ld	NONE, SCSIDATAL;
	ld	DSTPTR, &s_sg;
	ldsi	&cur_sg				call	copysg;
	jmp	phaselock;

msg_command_complete:
	ld	NONE, SCSIDATAL;
	ld	DSTPTR, &s_sg;
	ldsi	&cur_sg				call	copysg;
	tst	SSTAT1, BUSFREE			jns	.;
	add	iocount, 0xff;
	jmp	done_scb;

setstcnt:
	ld	DSTPTR, &STCNT0;
	ldsi	&HCNT0			jmp	copy3;


/*
 * For reasons that are unclear on sytems without 3.3
 * volts, maybe on pentium pro systems, we can't
 * dma command directly to scsi bus. We instead load
 * command into fifo and have u_code pump it out of
 * fifo to scsi bus.
 */
p_command:
	tst	gen_flags, G_ULTRA2		js	P_command;
	ldsi	s_cdblen			call	scb_dma;
	call	setstcnt;
	or	HADDR0, S_CDB_OFFSET, s_selfptr;

	ld	AC, HCNT0;
	ld	counter, AC;

	ld	DFCNTRL, HDMAEN | DIRECTION | FIFORESET; 
	tst	DFSTATUS, HDONE			jns	.;
p_command_l1:
	tst	SSTAT0, SPIORDY			jns	.;	/* bus free?? */
	add	counter, 0xff;
	ld	SCSIDATAL, DFDAT;
	cmp	counter, 0			jne	p_command_l1;
	ld	DFCNTRL, 0;
	jmp	phaselock;

p_status:

	
copysg:	call	copy4;			/* fall through for one more copy 4 */
	call	copy4;

copy4:	ld	DSTDATA, SRCDATA;
copy3:	ld	DSTDATA, SRCDATA;
	ld	DSTDATA, SRCDATA;
	ld	DSTDATA, SRCDATA		ret;


fetch_sg:
	/*
	 * first check if we have a working version
	 */
	tst	cur_sgflags, S_SGF_LOADED	js	return;
	ld	DSTPTR, &cio_ioaddr;
	ldsi	&cur_sgentry			call	copy4;
	ld	DSTPTR, &cur_sgaddr;
	ld	cio_iolen, SG_ENTRY_SIZE;
	ldsi	&cio_ioaddr			call	copyin;
	tst	cur_sgflags, S_SGF_OVERRUN	jns	. + 2;
	ldsi	DH_OVERRUN			jmp	driver_help;
	set	cur_sgflags, S_SGF_LOADED	ret;

/*
 *	called whenever a scsi dma transfer ends
 *	update to next entry in scatter gather list if
 *	no resid. If resid update current scatter gather
 *	values
 */
update_sg:
	tst	SSTAT0, DMADONE			jns	update_sg1;
	add	cur_sgentry, SIZEOF_SG;
	jnc	. + 2;
	add	cur_sgentry + 1, 1;
	clr	cur_sgflags, S_SGF_LOADED	ret;

update_sg1:
	set	SXFRCTL0, CLRCHIN;
	ld	DSTPTR, &cur_sgaddr;
	ldsi	&SHADDR0			call	copy4;
	ldsi	&STCNT0				jmp	copy3;

copyout_scb:
	ld	DSTPTR, &cio_scbaddr;
	ldsi	&s_selfptr			call	copy4;
	ld	AC, &scb;
	ld	SRCPTR, &cio_scbaddr;
copyout:
	tst	gen_flags, G_ULTRA2		js	Copyout;
	call	short_dma;
	ld	SRCPTR, AC;
copyout1:
	ld	counter, HCNT0;
	ld	DFCNTRL, HDMAEN | FIFORESET; 

	ld	DFDAT, SRCDATA;
	add	counter, 0xff;
	jnz	. - 2;
	ld	DFCNTRL, HDMAEN | FIFOFLUSH;
	tst	DFSTATUS, HDONE			jns	.;
	ld	DFCNTRL, 0			ret;
	
driver_help:
	ld	INTSTAT, SEQINT | SEQUENCE_HELP;
	
copyin:
	tst	gen_flags, G_ULTRA2		js	Copyin;
	call	short_dma;
	ld	counter, HCNT0;
	or	DFCNTRL, HDMAEN | DIRECTION | FIFORESET;
	ld	NONE, ALLZEROS;
	tst	DFSTATUS, HDONE			jns	.;
	ld	DSTDATA, DFDAT;
	add	counter, 0xff;
	jnz	. - 2;

	and	DFCNTRL, WIDEODD;	
	tst	DFCNTRL, HDMAEN			js	.;
return:	ret;



scb_dma:
	ld	HADDR1, s_selfptr1;
	ld	HADDR2, s_selfptr2;
	ld	HADDR3, s_selfptr3;
	ld	HCNT0,	SRCPTR;			/* contains data not address */
	ld	HCNT1,	0;		
	ld	HCNT2,	0			ret;	

short_dma:
	ld	HADDR0, SRCDATA;
	ld	HADDR1, SRCDATA;
	ld	HADDR2, SRCDATA;
	ld	HADDR3, SRCDATA;

	ld	HCNT0, SRCDATA;
	ld	HCNT2, 0;
	ld	HCNT1, 0			ret;

long_dma:
	ld	HADDR0, SRCDATA;
	ld	HADDR1, SRCDATA;
	ld	HADDR2, SRCDATA;
	ld	HADDR3, SRCDATA;

	ld	HCNT0, SRCDATA;
	ld	HCNT1, SRCDATA;
	ld	HCNT2, SRCDATA			ret;

more_msg:
	ld	NONE, SCSIDATAL;
	tst	SSTAT1, PHASEMIS		js phaselock1; # parity err ?
	tst	SSTAT0, SPIORDY			jns . - 1;
	ld	DSTDATA, SCSIBUSL		ret;


low_code_end::

Copyin:
	ld	CCSCBCTL, CCSCBRESET;
	mv	CCHADDR0, SRCDATA, 4;
	ld	counter, SRCDATA;
	ld	CCHCNT, counter;
	ld	CCSCBCTL, CCSCBEN | CCSCBDIR | CCSCBRESET;
	tst	CCSCBCTL, CCSCBDONE		jns .;
	ld	CCSCBCTL, 0;
	ld	CCSCBCNT, 0;	# gets loaded along with CCHCNT
	tst	CCSCBCTL, CCSCBEN		js .;
	ld	CCSCBADR, 0;
	ld	DSTDATA, CCSCBRAM;
	add	counter, 0xff;
	jnz	. -2;
	ld	CCSCBADR, 0			 ret;

Copyout:
	ld	CCSCBCTL, CCSCBRESET;
	mv	CCHADDR0, SRCDATA, 4;
	ld	counter, SRCDATA;
	ld	CCHCNT, counter;
	ld	SRCPTR, AC;
Copyout_up:
	ld	DSTPTR, &CCSCBRAM;
	ld	DSTDATA, SRCDATA;
	add	counter, 0xff;
	jnz	Copyout_up;
	ld	CCSCBCTL, CCSCBEN | CCSCBRESET;
	tst	CCSCBCTL, CCSCBDONE		jns .;
	ld	CCSCBCTL, 0;
	cmp	CCSCBCTL, 0			jne .;
	ret;

P_msgout:
	or	CCHADDR0, S_MSGO_OFFSET, s_selfptr;
	ldsi	s_msgolen			call Scb_load_fifo;
	ld	counter, s_msgolen;

Msgout_tloop:
	/*
	 * when only 1 byte left drop attention
	 * drop attentin and make sure at least two instructions
	 * before sending data. 
	 */
	tst	SSTAT0, SPIORDY			jns	.;	/* bus free?? */
	cmp	counter, 1			jne	. + 2;
	ld	CLRSINT1, ATNO;
	tst	SSTAT1, PHASEMIS		js 	Msgout_phasechange;
	add	counter, 0xff;
	ld	AC, CCSCBRAM;
	ld	SCSIDATAL, AC;
	cmp	counter, 0			jne	Msgout_tloop;
	jmp	phaselock;

Msgout_phasechange:
	ld	CLRSINT1, ATNO;			/* drop attention */
	jmp	phaselock;


P_command:
	or	CCHADDR0, S_CDB_OFFSET, s_selfptr;
	ldsi	s_cdblen			call	Scb_load_fifo;
	ld	counter, s_cdblen;
P_command_l1:
	tst	SSTAT0, SPIORDY			jns	.;	/* bus free?? */
	add	counter, 0xff;
	ld	SCSIDATAL, CCSCBRAM;
	cmp	counter, 0			jne	P_command_l1;
	jmp	phaselock;

P_datain:
	set	SXFRCTL0, CLRSTCNT;
P_datain_1:
	call	fetch_sg;
	mv	HADDR0, cur_sgaddr, 7;
	ld	SG_CACHEPTR, 0;
	tst	cur_sgflags, S_SGF_LASTSEG	jns	. + 2;
	ld	SG_CACHEPTR, SG_LASTSEG;
	ld	DFCNTRL, PRELOAD | SCSIEN | HDMAEN;
	tst	SSTAT0, DMADONE			js	.; /* wait to go */

	tst	SSTAT1, PHASEMIS		js 	Datain_phase_change;
	tst	SSTAT0, DMADONE			jns	. - 1;

	tst	cur_sgflags, S_SGF_LASTSEG	js	Datain_phase_change;
	ldsi	&cur_sgaddr			call	update_sg;
	jmp	P_datain_1;

Datain_phase_change:
	clr	DFCNTRL, SCSIEN;
	tst	DFCNTRL, SCSIEN		js .;
	or	DFCNTRL, FIFOFLUSH;
	tst	DFSTATUS, FIFOEMPTY		jns	.;
	clr	DFCNTRL, HDMAEN;
	tst	DFCNTRL, HDMAEN		js .;

	ldsi	&cur_sgaddr			call	update_sg;
	jmp	phaselock;

P_dataout:
	set	SXFRCTL0, CLRSTCNT;
P_dataout1:
	call	fetch_sg;
	mv	HADDR0, cur_sgaddr, 7;
	ld	SG_CACHEPTR, 0;
	tst	cur_sgflags, S_SGF_LASTSEG	jns	. + 2;
	ld	SG_CACHEPTR, SG_LASTSEG;
	ld	DFCNTRL, PRELOAD | SCSIEN | HDMAEN | DIRECTION;
	tst	SSTAT0, DMADONE			js	.; /* wait to go */

	tst	SSTAT1, PHASEMIS		js 	Dataout_phase_change;
	tst	SSTAT0, DMADONE			jns	. - 1;

	tst	cur_sgflags, S_SGF_LASTSEG	js	Dataout_phase_change;
	ldsi	&cur_sgaddr			call	update_sg;
	jmp	P_dataout1;

Dataout_phase_change:
	clr	DFCNTRL, SCSIEN | HDMAEN;
	tst	DFCNTRL, HDMAEN			js .;
	ldsi	&cur_sgaddr			call	update_sg;
	jmp	phaselock;

Msg2driver:
	ld	CCSCBCTL, CCSCBRESET;
	or	CCHADDR0, S_MSGI_OFFSET, s_selfptr;
	mv	CCHADDR1, s_selfptr1, 3;
	ld	CCHCNT, 8;
	ld	counter, 8;
	ld	SRCPTR, &last_msgin;
Msg2driver_l1:
	ld	DSTPTR, &CCSCBRAM;
	ld	DSTDATA, SRCDATA;
	add	counter, 0xff;
	jnz	Msg2driver_l1;
	ld	CCSCBCTL, CCSCBEN | CCSCBRESET;
	tst	CCSCBCTL, CCSCBDONE		jns .;
	ld	CCSCBCTL, 0;
	cmp	CCSCBCTL, 0			jne .;
	ld	INTSTAT, SEQINT | SEQUENCE_MSGIN;
	jmp	phaselock;

Scb_load_fifo:
	mv	CCHADDR1, s_selfptr1, 3;
Load_fifo:
	ld	CCHCNT, SRCPTR;
	ld	CCSCBCTL, CCSCBEN | CCSCBDIR | CCSCBRESET;
	tst	CCSCBCTL, CCSCBDONE		jns .;
	ld	CCSCBCTL, 0;
	ld	CCSCBCNT, 0;	# gets loaded along with CCHCNT
	tst	CCSCBCTL, CCSCBEN		js .;
	ld	CCSCBADR, 0;
	cmp	CCSCBCTL, 0			jne .;
	ret;

hi_code_end::

DH_OVERRUN		= 0x02
DH_UNKNOWN_PHASE	= 0x03
DH_NOCODE		= 0x04
