Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 13 Jun 2000 18:00:03 -0700 (PDT)
From:      Geir Inge Jensen <gij@jk.priv.no>
To:        freebsd-bugs@FreeBSD.org
Subject:   Re: i386/19226: SCSI timeouts during heavy load
Message-ID:  <200006140100.SAA13387@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help
The following reply was made to PR i386/19226; it has been noted by GNATS.

From: Geir Inge Jensen <gij@jk.priv.no>
To: ken@kdm.org (Kenneth D. Merry)
Cc: gij@jk.priv.no (Geir Inge Jensen),
	FreeBSD-gnats-submit@FreeBSD.ORG, gibbs@FreeBSD.ORG
Subject: Re: i386/19226: SCSI timeouts during heavy load
Date: Wed, 14 Jun 2000 02:59:11 +0200 (CEST)

 "Kenneth D. Merry" wrote:
 
 > I'm really not sure what your problem is, so I've handed it off to Justin
 > Gibbs <gibbs@FreeBSD.ORG>, who is the author of the Adaptec driver.
 
 We have done some more debugging since yesterday. 
 
 The timeouts could have been caused by a blocked interrupt line. However,
 we checked both the IOAPIC and cpl after we lost contact with the drives, 
 and that is not the case. This is the output of our kld module:
 
 SMP: blocked=0x00fba200, apending=0x00000000, imen=0x00fba200
 SMP: pending=0x00000000, active=0x00000000, cpl=0x00000000
 
  - blocked shows which interrupts has the IOART_INTMSET bit set in the 
    corresponding IOAPIC redirection table entry
  - apending shows which interrupts has the IOART_REM_IRR bit set in the
    corresponding IOAPIC redirection table entry.
  - imem is a copy of apic_imem.
  - pending is a copy of ipending.
  - active is a copy of iactive.
  - cpl is cpl.
 
 ahc0 has irq 11, and ahc1 has irq 18. As you can see, neither is blocked.
 
 We also tried to get in contact with the disk drives by polling ahc_poll
 for each controller 100 times pr. second. It didn't work. This action did
 help on another test system we had (by first disabling the interrupt 
 lines).
 
 So we began looking at the sequencer code. This is a typical timeout 
 message:
 
 (da6:ahc1:0:8:0): SCB 0x33 - timed out while idle, SEQADDR == 0x157
 
 The SCB's differ, but is is always with SEQADDR == 0x157.
 Below is the sequencer code for the AHA29160 controller. It looks
 like the cmd channel is 'stuck', and the sequencer code is hanging in
 fetch_byte. Or it might be that CCSGDONE never is set in CCSGCTL. So
 we end up with an endless loop.
 
  - Geir Inge
 
   ----- SEQUENCER CODE FOR AHA 29160 -----
 
 
 		/*
 		 * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
 		 *
 		 * Copyright (c) 1994-2000 Justin Gibbs.
 		 * All rights reserved.
 		 *
 		 * Redistribution and use in source and binary forms, with or without
 		 * modification, are permitted provided that the following conditions
 		 * are met:
 		 * 1. Redistributions of source code must retain the above copyright
 		 *    notice, this list of conditions, and the following disclaimer,
 		 *    without modification.
 		 * 2. The name of the author may not be used to endorse or promote products
 		 *    derived from this software without specific prior written permission.
 		 *
 		 * Alternatively, this software may be distributed under the terms of the
 		 * the GNU Public License ("GPL").
 		 *
 		 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 		 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 		 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 		 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 		 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 		 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 		 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 		 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 		 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 		 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 		 * SUCH DAMAGE.
 		 *
 		 * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.seq,v 1.95 2000/03/18 22:28:20 gibbs Exp $
 		 */
 		
 		#include <dev/aic7xxx/aic7xxx.reg>
 		#include <cam/scsi/scsi_message.h>
 		
 		/*
 		 * A few words on the waiting SCB list:
 		 * After starting the selection hardware, we check for reconnecting targets
 		 * as well as for our selection to complete just in case the reselection wins
 		 * bus arbitration.  The problem with this is that we must keep track of the
 		 * SCB that we've already pulled from the QINFIFO and started the selection
 		 * on just in case the reselection wins so that we can retry the selection at
 		 * a later time.  This problem cannot be resolved by holding a single entry
 		 * in scratch ram since a reconnecting target can request sense and this will
 		 * create yet another SCB waiting for selection.  The solution used here is to 
 		 * use byte 27 of the SCB as a psuedo-next pointer and to thread a list
 		 * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB indexes, 
 		 * SCB_LIST_NULL is 0xff which is out of range.  An entry is also added to
 		 * this list everytime a request sense occurs or after completing a non-tagged
 		 * command for which a second SCB has been queued.  The sequencer will
 		 * automatically consume the entries.
 		 */
 		
 		reset:
 000 ff6a0608		clr	SCSISIGO;		/* De-assert BSY */
 001 086a6800		mvi	MSG_OUT, MSG_NOOP;	/* No message to send */
 002 7f020408		and	SXFRCTL1, ~BITBUCKET;
 			/* Always allow reselection */
 003 32580008		and	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE;
 			if ((ahc->features & AHC_CMD_CHAN) != 0) {
 				/* Ensure that no DMA operations are in progress */
 004 ff6ad609			clr	CCSGCTL;
 005 ff6adc09			clr	CCSCBCTL;
 			}
 		
 		poll_for_work:
 006 0065ec59		call	clear_target_state;
 007 f7010208		and	SXFRCTL0, ~SPIOEN;
 			if ((ahc->features & AHC_QUEUE_REGS) == 0) {
 				mov	A, QINPOS;
 			}
 		poll_for_work_loop:
 			if ((ahc->features & AHC_QUEUE_REGS) == 0) {
 				and	SEQCTL, ~PAUSEDIS;
 			}
 008 600b7c68		test	SSTAT0, SELDO|SELDI	jnz selection;
 009 40001268		test	SCSISEQ, ENSELO	jnz poll_for_work_loop;
 			if ((ahc->features & AHC_TWIN) != 0) {
 				/*
 				 * Twin channel devices cannot handle things like SELTO
 				 * interrupts on the "background" channel.  So, if we
 				 * are selecting, keep polling the current channel util
 				 * either a selection or reselection occurs.
 				 */
 				xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
 				test	SSTAT0, SELDO|SELDI	jnz selection;
 				test	SCSISEQ, ENSELO	jnz poll_for_work;
 				xor	SBLKCTL,SELBUSB;	/* Toggle back */
 			}
 00a ff3e4a60		cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
 		test_queue:
 			/* Has the driver posted any work for us? */
 			if ((ahc->features & AHC_QUEUE_REGS) != 0) {
 00b 40fa1278			test	QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
 00c fff6d408			mov	NONE, SNSCB_QOFF;
 00d 014e9c18			inc	QINPOS;
 			} else {
 				or	SEQCTL, PAUSEDIS;
 				cmp	KERNEL_QINPOS, A je poll_for_work_loop;
 				inc	QINPOS;
 				and	SEQCTL, ~PAUSEDIS;
 			}
 		
 		/*
 		 * We have at least one queued SCB now and we don't have any 
 		 * SCBs in the list of SCBs awaiting selection.  If we have
 		 * any SCBs available for use, pull the tag from the QINFIFO
 		 * and get to work on it.
 		 */
 			if ((ahc->flags & AHC_PAGESCBS) != 0) {
 00e 006a945d			mov	ALLZEROS	call	get_free_or_disc_scb;
 			}
 		
 		dequeue_scb:
 00f ff4ec818		add	A, -1, QINPOS;
 010 016aa05c		mvi	QINFIFO_OFFSET call fetch_byte;
 		
 			if ((ahc->flags & AHC_PAGESCBS) == 0) {
 				/* In the non-paging case, the SCBID == hardware SCB index */
 				mov	SCBPTR, RETURN_2;
 			}
 		dma_queued_scb:
 		/*
 		 * DMA the SCB from host ram into the current SCB location.
 		 */
 011 0d6a6a00		mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
 012 00531e5d		mov	RETURN_2	 call dma_scb;
 		
 		/*
 		 * Preset the residual fields in case we never go through a data phase.
 		 * This isn't done by the host so we can avoid a DMA to clear these
 		 * fields for the normal case of I/O that completes without underrun
 		 * or overrun conditions.
 		 */
 			if ((ahc->features & AHC_CMD_CHAN) != 0) {
 013 03b05231			bmov	SCB_RESID_DCNT, SCB_DATACNT, 3;
 			} else {
 				mov	SCB_RESID_DCNT[0],SCB_DATACNT[0];
 				mov	SCB_RESID_DCNT[1],SCB_DATACNT[1];
 				mov	SCB_RESID_DCNT[2],SCB_DATACNT[2];
 			}
 014 ffa35009		mov	SCB_RESID_SGCNT, SCB_SGCOUNT;
 		
 		start_scb:
 			/*
 			 * Place us on the waiting list in case our selection
 			 * doesn't win during bus arbitration.
 			 */
 015 ff3e7409		mov	SCB_NEXT,WAITING_SCBH;
 016 ff907c08		mov	WAITING_SCBH, SCBPTR;
 		start_waiting:
 			/*
 			 * Pull the first entry off of the waiting SCB list.
 			 */
 017 ff3e2009		mov	SCBPTR, WAITING_SCBH;
 018 00655058		call	start_selection;
 019 00650c40		jmp	poll_for_work;
 		
 		start_selection:
 			if ((ahc->features & AHC_TWIN) != 0) {
 				and	SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
 				and	A,SELBUSB,SCB_TCL;	/* Get new channel bit */
 				or	SINDEX,A;
 				mov	SBLKCTL,SINDEX;		/* select channel */
 			}
 		initialize_scsiid:
 01a ff58ca08		mov	SINDEX, SCSISEQ_TEMPLATE;
 			if ((ahc->flags & AHC_TARGETMODE) != 0) {
 				test	SCB_CONTROL, TARGET_SCB jz . + 4;
 				if ((ahc->features & AHC_ULTRA2) != 0) {
 					mov	SCSIID_ULTRA2, SCB_CMDPTR[2];
 				} else {
 					mov	SCSIID, SCB_CMDPTR[2];
 				}
 				or	SINDEX, TEMODE;
 				jmp	initialize_scsiid_fini;
 			}
 			if ((ahc->features & AHC_ULTRA2) != 0) {
 01b f0a1c808			and	A, TID, SCB_TCL;	/* Get target ID */
 01c 0f0f1e08			and	SCSIID_ULTRA2, OID;	/* Clear old target */
 01d 000f1e00			or	SCSIID_ULTRA2, A;
 			} else {
 				and	A, TID, SCB_TCL;	/* Get target ID */
 				and	SCSIID, OID;		/* Clear old target */
 				or	SCSIID, A;
 			}
 		initialize_scsiid_fini:
 01e ff65000c		mov	SCSISEQ, SINDEX ret;
 		
 		/*
 		 * Initialize transfer settings and clear the SCSI channel.
 		 * SINDEX should contain any additional bit's the client wants
 		 * set in SXFRCTL0.  We also assume that the current SCB is
 		 * a valid SCB for the target we wish to talk to.
 		 */
 		initialize_channel:
 01f 12650200		or	SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX;
 		set_transfer_settings:
 			if ((ahc->features & AHC_ULTRA) != 0) {
 				test	SCB_CONTROL, ULTRAENB jz . + 2;
 				or	SXFRCTL0, FAST20;
 			} 
 		/*
 		 * Initialize SCSIRATE with the appropriate value for this target.
 		 */
 			if ((ahc->features & AHC_ULTRA2) != 0) {
 020 02bb0834			bmov	SCSIRATE, SCB_SCSIRATE, 2 ret;
 			} else {
 				mov	SCSIRATE, SCB_SCSIRATE ret;
 			}
 		
 		selection:
 021 400b1069		test	SSTAT0,SELDO	jnz select_out;
 022 206a1600		mvi	CLRSINT0, CLRSELDI;
 		select_in:
 			if ((ahc->flags & AHC_TARGETMODE) != 0) {
 				if ((ahc->flags & AHC_INITIATORMODE) != 0) {
 					test	SSTAT0, TARGET	jz initiator_reselect;
 				}
 		
 				/*
 				 * We've just been selected.  Assert BSY and
 				 * setup the phase for receiving messages
 				 * from the target.
 				 */
 				mvi	SCSISIGO, P_MESGOUT|BSYO;
 				mvi	CLRSINT1, CLRBUSFREE;
 		
 				/*
 				 * Setup the DMA for sending the identify and
 				 * command information.
 				 */
 				or	SEQ_FLAGS, CMDPHASE_PENDING;
 		
 				mov     A, TQINPOS;
 				if ((ahc->features & AHC_CMD_CHAN) != 0) {
 					mvi	DINDEX, CCHADDR;
 					mvi	TMODE_CMDADDR call set_32byte_addr;
 					mvi	CCSCBCTL, CCSCBRESET;
 				} else {
 					mvi	DINDEX, HADDR;
 					mvi	TMODE_CMDADDR call set_32byte_addr;
 					mvi	DFCNTRL, FIFORESET;
 				}
 		
 				/* Initiator that selected us */
 				and	SAVED_TCL, SELID_MASK, SELID;
 				if ((ahc->features & AHC_CMD_CHAN) != 0) {
 					mov	CCSCBRAM, SAVED_TCL;
 				} else {
 					mov	DFDAT, SAVED_TCL;
 				}
 		
 				/* The Target ID we were selected at */
 				if ((ahc->features & AHC_CMD_CHAN) != 0) {
 					if ((ahc->features & AHC_MULTI_TID) != 0) {
 						and	CCSCBRAM, OID, TARGIDIN;
 					} else if ((ahc->features & AHC_ULTRA2) != 0) {
 						and	CCSCBRAM, OID, SCSIID_ULTRA2;
 					} else {
 						and	CCSCBRAM, OID, SCSIID;
 					}
 				} else {
 					if ((ahc->features & AHC_MULTI_TID) != 0) {
 						and	DFDAT, OID, TARGIDIN;
 					} else if ((ahc->features & AHC_ULTRA2) != 0) {
 						and	DFDAT, OID, SCSIID_ULTRA2;
 					} else {
 						and	DFDAT, OID, SCSIID;
 					}
 				}
 		
 				/* No tag yet */
 				mvi	INITIATOR_TAG, SCB_LIST_NULL;
 		
 				/*
 				 * If ATN isn't asserted, the target isn't interested
 				 * in talking to us.  Go directly to bus free.
 				 */
 				test	SCSISIGI, ATNI	jz	target_busfree;
 		
 				/*
 				 * Watch ATN closely now as we pull in messages from the
 				 * initiator.  We follow the guidlines from section 6.5
 				 * of the SCSI-2 spec for what messages are allowed when.
 				 */
 				call	target_inb;
 		
 				/*
 				 * Our first message must be one of IDENTIFY, ABORT, or
 				 * BUS_DEVICE_RESET.
 				 */
 				/* XXX May need to be more lax here for older initiators... */
 				test	DINDEX, MSG_IDENTIFYFLAG jz host_target_message_loop;
 				/* Store for host */
 				if ((ahc->features & AHC_CMD_CHAN) != 0) {
 					mov	CCSCBRAM, DINDEX;
 				} else {
 					mov	DFDAT, DINDEX;
 				}
 		
 				/* Remember for disconnection decision */
 				test	DINDEX, MSG_IDENTIFY_DISCFLAG jnz . + 2;
 				/* XXX Honor per target settings too */
 				or	SEQ_FLAGS, NO_DISCONNECT;
 		
 				test	SCSISIGI, ATNI	jz	ident_messages_done;
 				call	target_inb;
 				/*
 				 * If this is a tagged request, the tagged message must
 				 * immediately follow the identify.  We test for a valid
 				 * tag message by seeing if it is >= MSG_SIMPLE_Q_TAG and
 				 * < MSG_IGN_WIDE_RESIDUE.
 				 */
 				add	A, -MSG_SIMPLE_Q_TAG, DINDEX;
 				jnc	ident_messages_done;
 				add	A, -MSG_IGN_WIDE_RESIDUE, DINDEX;
 				jc	ident_messages_done;
 				/* Store for host */
 				if ((ahc->features & AHC_CMD_CHAN) != 0) {
 					mov	CCSCBRAM, DINDEX;
 				} else {
 					mov	DFDAT, DINDEX;
 				}
 				
 				/*
 				 * If the initiator doesn't feel like providing a tag number,
 				 * we've got a failed selection and must transition to bus
 				 * free.
 				 */
 				test	SCSISIGI, ATNI	jz	target_busfree;
 		
 				/*
 				 * Store the tag for the host.
 				 */
 				call	target_inb;
 				if ((ahc->features & AHC_CMD_CHAN) != 0) {
 					mov	CCSCBRAM, DINDEX;
 				} else {
 					mov	DFDAT, DINDEX;
 				}
 				mov	INITIATOR_TAG, DINDEX;
 				jmp	ident_messages_done;
 		
 				/*
 				 * Pushed message loop to allow the kernel to
 				 * run it's own target mode message state engine.
 				 */
 		host_target_message_loop:
 				mvi	INTSTAT, HOST_MSG_LOOP;
 				nop;
 				cmp	RETURN_1, EXIT_MSG_LOOP	je target_ITloop;
 				test	SSTAT0, SPIORDY jz .;
 				jmp	host_target_message_loop;
 		
 		ident_messages_done:
 				/* If ring buffer is full, return busy or queue full */
 				if ((ahc->features & AHC_HS_MAILBOX) != 0) {
 					and	A, HOST_TQINPOS, HS_MAILBOX;
 				} else {
 					mov	A, KERNEL_TQINPOS;
 				}
 				cmp	TQINPOS, A jne tqinfifo_has_space;
 				mvi	P_STATUS|BSYO call change_phase;
 				cmp	INITIATOR_TAG, SCB_LIST_NULL je . + 3;
 				mvi	STATUS_QUEUE_FULL call target_outb;
 				jmp	target_busfree_wait;
 				mvi	STATUS_BUSY call target_outb;
 				jmp	target_busfree_wait;
 		tqinfifo_has_space:	
 				/* Terminate the ident list */
 				if ((ahc->features & AHC_CMD_CHAN) != 0) {
 					mvi	CCSCBRAM, SCB_LIST_NULL;
 				} else {
 					mvi	DFDAT, SCB_LIST_NULL;
 				}
 				or	SEQ_FLAGS, TARG_CMD_PENDING|IDENTIFY_SEEN;
 				test	SCSISIGI, ATNI	jnz target_mesgout_pending_msg;
 				jmp	target_ITloop;
 				
 		/*
 		 * We carefully toggle SPIOEN to allow us to return the 
 		 * message byte we receive so it can be checked prior to
 		 * driving REQ on the bus for the next byte.
 		 */
 		target_inb:
 				/*
 				 * Drive REQ on the bus by enabling SCSI PIO.
 				 */
 				or	SXFRCTL0, SPIOEN;
 				/* Wait for the byte */
 				test	SSTAT0, SPIORDY jz .;
 				/* Prevent our read from triggering another REQ */
 				and	SXFRCTL0, ~SPIOEN;
 				/* Save latched contents */
 				mov	DINDEX, SCSIDATL ret;
 			}
 		
 		if ((ahc->flags & AHC_INITIATORMODE) != 0) {
 		/*
 		 * Reselection has been initiated by a target. Make a note that we've been
 		 * reselected, but haven't seen an IDENTIFY message from the target yet.
 		 */
 		initiator_reselect:
 			/* XXX test for and handle ONE BIT condition */
 023 f0196e08		and	SAVED_TCL, SELID_MASK, SELID;
 			if ((ahc->features & AHC_TWIN) != 0) {
 				test	SBLKCTL, SELBUSB	jz . + 2;
 				or	SAVED_TCL, SELBUSB;
 			}
 024 1a010200		or	SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
 025 086a1800		mvi	CLRSINT1,CLRBUSFREE;
 026 08112200		or	SIMODE1, ENBUSFREE;		/*
 								 * We aren't expecting a
 								 * bus free, so interrupt
 								 * the kernel driver if it
 								 * happens.
 								 */
 027 0065ce41		jmp	ITloop;
 		}
 		
 		/*
 		 * After the selection, remove this SCB from the "waiting SCB"
 		 * list.  This is achieved by simply moving our "next" pointer into
 		 * WAITING_SCBH.  Our next pointer will be set to null the next time this
 		 * SCB is used, so don't bother with it now.
 		 */
 		select_out:
 			/* Turn off the selection hardware */
 028 b2000008		and	SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ;
 029 406a1600		mvi	CLRSINT0, CLRSELDO;
 02a ff3e2009		mov	SCBPTR, WAITING_SCBH;
 02b ffba7c08		mov	WAITING_SCBH,SCB_NEXT;
 02c ffa16e08		mov	SAVED_TCL, SCB_TCL;
 			if ((ahc->flags & AHC_TARGETMODE) != 0) {
 				test	SSTAT0, TARGET	jz initiator_select;
 		
 				/*
 				 * We've just re-selected an initiator.
 				 * Assert BSY and setup the phase for
 				 * sending our identify messages.
 				 */
 				mvi	P_MESGIN|BSYO call change_phase;
 				mvi	CLRSINT1,CLRBUSFREE;
 		
 				/*
 				 * Start out with a simple identify message.
 				 */
 				and	A, LID, SCB_TCL;
 				or	A, MSG_IDENTIFYFLAG call target_outb;
 		
 				/*
 				 * If we are the result of a tagged command, send
 				 * a simple Q tag and the tag id.
 				 */
 				test	SCB_CONTROL, TAG_ENB	jz . + 3;
 				mvi	MSG_SIMPLE_Q_TAG call target_outb;
 				mov	SCB_INITIATOR_TAG call target_outb;
 				mov	INITIATOR_TAG, SCB_INITIATOR_TAG;
 		target_synccmd:
 				/*
 				 * Now determine what phases the host wants us
 				 * to go through.
 				 */
 				mov	SEQ_FLAGS, SCB_TARGET_PHASES;
 				
 		
 		target_ITloop:
 				/*
 				 * Start honoring ATN signals now that
 				 * we properly identified ourselves.
 				 */
 				test	SCSISIGI, ATNI			jnz target_mesgout;
 				test	SEQ_FLAGS, CMDPHASE_PENDING	jnz target_cmdphase;
 				test	SEQ_FLAGS, DPHASE_PENDING	jnz target_dphase;
 				test	SEQ_FLAGS, SPHASE_PENDING	jnz target_sphase;
 		
 				/*
 				 * No more work to do.  Either disconnect or not depending
 				 * on the state of NO_DISCONNECT.
 				 */
 				test	SEQ_FLAGS, NO_DISCONNECT jz target_disconnect; 
 				if ((ahc->flags & AHC_PAGESCBS) != 0) {
 					mov	ALLZEROS	call	get_free_or_disc_scb;
 				}
 				mov	RETURN_1, ALLZEROS;
 				call	complete_target_cmd;
 				cmp	RETURN_1, CONT_MSG_LOOP jne .;
 				mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
 				mov	SCB_TAG	 call dma_scb;
 				jmp	target_synccmd;
 		
 		target_mesgout:
 				mvi	SCSISIGO, P_MESGOUT|BSYO;
 				call	target_inb;
 				/* Local Processing goes here... */
 		target_mesgout_pending_msg:
 				jmp	host_target_message_loop;
 				
 		target_disconnect:
 				mvi	P_MESGIN|BSYO call change_phase;
 				test	SEQ_FLAGS, DPHASE	jz . + 2;
 				mvi	MSG_SAVEDATAPOINTER call target_outb;
 				mvi	MSG_DISCONNECT call target_outb;
 		
 		target_busfree_wait:
 				/* Wait for preceeding I/O session to complete. */
 				test	SCSISIGI, ACKI jnz .;
 		target_busfree:
 				clr	SCSISIGO;
 				mvi	LASTPHASE, P_BUSFREE;
 				call	complete_target_cmd;
 				jmp	poll_for_work;
 		
 		target_cmdphase:
 				mvi	P_COMMAND|BSYO call change_phase;
 				call	target_inb;
 				mov	A, DINDEX;
 				/* Store for host */
 				if ((ahc->features & AHC_CMD_CHAN) != 0) {
 					mov	CCSCBRAM, A;
 				} else {
 					mov	DFDAT, A;
 				}
 		
 				/*
 				 * Determine the number of bytes to read
 				 * based on the command group code via table lookup.
 				 * We reuse the first 8 bytes of the TARG_SCSIRATE
 				 * BIOS array for this table. Count is one less than
 				 * the total for the command since we've already fetched
 				 * the first byte.
 				 */
 				shr	A, CMD_GROUP_CODE_SHIFT;
 				add	SINDEX, TARG_SCSIRATE, A;
 				mov	A, SINDIR;
 		
 				test	A, 0xFF jz command_phase_done;
 		command_loop:
 				or	SXFRCTL0, SPIOEN;
 				test	SSTAT0, SPIORDY jz .;
 				cmp	A, 1 jne . + 2;
 				and	SXFRCTL0, ~SPIOEN;	/* Last Byte */
 				if ((ahc->features & AHC_CMD_CHAN) != 0) {
 					mov	CCSCBRAM, SCSIDATL;
 				} else {
 					mov	DFDAT, SCSIDATL;
 				}
 				dec	A;
 				test	A, 0xFF jnz command_loop;
 		
 		command_phase_done:
 				and	SEQ_FLAGS, ~CMDPHASE_PENDING;
 				jmp	target_ITloop;
 		
 		target_dphase:
 				/*
 				 * Data direction flags are from the
 				 * perspective of the initiator.
 				 */
 				test	SCB_TARGET_PHASES[1], TARGET_DATA_IN jz . + 4;
 				mvi	LASTPHASE, P_DATAOUT;
 				mvi	P_DATAIN|BSYO call change_phase;
 				jmp	. + 3;
 				mvi	LASTPHASE, P_DATAIN;
 				mvi	P_DATAOUT|BSYO call change_phase;
 				mov	ALLZEROS call initialize_channel;
 				jmp	p_data;
 		
 		target_sphase:
 				mvi	P_STATUS|BSYO call change_phase;
 				mvi	LASTPHASE, P_STATUS;
 				mov	SCB_TARGET_STATUS call target_outb;
 				/* XXX Watch for ATN or parity errors??? */
 				mvi	SCSISIGO, P_MESGIN|BSYO;
 				/* MSG_CMDCMPLT is 0, but we can't do an immediate of 0 */
 				mov	ALLZEROS call target_outb;
 				jmp	target_busfree_wait;
 			
 		complete_target_cmd:
 				test	SEQ_FLAGS, TARG_CMD_PENDING	jnz . + 2;
 				mov	SCB_TAG jmp complete_post;
 				if ((ahc->features & AHC_CMD_CHAN) != 0) {
 					/* Set the valid byte */
 					mvi	CCSCBADDR, 24;
 					mov	CCSCBRAM, ALLONES;
 					mvi	CCHCNT, 28;
 					or	CCSCBCTL, CCSCBEN|CCSCBRESET;
 					test	CCSCBCTL, CCSCBDONE jz .;
 					clr	CCSCBCTL;
 				} else {
 					/* Set the valid byte */
 					or	DFCNTRL, FIFORESET;
 					mvi	DFWADDR, 3; /* Third 64bit word or byte 24 */
 					mov	DFDAT, ALLONES;
 					mvi	HCNT[0], 28;
 					clr	HCNT[1];
 					clr	HCNT[2];
 					or	DFCNTRL, HDMAEN|FIFOFLUSH;
 					call	dma_finish;
 				}
 				inc	TQINPOS;
 				mvi	INTSTAT,CMDCMPLT ret;
 			}
 		
 		if ((ahc->flags & AHC_INITIATORMODE) != 0) {
 		initiator_select:
 02d 086a7258		mvi	SPIOEN call	initialize_channel;
 		
 			/*
 			 * We aren't expecting a bus free, so interrupt
 			 * the kernel driver if it happens.
 			 */
 02e 086a1800		mvi	CLRSINT1,CLRBUSFREE;
 02f 08112200		or	SIMODE1, ENBUSFREE;
 		
 			/*
 			 * As soon as we get a successful selection, the target
 			 * should go into the message out phase since we have ATN
 			 * asserted.
 			 */
 030 806a6800		mvi	MSG_OUT, MSG_IDENTIFYFLAG;
 031 80366c00		or	SEQ_FLAGS, IDENTIFY_SEEN;
 		
 			/*
 			 * Main loop for information transfer phases.  Wait for the
 			 * target to assert REQ before checking MSG, C/D and I/O for
 			 * the bus phase.
 			 */
 		ITloop:
 032 0065ec5c		call	phase_lock;
 		
 033 ff3dc808		mov	A, LASTPHASE;
 		
 034 bf64067a		test	A, ~P_DATAIN	jz p_data;
 035 8064da72		cmp	A,P_COMMAND	je p_command;
 036 a0641473		cmp	A,P_MESGOUT	je p_mesgout;
 037 c0640c73		cmp	A,P_STATUS	je p_status;
 038 e0645c73		cmp	A,P_MESGIN	je p_mesgin;
 		
 039 016a2201		mvi	INTSTAT,BAD_PHASE;
 03a 0065ce41		jmp	ITloop;			/* Try reading the bus again. */
 		
 		await_busfree:
 03b f7112208		and	SIMODE1, ~ENBUSFREE;
 03c ff06d408		mov	NONE, SCSIDATL;		/* Ack the last byte */
 03d f7010208		and	SXFRCTL0, ~SPIOEN;
 03e 090ce679		test	SSTAT1,REQINIT|BUSFREE	jz .;
 03f 080c0c68		test	SSTAT1, BUSFREE jnz poll_for_work;
 040 016a2201		mvi	INTSTAT, BAD_PHASE;
 		}
 			
 		clear_target_state:
 			/*
 			 * We assume that the kernel driver may reset us
 			 * at any time, even in the middle of a DMA, so
 			 * clear DFCNTRL too.
 			 */
 041 ff6a2609		clr	DFCNTRL;
 		
 			/*
 			 * We don't know the target we will connect to,
 			 * so default to narrow transfers to avoid
 			 * parity problems.
 			 */
 			if ((ahc->features & AHC_ULTRA2) != 0) {
 042 026a0830			bmov	SCSIRATE, ALLZEROS, 2;
 			} else {
 				clr	SCSIRATE;
 				and	SXFRCTL0, ~(FAST20);
 			}
 043 016a7a00		mvi	LASTPHASE, P_BUSFREE;
 			/* clear target specific flags */
 044 ff6a6c0c		clr	SEQ_FLAGS ret;
 		
 		/*
 		 * If we re-enter the data phase after going through another phase, the
 		 * STCNT may have been cleared, so restore it from the residual field.
 		 */
 		data_phase_reinit:
 			if ((ahc->features & AHC_ULTRA2) != 0) {
 				/*
 				 * The preload circuitry requires us to
 				 * reload the address too, so pull it from
 				 * the shaddow address.
 				 */
 045 04141031			bmov	HADDR, SHADDR, 4;
 046 03a91831			bmov	HCNT, SCB_RESID_DCNT, 3;
 			} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
 				bmov	STCNT, SCB_RESID_DCNT, 3;
 			} else {
 				mvi	DINDEX, STCNT;
 				mvi	SCB_RESID_DCNT	call bcopy_3;
 			}
 047 01a9b208		and	DATA_COUNT_ODD, 0x1, SCB_RESID_DCNT[0];
 048 00652842		jmp	data_phase_loop;
 		
 		p_data:
 			if ((ahc->features & AHC_ULTRA2) != 0) {
 049 a86a6a00			mvi	DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
 			} else {
 				mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
 			}
 04a 403d0e6a		test	LASTPHASE, IOI jnz . + 2;
 04b 04356a00		or	DMAPARAMS, DIRECTION;
 04c 00655a5c		call	assert;			/*
 							 * Ensure entering a data
 							 * phase is okay - seen identify, etc.
 							 */
 			if ((ahc->features & AHC_CMD_CHAN) != 0) {
 04d 806ad401			mvi	CCSGADDR, CCSGADDR_MAX;
 			}
 04e 2036f869		test	SEQ_FLAGS, DPHASE	jnz data_phase_reinit;
 		
 			/* We have seen a data phase */
 04f 20366c00		or	SEQ_FLAGS, DPHASE;
 		
 			/*
 			 * Initialize the DMA address and counter from the SCB.
 			 * Also set SG_COUNT and SG_NEXT in memory since we cannot
 			 * modify the values in the SCB itself until we see a
 			 * save data pointers message.
 			 */
 			if ((ahc->features & AHC_CMD_CHAN) != 0) {
 050 07ac1031			bmov	HADDR, SCB_DATAPTR, 7;
 			} else {
 				mvi	DINDEX, HADDR;
 				mvi	SCB_DATAPTR	call bcopy_7;
 			}
 051 01b0b208		and	DATA_COUNT_ODD, 0x1, SCB_DATACNT[0];
 		
 			if ((ahc->features & AHC_ULTRA2) == 0) {
 				if ((ahc->features & AHC_CMD_CHAN) != 0) {
 					bmov	STCNT, HCNT, 3;
 				} else {
 					call	set_stcnt_from_hcnt;
 				}
 			}
 		
 			if ((ahc->features & AHC_CMD_CHAN) != 0) {
 052 05a37030			bmov	SG_COUNT, SCB_SGCOUNT, 5;
 			} else {
 				mvi	DINDEX, SG_COUNT;
 				mvi	SCB_SGCOUNT	call bcopy_5;
 			}
 		
 		data_phase_loop:
 		/* Guard against overruns */
 053 ff38386a		test	SG_COUNT, 0xff jnz data_phase_inbounds;
 		/*
 		 * Turn on 'Bit Bucket' mode, set the transfer count to
 		 * 16meg and let the target run until it changes phase.
 		 * When the transfer completes, notify the host that we
 		 * had an overrun.
 		 */
 054 80020400		or	SXFRCTL1,BITBUCKET;
 055 e7356a08		and	DMAPARAMS, ~(HDMAEN|SDMAEN);
 			if ((ahc->features & AHC_ULTRA2) != 0) {
 056 03691831			bmov	HCNT, ALLONES, 3;
 			} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
 				bmov	STCNT, ALLONES, 3;
 			} else {
 				mvi	STCNT[0], 0xFF;
 				mvi	STCNT[1], 0xFF;
 				mvi	STCNT[2], 0xFF;
 			}
 		data_phase_inbounds:
 		/* If we are the last SG block, tell the hardware. */
 057 01384262		cmp	SG_COUNT,0x01 jne data_phase_wideodd;
 			if ((ahc->features & AHC_ULTRA2) != 0) {
 058 02fcf801			or	SG_CACHEPTR, LAST_SEG;
 			} else {
 				if ((ahc->flags & AHC_TARGETMODE) != 0) {
 					test	SSTAT0, TARGET jz . + 2;
 					test	DMAPARAMS, DIRECTION jz data_phase_wideodd;
 				}
 				and	DMAPARAMS, ~WIDEODD;
 			}
 		data_phase_wideodd:
 			if ((ahc->features & AHC_ULTRA2) != 0) {
 059 ff69ca08			mov	SINDEX, ALLONES;
 05a ff352609			mov	DFCNTRL, DMAPARAMS;
 05b 040b466a			test	SSTAT0, SDONE jnz .;/* Wait for preload to complete */
 		data_phase_dma_loop:
 05c 040b4e6a			test	SSTAT0,	SDONE jnz data_phase_dma_done;
 05d 100c487a			test	SSTAT1,PHASEMIS	jz data_phase_dma_loop;	/* ie. underrun */
 			} else {
 				mov	DMAPARAMS  call dma;
 			}
 		
 		data_phase_dma_done:
 		/* Go tell the host about any overruns */
 05e 8002b66a		test	SXFRCTL1,BITBUCKET jnz data_phase_overrun;
 		
 		/* See if we completed this segment */
 05f ff089c6a		test	STCNT[0], 0xff	jnz data_phase_finish;
 060 ff099c6a		test	STCNT[1], 0xff	jnz data_phase_finish;
 061 ff0a9c6a		test	STCNT[2], 0xff	jnz data_phase_finish;
 		
 		/*
 		 * Advance the scatter-gather pointers if needed 
 		 */
 		sg_advance:
 062 ff387018		dec	SG_COUNT;	/* one less segment to go */
 		
 063 ff389c7a		test	SG_COUNT, 0xff	jz data_phase_finish; /* Are we done? */
 		/*
 		 * Load a struct scatter and set up the data address and length.
 		 * If the working value of the SG count is nonzero, then
 		 * we need to load a new set of values.
 		 *
 		 * This, like all DMA's, assumes little-endian host data storage.
 		 */
 		sg_load:
 			if ((ahc->features & AHC_CMD_CHAN) != 0) {
 				/*
 				 * Do we have any prefetch left???
 				 */
 064 80ea7262			cmp	CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail;
 		
 				/*
 				 * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
 				 */
 065 ef38c818			add	A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
 066 806ac800			mvi	A, CCSGADDR_MAX;
 067 0065644a			jc	. + 2;
 068 3338c828			shl	A, 3, SG_COUNT;
 069 ff64d009			mov	CCHCNT, A;
 06a 0439c031			bmov	CCHADDR, SG_NEXT, 4;
 06b 096ad601			mvi	CCSGCTL, CCSGEN|CCSGRESET;
 06c 80eb6a7a			test	CCSGCTL, CCSGDONE jz .;
 06d f7ebd609			and	CCSGCTL, ~CCSGEN;
 06e 08eb6e6a			test	CCSGCTL, CCSGEN jnz .;
 06f 016ad601			mvi	CCSGCTL, CCSGRESET;
 		prefetched_segs_avail:
 070 08e91031			bmov 	HADDR, CCSGRAM, 8;
 			} else {
 				mvi	DINDEX, HADDR;
 				mvi	SG_NEXT	call bcopy_4;
 		
 				mvi	HCNT[0],SG_SIZEOF;
 				clr	HCNT[1];
 				clr	HCNT[2];
 		
 				or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
 		
 				call	dma_finish;
 		
 				/*
 				 * Copy data from FIFO into SCB data pointer and data count.
 				 * This assumes that the SG segments are of the form:
 				 * struct ahc_dma_seg {
 				 *	u_int32_t	addr;	four bytes, little-endian order
 				 *	u_int32_t	len;	four bytes, little endian order
 				 * };
 				 */
 				mvi	HADDR	call dfdat_in_7;
 			}
 		
 			/* Track odd'ness */
 071 018c887a		test	HCNT[0], 0x1 jz . + 2;
 072 0159b210		xor	DATA_COUNT_ODD, 0x1;
 		
 			if ((ahc->features & AHC_ULTRA2) == 0) {
 				/* Load STCNT as well.  It is a mirror of HCNT */
 				if ((ahc->features & AHC_CMD_CHAN) != 0) {
 					bmov	STCNT, HCNT, 3;
 				} else {
 					call	set_stcnt_from_hcnt;
 				}
 			}
 		
 		/* Advance the SG pointer */
 073 ff6ac808		clr	A;			/* add sizeof(struct scatter) */
 074 08397218		add	SG_NEXT[0],SG_SIZEOF;
 075 003a7420		adc	SG_NEXT[1],A;
 		
 			if ((ahc->flags & AHC_TARGETMODE) != 0) {
 				test	SSTAT0, TARGET jnz data_phase_loop;
 			}
 076 010c947a		test	SSTAT1, REQINIT jz .;
 077 100c287a		test	SSTAT1,PHASEMIS	jz data_phase_loop;
 		
 			/* Ensure the last seg is visable at the shaddow layer */
 			if ((ahc->features & AHC_ULTRA2) != 0) {
 078 ff352609			mov	DFCNTRL, DMAPARAMS;
 079 040b9a6a			test	SSTAT0, SDONE jnz .;/* Wait for preload to complete */
 			}
 		
 		data_phase_finish:
 			if ((ahc->features & AHC_ULTRA2) != 0) {
 07a 0065c05a			call	ultra2_dmafinish;
 			}
 		/*
 		 * After a DMA finishes, save the SG and STCNT residuals back into the SCB
 		 * We use STCNT instead of HCNT, since it's a reflection of how many bytes 
 		 * were transferred on the SCSI (as opposed to the host) bus.
 		 */
 			if ((ahc->features & AHC_CMD_CHAN) != 0) {
 07b 03085231			bmov	SCB_RESID_DCNT, STCNT, 3;
 			} else {
 				mov	SCB_RESID_DCNT[0],STCNT[0];
 				mov	SCB_RESID_DCNT[1],STCNT[1];
 				mov	SCB_RESID_DCNT[2],STCNT[2];
 			}
 07c ff385009		mov	SCB_RESID_SGCNT, SG_COUNT;
 		
 			if ((ahc->features & AHC_ULTRA2) != 0) {
 07d 12010200			or	SXFRCTL0, CLRSTCNT|CLRCHN;
 			}
 		
 			if ((ahc->flags & AHC_TARGETMODE) != 0) {
 				test	SEQ_FLAGS, DPHASE_PENDING jz ITloop;
 				and	SEQ_FLAGS, ~DPHASE_PENDING;
 				/*
 				 * For data-in phases, wait for any pending acks from the
 				 * initiator before changing phase.
 				 */
 				test	DFCNTRL, DIRECTION jz target_ITloop;
 				test	SSTAT1, REQINIT	jnz .;
 				jmp	target_ITloop;
 			}
 07e 0065ce41		jmp	ITloop;
 		
 		data_phase_overrun:
 			if ((ahc->features & AHC_ULTRA2) != 0) {
 07f 0065c05a			call	ultra2_dmafinish;
 080 12010200			or	SXFRCTL0, CLRSTCNT|CLRCHN;
 			}
 		/*
 		 * Turn off BITBUCKET mode and notify the host
 		 */
 081 7f020408		and	SXFRCTL1, ~BITBUCKET;
 082 f16a2201		mvi	INTSTAT,DATA_OVERRUN;
 083 0065ce41		jmp	ITloop;
 		
 		ultra2_dmafinish:
 			if ((ahc->features & AHC_ULTRA2) != 0) {
 084 0493d26a			test	DFCNTRL, DIRECTION jnz ultra2_dmafifoempty;
 085 df932609			and	DFCNTRL, ~SCSIEN;
 086 2093c46a			test	DFCNTRL, SCSIEN jnz .;
 		ultra2_dmafifoflush:
 087 02932601			or	DFCNTRL, FIFOFLUSH;
 				/*
 				 * The FIFOEMP status bit on the Ultra2 class
 				 * of controllers seems to be a bit flaky.
 				 * It appears that if the FIFO is full and the
 				 * transfer ends with some data in the REQ/ACK
 				 * FIFO, FIFOEMP will fall temporarily
 				 * as the data is transferred to the PCI bus.
 				 * This glitch lasts for fewer than 5 clock cycles,
 				 * so we work around the problem by ensuring the
 				 * status bit stays false through a full glitch
 				 * window.
 				 */
 088 0194c67a			test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
 089 0194c67a			test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
 08a 0194c67a			test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
 08b 0194c67a			test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
 08c 0194c67a			test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
 		
 		ultra2_dmafifoempty:
 				/* Don't clobber an inprogress host data transfer */
 08d 1094d26a			test	DFSTATUS, MREQPEND	jnz ultra2_dmafifoempty;
 		
 		ultra2_dmahalt:
 08e d7932609			and     DFCNTRL, ~(SCSIEN|HDMAEN);
 08f 0893d66a			test	DFCNTRL, HDMAEN jnz .;
 090 ff6ad40c			ret;
 			}
 		
 		if ((ahc->flags & AHC_INITIATORMODE) != 0) {
 		/*
 		 * Command phase.  Set up the DMA registers and let 'er rip.
 		 */
 		p_command:
 091 00655a5c		call	assert;
 		
 			if ((ahc->features & AHC_CMD_CHAN) != 0) {
 092 ffb81809			mov	HCNT[0], SCB_CMDLEN;
 093 026a1a31			bmov	HCNT[1], ALLZEROS, 2;
 				if ((ahc->features & AHC_ULTRA2) == 0) {
 					bmov	STCNT, HCNT, 3;
 				}
 094 efb8d418			add	NONE, -17, SCB_CMDLEN;
 095 0065f04a			jc	dma_cmd_data;
 				/*
 				 * The data fifo seems to require 4 byte alligned
 				 * transfers from the sequencer.  Force this to
 				 * be the case by clearing HADDR[0] even though
 				 * we aren't going to touch host memeory.
 				 */
 096 016a1031			bmov	HADDR[0], ALLZEROS, 1;
 				if ((ahc->features & AHC_ULTRA2) != 0) {
 097 a46a2601				mvi	DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION);
 				} else {
 					mvi	DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
 				}
 098 10c03231			bmov   DFDAT, SCB_CMDSTORE, 16; 
 099 00650043			jmp	cmd_loop;
 		dma_cmd_data:
 09a 04b41031			bmov	HADDR, SCB_CMDPTR, 4;
 			} else {
 				mvi	DINDEX, HADDR;
 				mvi	SCB_CMDPTR	call bcopy_5;
 				clr	HCNT[1];
 				clr	HCNT[2];
 			}
 		
 			if ((ahc->features & AHC_ULTRA2) == 0) {
 				if ((ahc->features & AHC_CMD_CHAN) == 0) {
 					call	set_stcnt_from_hcnt;
 				}
 				mvi	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET);
 			} else {
 09b ac6a2601			mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
 			}
 		cmd_loop:
 09c 040b046b		test	SSTAT0, SDONE jnz . + 2;
 09d 100c007b		test    SSTAT1, PHASEMIS jz cmd_loop;
 			/*
 			 * Wait for our ACK to go-away on it's own
 			 * instead of being killed by SCSIEN getting cleared.
 			 */
 09e 0103046b		test	SCSISIGI, ACKI jnz .;
 09f c7932609		and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
 0a0 3893086b		test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .;
 0a1 0065ce41		jmp	ITloop;
 		
 		/*
 		 * Status phase.  Wait for the data byte to appear, then read it
 		 * and store it into the SCB.
 		 */
 		p_status:
 0a2 00655a5c		call	assert;
 		
 0a3 ff064409		mov	SCB_TARGET_STATUS, SCSIDATL;
 0a4 0065ce41		jmp	ITloop;
 		
 		/*
 		 * Message out phase.  If MSG_OUT is MSG_IDENTIFYFLAG, build a full
 		 * indentify message sequence and send it to the target.  The host may
 		 * override this behavior by setting the MK_MESSAGE bit in the SCB
 		 * control byte.  This will cause us to interrupt the host and allow
 		 * it to handle the message phase completely on its own.  If the bit
 		 * associated with this target is set, we will also interrupt the host,
 		 * thereby allowing it to send a message on the next selection regardless
 		 * of the transaction being sent.
 		 * 
 		 * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
 		 * This is done to allow the host to send messages outside of an identify
 		 * sequence while protecting the seqencer from testing the MK_MESSAGE bit
 		 * on an SCB that might not be for the current nexus. (For example, a
 		 * BDR message in responce to a bad reselection would leave us pointed to
 		 * an SCB that doesn't have anything to do with the current target).
 		 *
 		 * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
 		 * bus device reset).
 		 *
 		 * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
 		 * in case the target decides to put us in this phase for some strange
 		 * reason.
 		 */
 		p_mesgout_retry:
 0a5 103d0600		or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
 		p_mesgout:
 0a6 ff34ca08		mov	SINDEX, MSG_OUT;
 0a7 80654863		cmp	SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
 0a8 10a06a6b		test	SCB_CONTROL,MK_MESSAGE	jnz host_message_loop;
 0a9 ffa1dc08		mov	FUNCTION1, SCB_TCL;
 0aa ff6ec808		mov	A, FUNCTION1;
 			if ((ahc->features & AHC_HS_MAILBOX) != 0) {
 				/*
 				 * Work around a pausing bug in at least the aic7890.
 				 * If the host needs to update the TARGET_MSG_REQUEST
 				 * bit field, it will set the HS_MAILBOX to 1.  In
 				 * response, we pause with a specific interrupt code
 				 * asking for the mask to be updated before we continue.
 				 * Ugh.
 				 */
 0ab f086227b			test	HS_MAILBOX, 0xF0	jz . + 2;
 0ac 616a2201			mvi	INTSTAT, UPDATE_TMSG_REQ;
 0ad ff6ad408			nop;
 			}
 0ae ff56ca08		mov	SINDEX, TARGET_MSG_REQUEST[0];
 			if ((ahc->features & AHC_TWIN) != 0) {
 				/* Second Channel uses high byte bits */
 				test	SCB_TCL, SELBUSB	jz . + 2;
 				mov	SINDEX, TARGET_MSG_REQUEST[1];
 			} else if ((ahc->features & AHC_WIDE) != 0) {
 0af 80a12e7b			test	SCB_TCL, 0x80		jz . + 2; /* target > 7 */
 0b0 ff57ca08			mov	SINDEX, TARGET_MSG_REQUEST[1];
 			}
 0b1 00656a6b		test	SINDEX, A	jnz host_message_loop;
 		p_mesgout_identify:
 0b2 07a1ca08		and	SINDEX,LID,SCB_TCL;	/* lun */
 0b3 40a0c808		and	A,DISCENB,SCB_CONTROL;	/* mask off disconnect privledge */
 0b4 0065ca00		or	SINDEX,A;		/* or in disconnect privledge */
 0b5 8065ca00		or	SINDEX,MSG_IDENTIFYFLAG;
 		/*
 		 * Send a tag message if TAG_ENB is set in the SCB control block.
 		 * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
 		 */
 		p_mesgout_tag:
 0b6 20a04c7b		test	SCB_CONTROL,TAG_ENB jz  p_mesgout_onebyte;
 0b7 ff650c08		mov	SCSIDATL, SINDEX;	/* Send the identify message */
 0b8 0065ec5c		call	phase_lock;
 0b9 a03d5463		cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
 0ba 23a00c08		and	SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
 0bb 0065ec5c		call	phase_lock;
 0bc a03d5463		cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
 0bd 00b94c43		mov	SCB_TAG	jmp p_mesgout_onebyte;
 		/*
 		 * Interrupt the driver, and allow it to handle this message
 		 * phase and any required retries.
 		 */
 		p_mesgout_from_host:
 0be ff654c63		cmp	SINDEX, HOST_MSG	jne p_mesgout_onebyte;
 0bf 00656a43		jmp	host_message_loop;
 		
 		p_mesgout_onebyte:
 0c0 406a1800		mvi	CLRSINT1, CLRATNO;
 0c1 ff650c08		mov	SCSIDATL, SINDEX;
 		
 		/*
 		 * If the next bus phase after ATN drops is message out, it means
 		 * that the target is requesting that the last message(s) be resent.
 		 */
 0c2 0065ec5c		call	phase_lock;
 0c3 a03d1273		cmp	LASTPHASE, P_MESGOUT	je p_mesgout_retry;
 		
 		p_mesgout_done:
 0c4 406a1800		mvi	CLRSINT1,CLRATNO;	/* Be sure to turn ATNO off */
 0c5 ff34a808		mov	LAST_MSG, MSG_OUT;
 0c6 086a6800		mvi	MSG_OUT, MSG_NOOP;	/* No message left */
 0c7 0065ce41		jmp	ITloop;
 		
 		/*
 		 * Message in phase.  Bytes are read using Automatic PIO mode.
 		 */
 		p_mesgin:
 0c8 646a1c5c		mvi	ACCUM		call inb_first;	/* read the 1st message byte */
 		
 0c9 8064cc6b		test	A,MSG_IDENTIFYFLAG	jnz mesgin_identify;
 0ca 0464ae73		cmp	A,MSG_DISCONNECT	je mesgin_disconnect;
 0cb 0264b473		cmp	A,MSG_SAVEDATAPOINTER	je mesgin_sdptrs;
 0cc 006a7673		cmp	ALLZEROS,A		je mesgin_complete;
 0cd 0364c873		cmp	A,MSG_RESTOREPOINTERS	je mesgin_rdptrs;
 0ce 08647273		cmp	A,MSG_NOOP		je mesgin_done;
 		
 		/*
 		 * Pushed message loop to allow the kernel to
 		 * run it's own message state engine.  To avoid an
 		 * extra nop instruction after signaling the kernel,
 		 * we perform the phase_lock before checking to see
 		 * if we should exit the loop and skip the phase_lock
 		 * in the ITloop.  Performing back to back phase_locks
 		 * shouldn't hurt, but why do it twice...
 		 */
 		host_message_loop:
 0cf a16a2201		mvi	INTSTAT, HOST_MSG_LOOP;
 0d0 0065ec5c		call	phase_lock;
 0d1 0852d071		cmp	RETURN_1, EXIT_MSG_LOOP	je ITloop + 1;
 0d2 00656a43		jmp	host_message_loop;
 		
 		mesgin_done:
 0d3 ff06d408		mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
 0d4 0065ce41		jmp	ITloop;
 		
 		
 		mesgin_complete:
 		/*
 		 * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO,
 		 * and trigger a completion interrupt.  Before doing so, check to see if there
 		 * is a residual or the status byte is something other than STATUS_GOOD (0).
 		 * In either of these conditions, we upload the SCB back to the host so it can
 		 * process this information.  In the case of a non zero status byte, we 
 		 * additionally interrupt the kernel driver synchronously, allowing it to
 		 * decide if sense should be retrieved.  If the kernel driver wishes to request
 		 * sense, it will fill the kernel SCB with a request sense command and set
 		 * RETURN_1 to SEND_SENSE.  If RETURN_1 is set to SEND_SENSE we redownload
 		 * the SCB, and process it as the next command by adding it to the waiting list.
 		 * If the kernel driver does not wish to request sense, it need only clear
 		 * RETURN_1, and the command is allowed to complete normally.  We don't bother
 		 * to post to the QOUTFIFO in the error cases since it would require extra
 		 * work in the kernel driver to ensure that the entry was removed before the
 		 * command complete code tried processing it.
 		 */
 		
 		/*
 		 * First check for residuals
 		 */
 0d5 ffa87a6b		test	SCB_RESID_SGCNT,0xff	jnz upload_scb;
 0d6 ffa2927b		test	SCB_TARGET_STATUS,0xff	jz complete;	/* Good Status? */
 		upload_scb:
 0d7 016a6a00		mvi	DMAPARAMS, FIFORESET;
 0d8 00b91e5d		mov	SCB_TAG		call dma_scb;
 		check_status:
 0d9 ffa2927b		test	SCB_TARGET_STATUS,0xff	jz complete;	/* Just a residual? */
 0da 716a2201		mvi	INTSTAT,BAD_STATUS;			/* let driver know */
 0db ff6ad408		nop;
 0dc 40529263		cmp	RETURN_1, SEND_SENSE	jne complete;
 			/* This SCB becomes the next to execute as it will retrieve sense */
 0dd 0d6a6a00		mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
 0de 00b91e5d		mov	SCB_TAG		call dma_scb;
 		add_to_waiting_list:
 0df ff3e7409		mov	SCB_NEXT,WAITING_SCBH;
 0e0 ff907c08		mov	WAITING_SCBH, SCBPTR;
 			/*
 			 * Prepare our selection hardware before the busfree so we have a
 			 * high probability of winning arbitration.
 			 */
 0e1 00655058		call	start_selection;
 0e2 0065e041		jmp	await_busfree;
 		
 		complete:
 			/* If we are untagged, clear our address up in host ram */
 0e3 20a09a6b		test	SCB_CONTROL, TAG_ENB jnz complete_queue;
 0e4 ff37c808		mov	A, SAVED_TCL;
 0e5 026ac05c		mvi	UNTAGGEDSCB_OFFSET call post_byte_setup;
 0e6 ff6ad65c		mvi	SCB_LIST_NULL call post_byte;
 		
 		complete_queue:
 0e7 00b99e5b		mov	SCB_TAG call complete_post;
 0e8 0065e041		jmp	await_busfree;
 		}
 		
 		complete_post:
 			/* Post the SCBID in SINDEX and issue an interrupt */
 0e9 00658c5d		call	add_scb_to_free_list;
 0ea ff65a408		mov	ARG_1, SINDEX;
 			if ((ahc->features & AHC_QUEUE_REGS) != 0) {
 0eb fff8c808			mov	A, SDSCB_QOFF;
 			} else {
 				mov	A, QOUTPOS;
 			}
 0ec 006ac05c		mvi	QOUTFIFO_OFFSET call post_byte_setup;
 0ed 0052d65c		mov	ARG_1 call post_byte;
 			if ((ahc->features & AHC_QUEUE_REGS) == 0) {
 				inc 	QOUTPOS;
 			}
 0ee 026a2205		mvi	INTSTAT,CMDCMPLT ret;
 		
 		if ((ahc->flags & AHC_INITIATORMODE) != 0) {
 		/*
 		 * Is it a disconnect message?  Set a flag in the SCB to remind us
 		 * and await the bus going free.
 		 */
 		mesgin_disconnect:
 0ef 04a04001		or	SCB_CONTROL,DISCONNECTED;
 0f0 0065a65d		call	add_scb_to_disc_list;
 0f1 0065e041		jmp	await_busfree;
 		
 		/*
 		 * Save data pointers message:
 		 * Copying RAM values back to SCB, for Save Data Pointers message, but
 		 * only if we've actually been into a data phase to change them.  This
 		 * protects against bogus data in scratch ram and the residual counts
 		 * since they are only initialized when we go into data_in or data_out.
 		 */
 		mesgin_sdptrs:
 0f2 2036727b		test	SEQ_FLAGS, DPHASE	jz mesgin_done;
 		
 			/*
 			 * The SCB SGPTR becomes the next one we'll download,
 			 * and the SCB DATAPTR becomes the current SHADDR.
 			 * Use the residual number since STCNT is corrupted by
 			 * any message transfer.
 			 */
 			if ((ahc->features & AHC_CMD_CHAN) != 0) {
 0f3 05384631			bmov	SCB_SGCOUNT, SG_COUNT, 5;
 0f4 04145831			bmov	SCB_DATAPTR, SHADDR, 4;
 0f5 03a96031			bmov	SCB_DATACNT, SCB_RESID_DCNT, 3;
 			} else {
 				mvi	DINDEX, SCB_SGCOUNT;
 				mvi	SG_COUNT call bcopy_5;
 			
 				mvi	DINDEX, SCB_DATAPTR;
 				mvi	SHADDR		call bcopy_4;
 				mvi	SCB_RESID_DCNT	call bcopy_3;
 			}
 0f6 00657243		jmp	mesgin_done;
 		
 		/*
 		 * Restore pointers message?  Data pointers are recopied from the
 		 * SCB anytime we enter a data phase for the first time, so all
 		 * we need to do is clear the DPHASE flag and let the data phase
 		 * code do the rest.
 		 */
 		mesgin_rdptrs:
 0f7 df366c08		and	SEQ_FLAGS, ~DPHASE;		/*
 								 * We'll reload them
 								 * the next time through
 								 * the dataphase.
 								 */
 0f8 00657243		jmp	mesgin_done;
 		
 		/*
 		 * Identify message?  For a reconnecting target, this tells us the lun
 		 * that the reconnection is for - find the correct SCB and switch to it,
 		 * clearing the "disconnected" bit so we don't "find" it by accident later.
 		 */
 		mesgin_identify:
 			if ((ahc->features & AHC_WIDE) != 0) {
 0f9 0f64c808			and	A,0x0f;		/* lun in lower four bits */
 			} else {
 				and	A,0x07;		/* lun in lower three bits */
 			}
 0fa 00376e00		or      SAVED_TCL,A;		/* SAVED_TCL should be complete now */
 		
 0fb ff6aa600		mvi	ARG_2, SCB_LIST_NULL;	/* SCBID of prev SCB in disc List */
 0fc 0065905c		call	get_untagged_SCBID;
 0fd ff52de73		cmp	ARG_1, SCB_LIST_NULL	je snoop_tag;
 			if ((ahc->flags & AHC_PAGESCBS) != 0) {
 0fe 4036e87b			test	SEQ_FLAGS, SCBPTR_VALID	jz use_retrieveSCB;
 			}
 			/*
 			 * If the SCB was found in the disconnected list (as is
 			 * always the case in non-paging scenarios), SCBPTR is already
 			 * set to the correct SCB.  So, simply setup the SCB and get
 			 * on with things.
 			 */
 0ff 00657e5c		call	rem_scb_from_disc_list;
 100 0065ea43		jmp	setup_SCB;
 		/*
 		 * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
 		 * If we get one, we use the tag returned to find the proper
 		 * SCB.  With SCB paging, this requires using search for both tagged
 		 * and non-tagged transactions since the SCB may exist in any slot.
 		 * If we're not using SCB paging, we can use the tag as the direct
 		 * index to the SCB.
 		 */
 		snoop_tag:
 101 ff06d408		mov	NONE,SCSIDATL;		/* ACK Identify MSG */
 		snoop_tag_loop:
 102 0065ec5c		call	phase_lock;
 103 e03d0664		cmp	LASTPHASE, P_MESGIN	jne not_found;
 104 20120664		cmp	SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
 		get_tag:
 105 526a125c		mvi	ARG_1	call inb_next;	/* tag value */
 		
 			/*
 			 * Ensure that the SCB the tag points to is for
 			 * an SCB transaction to the reconnecting target.
 			 */
 		use_retrieveSCB:
 106 0065785c		call	retrieveSCB;
 		setup_SCB:
 107 ff37c808		mov	A, SAVED_TCL;
 108 00a1fe63		cmp	SCB_TCL, A	jne not_found_cleanup_scb;
 109 04a0fe7b		test	SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
 10a fba04009		and	SCB_CONTROL,~DISCONNECTED;
 10b 80366c00		or	SEQ_FLAGS,IDENTIFY_SEEN;	/* make note of IDENTIFY */
 10c 00657458		call	set_transfer_settings;
 			/* See if the host wants to send a message upon reconnection */
 10d 10a0727b		test	SCB_CONTROL, MK_MESSAGE jz mesgin_done;
 10e efa04009		and	SCB_CONTROL, ~MK_MESSAGE;
 10f ff6a0a5c		mvi	HOST_MSG	call mk_mesg;
 110 00657243		jmp	mesgin_done;
 		
 		not_found_cleanup_scb:
 111 04a0047c		test	SCB_CONTROL, DISCONNECTED jz . + 3;
 112 0065a65d		call	add_scb_to_disc_list;
 113 00650644		jmp	not_found;
 114 00658c5d		call	add_scb_to_free_list;
 		not_found:
 115 316a2201		mvi	INTSTAT, NO_MATCH;
 116 00657243		jmp	mesgin_done;
 		
 		/*
 		 * [ ADD MORE MESSAGE HANDLING HERE ]
 		 */
 		
 		/*
 		 * Locking the driver out, build a one-byte message passed in SINDEX
 		 * if there is no active message already.  SINDEX is returned intact.
 		 */
 		mk_mesg:
 117 103d0600		or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
 118 ff65680c		mov	MSG_OUT,SINDEX ret;
 		
 		/*
 		 * Functions to read data in Automatic PIO mode.
 		 *
 		 * According to Adaptec's documentation, an ACK is not sent on input from
 		 * the target until SCSIDATL is read from.  So we wait until SCSIDATL is
 		 * latched (the usual way), then read the data byte directly off the bus
 		 * using SCSIBUSL.  When we have pulled the ATN line, or we just want to
 		 * acknowledge the byte, then we do a dummy read from SCISDATL.  The SCSI
 		 * spec guarantees that the target will hold the data byte on the bus until
 		 * we send our ACK.
 		 *
 		 * The assumption here is that these are called in a particular sequence,
 		 * and that REQ is already set when inb_first is called.  inb_{first,next}
 		 * use the same calling convention as inb.
 		 */
 		inb_next_wait_perr:
 119 b16a2201		mvi	INTSTAT, PERR_DETECTED;
 11a 00651444		jmp	inb_next_wait;
 		inb_next:
 11b ff06d408		mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
 		inb_next_wait:
 			/*
 			 * If there is a parity error, wait for the kernel to
 			 * see the interrupt and prepare our message response
 			 * before continuing.
 			 */
 11c 010c147c		test	SSTAT1, REQINIT	jz inb_next_wait;
 11d 040c0e6c		test	SSTAT1, SCSIPERR jnz inb_next_wait_perr;
 		inb_next_check_phase:
 11e e0037a08		and	LASTPHASE, PHASE_MASK, SCSISIGI;
 11f e03d4264		cmp	LASTPHASE, P_MESGIN jne mesgin_phasemis;
 		inb_first:
 120 ff65cc08		mov	DINDEX,SINDEX;
 121 ff12da0c		mov	DINDIR,SCSIBUSL	ret;		/*read byte directly from bus*/
 		inb_last:
 122 ff06d40c		mov	NONE,SCSIDATL ret;		/*dummy read from latch to ACK*/
 		}
 		
 		if ((ahc->flags & AHC_TARGETMODE) != 0) {
 		/*
 		 * Change to a new phase.  If we are changing the state of the I/O signal,
 		 * from out to in, wait an additional data release delay before continuing.
 		 */
 		change_phase:
 			/* Wait for preceeding I/O session to complete. */
 			test	SCSISIGI, ACKI jnz .;
 		
 			/* Change the phase */
 			and	DINDEX, IOI, SCSISIGI;
 			mov	SCSISIGO, SINDEX;
 			and	A, IOI, SINDEX;
 		
 			/*
 			 * If the data direction has changed, from
 			 * out (initiator driving) to in (target driving),
 			 * we must waitat least a data release delay plus
 			 * the normal bus settle delay. [SCSI III SPI 10.11.0]
 			 */
 			cmp 	DINDEX, A je change_phase_wait;
 			test	SINDEX, IOI jz change_phase_wait;
 			call	change_phase_wait;
 		change_phase_wait:
 			nop;
 			nop;
 			nop;
 			nop ret;
 		
 		/*
 		 * Send a byte to an initiator in Automatic PIO mode.
 		 */
 		target_outb:
 			or	SXFRCTL0, SPIOEN;
 			test	SSTAT0, SPIORDY	jz .;
 			mov	SCSIDATL, SINDEX;
 			test	SSTAT0, SPIORDY	jz .;
 			and	SXFRCTL0, ~SPIOEN ret;
 		}
 			
 		mesgin_phasemis:
 		/*
 		 * We expected to receive another byte, but the target changed phase
 		 */
 123 e16a2201		mvi	INTSTAT, MSGIN_PHASEMIS;
 124 0065ce41		jmp	ITloop;
 		
 		/*
 		 * DMA data transfer.  HADDR and HCNT must be loaded first, and
 		 * SINDEX should contain the value to load DFCNTRL with - 0x3d for
 		 * host->scsi, or 0x39 for scsi->host.  The SCSI channel is cleared
 		 * during initialization.
 		 */
 		dma:
 125 ff652609		mov	DFCNTRL,SINDEX;
 		dma_loop:
 126 010b526c		test	SSTAT0,DMADONE	jnz dma_dmadone;
 127 100c487c		test	SSTAT1,PHASEMIS	jz dma_loop;	/* ie. underrun */
 		dma_phasemis:
 		
 		/*
 		 * We will be "done" DMAing when the transfer count goes to zero, or
 		 * the target changes the phase (in light of this, it makes sense that
 		 * the DMA circuitry doesn't ACK when PHASEMIS is active).  If we are
 		 * doing a SCSI->Host transfer, the data FIFO should be flushed auto-
 		 * magically on STCNT=0 or a phase change, so just wait for FIFO empty
 		 * status.
 		 */
 		dma_checkfifo:
 128 0493506c		test	DFCNTRL,DIRECTION	jnz dma_fifoempty;
 		dma_fifoflush:
 129 01944e7c		test	DFSTATUS,FIFOEMP	jz dma_fifoflush;
 		
 		dma_fifoempty:
 			/* Don't clobber an inprogress host data transfer */
 12a 1094506c		test	DFSTATUS, MREQPEND	jnz dma_fifoempty;
 		/*
 		 * Now shut the DMA enables off and make sure that the DMA enables are 
 		 * actually off first lest we get an ILLSADDR.
 		 */
 		dma_dmadone:
 12b c7932609		and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
 		dma_halt:
 			/*
 			 * Some revisions of the aic7880 have a problem where, if the
 			 * data fifo is full, but the PCI input latch is not empty, 
 			 * HDMAEN cannot be cleared.  The fix used here is to attempt
 			 * to drain the data fifo until there is space for the input
 			 * latch to drain and HDMAEN de-asserts.
 			 */
 			if ((ahc->features & AHC_ULTRA2) == 0) {
 				mov	NONE, DFDAT;
 			}
 12c 3893546c		test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
 		return:
 12d ff6ad40c		ret;
 		
 		/*
 		 * Assert that if we've been reselected, then we've seen an IDENTIFY
 		 * message.
 		 */
 		assert:
 12e 8036586c		test	SEQ_FLAGS,IDENTIFY_SEEN	jnz return;	/* seen IDENTIFY? */
 		
 12f 216a2205		mvi	INTSTAT,NO_IDENT 	ret;	/* no - tell the kernel */
 		
 		/*
 		 * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL)
 		 * or by the SCBID ARG_1.  The search begins at the SCB index passed in
 		 * via SINDEX which is an SCB that must be on the disconnected list.  If
 		 * the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR
 		 * is set to the proper SCB.
 		 */
 		findSCB:
 130 ff652009		mov	SCBPTR,SINDEX;			/* Initialize SCBPTR */
 131 ff526664		cmp	ARG_1, SCB_LIST_NULL	jne findSCB_by_SCBID;
 132 ff37c808		mov	A, SAVED_TCL;
 133 a16a7244		mvi	SCB_TCL	jmp findSCB_loop;	/* &SCB_TCL -> SINDEX */
 		findSCB_by_SCBID:
 134 ff52c808		mov	A, ARG_1;			/* Tag passed in ARG_1 */
 135 b96a7244		mvi	SCB_TAG	jmp findSCB_loop;	/* &SCB_TAG -> SINDEX */
 		findSCB_next:
 136 ff90a608		mov	ARG_2, SCBPTR;
 137 ffba7674		cmp	SCB_NEXT, SCB_LIST_NULL je notFound;
 138 ffba2009		mov	SCBPTR,SCB_NEXT;
 139 ff65ca18		dec	SINDEX;		/* Last comparison moved us too far */
 		findSCB_loop:
 13a 006c6a64		cmp	SINDIR, A	jne findSCB_next;
 13b ff90ca0c		mov	SINDEX, SCBPTR 	ret;
 		notFound:
 13c ff6aca04		mvi	SINDEX, SCB_LIST_NULL	ret;
 		
 		/*
 		 * Retrieve an SCB by SCBID first searching the disconnected list falling
 		 * back to DMA'ing the SCB down from the host.  This routine assumes that
 		 * ARG_1 is the SCBID of interrest and that SINDEX is the position in the
 		 * disconnected list to start the search from.  If SINDEX is SCB_LIST_NULL,
 		 * we go directly to the host for the SCB.
 		 */
 		retrieveSCB:
 13d 40368a7c		test	SEQ_FLAGS, SCBPTR_VALID	jz retrieve_from_host;
 13e 00905e5c		mov	SCBPTR	call findSCB;	/* Continue the search */
 13f ff658a74		cmp	SINDEX, SCB_LIST_NULL	je retrieve_from_host;
 		
 		/*
 		 * This routine expects SINDEX to contain the index of the SCB to be
 		 * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the
 		 * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL
 		 * if it is at the head.
 		 */
 		rem_scb_from_disc_list:
 		/* Remove this SCB from the disconnection list */
 140 ff538874		cmp	ARG_2, SCB_LIST_NULL	je rHead;
 141 ffbacc08		mov	DINDEX, SCB_NEXT;
 142 ff532009		mov	SCBPTR, ARG_2;
 143 ff667409		mov	SCB_NEXT, DINDEX;
 144 ff65200d		mov	SCBPTR, SINDEX ret;
 		rHead:
 145 ffba7e0c		mov	DISCONNECTED_SCBH,SCB_NEXT ret;
 		
 		retrieve_from_host:
 		/*
 		 * We didn't find it.  Pull an SCB and DMA down the one we want.
 		 * We should never get here in the non-paging case.
 		 */
 146 006a945d		mov	ALLZEROS	call	get_free_or_disc_scb;
 147 0d6a6a00		mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
 			/* Jump instead of call as we want to return anyway */
 148 00521e45		mov	ARG_1	jmp dma_scb;
 		
 		/*
 		 * Determine whether a target is using tagged or non-tagged transactions
 		 * by first looking for a matching transaction based on the TCL and if
 		 * that fails, looking up this device in the host's untagged SCB array.
 		 * The TCL to search for is assumed to be in SAVED_TCL.  The value is
 		 * returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged).
 		 * The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information
 		 * in an SCB instead of having to go to the host.
 		 */
 		get_untagged_SCBID:
 149 ff3fe474		cmp	DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host;
 14a ff6aa400		mvi	ARG_1, SCB_LIST_NULL;
 14b 003f5e5c		mov	DISCONNECTED_SCBH call findSCB;
 14c ff65e474		cmp	SINDEX, SCB_LIST_NULL	je get_SCBID_from_host;
 14d 40366c00		or	SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */
 14e 20a09e6c		test	SCB_CONTROL, TAG_ENB	jnz . + 2;
 14f ffb9a40c		mov	ARG_1, SCB_TAG	ret;
 150 ff6aa404		mvi	ARG_1, SCB_LIST_NULL ret;
 		
 		/*
 		 * Fetch a byte from host memory given an index of (A + (256 * SINDEX))
 		 * and a base address of SCBID_ADDR.  The byte is returned in RETURN_2.
 		 */
 		fetch_byte:
 151 ff65a608		mov	ARG_2, SINDEX;
 			if ((ahc->features & AHC_CMD_CHAN) != 0) {
 152 e06acc00			mvi	DINDEX, CCHADDR;
 153 456a125d			mvi	SCBID_ADDR call set_1byte_addr;
 154 016ad001			mvi	CCHCNT, 1;
 155 096ad601			mvi	CCSGCTL, CCSGEN|CCSGRESET;
 156 80ebaa7c			test	CCSGCTL, CCSGDONE jz .;
 157 016ad601			mvi	CCSGCTL, CCSGRESET;
 158 01e9a634			bmov	RETURN_2, CCSGRAM, 1 ret;
 			} else {
 				mvi	DINDEX, HADDR;
 				mvi	SCBID_ADDR call set_1byte_addr;
 				mvi	HCNT[0], 1;
 				clr	HCNT[1];
 				clr	HCNT[2];
 				mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
 				call	dma_finish;
 				mov	RETURN_2, DFDAT ret;
 			}
 		
 		/*
 		 * Prepare the hardware to post a byte to host memory given an
 		 * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR.
 		 */
 		post_byte_setup:
 159 ff65a608		mov	ARG_2, SINDEX;
 			if ((ahc->features & AHC_CMD_CHAN) != 0) {
 15a e06acc00			mvi	DINDEX, CCHADDR;
 15b 456a125d			mvi	SCBID_ADDR call	set_1byte_addr;
 15c 016ad001			mvi	CCHCNT, 1;
 15d 016adc05			mvi	CCSCBCTL, CCSCBRESET ret;
 			} else {
 				mvi	DINDEX, HADDR;
 				mvi	SCBID_ADDR call	set_1byte_addr;
 				mvi	HCNT[0], 1;
 				clr	HCNT[1];
 				clr	HCNT[2];
 				mvi	DFCNTRL, FIFORESET ret;
 			}
 		
 		post_byte:
 			if ((ahc->features & AHC_CMD_CHAN) != 0) {
 15e 0165d831			bmov	CCSCBRAM, SINDEX, 1;
 15f 09eedc01			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
 160 80eeda7c			test	CCSCBCTL, CCSCBDONE jz .;
 161 ff6adc0d			clr	CCSCBCTL ret;
 			} else {
 				mov	DFDAT, SINDEX;
 				or	DFCNTRL, HDMAEN|FIFOFLUSH;
 				jmp	dma_finish;
 			}
 		
 		get_SCBID_from_host:
 162 ff37c808		mov	A, SAVED_TCL;
 163 026aa05c		mvi	UNTAGGEDSCB_OFFSET call fetch_byte;
 164 ff53a40c		mov	RETURN_1,  RETURN_2 ret;
 		
 		phase_lock_perr:
 165 b16a2201		mvi	INTSTAT, PERR_DETECTED;
 		phase_lock:     
 			/*
 			 * If there is a parity error, wait for the kernel to
 			 * see the interrupt and prepare our message response
 			 * before continuing.
 			 */
 166 010cec7c		test	SSTAT1, REQINIT jz phase_lock;
 167 040cea6c		test	SSTAT1, SCSIPERR jnz phase_lock_perr;
 		phase_lock_latch_phase:
 168 e0030608		and	SCSISIGO, PHASE_MASK, SCSISIGI;
 169 e0037a0c		and	LASTPHASE, PHASE_MASK, SCSISIGI ret;
 		
 		if ((ahc->features & AHC_CMD_CHAN) == 0) {
 		set_stcnt_from_hcnt:
 			mov	STCNT[0], HCNT[0];
 			mov	STCNT[1], HCNT[1];
 			mov	STCNT[2], HCNT[2] ret;
 		
 		bcopy_7:
 			mov	DINDIR, SINDIR;
 			mov	DINDIR, SINDIR;
 		bcopy_5:
 			mov	DINDIR, SINDIR;
 		bcopy_4:
 			mov	DINDIR, SINDIR;
 		bcopy_3:
 			mov	DINDIR, SINDIR;
 			mov	DINDIR, SINDIR;
 			mov	DINDIR, SINDIR ret;
 		}
 		
 		if ((ahc->flags & AHC_TARGETMODE) != 0) {
 		/*
 		 * Setup addr assuming that A is an index into
 		 * an array of 32byte objects, SINDEX contains
 		 * the base address of that array, and DINDEX
 		 * contains the base address of the location
 		 * to store the indexed address.
 		 */
 		set_32byte_addr:
 			shr	ARG_2, 3, A;
 			shl	A, 5;
 			jmp	set_1byte_addr;
 		}
 		
 		/*
 		 * Setup addr assuming that A is an index into
 		 * an array of 64byte objects, SINDEX contains
 		 * the base address of that array, and DINDEX
 		 * contains the base address of the location
 		 * to store the indexed address.
 		 */
 		set_64byte_addr:
 16a 2e64a628		shr	ARG_2, 2, A;
 16b 6664c828		shl	A, 6;
 		
 		/*
 		 * Setup addr assuming that A + (ARG_1 * 256) is an
 		 * index into an array of 1byte objects, SINDEX contains
 		 * the base address of that array, and DINDEX contains
 		 * the base address of the location to store the computed
 		 * address.
 		 */
 		set_1byte_addr:
 16c 006cda18		add     DINDIR, A, SINDIR;
 16d ff53c808		mov     A, ARG_2;
 16e 006cda20		adc	DINDIR, A, SINDIR;
 16f ff6ac808		clr	A;
 170 006cda20		adc	DINDIR, A, SINDIR;
 171 006cda24		adc	DINDIR, A, SINDIR ret;
 		
 		/*
 		 * Either post or fetch and SCB from host memory based on the
 		 * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
 		 */
 		dma_scb:
 172 ff65c808		mov	A, SINDEX;
 			if ((ahc->features & AHC_CMD_CHAN) != 0) {
 173 e06acc00			mvi	DINDEX, CCHADDR;
 174 416a0e5d			mvi	HSCB_ADDR call set_64byte_addr;
 175 ff90e209			mov	CCSCBPTR, SCBPTR;
 176 0435307d			test	DMAPARAMS, DIRECTION jz dma_scb_tohost;
 177 306ad001			mvi	CCHCNT, SCB_64BYTE_SIZE;
 178 1d6adc01			mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
 179 dcee2c65			cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
 17a 00653e45			jmp	dma_scb_finish;
 		dma_scb_tohost:
 17b 1c6ad001			mvi	CCHCNT, SCB_32BYTE_SIZE;
 				if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
 					mvi	CCSCBCTL, CCSCBRESET;
 					bmov	CCSCBRAM, SCB_CONTROL, SCB_32BYTE_SIZE;
 					or	CCSCBCTL, CCSCBEN|CCSCBRESET;
 					test	CCSCBCTL, CCSCBDONE jz .;
 				} else {
 17c 196adc01				mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
 17d d8ee3c65				cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
 				}
 		dma_scb_finish:
 17e ff6adc09			clr	CCSCBCTL;
 17f 18ee406d			test	CCSCBCTL, CCARREN|CCSCBEN jnz .;
 180 ff6ad40c			ret;
 			} else {
 				mvi	DINDEX, HADDR;
 				mvi	HSCB_ADDR call set_64byte_addr;
 				mvi	HCNT[0], SCB_32BYTE_SIZE;
 				clr	HCNT[1];
 				clr	HCNT[2];
 				mov	DFCNTRL, DMAPARAMS;
 				test	DMAPARAMS, DIRECTION	jnz dma_scb_fromhost;
 				/* Fill it with the SCB data */
 		copy_scb_tofifo:
 				mvi	SINDEX, SCB_CONTROL;
 				add	A, SCB_32BYTE_SIZE, SINDEX;
 		copy_scb_tofifo_loop:
 				mov	DFDAT,SINDIR;
 				mov	DFDAT,SINDIR;
 				mov	DFDAT,SINDIR;
 				mov	DFDAT,SINDIR;
 				mov	DFDAT,SINDIR;
 				mov	DFDAT,SINDIR;
 				mov	DFDAT,SINDIR;
 				cmp	SINDEX, A jne copy_scb_tofifo_loop;
 				or	DFCNTRL, HDMAEN|FIFOFLUSH;
 		dma_scb_fromhost:
 				call	dma_finish;
 				/* If we were putting the SCB, we are done */
 				test	DMAPARAMS, DIRECTION	jz	return;
 				mvi	SCB_CONTROL  call dfdat_in_7;
 				call	dfdat_in_7_continued;
 				call	dfdat_in_7_continued;
 				jmp	dfdat_in_7_continued;
 		dfdat_in_7:
 				mov     DINDEX,SINDEX;
 		dfdat_in_7_continued:
 				mov	DINDIR,DFDAT;
 				mov	DINDIR,DFDAT;
 				mov	DINDIR,DFDAT;
 				mov	DINDIR,DFDAT;
 				mov	DINDIR,DFDAT;
 				mov	DINDIR,DFDAT;
 				mov	DINDIR,DFDAT ret;
 			}
 		
 		
 		/*
 		 * Wait for DMA from host memory to data FIFO to complete, then disable
 		 * DMA and wait for it to acknowledge that it's off.
 		 */
 		dma_finish:
 181 0894847d		test	DFSTATUS,HDONE	jz dma_finish;
 			/* Turn off DMA */
 182 f7932609		and	DFCNTRL, ~HDMAEN;
 183 0893886d		test	DFCNTRL, HDMAEN jnz .;
 184 ff6ad40c		ret;
 		
 		add_scb_to_free_list:
 			if ((ahc->flags & AHC_PAGESCBS) != 0) {
 185 ff407409			mov	SCB_NEXT, FREE_SCBH;
 186 ff6a7201			mvi	SCB_TAG, SCB_LIST_NULL;
 187 ff90800c			mov	FREE_SCBH, SCBPTR ret;
 			} else {
 				mvi	SCB_TAG, SCB_LIST_NULL ret;
 			}
 		
 		if ((ahc->flags & AHC_PAGESCBS) != 0) {
 		get_free_or_disc_scb:
 188 ff40a265		cmp	FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
 189 ff3f9a65		cmp	DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
 		return_error:
 18a ff6aca04		mvi	SINDEX, SCB_LIST_NULL	ret;
 		dequeue_disc_scb:
 18b ff3f2009		mov	SCBPTR, DISCONNECTED_SCBH;
 		dma_up_scb:
 18c 016a6a00		mvi	DMAPARAMS, FIFORESET;
 18d 00b91e5d		mov	SCB_TAG		call dma_scb;
 		unlink_disc_scb:
 18e ffba7e0c		mov	DISCONNECTED_SCBH, SCB_NEXT ret;
 		dequeue_free_scb:
 18f ff402009		mov	SCBPTR, FREE_SCBH;
 190 ffba800c		mov	FREE_SCBH, SCB_NEXT ret;
 		}
 		
 		add_scb_to_disc_list:
 		/*
 		 * Link this SCB into the DISCONNECTED list.  This list holds the
 		 * candidates for paging out an SCB if one is needed for a new command.
 		 * Modifying the disconnected list is a critical(pause dissabled) section.
 		 */
 191 ff3f7409		mov	SCB_NEXT, DISCONNECTED_SCBH;
 192 ff907e0c		mov	DISCONNECTED_SCBH, SCBPTR ret;
 
 
 


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200006140100.SAA13387>