/*-
 * Copyright (c) 1995 Berkeley Software Design, Inc.  All rights reserved.
 * The Berkeley Software Design Inc. software License Agreement specifies
 * the terms and conditions for redistribution.
 *
 *	BSDI $Id: aic_code,v 2.3 1995/09/25 16:53:13 cp Exp $
 */

/*
 * Note
 *	Dates refer to when note made, not when any code
 *	was necessarily changed.
 *
 * Message out:Phase change: Attention out		June 19, 1995
 *	Code drops attention when phase change occurs during message
 *	out. This handles the case of targets which don't correctly reject
 *	the messge. This also handles targets which won't allow resending
 *	of MSG_IDENTIFY.
 *
 * Message out still not right.				June 20, 1995
 *	The message out code is still not really right. It now sends
 *	a initiator detected error in response to a parity error during
 *	a message in phase. Setting the length to one after a reject
 *	message really is not right either. It will not correctly handle
 *	the case where the target wants the message resent because of an
 *	error.
 *	
 */

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

LISTEND = 	0x80
COMPLETE =	0x01

SEQUENCE_OK		= 0x10
SEQUENCE_HELP		= 0x20
SEQUENCE_DEBUG		= 0x30
SEQUENCE_MSGO_PC	= 0x40			/* msg out phase changed */
SEQUENCE_NO_CODE	= 0x30

TARGETSIZE = 12 + (8 * 4)

#ld	SEQCTL, STEP;


	ld	NONE, ALLZEROS;
	ld	INTSTAT, SEQINT | SEQUENCE_OK;

idle:
	cmp	iocount, 0			jne	. + 2;
	clr	SBLKCTL, DIAGLEDON;
	ld	SCSISEQ, ENRESELI;
idleloop:
	tst	SSTAT0, SELDI			js	reconnect;
	cmp	QINCNT, 0			je	idleloop;

look4work:
	clr	gen_flags, G_SELECTDONE | G_RESELECT | G_MSGOUT;
	ld	NONE, QINFIFO;
	ld	DSTPTR, &cio_targetaddr;
	ldsi	&targethead			call	copy4;

scanloop:
	ld	DSTPTR, &target;
	ldsi	&cio_targetaddr			call	copyin;
	tst	t_newwork, 1			js	foundit;
	tst	t_flags, LISTEND		js	idle;
	add	cio_targetaddr, TARGETSIZE;
	jnc	scanloop;
	add	cio_targetaddr + 1, 1;
	jmp	scanloop;


reconnect:
	clr	gen_flags, G_SELECTDONE | G_RESELECT | G_MSGOUT;
	/*
	 * for now we just use target to figure out who to reconnect
	 */
	sr	AC, 4, SELID;	

	cmp	t_target, AC			je	righttarget;

	ld	t_target, AC;
	ld	counter, 0;
	ld	DSTPTR, &cio_targetaddr;
	ldsi	&targethead			call	copy4;
getaddr_loop:
	ld	AC, t_target;
	cmp	counter, AC			je	gotaddress;

	add	cio_targetaddr, TARGETSIZE;
	jnc	. + 2;
	add	cio_targetaddr + 1, 1;
	add	counter, 1;
	jmp	getaddr_loop;

gotaddress:
	ld	DSTPTR, &target;
	ldsi	&cio_targetaddr			call	copyin;

	/*
	 * At this point the target as been loaded.
	 * Now need to check if there is a loaded SCB. If
	 * so is it for current target or do we need to un-load
	 * it and load ours.
	 * If none loaded just go load ours.
	 */
righttarget:
	tst	gen_flags,  G_SCB_LOADED	jns	reloadscb;
	ld	AC, t_target;
	cmp	s_target, AC			je	rightscb;
	call	copyout_scb;
reloadscb:
	ld	DSTPTR, &cio_ioaddr;
	ldsi	&t_scblist			call	copy4;
	ld	cio_iolen, 4;
	ld	DSTPTR, &cio_scbaddr;
	ldsi	&cio_ioaddr			call	copyin;
	ld	DSTPTR, &scb;
	ldsi	&cio_scbaddr			call	copyin;
	set	gen_flags, G_SCB_LOADED;
rightscb:
	ld	DSTPTR, &cur_sg;
	ldsi	&s_sg				call	copysg;
	ld	SCSIID, s_scsiid;
	set	gen_flags, G_RESELECT;
	jmp	reconnectdone;
	

foundit:
	set	SBLKCTL, DIAGLEDON;
	clr	t_newwork, 1;		/* don't copy back */

	#	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	&t_newwork			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;
	ld	SCSIID, s_scsiid;
	ld	SCSISEQ, ENSELO | ENRESELI| ENAUTOATNO;

	tst	SSTAT0, SELDO			js	seloutdone;
	tst	SSTAT0, SELDI			js	reconnect;
	tst	SSTAT1, SELTIMO			jns	. -2;

	#	The device selection timed out.
	ldsi	T_NEWWORK			call	targout;
	ld	s_status, 0x80;
	ld	SCSISEQ, 0;
done_scb:
	set	s_flags, S_DONE;

	call	copyout_scb;
	clr	gen_flags, G_SCB_LOADED;
	set	t_flags, COMPLETE;
	ldsi	T_FLAGS				call	targout;
	ld	INTSTAT, CMDCMPLT;
	jmp	look4work;

seloutdone:
	/*
	 *	Tell driver we have address of SCB
	 *	Wait until now because we still need the newwork
	 *	bit if we get a reselect instead of select. Also
	 *	wait until now to update iocount so it won't
	 *	update twice in the case of of a reselect.
	 */
	add	iocount, 1;
	ldsi	T_NEWWORK			call	targout;
	set	gen_flags, G_SELECTDONE;
reconnectdone:
	ld	SCSISEQ, 0;			/* atno on parity here */
	ld	CLRSINT1, BUSFREE;
	ld	SCSIRATE, s_scsirate;
	set	SXFRCTL0, CLRCHIN;
	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;
	clr	gen_flags, G_MSGOUT;

	cmp	AC, DATA_OUT			je	p_dataout;
	cmp	AC, DATA_IN			je	p_datain;
	cmp	AC, MSG_IN			je	p_msgin;
	cmp	AC, COMMAND			je	p_command;
	cmp	AC, STATUS			je	p_status;
	ldsi	DH_UNKNOWN_PHASE		jmp	driver_help;

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

sendnop:
	ld	SCSIDATAL, MSG_NOOP;
	jmp	phaselock;

p_msgout:
	tst	gen_flags, G_RESELECT		js	sendnop;
	tst	gen_flags, G_SELECTDONE		jns	ide;
	/*
	 * 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	SCSIDATAL, DFDAT;
	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:
	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:
	call	fetch_sg;
	ldsi	&cur_sgaddr			call	long_dma;
	call	setstcnt;
	ld	DFCNTRL, SCSIEN | SDMAEN | HDMAEN | FIFORESET;
	tst	SSTAT1, PHASEMIS		js 	. + 2;
	tst	SSTAT0, DMADONE			jns	. - 1;
	tst	DFCNTRL,  FIFOFLUSH		js	.;
	tst	DFSTATUS, FIFOEMPTY		jns	.;
	tst	DFSTATUS, MREQPEND		js	.;

	ld	DFCNTRL, 0;

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

p_msgin:
	ld	DSTPTR, &last_msgin;
	call	begin_msg;
	
	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;
	cmp	last_msgin, MESSAGE_REJECT	je	message_reject;
	ldsi	DH_UNKNOWN_MSGIN		jmp	driver_help;

message_reject:
	ld	s_msgolen, 1;
	jmp	do_endmsg;

extmsg:
	call	more_msg;
	cmp	last_msgin + 1, 3		jne	tossextended;
	call	more_msg;
	call	more_msg;
	call	more_msg;
	ldsi	6				call	scb_dma;
	or	HADDR0, S_MSGI_OFFSET, s_selfptr;
	ldsi	&last_msgin			call	copyout1;
do_endmsg:
	call	end_msg;
	jmp	phaselock;

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

msg_ignore:
	call	end_msg;
	jmp	phaselock;

disconnect:
	call	end_msg;
	tst	SSTAT1, BUSFREE			jns	.;
	jmp	idle;

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

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

msg_command_complete:
	call	end_msg;
	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;

p_command:
	ldsi	s_cdblen			call	scb_dma;
	call	setstcnt;
	or	HADDR0, S_CDB_OFFSET, s_selfptr;
	ld	DFCNTRL, SCSIEN | SDMAEN | HDMAEN | DIRECTION | FIFORESET;
	tst	SSTAT1, PHASEMIS		js 	. + 2;
	tst	SSTAT0, DMADONE			jns	. - 1;
	tst	DFSTATUS, FIFOEMPTY		jns	.;
	ld	DFCNTRL, 0;
	jmp	phaselock;

p_status:
	ld	s_status, SCSIDATAL;
	jmp	phaselock;
	
copysg:	call	copy4;
	call	copy4;			/* fall through for one more copy 4 */

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;

targout:	
	ld	AC, SRCPTR;
	ld	DSTPTR, &cio_ioaddr;
	ldsi	&cio_targetaddr			call	copy4;
	add	cio_ioaddr, AC;
	jnc	. + 2;
	add	cio_ioaddr + 1, 1;
	add	AC, &target;
	ld	SRCPTR, &cio_ioaddr;
	ld	cio_iolen, 1;
	jmp	copyout;

copyout_scb:
	ld	DSTPTR, &cio_scbaddr;
	ldsi	&s_selfptr			call	copy4;
	ld	AC, &scb;
	ld	SRCPTR, &cio_scbaddr;
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:
	call	short_dma;
	ld	counter, HCNT0;
	ld	DFCNTRL, HDMAEN | DIRECTION | FIFORESET; 
	ld	NONE, ALLZEROS;
	tst	DFSTATUS, HDONE			jns	.;
	ld	DSTDATA, DFDAT;
	add	counter, 0xff;
	jnz	. - 2;

	ld	DFCNTRL, 0;	
	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 msgin_phase_change;
	tst	SSTAT0, SPIORDY			jns . - 1;

begin_msg:
	ld	DSTDATA,SCSIBUSL		ret;

end_msg:
	ld	NONE, SCSIDATAL			ret;

msgin_phase_change:
	ldsi	DH_MSGIN_PHASECHNG		jmp	driver_help;

code_end:

DH_MSGOUT		= 0x00
DH_UNKNOWN_MSGIN	= 0x01
DH_OVERRUN		= 0x02
DH_UNKNOWN_PHASE	= 0x03
DH_MSGIN_PHASECHNG	= 0x04

