head	1.25;
access;
symbols
	netbsd-11-0-RC7:1.24
	netbsd-11-0-RC6:1.24
	netbsd-11-0-RC5:1.24
	netbsd-11-0-RC4:1.24
	netbsd-11-0-RC3:1.24
	netbsd-11-0-RC2:1.24
	netbsd-11-0-RC1:1.24
	perseant-exfatfs-base-20250801:1.24
	netbsd-11:1.24.0.4
	netbsd-11-base:1.24
	netbsd-10-1-RELEASE:1.23
	perseant-exfatfs-base-20240630:1.24
	perseant-exfatfs:1.24.0.2
	perseant-exfatfs-base:1.24
	netbsd-8-3-RELEASE:1.19
	netbsd-9-4-RELEASE:1.20
	netbsd-10-0-RELEASE:1.23
	netbsd-10-0-RC6:1.23
	netbsd-10-0-RC5:1.23
	netbsd-10-0-RC4:1.23
	netbsd-10-0-RC3:1.23
	netbsd-10-0-RC2:1.23
	thorpej-ifq:1.23.0.8
	thorpej-ifq-base:1.23
	thorpej-altq-separation:1.23.0.6
	thorpej-altq-separation-base:1.23
	netbsd-10-0-RC1:1.23
	netbsd-10:1.23.0.4
	netbsd-10-base:1.23
	bouyer-sunxi-drm:1.23.0.2
	bouyer-sunxi-drm-base:1.23
	netbsd-9-3-RELEASE:1.20
	thorpej-i2c-spi-conf2:1.20.0.24
	thorpej-i2c-spi-conf2-base:1.20
	thorpej-futex2:1.20.0.22
	thorpej-futex2-base:1.20
	thorpej-cfargs2:1.20.0.20
	thorpej-cfargs2-base:1.20
	cjep_sun2x-base1:1.20
	cjep_sun2x:1.20.0.18
	cjep_sun2x-base:1.20
	cjep_staticlib_x-base1:1.20
	netbsd-9-2-RELEASE:1.20
	cjep_staticlib_x:1.20.0.16
	cjep_staticlib_x-base:1.20
	thorpej-i2c-spi-conf:1.20.0.14
	thorpej-i2c-spi-conf-base:1.20
	thorpej-cfargs:1.20.0.12
	thorpej-cfargs-base:1.20
	thorpej-futex:1.20.0.10
	thorpej-futex-base:1.20
	netbsd-9-1-RELEASE:1.20
	bouyer-xenpvh-base2:1.20
	phil-wifi-20200421:1.20
	bouyer-xenpvh-base1:1.20
	phil-wifi-20200411:1.20
	bouyer-xenpvh:1.20.0.8
	bouyer-xenpvh-base:1.20
	is-mlppp:1.20.0.6
	is-mlppp-base:1.20
	phil-wifi-20200406:1.20
	netbsd-8-2-RELEASE:1.19
	ad-namecache-base3:1.20
	netbsd-9-0-RELEASE:1.20
	netbsd-9-0-RC2:1.20
	ad-namecache-base2:1.20
	ad-namecache-base1:1.20
	ad-namecache:1.20.0.4
	ad-namecache-base:1.20
	netbsd-9-0-RC1:1.20
	phil-wifi-20191119:1.20
	netbsd-9:1.20.0.2
	netbsd-9-base:1.20
	phil-wifi-20190609:1.20
	netbsd-8-1-RELEASE:1.19
	netbsd-8-1-RC1:1.19
	isaki-audio2:1.19.0.24
	isaki-audio2-base:1.19
	pgoyette-compat-merge-20190127:1.19
	pgoyette-compat-20190127:1.19
	pgoyette-compat-20190118:1.19
	pgoyette-compat-1226:1.19
	pgoyette-compat-1126:1.19
	pgoyette-compat-1020:1.19
	pgoyette-compat-0930:1.19
	pgoyette-compat-0906:1.19
	jdolecek-ncqfixes:1.19.0.22
	jdolecek-ncqfixes-base:1.19
	netbsd-7-2-RELEASE:1.18
	pgoyette-compat-0728:1.19
	netbsd-8-0-RELEASE:1.19
	phil-wifi:1.19.0.20
	phil-wifi-base:1.19
	pgoyette-compat-0625:1.19
	netbsd-8-0-RC2:1.19
	pgoyette-compat-0521:1.19
	pgoyette-compat-0502:1.19
	pgoyette-compat-0422:1.19
	netbsd-8-0-RC1:1.19
	pgoyette-compat-0415:1.19
	pgoyette-compat-0407:1.19
	pgoyette-compat-0330:1.19
	pgoyette-compat-0322:1.19
	pgoyette-compat-0315:1.19
	netbsd-7-1-2-RELEASE:1.18
	pgoyette-compat:1.19.0.18
	pgoyette-compat-base:1.19
	netbsd-7-1-1-RELEASE:1.18
	tls-maxphys-base-20171202:1.19
	matt-nb8-mediatek:1.19.0.16
	matt-nb8-mediatek-base:1.19
	nick-nhusb-base-20170825:1.19
	perseant-stdc-iso10646:1.19.0.14
	perseant-stdc-iso10646-base:1.19
	netbsd-8:1.19.0.12
	netbsd-8-base:1.19
	prg-localcount2-base3:1.19
	prg-localcount2-base2:1.19
	prg-localcount2-base1:1.19
	prg-localcount2:1.19.0.10
	prg-localcount2-base:1.19
	pgoyette-localcount-20170426:1.19
	bouyer-socketcan-base1:1.19
	jdolecek-ncq:1.19.0.8
	jdolecek-ncq-base:1.19
	pgoyette-localcount-20170320:1.19
	netbsd-7-1:1.18.0.122
	netbsd-7-1-RELEASE:1.18
	netbsd-7-1-RC2:1.18
	nick-nhusb-base-20170204:1.19
	netbsd-7-nhusb-base-20170116:1.18
	bouyer-socketcan:1.19.0.6
	bouyer-socketcan-base:1.19
	pgoyette-localcount-20170107:1.19
	netbsd-7-1-RC1:1.18
	nick-nhusb-base-20161204:1.19
	pgoyette-localcount-20161104:1.19
	netbsd-7-0-2-RELEASE:1.18
	nick-nhusb-base-20161004:1.19
	localcount-20160914:1.19
	netbsd-7-nhusb:1.18.0.120
	netbsd-7-nhusb-base:1.18
	pgoyette-localcount-20160806:1.19
	pgoyette-localcount-20160726:1.19
	pgoyette-localcount:1.19.0.4
	pgoyette-localcount-base:1.19
	nick-nhusb-base-20160907:1.19
	nick-nhusb-base-20160529:1.19
	netbsd-7-0-1-RELEASE:1.18
	nick-nhusb-base-20160422:1.19
	nick-nhusb-base-20160319:1.19
	nick-nhusb-base-20151226:1.19
	netbsd-7-0:1.18.0.118
	netbsd-7-0-RELEASE:1.18
	nick-nhusb-base-20150921:1.19
	netbsd-7-0-RC3:1.18
	netbsd-7-0-RC2:1.18
	netbsd-7-0-RC1:1.18
	nick-nhusb-base-20150606:1.19
	nick-nhusb-base-20150406:1.19
	nick-nhusb:1.19.0.2
	nick-nhusb-base:1.19
	netbsd-5-2-3-RELEASE:1.18
	netbsd-5-1-5-RELEASE:1.18
	netbsd-6-0-6-RELEASE:1.18
	netbsd-6-1-5-RELEASE:1.18
	netbsd-7:1.18.0.116
	netbsd-7-base:1.18
	yamt-pagecache-base9:1.18
	yamt-pagecache-tag8:1.18
	netbsd-6-1-4-RELEASE:1.18
	netbsd-6-0-5-RELEASE:1.18
	tls-earlyentropy:1.18.0.114
	tls-earlyentropy-base:1.18
	riastradh-xf86-video-intel-2-7-1-pre-2-21-15:1.18
	riastradh-drm2-base3:1.18
	netbsd-6-1-3-RELEASE:1.18
	netbsd-6-0-4-RELEASE:1.18
	netbsd-5-2-2-RELEASE:1.18
	netbsd-5-1-4-RELEASE:1.18
	netbsd-6-1-2-RELEASE:1.18
	netbsd-6-0-3-RELEASE:1.18
	netbsd-5-2-1-RELEASE:1.18
	netbsd-5-1-3-RELEASE:1.18
	rmind-smpnet-nbase:1.18
	netbsd-6-1-1-RELEASE:1.18
	riastradh-drm2-base2:1.18
	riastradh-drm2-base1:1.18
	riastradh-drm2:1.18.0.112
	riastradh-drm2-base:1.18
	rmind-smpnet:1.18.0.104
	rmind-smpnet-base:1.18
	netbsd-6-1:1.18.0.110
	netbsd-6-0-2-RELEASE:1.18
	netbsd-6-1-RELEASE:1.18
	khorben-n900:1.18.0.108
	netbsd-6-1-RC4:1.18
	netbsd-6-1-RC3:1.18
	agc-symver:1.18.0.106
	agc-symver-base:1.18
	netbsd-6-1-RC2:1.18
	netbsd-6-1-RC1:1.18
	yamt-pagecache-base8:1.18
	netbsd-5-2:1.18.0.102
	netbsd-6-0-1-RELEASE:1.18
	yamt-pagecache-base7:1.18
	netbsd-5-2-RELEASE:1.18
	netbsd-5-2-RC1:1.18
	matt-nb6-plus-nbase:1.18
	yamt-pagecache-base6:1.18
	netbsd-6-0:1.18.0.100
	netbsd-6-0-RELEASE:1.18
	netbsd-6-0-RC2:1.18
	tls-maxphys:1.18.0.98
	tls-maxphys-base:1.18
	matt-nb6-plus:1.18.0.96
	matt-nb6-plus-base:1.18
	netbsd-6-0-RC1:1.18
	jmcneill-usbmp-base10:1.18
	yamt-pagecache-base5:1.18
	jmcneill-usbmp-base9:1.18
	yamt-pagecache-base4:1.18
	jmcneill-usbmp-base8:1.18
	jmcneill-usbmp-base7:1.18
	jmcneill-usbmp-base6:1.18
	jmcneill-usbmp-base5:1.18
	jmcneill-usbmp-base4:1.18
	jmcneill-usbmp-base3:1.18
	jmcneill-usbmp-pre-base2:1.18
	jmcneill-usbmp-base2:1.18
	netbsd-6:1.18.0.94
	netbsd-6-base:1.18
	netbsd-5-1-2-RELEASE:1.18
	netbsd-5-1-1-RELEASE:1.18
	jmcneill-usbmp:1.18.0.92
	jmcneill-usbmp-base:1.18
	jmcneill-audiomp3:1.18.0.90
	jmcneill-audiomp3-base:1.18
	yamt-pagecache-base3:1.18
	yamt-pagecache-base2:1.18
	yamt-pagecache:1.18.0.88
	yamt-pagecache-base:1.18
	rmind-uvmplock-nbase:1.18
	cherry-xenmp:1.18.0.86
	cherry-xenmp-base:1.18
	bouyer-quota2-nbase:1.18
	bouyer-quota2:1.18.0.84
	bouyer-quota2-base:1.18
	jruoho-x86intr:1.18.0.82
	jruoho-x86intr-base:1.18
	matt-mips64-premerge-20101231:1.18
	matt-nb5-mips64-premerge-20101231:1.18
	matt-nb5-pq3:1.18.0.80
	matt-nb5-pq3-base:1.18
	netbsd-5-1:1.18.0.78
	netbsd-5-1-RELEASE:1.18
	uebayasi-xip-base4:1.18
	uebayasi-xip-base3:1.18
	yamt-nfs-mp-base11:1.18
	netbsd-5-1-RC4:1.18
	matt-nb5-mips64-k15:1.18
	uebayasi-xip-base2:1.18
	yamt-nfs-mp-base10:1.18
	netbsd-5-1-RC3:1.18
	netbsd-5-1-RC2:1.18
	uebayasi-xip-base1:1.18
	netbsd-5-1-RC1:1.18
	rmind-uvmplock:1.18.0.76
	rmind-uvmplock-base:1.18
	yamt-nfs-mp-base9:1.18
	uebayasi-xip:1.18.0.74
	uebayasi-xip-base:1.18
	netbsd-5-0-2-RELEASE:1.18
	matt-nb5-mips64-premerge-20091211:1.18
	matt-premerge-20091211:1.18
	yamt-nfs-mp-base8:1.18
	matt-nb5-mips64-u2-k2-k4-k7-k8-k9:1.18
	matt-nb4-mips64-k7-u2a-k9b:1.18
	matt-nb5-mips64-u1-k1-k5:1.18
	yamt-nfs-mp-base7:1.18
	matt-nb5-mips64:1.18.0.72
	netbsd-5-0-1-RELEASE:1.18
	jymxensuspend-base:1.18
	yamt-nfs-mp-base6:1.18
	yamt-nfs-mp-base5:1.18
	yamt-nfs-mp-base4:1.18
	jym-xensuspend-nbase:1.18
	yamt-nfs-mp-base3:1.18
	nick-hppapmap-base4:1.18
	nick-hppapmap-base3:1.18
	netbsd-5-0:1.18.0.70
	netbsd-5-0-RELEASE:1.18
	netbsd-5-0-RC4:1.18
	netbsd-5-0-RC3:1.18
	nick-hppapmap-base2:1.18
	netbsd-5-0-RC2:1.18
	jym-xensuspend:1.18.0.68
	jym-xensuspend-base:1.18
	netbsd-5-0-RC1:1.18
	haad-dm-base2:1.18
	haad-nbase2:1.18
	ad-audiomp2:1.18.0.66
	ad-audiomp2-base:1.18
	netbsd-5:1.18.0.64
	netbsd-5-base:1.18
	nick-hppapmap:1.18.0.62
	nick-hppapmap-base:1.18
	matt-mips64-base2:1.18
	matt-mips64:1.18.0.60
	haad-dm-base1:1.18
	wrstuden-revivesa-base-4:1.18
	netbsd-4-0-1-RELEASE:1.18
	wrstuden-revivesa-base-3:1.18
	wrstuden-revivesa-base-2:1.18
	wrstuden-fixsa-newbase:1.18
	nick-csl-alignment-base5:1.18
	haad-dm:1.18.0.58
	haad-dm-base:1.18
	wrstuden-revivesa-base-1:1.18
	simonb-wapbl-nbase:1.18
	yamt-pf42-base4:1.18
	simonb-wapbl:1.18.0.56
	simonb-wapbl-base:1.18
	yamt-pf42-base3:1.18
	hpcarm-cleanup-nbase:1.18
	yamt-pf42-baseX:1.18
	yamt-pf42-base2:1.18
	yamt-nfs-mp-base2:1.18
	wrstuden-revivesa:1.18.0.54
	wrstuden-revivesa-base:1.18
	yamt-nfs-mp:1.18.0.52
	yamt-nfs-mp-base:1.18
	yamt-pf42:1.18.0.50
	yamt-pf42-base:1.18
	ad-socklock-base1:1.18
	yamt-lazymbuf-base15:1.18
	yamt-lazymbuf-base14:1.18
	keiichi-mipv6-nbase:1.18
	mjf-devfs2:1.18.0.48
	mjf-devfs2-base:1.18
	nick-net80211-sync:1.18.0.46
	nick-net80211-sync-base:1.18
	keiichi-mipv6:1.18.0.44
	keiichi-mipv6-base:1.18
	bouyer-xeni386-merge1:1.18
	matt-armv6-prevmlocking:1.18
	wrstuden-fixsa-base-1:1.18
	vmlocking2-base3:1.18
	netbsd-4-0:1.18.0.42
	netbsd-4-0-RELEASE:1.18
	bouyer-xeni386-nbase:1.18
	yamt-kmem-base3:1.18
	cube-autoconf:1.18.0.40
	cube-autoconf-base:1.18
	yamt-kmem-base2:1.18
	bouyer-xeni386:1.18.0.38
	bouyer-xeni386-base:1.18
	yamt-kmem:1.18.0.36
	yamt-kmem-base:1.18
	vmlocking2-base2:1.18
	reinoud-bufcleanup-nbase:1.18
	vmlocking2:1.18.0.34
	vmlocking2-base1:1.18
	netbsd-4-0-RC5:1.18
	matt-nb4-arm:1.18.0.32
	matt-nb4-arm-base:1.18
	matt-armv6-nbase:1.18
	jmcneill-base:1.18
	netbsd-4-0-RC4:1.18
	mjf-devfs:1.18.0.30
	mjf-devfs-base:1.18
	bouyer-xenamd64-base2:1.18
	vmlocking-nbase:1.18
	yamt-x86pmap-base4:1.18
	bouyer-xenamd64:1.18.0.28
	bouyer-xenamd64-base:1.18
	netbsd-4-0-RC3:1.18
	yamt-x86pmap-base3:1.18
	yamt-x86pmap-base2:1.18
	netbsd-4-0-RC2:1.18
	yamt-x86pmap:1.18.0.26
	yamt-x86pmap-base:1.18
	netbsd-4-0-RC1:1.18
	matt-armv6:1.18.0.24
	matt-armv6-base:1.18
	matt-mips64-base:1.18
	jmcneill-pm:1.18.0.22
	jmcneill-pm-base:1.18
	hpcarm-cleanup:1.18.0.20
	hpcarm-cleanup-base:1.18
	nick-csl-alignment:1.18.0.18
	nick-csl-alignment-base:1.18
	netbsd-3-1-1-RELEASE:1.16
	netbsd-3-0-3-RELEASE:1.16
	yamt-idlelwp-base8:1.18
	wrstuden-fixsa:1.18.0.16
	wrstuden-fixsa-base:1.18
	thorpej-atomic:1.18.0.14
	thorpej-atomic-base:1.18
	reinoud-bufcleanup:1.18.0.12
	reinoud-bufcleanup-base:1.18
	mjf-ufs-trans:1.18.0.10
	mjf-ufs-trans-base:1.18
	vmlocking:1.18.0.8
	vmlocking-base:1.18
	ad-audiomp:1.18.0.6
	ad-audiomp-base:1.18
	yamt-idlelwp:1.18.0.4
	post-newlock2-merge:1.18
	newlock2-nbase:1.18
	yamt-splraiseipl-base5:1.18
	yamt-splraiseipl-base4:1.18
	yamt-splraiseipl-base3:1.18
	abandoned-netbsd-4-base:1.17
	abandoned-netbsd-4:1.17.0.18
	netbsd-3-1:1.16.0.14
	netbsd-3-1-RELEASE:1.16
	netbsd-3-0-2-RELEASE:1.16
	yamt-splraiseipl-base2:1.17
	netbsd-3-1-RC4:1.16
	yamt-splraiseipl:1.17.0.22
	yamt-splraiseipl-base:1.17
	netbsd-3-1-RC3:1.16
	yamt-pdpolicy-base9:1.17
	newlock2:1.17.0.20
	newlock2-base:1.18
	yamt-pdpolicy-base8:1.17
	netbsd-3-1-RC2:1.16
	netbsd-3-1-RC1:1.16
	yamt-pdpolicy-base7:1.17
	netbsd-4:1.18.0.2
	netbsd-4-base:1.18
	yamt-pdpolicy-base6:1.17
	chap-midi-nbase:1.17
	netbsd-3-0-1-RELEASE:1.16
	gdamore-uart:1.17.0.16
	gdamore-uart-base:1.17
	simonb-timcounters-final:1.17
	yamt-pdpolicy-base5:1.17
	chap-midi:1.17.0.14
	chap-midi-base:1.17
	yamt-pdpolicy-base4:1.17
	yamt-pdpolicy-base3:1.17
	peter-altq-base:1.17
	peter-altq:1.17.0.12
	yamt-pdpolicy-base2:1.17
	elad-kernelauth-base:1.17
	elad-kernelauth:1.17.0.10
	yamt-pdpolicy:1.17.0.8
	yamt-pdpolicy-base:1.17
	yamt-uio_vmspace-base5:1.17
	simonb-timecounters:1.17.0.6
	simonb-timecounters-base:1.17
	rpaulo-netinet-merge-pcb:1.17.0.4
	rpaulo-netinet-merge-pcb-base:1.17
	yamt-uio_vmspace:1.17.0.2
	netbsd-3-0:1.16.0.12
	netbsd-3-0-RELEASE:1.16
	netbsd-3-0-RC6:1.16
	yamt-readahead-base3:1.16
	netbsd-3-0-RC5:1.16
	netbsd-3-0-RC4:1.16
	netbsd-3-0-RC3:1.16
	yamt-readahead-base2:1.16
	netbsd-3-0-RC2:1.16
	yamt-readahead-pervnode:1.16
	yamt-readahead-perfile:1.16
	yamt-readahead:1.16.0.10
	yamt-readahead-base:1.16
	netbsd-3-0-RC1:1.16
	yamt-vop-base3:1.16
	netbsd-2-0-3-RELEASE:1.15
	netbsd-2-1:1.15.0.16
	yamt-vop-base2:1.16
	thorpej-vnode-attr:1.16.0.8
	thorpej-vnode-attr-base:1.16
	netbsd-2-1-RELEASE:1.15
	yamt-vop:1.16.0.6
	yamt-vop-base:1.16
	netbsd-2-1-RC6:1.15
	netbsd-2-1-RC5:1.15
	netbsd-2-1-RC4:1.15
	netbsd-2-1-RC3:1.15
	netbsd-2-1-RC2:1.15
	netbsd-2-1-RC1:1.15
	yamt-lazymbuf:1.16.0.4
	yamt-km-base4:1.16
	netbsd-2-0-2-RELEASE:1.15
	yamt-km-base3:1.16
	netbsd-3:1.16.0.2
	netbsd-3-base:1.16
	yamt-km-base2:1.15
	yamt-km:1.15.0.12
	yamt-km-base:1.15
	kent-audio2:1.15.0.10
	kent-audio2-base:1.16
	netbsd-2-0-1-RELEASE:1.15
	kent-audio1-beforemerge:1.15
	netbsd-2:1.15.0.8
	netbsd-2-base:1.15
	kent-audio1:1.15.0.6
	kent-audio1-base:1.15
	netbsd-2-0-RELEASE:1.15
	netbsd-2-0-RC5:1.15
	netbsd-2-0-RC4:1.15
	netbsd-2-0-RC3:1.15
	netbsd-2-0-RC2:1.15
	netbsd-2-0-RC1:1.15
	netbsd-2-0:1.15.0.4
	netbsd-2-0-base:1.15
	netbsd-1-6-PATCH002-RELEASE:1.13
	netbsd-1-6-PATCH002:1.13
	netbsd-1-6-PATCH002-RC4:1.13
	netbsd-1-6-PATCH002-RC3:1.13
	netbsd-1-6-PATCH002-RC2:1.13
	netbsd-1-6-PATCH002-RC1:1.13
	ktrace-lwp:1.15.0.2
	ktrace-lwp-base:1.16
	netbsd-1-6-PATCH001:1.13
	netbsd-1-6-PATCH001-RELEASE:1.13
	netbsd-1-6-PATCH001-RC3:1.13
	netbsd-1-6-PATCH001-RC2:1.13
	netbsd-1-6-PATCH001-RC1:1.13
	nathanw_sa_end:1.11.6.2
	nathanw_sa_before_merge:1.13
	fvdl_fs64_base:1.13
	gmcgarry_ctxsw:1.13.0.18
	gmcgarry_ctxsw_base:1.13
	gmcgarry_ucred:1.13.0.16
	gmcgarry_ucred_base:1.13
	nathanw_sa_base:1.13
	kqueue-aftermerge:1.13
	kqueue-beforemerge:1.13
	netbsd-1-6-RELEASE:1.13
	netbsd-1-6-RC3:1.13
	netbsd-1-6-RC2:1.13
	netbsd-1-6-RC1:1.13
	netbsd-1-6:1.13.0.14
	netbsd-1-6-base:1.13
	gehenna-devsw:1.13.0.12
	gehenna-devsw-base:1.13
	netbsd-1-5-PATCH003:1.11
	eeh-devprop:1.13.0.10
	eeh-devprop-base:1.13
	newlock:1.13.0.8
	newlock-base:1.13
	ifpoll-base:1.13
	thorpej-mips-cache:1.13.0.4
	thorpej-mips-cache-base:1.13
	thorpej-devvp-base3:1.13
	thorpej-devvp-base2:1.13
	post-chs-ubcperf:1.13
	pre-chs-ubcperf:1.13
	thorpej-devvp:1.13.0.2
	thorpej-devvp-base:1.13
	netbsd-1-5-PATCH002:1.11
	kqueue:1.12.0.2
	kqueue-base:1.13
	netbsd-1-5-PATCH001:1.11
	thorpej_scsipi_beforemerge:1.11
	nathanw_sa:1.11.0.6
	thorpej_scsipi_nbase:1.11
	netbsd-1-5-RELEASE:1.11
	netbsd-1-5-BETA2:1.11
	netbsd-1-5-BETA:1.11
	netbsd-1-4-PATCH003:1.10
	netbsd-1-5-ALPHA2:1.11
	netbsd-1-5:1.11.0.4
	netbsd-1-5-base:1.11
	minoura-xpg4dl-base:1.11
	minoura-xpg4dl:1.11.0.2
	netbsd-1-4-PATCH002:1.10
	chs-ubc2-newbase:1.10
	wrstuden-devbsize-19991221:1.10
	wrstuden-devbsize:1.10.0.20
	wrstuden-devbsize-base:1.10
	kame_141_19991130:1.10
	comdex-fall-1999:1.10.0.18
	comdex-fall-1999-base:1.10
	fvdl-softdep:1.10.0.16
	fvdl-softdep-base:1.10
	thorpej_scsipi:1.10.0.14
	thorpej_scsipi_base:1.11
	netbsd-1-4-PATCH001:1.10
	kame_14_19990705:1.10
	kame_14_19990628:1.10
	kame:1.10.0.12
	chs-ubc2:1.10.0.10
	chs-ubc2-base:1.10
	netbsd-1-4-RELEASE:1.10
	netbsd-1-4:1.10.0.8
	netbsd-1-4-base:1.10
	netbsd-1-3-PATCH003:1.6
	netbsd-1-3-PATCH003-CANDIDATE2:1.6
	kenh-if-detach:1.10.0.6
	kenh-if-detach-base:1.10
	netbsd-1-3-PATCH003-CANDIDATE1:1.6
	netbsd-1-3-PATCH003-CANDIDATE0:1.6
	chs-ubc:1.10.0.4
	chs-ubc-base:1.10
	eeh-paddr_t:1.10.0.2
	eeh-paddr_t-base:1.10
	netbsd-1-3-PATCH002:1.6
	netbsd-1-3-PATCH001:1.6
	netbsd-1-3-RELEASE:1.6
	netbsd-1-3-BETA:1.6
	netbsd-1-3:1.6.0.14
	netbsd-1-3-base:1.6
	thorpej-signal:1.6.0.12
	thorpej-signal-base:1.6
	marc-pcmcia:1.6.0.10
	marc-pcmcia-bp:1.6
	marc-pcmcia-base:1.6
	bouyer-scsipi:1.6.0.8
	is-newarp-before-merge:1.6
	is-newarp:1.6.0.6
	is-newarp-base:1.6
	netbsd-1-2-PATCH001:1.3.4.2
	mrg-vm-swap:1.6.0.4
	thorpej-setroot:1.6.0.2
	netbsd-1-2-RELEASE:1.3.4.1
	netbsd-1-2-BETA:1.3
	netbsd-1-2:1.3.0.4
	netbsd-1-2-base:1.3
	date-03-may-96:1.1.1.1
	netbsd:1.1.1
	netbsd-1-1-PATCH001:1.1
	netbsd-1-1-RELEASE:1.1
	netbsd-1-1:1.1.0.2
	netbsd-1-1-base:1.1;
locks; strict;
comment	@# @;


1.25
date	2026.06.11.19.46.19;	author andvar;	state Exp;
branches;
next	1.24;
commitid	w73Cem6smi2hVpJG;

1.24
date	2024.02.10.08.36.04;	author andvar;	state Exp;
branches;
next	1.23;
commitid	7fk0iLBOy4ZeBRXE;

1.23
date	2022.06.27.22.41.29;	author andvar;	state Exp;
branches;
next	1.22;
commitid	NZUyP4RB0ph0lJJD;

1.22
date	2022.01.25.22.01.34;	author andvar;	state Exp;
branches;
next	1.21;
commitid	uadEc5exKQRF14qD;

1.21
date	2021.09.03.22.33.17;	author andvar;	state Exp;
branches;
next	1.20;
commitid	gXr0XdzPsZd0Py7D;

1.20
date	2019.06.04.10.15.22;	author msaitoh;	state Exp;
branches;
next	1.19;
commitid	Ei516LpSmvd6bRpB;

1.19
date	2014.10.18.08.33.28;	author snj;	state Exp;
branches
	1.19.20.1;
next	1.18;

1.18
date	2006.11.25.16.48.32;	author christos;	state Exp;
branches
	1.18.98.1;
next	1.17;

1.17
date	2005.12.11.12.22.18;	author christos;	state Exp;
branches
	1.17.20.1
	1.17.22.1;
next	1.16;

1.16
date	2005.02.27.00.27.23;	author perry;	state Exp;
branches
	1.16.4.1;
next	1.15;

1.15
date	2003.05.03.18.11.31;	author wiz;	state Exp;
branches
	1.15.2.1
	1.15.10.1
	1.15.12.1;
next	1.14;

1.14
date	2003.04.19.19.26.10;	author fvdl;	state Exp;
branches;
next	1.13;

1.13
date	2001.08.20.12.00.52;	author wiz;	state Exp;
branches;
next	1.12;

1.12
date	2001.06.19.13.45.57;	author wiz;	state Exp;
branches
	1.12.2.1;
next	1.11;

1.11
date	2000.03.15.02.09.11;	author fvdl;	state Exp;
branches
	1.11.6.1;
next	1.10;

1.10
date	98.07.04.22.29.46;	author mjacob;	state Exp;
branches
	1.10.14.1;
next	1.9;

1.9
date	98.07.03.23.45.10;	author mjacob;	state Exp;
branches;
next	1.8;

1.8
date	98.04.16.07.07.37;	author leo;	state Exp;
branches;
next	1.7;

1.7
date	98.03.11.20.57.03;	author leo;	state Exp;
branches;
next	1.6;

1.6
date	96.10.08.03.04.06;	author gibbs;	state Exp;
branches;
next	1.5;

1.5
date	96.08.10.08.36.58;	author mycroft;	state Exp;
branches;
next	1.4;

1.4
date	96.07.10.22.51.23;	author explorer;	state Exp;
branches;
next	1.3;

1.3
date	96.05.20.00.48.45;	author thorpej;	state Exp;
branches
	1.3.4.1;
next	1.2;

1.2
date	96.05.16.03.51.45;	author mycroft;	state Exp;
branches;
next	1.1;

1.1
date	95.10.09.09.49.37;	author mycroft;	state Exp;
branches;
next	;

1.19.20.1
date	2019.06.10.22.07.13;	author christos;	state Exp;
branches;
next	;
commitid	jtc8rnCzWiEEHGqB;

1.18.98.1
date	2017.12.03.11.37.05;	author jdolecek;	state Exp;
branches;
next	;
commitid	XcIYRZTAh1LmerhA;

1.17.20.1
date	2007.01.12.00.57.38;	author ad;	state Exp;
branches;
next	;

1.17.22.1
date	2006.12.10.07.17.36;	author yamt;	state Exp;
branches;
next	;

1.16.4.1
date	2006.12.30.20.48.35;	author yamt;	state Exp;
branches;
next	;

1.15.2.1
date	2005.03.04.16.43.50;	author skrll;	state Exp;
branches;
next	;

1.15.10.1
date	2005.04.29.11.28.56;	author kent;	state Exp;
branches;
next	;

1.15.12.1
date	2005.03.19.08.34.42;	author yamt;	state Exp;
branches;
next	;

1.12.2.1
date	2001.08.25.06.16.19;	author thorpej;	state Exp;
branches;
next	;

1.11.6.1
date	2001.06.21.20.04.10;	author nathanw;	state Exp;
branches;
next	1.11.6.2;

1.11.6.2
date	2001.08.24.00.09.54;	author nathanw;	state Exp;
branches;
next	;

1.10.14.1
date	2000.11.20.11.41.33;	author bouyer;	state Exp;
branches;
next	;

1.3.4.1
date	96.07.18.00.38.01;	author jtc;	state Exp;
branches;
next	1.3.4.2;

1.3.4.2
date	97.03.04.14.55.23;	author mycroft;	state Exp;
branches;
next	;


desc
@@


1.25
log
@fix various typos in comments and documentation.
@
text
@/*
 * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
 *
 * Copyright (c) 1994-2001 Justin T. Gibbs.
 * Copyright (c) 2000-2001 Adaptec Inc.
 * 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. Redistributions in binary form must reproduce at minimum a disclaimer
 *    substantially similar to the "NO WARRANTY" disclaimer below
 *    ("Disclaimer") and any redistribution must be conditioned upon
 *    including a substantially similar Disclaimer requirement for further
 *    binary redistribution.
 * 3. Neither the names of the above-listed copyright holders nor the names
 *    of any contributors may 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
 * GNU General Public License ("GPL") version 2 as published by the Free
 * Software Foundation.
 *
 * NO WARRANTY
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
 *
 * $FreeBSD: /repoman/r/ncvs/src/sys/dev/aic7xxx/aic7xxx.seq,v 1.123 2003/01/20 20:44:55 gibbs Exp $
 */

VERSION = "$NetBSD: aic7xxx.seq,v 1.24 2024/02/10 08:36:04 andvar Exp $"
PATCH_ARG_LIST = "struct ahc_softc *ahc"
PREFIX = "ahc_"

#include <dev/microcode/aic7xxx/aic7xxx.reg>
#include <dev/scsipi/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 pseudo-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.
 */

bus_free_sel:
	/*
	 * Turn off the selection hardware.  We need to reset the
	 * selection request in order to perform a new selection.
	 */
	and	SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP;
	and	SIMODE1, ~ENBUSFREE;
poll_for_work:
	call	clear_target_state;
	and	SXFRCTL0, ~SPIOEN;
	if ((ahc->features & AHC_ULTRA2) != 0) {
		clr	SCSIBUSL;
	}
	test	SCSISEQ, ENSELO	jnz poll_for_selection;
	if ((ahc->features & AHC_TWIN) != 0) {
		xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
		test	SCSISEQ, ENSELO		jnz poll_for_selection;
	}
	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
poll_for_work_loop:
	if ((ahc->features & AHC_TWIN) != 0) {
		xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
	}
	test	SSTAT0, SELDO|SELDI	jnz selection;
test_queue:
	/* Has the driver posted any work for us? */
BEGIN_CRITICAL;
	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
		test	QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
	} else {
		mov	A, QINPOS;
		cmp	KERNEL_QINPOS, A je poll_for_work_loop;
	}
	mov	ARG_1, NEXT_QUEUED_SCB;

	/*
	 * We have at least one queued SCB now and we don't have any
	 * SCBs in the list of SCBs awaiting selection.  Allocate a
	 * card SCB for the host's SCB and get to work on it.
	 */
	if ((ahc->flags & AHC_PAGESCBS) != 0) {
		mov	ALLZEROS	call	get_free_or_disc_scb;
	} else {
		/* In the non-paging case, the SCBID == hardware SCB index */
		mov	SCBPTR, ARG_1;
	}
	or	SEQ_FLAGS2, SCB_DMA;
END_CRITICAL;
dma_queued_scb:
	/*
	 * DMA the SCB from host ram into the current SCB location.
	 */
	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
	mov	ARG_1	call dma_scb;
	/*
	 * Check one last time to see if this SCB was canceled
	 * before we completed the DMA operation.  If it was,
	 * the QINFIFO next pointer will not match our saved
	 * value.
	 */
	mov	A, ARG_1;
BEGIN_CRITICAL;
	cmp	NEXT_QUEUED_SCB, A jne abort_qinscb;
	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
		cmp	SCB_TAG, A je . + 2;
		mvi	SCB_MISMATCH call set_seqint;
	}
	mov	NEXT_QUEUED_SCB, SCB_NEXT;
	mov	SCB_NEXT,WAITING_SCBH;
	mov	WAITING_SCBH, SCBPTR;
	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
		mov	NONE, SNSCB_QOFF;
	} else {
		inc	QINPOS;
	}
	and	SEQ_FLAGS2, ~SCB_DMA;
END_CRITICAL;
start_waiting:
	/*
	 * Start the first entry on the waiting SCB list.
	 */
	mov	SCBPTR, WAITING_SCBH;
	call	start_selection;

poll_for_selection:
	/*
	 * Twin channel devices cannot handle things like SELTO
	 * interrupts on the "background" channel.  So, while
	 * selecting, keep polling the current channel until
	 * either a selection or reselection occurs.
	 */
	test	SSTAT0, SELDO|SELDI	jz poll_for_selection;

selection:
	/*
	 * We aren't expecting a bus free, so interrupt
	 * the kernel driver if it happens.
	 */
	mvi	CLRSINT1,CLRBUSFREE;
	if ((ahc->features & AHC_DT) == 0) {
		or	SIMODE1, ENBUSFREE;
	}

	/*
	 * Guard against a bus free after (re)selection
	 * but prior to enabling the busfree interrupt.  SELDI
	 * and SELDO will be cleared in that case.
	 */
	test	SSTAT0, SELDI|SELDO	jz bus_free_sel;
	test	SSTAT0,SELDO	jnz select_out;
select_in:
	if ((ahc->flags & AHC_TARGETROLE) != 0) {
		if ((ahc->flags & AHC_INITIATORROLE) != 0) {
			test	SSTAT0, TARGET	jz initiator_reselect;
		}
		mvi	CLRSINT0, CLRSELDI;

		/*
		 * We've just been selected.  Assert BSY and
		 * setup the phase for receiving messages
		 * from the target.
		 */
		mvi	SCSISIGO, P_MESGOUT|BSYO;

		/*
		 * Setup the DMA for sending the identify and
		 * command information.
		 */
		mvi	SEQ_FLAGS, CMDPHASE_PENDING;

		mov     A, TQINPOS;
		if ((ahc->features & AHC_CMD_CHAN) != 0) {
			mvi	DINDEX, CCHADDR;
			mvi	SHARED_DATA_ADDR call set_32byte_addr;
			mvi	CCSCBCTL, CCSCBRESET;
		} else {
			mvi	DINDEX, HADDR;
			mvi	SHARED_DATA_ADDR call set_32byte_addr;
			mvi	DFCNTRL, FIFORESET;
		}

		/* Initiator that selected us */
		and	SAVED_SCSIID, SELID_MASK, SELID;
		/* The Target ID we were selected at */
		if ((ahc->features & AHC_MULTI_TID) != 0) {
			and	A, OID, TARGIDIN;
		} else if ((ahc->features & AHC_ULTRA2) != 0) {
			and	A, OID, SCSIID_ULTRA2;
		} else {
			and	A, OID, SCSIID;
		}
		or	SAVED_SCSIID, A;
		if ((ahc->features & AHC_TWIN) != 0) {
			test 	SBLKCTL, SELBUSB jz . + 2;
			or	SAVED_SCSIID, TWIN_CHNLB;
		}
		if ((ahc->features & AHC_CMD_CHAN) != 0) {
			mov	CCSCBRAM, SAVED_SCSIID;
		} else {
			mov	DFDAT, SAVED_SCSIID;
		}

		/*
		 * If ATN isn't asserted, the target isn't interested
		 * in talking to us.  Go directly to bus free.
		 * XXX SCSI-1 may require us to assume lun 0 if
		 * ATN is false.
		 */
		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.
		 */
		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;
		}
		and	SAVED_LUN, MSG_IDENTIFY_LUNMASK, 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_msg_pending;
		add	A, -MSG_IGN_WIDE_RESIDUE, DINDEX;
		jc	ident_messages_done_msg_pending;

		/* 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;
		or	SEQ_FLAGS, TARGET_CMD_IS_TAGGED;

ident_messages_done:
		/* 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;
		test	SEQ_FLAGS2, TARGET_MSG_PENDING
			jnz target_mesgout_pending;
		test	SCSISIGI, ATNI jnz target_mesgout_continue;
		jmp	target_ITloop;


ident_messages_done_msg_pending:
		or	SEQ_FLAGS2, TARGET_MSG_PENDING;
		jmp	ident_messages_done;

		/*
		 * Pushed message loop to allow the kernel to
		 * run its own target mode message state engine.
		 */
host_target_message_loop:
		mvi	HOST_MSG_LOOP call set_seqint;
		cmp	RETURN_1, EXIT_MSG_LOOP	je target_ITloop;
		test	SSTAT0, SPIORDY jz .;
		jmp	host_target_message_loop;
	}

if ((ahc->flags & AHC_INITIATORROLE) != 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 */
	or	SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
	and	SAVED_SCSIID, SELID_MASK, SELID;
	if ((ahc->features & AHC_ULTRA2) != 0) {
		and	A, OID, SCSIID_ULTRA2;
	} else {
		and	A, OID, SCSIID;
	}
	or	SAVED_SCSIID, A;
	if ((ahc->features & AHC_TWIN) != 0) {
		test	SBLKCTL, SELBUSB	jz . + 2;
		or	SAVED_SCSIID, TWIN_CHNLB;
	}
	mvi	CLRSINT0, CLRSELDI;
	jmp	ITloop;
}

abort_qinscb:
	call	add_scb_to_free_list;
	jmp	poll_for_work_loop;

start_selection:
	/*
	 * If bus reset interrupts have been disabled (from a previous
	 * reset), re-enable them now.  Resets are only of interest
	 * when we have outstanding transactions, so we can safely
	 * defer re-enabling the interrupt until, as an initiator,
	 * we start sending out transactions again.
	 */
	test	SIMODE1, ENSCSIRST	jnz . + 3;
	mvi	CLRSINT1, CLRSCSIRSTI;
	or	SIMODE1, ENSCSIRST;
	if ((ahc->features & AHC_TWIN) != 0) {
		and	SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
		test	SCB_SCSIID, TWIN_CHNLB jz . + 2;
		or	SINDEX, SELBUSB;
		mov	SBLKCTL,SINDEX;		/* select channel */
	}
initialize_scsiid:
	if ((ahc->features & AHC_ULTRA2) != 0) {
		mov	SCSIID_ULTRA2, SCB_SCSIID;
	} else if ((ahc->features & AHC_TWIN) != 0) {
		and	SCSIID, TWIN_TID|OID, SCB_SCSIID;
	} else {
		mov	SCSIID, SCB_SCSIID;
	}
	if ((ahc->flags & AHC_TARGETROLE) != 0) {
		mov	SINDEX, SCSISEQ_TEMPLATE;
		test	SCB_CONTROL, TARGET_SCB jz . + 2;
		or	SINDEX, TEMODE;
		mov	SCSISEQ, SINDEX ret;
	} else {
		mov	SCSISEQ, SCSISEQ_TEMPLATE ret;
	}

/*
 * Initialize transfer settings with SCB provided settings.
 */
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) {
		bmov	SCSIRATE, SCB_SCSIRATE, 2 ret;
	} else {
		mov	SCSIRATE, SCB_SCSIRATE ret;
	}

if ((ahc->flags & AHC_TARGETROLE) != 0) {
/*
 * 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;
}

/*
 * 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 */
	and	SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ;
	mov	SCBPTR, WAITING_SCBH;
	mov	WAITING_SCBH,SCB_NEXT;
	mov	SAVED_SCSIID, SCB_SCSIID;
	mov	SAVED_LUN, SCB_LUN;
	call	set_transfer_settings;
	if ((ahc->flags & AHC_TARGETROLE) != 0) {
		test	SSTAT0, TARGET	jz initiator_select;

		or	SXFRCTL0, CLRSTCNT|CLRCHN;

		/*
		 * Put tag in canonical location since not
		 * all connections have an SCB.
		 */
		mov	INITIATOR_TAG, SCB_TARGET_ITAG;

		/*
		 * 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	CLRSINT0, CLRSELDO;

		/*
		 * Start out with a simple identify message.
		 */
		or	SCB_LUN, 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_TARGET_ITAG call target_outb;
target_synccmd:
		/*
		 * Now determine what phases the host wants us
		 * to go through.
		 */
		mov	SEQ_FLAGS, SCB_TARGET_PHASES;

		test	SCB_CONTROL, MK_MESSAGE	jz target_ITloop;
		mvi	P_MESGIN|BSYO call change_phase;
		jmp	host_target_message_loop;
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;
		mvi	TARG_IMMEDIATE_SCB, SCB_LIST_NULL;
		call	complete_target_cmd;
		if ((ahc->flags & AHC_PAGESCBS) != 0) {
			mov	ALLZEROS	call	get_free_or_disc_scb;
		}
		cmp	TARG_IMMEDIATE_SCB, SCB_LIST_NULL je .;
		mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
		mov	TARG_IMMEDIATE_SCB call dma_scb;
		call	set_transfer_settings;
		or	SXFRCTL0, CLRSTCNT|CLRCHN;
		jmp	target_synccmd;

target_mesgout:
		mvi	SCSISIGO, P_MESGOUT|BSYO;
target_mesgout_continue:
		call	target_inb;
target_mesgout_pending:
		and	SEQ_FLAGS2, ~TARGET_MSG_PENDING;
		/* Local Processing goes here... */
		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 preceding I/O session to complete. */
		test	SCSISIGI, ACKI jnz .;
target_busfree:
		and	SIMODE1, ~ENBUSFREE;
		if ((ahc->features & AHC_ULTRA2) != 0) {
			clr	SCSIBUSL;
		}
		clr	SCSISIGO;
		mvi	LASTPHASE, P_BUSFREE;
		call	complete_target_cmd;
		jmp	poll_for_work;

target_cmdphase:
		/*
		 * The target has dropped ATN (doesn't want to abort or BDR)
		 * and we believe this selection to be valid.  If the ring
		 * buffer for new commands 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;
		test	SEQ_FLAGS, TARGET_CMD_IS_TAGGED jz . + 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:
		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, CMDSIZE_TABLE, A;
		mov	A, SINDIR;

		test	A, 0xFF jz command_phase_done;
		or	SXFRCTL0, SPIOEN;
command_loop:
		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 phases on the bus are from the
		 * perspective of the initiator.  The DMA
		 * code looks at LASTPHASE to determine the
		 * data direction of the DMA.  Toggle it for
		 * target transfers.
		 */
		xor	LASTPHASE, IOI, SCB_TARGET_DATA_DIR;
		or	SCB_TARGET_DATA_DIR, BSYO call change_phase;
		jmp	p_data;

target_sphase:
		mvi	P_STATUS|BSYO call change_phase;
		mvi	LASTPHASE, P_STATUS;
		mov	SCB_SCSI_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	28	call set_hcnt;
			or	DFCNTRL, HDMAEN|FIFOFLUSH;
			call	dma_finish;
		}
		inc	TQINPOS;
		mvi	INTSTAT,CMDCMPLT ret;
	}

if ((ahc->flags & AHC_INITIATORROLE) != 0) {
initiator_select:
	or	SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
	/*
	 * As soon as we get a successful selection, the target
	 * should go into the message out phase since we have ATN
	 * asserted.
	 */
	mvi	MSG_OUT, MSG_IDENTIFYFLAG;
	mvi	SEQ_FLAGS, NO_CDB_SENT;
	mvi	CLRSINT0, CLRSELDO;

	/*
	 * 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.
	 */
mesgin_phasemis:
ITloop:
	call	phase_lock;

	mov	A, LASTPHASE;

	test	A, ~P_DATAIN	jz p_data;
	cmp	A,P_COMMAND	je p_command;
	cmp	A,P_MESGOUT	je p_mesgout;
	cmp	A,P_STATUS	je p_status;
	cmp	A,P_MESGIN	je p_mesgin;

	mvi	BAD_PHASE call set_seqint;
	jmp	ITloop;			/* Try reading the bus again. */

await_busfree:
	and	SIMODE1, ~ENBUSFREE;
	mov	NONE, SCSIDATL;		/* Ack the last byte */
	if ((ahc->features & AHC_ULTRA2) != 0) {
		clr	SCSIBUSL;	/* Prevent bit leakage durint SELTO */
	}
	and	SXFRCTL0, ~SPIOEN;
	test	SSTAT1,REQINIT|BUSFREE	jz .;
	test	SSTAT1, BUSFREE jnz poll_for_work;
	mvi	MISSED_BUSFREE call set_seqint;
}

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.
	 */
	clr	DFCNTRL;
	or	SXFRCTL0, CLRSTCNT|CLRCHN;

	/*
	 * 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) {
		bmov	SCSIRATE, ALLZEROS, 2;
	} else {
		clr	SCSIRATE;
		if ((ahc->features & AHC_ULTRA) != 0) {
			and	SXFRCTL0, ~(FAST20);
		}
	}
	mvi	LASTPHASE, P_BUSFREE;
	/* clear target specific flags */
	mvi	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT ret;

sg_advance:
	clr	A;			/* add sizeof(struct scatter) */
	add	SCB_RESIDUAL_SGPTR[0],SG_SIZEOF;
	adc	SCB_RESIDUAL_SGPTR[1],A;
	adc	SCB_RESIDUAL_SGPTR[2],A;
	adc	SCB_RESIDUAL_SGPTR[3],A ret;

if ((ahc->features & AHC_CMD_CHAN) != 0) {
disable_ccsgen:
	test	CCSGCTL, CCSGEN jz return;
	test	CCSGCTL, CCSGDONE jz .;
disable_ccsgen_fetch_done:
	clr	CCSGCTL;
	test	CCSGCTL, CCSGEN jnz .;
	ret;
idle_loop:
	/*
	 * Do we need any more segments for this transfer?
	 */
	test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jnz return;

	/* Did we just finish fetching segs? */
	cmp	CCSGCTL, CCSGEN|CCSGDONE je idle_sgfetch_complete;

	/* Are we actively fetching segments? */
	test	CCSGCTL, CCSGEN jnz return;

	/*
	 * Do we have any prefetch left???
	 */
	cmp	CCSGADDR, SG_PREFETCH_CNT jne idle_sg_avail;

	/*
	 * Need to fetch segments, but we can only do that
	 * if the command channel is completely idle.  Make
	 * sure we don't have an SCB prefetch going on.
	 */
	test	CCSCBCTL, CCSCBEN jnz return;

	/*
	 * We fetch a "cacheline aligned" and sized amount of data
	 * so we don't end up referencing a non-existent page.
	 * Cacheline aligned is in quotes because the kernel will
	 * set the prefetch amount to a reasonable level if the
	 * cacheline size is unknown.
	 */
	mvi	CCHCNT, SG_PREFETCH_CNT;
	and	CCHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR;
	bmov	CCHADDR[1], SCB_RESIDUAL_SGPTR[1], 3;
	mvi	CCSGCTL, CCSGEN|CCSGRESET ret;
idle_sgfetch_complete:
	call	disable_ccsgen_fetch_done;
	and	CCSGADDR, SG_PREFETCH_ADDR_MASK, SCB_RESIDUAL_SGPTR;
idle_sg_avail:
	if ((ahc->features & AHC_ULTRA2) != 0) {
		/* Does the hardware have space for another SG entry? */
		test	DFSTATUS, PRELOAD_AVAIL jz return;
		bmov 	HADDR, CCSGRAM, 7;
		test	HCNT[0], 0x1 jz . + 2;
		xor	DATA_COUNT_ODD, 0x1;
		bmov	SCB_RESIDUAL_DATACNT[3], CCSGRAM, 1;
		if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
			mov	SCB_RESIDUAL_DATACNT[3] call set_hhaddr;
		}
		call	sg_advance;
		mov	SINDEX, SCB_RESIDUAL_SGPTR[0];
		test	DATA_COUNT_ODD, 0x1 jz . + 2;
		or	SINDEX, ODD_SEG;
		test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 2;
		or	SINDEX, LAST_SEG;
		mov	SG_CACHE_PRE, SINDEX;
		/* Load the segment */
		or	DFCNTRL, PRELOADEN;
	}
	ret;
}

if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) {
/*
 * Calculate the trailing portion of this S/G segment that cannot
 * be transferred using memory write and invalidate PCI transactions.
 * XXX Can we optimize this for PCI writes only???
 */
calc_mwi_residual:
	/*
	 * If the ending address is on a cacheline boundary,
	 * there is no need for an extra segment.
	 */
	mov	A, HCNT[0];
	add	A, A, HADDR[0];
	and	A, CACHESIZE_MASK;
	test	A, 0xFF jz return;

	/*
	 * If the transfer is less than a cachline,
	 * there is no need for an extra segment.
	 */
	test	HCNT[1], 0xFF	jnz calc_mwi_residual_final;
	test	HCNT[2], 0xFF	jnz calc_mwi_residual_final;
	add	NONE, INVERTED_CACHESIZE_MASK, HCNT[0];
	jnc	return;

calc_mwi_residual_final:
	mov	MWI_RESIDUAL, A;
	not	A;
	inc	A;
	add	HCNT[0], A;
	adc	HCNT[1], -1;
	adc	HCNT[2], -1 ret;
}

p_data:
	test	SEQ_FLAGS,NOT_IDENTIFIED|NO_CDB_SENT jz p_data_allowed;
	mvi	PROTO_VIOLATION call set_seqint;
p_data_allowed:
	if ((ahc->features & AHC_ULTRA2) != 0) {
		mvi	DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
	} else {
		mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
	}
	test	LASTPHASE, IOI jnz . + 2;
	or	DMAPARAMS, DIRECTION;
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		/* We don't have any valid S/G elements */
		mvi	CCSGADDR, SG_PREFETCH_CNT;
	}
	test	SEQ_FLAGS, DPHASE	jz data_phase_initialize;

	/*
	 * If we re-enter the data phase after going through another
	 * phase, our transfer location has almost certainly been
	 * corrupted by the interveining, non-data, transfers.  Ask
	 * the host driver to fix us up based on the transfer residual.
	 */
	mvi	PDATA_REINIT	call set_seqint;
	jmp	data_phase_loop;

data_phase_initialize:
	/* We have seen a data phase for the first time */
	or	SEQ_FLAGS, DPHASE;

	/*
	 * Initialize the DMA address and counter from the SCB.
	 * Also set SCB_RESIDUAL_SGPTR, including the LAST_SEG
	 * flag in the highest byte of the data count.  We cannot
	 * modify the saved values in the SCB until we see a save
	 * data pointers message.
	 */
	if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
		/* The lowest address byte must be loaded last. */
		mov	SCB_DATACNT[3] call set_hhaddr;
	}
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		bmov	HADDR, SCB_DATAPTR, 7;
		bmov	SCB_RESIDUAL_DATACNT[3], SCB_DATACNT[3], 5;
	} else {
		mvi	DINDEX, HADDR;
		mvi	SCB_DATAPTR	call bcopy_7;
		mvi	DINDEX, SCB_RESIDUAL_DATACNT + 3;
		mvi	SCB_DATACNT + 3 call bcopy_5;
	}
	if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) {
		call	calc_mwi_residual;
	}
	and	SCB_RESIDUAL_SGPTR[0], ~SG_FULL_RESID;
	and	DATA_COUNT_ODD, 0x1, HCNT[0];

	if ((ahc->features & AHC_ULTRA2) == 0) {
		if ((ahc->features & AHC_CMD_CHAN) != 0) {
			bmov	STCNT, HCNT, 3;
		} else {
			call	set_stcnt_from_hcnt;
		}
	}

data_phase_loop:
	/* Guard against overruns */
	test	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz data_phase_inbounds;

	/*
	 * Turn on `Bit Bucket' mode, wait until the target takes
	 * us to another phase, and then notify the host.
	 */
	and	DMAPARAMS, DIRECTION;
	mov	DFCNTRL, DMAPARAMS;
	or	SXFRCTL1,BITBUCKET;
	if ((ahc->features & AHC_DT) == 0) {
		test	SSTAT1,PHASEMIS	jz .;
	} else {
		test	SCSIPHASE, DATA_PHASE_MASK jnz .;
	}
	and	SXFRCTL1, ~BITBUCKET;
	mvi	DATA_OVERRUN call set_seqint;
	jmp	ITloop;

data_phase_inbounds:
	if ((ahc->features & AHC_ULTRA2) != 0) {
		mov	SINDEX, SCB_RESIDUAL_SGPTR[0];
		test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 2;
		or	SINDEX, LAST_SEG;
		test	DATA_COUNT_ODD, 0x1 jz . + 2;
		or	SINDEX, ODD_SEG;
		mov	SG_CACHE_PRE, SINDEX;
		mov	DFCNTRL, DMAPARAMS;
ultra2_dma_loop:
		call	idle_loop;
		/*
		 * The transfer is complete if either the last segment
		 * completes or the target changes phase.
		 */
		test	SG_CACHE_SHADOW, LAST_SEG_DONE jnz ultra2_dmafinish;
		if ((ahc->features & AHC_DT) == 0) {
			if ((ahc->flags & AHC_TARGETROLE) != 0) {
				 /*
				  * As a target, we control the phases,
				  * so ignore PHASEMIS.
				  */
				test	SSTAT0, TARGET jnz ultra2_dma_loop;
			}
			if ((ahc->flags & AHC_INITIATORROLE) != 0) {
				test	SSTAT1,PHASEMIS	jz ultra2_dma_loop;
			}
		} else {
			test	DFCNTRL, SCSIEN jnz ultra2_dma_loop;
		}

ultra2_dmafinish:
		/*
		 * The transfer has terminated either due to a phase
		 * change, and/or the completion of the last segment.
		 * We have two goals here.  Do as much other work
		 * as possible while the data fifo drains on a read
		 * and respond as quickly as possible to the standard
		 * messages (save data pointers/disconnect and command
		 * complete) that usually follow a data phase.
		 */
		if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) {
			/*
			 * On chips with broken auto-flush, start
			 * the flushing process now.  We'll poke
			 * the chip from time to time to keep the
			 * flush process going as we complete the
			 * data phase.
			 */
			or	DFCNTRL, FIFOFLUSH;
		}
		/*
		 * We assume that, even though data may still be
		 * transferring to the host, that the SCSI side of
		 * the DMA engine is now in a static state.  This
		 * allows us to update our notion of where we are
		 * in this transfer.
		 *
		 * If, by chance, we stopped before being able
		 * to fetch additional segments for this transfer,
		 * yet the last S/G was completely exhausted,
		 * call our idle loop until it is able to load
		 * another segment.  This will allow us to immediately
		 * pickup on the next segment on the next data phase.
		 *
		 * If we happened to stop on the last segment, then
		 * our residual information is still correct from
		 * the idle loop and there is no need to perform
		 * any fixups.
		 */
ultra2_ensure_sg:
		test	SG_CACHE_SHADOW, LAST_SEG jz ultra2_shvalid;
		/* Record if we've consumed all S/G entries */
		test	SSTAT2, SHVALID	jnz residuals_correct;
		or	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL;
		jmp	residuals_correct;

ultra2_shvalid:
		test	SSTAT2, SHVALID	jnz sgptr_fixup;
		call	idle_loop;
		jmp	ultra2_ensure_sg;

sgptr_fixup:
		/*
		 * Fixup the residual next S/G pointer.  The S/G preload
		 * feature of the chip allows us to load two elements
		 * in addition to the currently active element.  We
		 * store the bottom byte of the next S/G pointer in
		 * the SG_CACEPTR register so we can restore the
		 * correct value when the DMA completes.  If the next
		 * sg ptr value has advanced to the point where higher
		 * bytes in the address have been affected, fix them
		 * too.
		 */
		test	SG_CACHE_SHADOW, 0x80 jz sgptr_fixup_done;
		test	SCB_RESIDUAL_SGPTR[0], 0x80 jnz sgptr_fixup_done;
		add	SCB_RESIDUAL_SGPTR[1], -1;
		adc	SCB_RESIDUAL_SGPTR[2], -1;
		adc	SCB_RESIDUAL_SGPTR[3], -1;
sgptr_fixup_done:
		and	SCB_RESIDUAL_SGPTR[0], SG_ADDR_MASK, SG_CACHE_SHADOW;
		clr	DATA_COUNT_ODD;
		test	SG_CACHE_SHADOW, ODD_SEG jz . + 2;
		or	DATA_COUNT_ODD, 0x1;
		clr	SCB_RESIDUAL_DATACNT[3]; /* We are not the last seg */
residuals_correct:
		/*
		 * Go ahead and shut down the DMA engine now.
		 * In the future, we'll want to handle end of
		 * transfer messages prior to doing this, but this
		 * requires similar restructuring for pre-ULTRA2
		 * controllers.
		 */
		test	DMAPARAMS, DIRECTION jnz ultra2_fifoempty;
ultra2_fifoflush:
		if ((ahc->features & AHC_DT) == 0) {
			if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) {
				/*
				 * On Rev A of the aic7890, the autoflush
				 * feature doesn't function correctly.
				 * Perform an explicit manual flush.  During
				 * a manual flush, the FIFOEMP bit becomes
				 * true every time the PCI FIFO empties
				 * regardless of the state of the SCSI FIFO.
				 * It can take up to 4 clock cycles for the
				 * SCSI FIFO to get data into the PCI FIFO
				 * and for FIFOEMP to de-assert.  Here we
				 * guard against this condition by making
				 * sure the FIFOEMP bit stays on for 5 full
				 * clock cycles.
				 */
				or	DFCNTRL, FIFOFLUSH;
				test	DFSTATUS, FIFOEMP jz ultra2_fifoflush;
				test	DFSTATUS, FIFOEMP jz ultra2_fifoflush;
				test	DFSTATUS, FIFOEMP jz ultra2_fifoflush;
				test	DFSTATUS, FIFOEMP jz ultra2_fifoflush;
			}
			test	DFSTATUS, FIFOEMP jz ultra2_fifoflush;
		} else {
			/*
			 * We enable the auto-ack feature on DT capable
			 * controllers.  This means that the controller may
			 * have already transferred some overrun bytes into
			 * the data FIFO and acked them on the bus.  The only
			 * way to detect this situation is to wait for
			 * LAST_SEG_DONE to come true on a completed transfer
			 * and then test to see if the data FIFO is non-empty.
			 */
			test	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 4;
			test	SG_CACHE_SHADOW, LAST_SEG_DONE jz .;
			test	DFSTATUS, FIFOEMP jnz ultra2_fifoempty;
			/* Overrun */
			jmp	data_phase_loop;
			test	DFSTATUS, FIFOEMP jz .;
		}
ultra2_fifoempty:
		/* Don't clobber an inprogress host data transfer */
		test	DFSTATUS, MREQPEND	jnz ultra2_fifoempty;
ultra2_dmahalt:
		and     DFCNTRL, ~(SCSIEN|HDMAEN);
		test	DFCNTRL, SCSIEN|HDMAEN jnz .;
		if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
			/*
			 * Keep HHADDR cleared for future, 32bit addressed
			 * only, DMA operations.
			 *
			 * Due to bayonette style S/G handling, our residual
			 * data must be "fixed up" once the transfer is halted.
			 * Here we fixup the HSHADDR stored in the high byte
			 * of the residual data cnt.  By postponing the fixup,
			 * we can batch the clearing of HADDR with the fixup.
			 * If we halted on the last segment, the residual is
			 * already correct.   If we are not on the last
			 * segment, copy the high address directly from HSHADDR.
			 * We don't need to worry about maintaining the
			 * SG_LAST_SEG flag as it will always be false in the
			 * case where an update is required.
			 */
			or	DSCOMMAND1, HADDLDSEL0;
			test	SG_CACHE_SHADOW, LAST_SEG jnz . + 2;
			mov	SCB_RESIDUAL_DATACNT[3], SHADDR;
			clr	HADDR;
			and	DSCOMMAND1, ~HADDLDSEL0;
		}
	} else {
		/* If we are the last SG block, tell the hardware. */
		if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
		  && ahc->pci_cachesize != 0) {
			test	MWI_RESIDUAL, 0xFF jnz dma_mid_sg;
		}
		test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz dma_mid_sg;
		if ((ahc->flags & AHC_TARGETROLE) != 0) {
			test	SSTAT0, TARGET jz dma_last_sg;
			if ((ahc->flags & AHC_TMODE_WIDEODD_BUG) != 0) {
				test	DMAPARAMS, DIRECTION jz dma_mid_sg;
			}
		}
dma_last_sg:
		and	DMAPARAMS, ~WIDEODD;
dma_mid_sg:
		/* Start DMA data transfer. */
		mov	DFCNTRL, DMAPARAMS;
dma_loop:
		if ((ahc->features & AHC_CMD_CHAN) != 0) {
			call	idle_loop;
		}
		test	SSTAT0,DMADONE	jnz dma_dmadone;
		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:
		test	DFCNTRL,DIRECTION	jnz dma_fifoempty;
dma_fifoflush:
		test	DFSTATUS,FIFOEMP	jz dma_fifoflush;
dma_fifoempty:
		/* Don't clobber an inprogress host data transfer */
		test	DFSTATUS, MREQPEND	jnz dma_fifoempty;

		/*
		 * Now shut off the DMA and make sure that the DMA
		 * hardware has actually stopped.  Touching the DMA
		 * counters, etc. while a DMA is active will result
		 * in an ILLSADDR exception.
		 */
dma_dmadone:
		and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
dma_halt:
		/*
		 * Some revisions of the aic78XX 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 drain
		 * the prefetched but unused data from the data fifo until
		 * there is space for the input latch to drain.
		 */
		if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0) {
			mov	NONE, DFDAT;
		}
		test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;

		/* See if we have completed this last segment */
		test	STCNT[0], 0xff	jnz data_phase_finish;
		test	STCNT[1], 0xff	jnz data_phase_finish;
		test	STCNT[2], 0xff	jnz data_phase_finish;

		/*
		 * Advance the scatter-gather pointers if needed
		 */
		if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
		  && ahc->pci_cachesize != 0) {
			test	MWI_RESIDUAL, 0xFF jz no_mwi_resid;
			/*
			 * Reload HADDR from SHADDR and setup the
			 * count to be the size of our residual.
			 */
			if ((ahc->features & AHC_CMD_CHAN) != 0) {
				bmov	HADDR, SHADDR, 4;
				mov	HCNT, MWI_RESIDUAL;
				bmov	HCNT[1], ALLZEROS, 2;
			} else {
				mvi	DINDEX, HADDR;
				mvi	SHADDR call bcopy_4;
				mov	MWI_RESIDUAL call set_hcnt;
			}
			clr	MWI_RESIDUAL;
			jmp	sg_load_done;
no_mwi_resid:
		}
		test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz sg_load;
		or	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL;
		jmp	data_phase_finish;
sg_load:
		/*
		 * Load the next SG element's data address and length
		 * into the DMA engine.  If we don't have hardware
		 * to perform a prefetch, we'll have to fetch the
		 * segment from host memory first.
		 */
		if ((ahc->features & AHC_CMD_CHAN) != 0) {
			/* Wait for the idle loop to complete */
			test	CCSGCTL, CCSGEN jz . + 3;
			call	idle_loop;
			test	CCSGCTL, CCSGEN jnz . - 1;
			bmov 	HADDR, CCSGRAM, 7;
			/*
			 * Workaround for flaky external SCB RAM
			 * on certain aic7895 setups.  It seems
			 * unable to handle direct transfers from
			 * S/G ram to certain SCB locations.
			 */
			mov	SINDEX, CCSGRAM;
			mov	SCB_RESIDUAL_DATACNT[3], SINDEX;
		} else {
			if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
				mov	ALLZEROS call set_hhaddr;
			}
			mvi	DINDEX, HADDR;
			mvi	SCB_RESIDUAL_SGPTR	call bcopy_4;

			mvi	SG_SIZEOF	call set_hcnt;

			or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;

			call	dma_finish;

			mvi	DINDEX, HADDR;
			call	dfdat_in_7;
			mov	SCB_RESIDUAL_DATACNT[3], DFDAT;
		}

		if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
			mov	SCB_RESIDUAL_DATACNT[3] call set_hhaddr;

			/*
			 * The lowest address byte must be loaded
			 * last as it triggers the computation of
			 * some items in the PCI block.  The ULTRA2
			 * chips do this on PRELOAD.
			 */
			mov	HADDR, HADDR;
		}
		if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
		  && ahc->pci_cachesize != 0) {
			call calc_mwi_residual;
		}

		/* Point to the new next sg in memory */
		call	sg_advance;

sg_load_done:
		if ((ahc->features & AHC_CMD_CHAN) != 0) {
			bmov	STCNT, HCNT, 3;
		} else {
			call	set_stcnt_from_hcnt;
		}
		/* Track odd'ness */
		test	HCNT[0], 0x1 jz . + 2;
		xor	DATA_COUNT_ODD, 0x1;

		if ((ahc->flags & AHC_TARGETROLE) != 0) {
			test	SSTAT0, TARGET jnz data_phase_loop;
		}
	}
data_phase_finish:
	/*
	 * If the target has left us in data phase, loop through
	 * the DMA code again.  In the case of ULTRA2 adapters,
	 * we should only loop if there is a data overrun.  For
	 * all other adapters, we'll loop after each S/G element
	 * is loaded as well as if there is an overrun.
	 */
	if ((ahc->flags & AHC_TARGETROLE) != 0) {
		test	SSTAT0, TARGET jnz data_phase_done;
	}
	if ((ahc->flags & AHC_INITIATORROLE) != 0) {
		test	SSTAT1, REQINIT jz .;
		if ((ahc->features & AHC_DT) == 0) {
			test	SSTAT1,PHASEMIS	jz data_phase_loop;
		} else {
			test	SCSIPHASE, DATA_PHASE_MASK jnz data_phase_loop;
		}
	}

data_phase_done:
	/*
	 * 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) {
		/* Kill off any pending prefetch */
		call	disable_ccsgen;
	}

	if ((ahc->features & AHC_ULTRA2) == 0) {
		/*
		 * Clear the high address byte so that all other DMA
		 * operations, which use 32bit addressing, can assume
		 * HHADDR is 0.
		 */
		if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
			mov	ALLZEROS call set_hhaddr;
		}
	}

	/*
	 * Update our residual information before the information is
	 * lost by some other type of SCSI I/O (e.g. PIO).  If we have
	 * transferred all data, no update is needed.
	 *
	 */
	test	SCB_RESIDUAL_SGPTR, SG_LIST_NULL jnz residual_update_done;
	if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
	  && ahc->pci_cachesize != 0) {
		if ((ahc->features & AHC_CMD_CHAN) != 0) {
			test	MWI_RESIDUAL, 0xFF jz bmov_resid;
		}
		mov	A, MWI_RESIDUAL;
		add	SCB_RESIDUAL_DATACNT[0], A, STCNT[0];
		clr	A;
		adc	SCB_RESIDUAL_DATACNT[1], A, STCNT[1];
		adc	SCB_RESIDUAL_DATACNT[2], A, STCNT[2];
		clr	MWI_RESIDUAL;
		if ((ahc->features & AHC_CMD_CHAN) != 0) {
			jmp	. + 2;
bmov_resid:
			bmov	SCB_RESIDUAL_DATACNT, STCNT, 3;
		}
	} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
		bmov	SCB_RESIDUAL_DATACNT, STCNT, 3;
	} else {
		mov	SCB_RESIDUAL_DATACNT[0], STCNT[0];
		mov	SCB_RESIDUAL_DATACNT[1], STCNT[1];
		mov	SCB_RESIDUAL_DATACNT[2], STCNT[2];
	}
residual_update_done:
	/*
	 * Since we've been through a data phase, the SCB_RESID* fields
	 * are now initialized.  Clear the full residual flag.
	 */
	and	SCB_SGPTR[0], ~SG_FULL_RESID;

	if ((ahc->features & AHC_ULTRA2) != 0) {
		/* Clear the channel in case we return to data phase later */
		or	SXFRCTL0, CLRSTCNT|CLRCHN;
		or	SXFRCTL0, CLRSTCNT|CLRCHN;
	}

	if ((ahc->flags & AHC_TARGETROLE) != 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.  We only need to
		 * send Ignore Wide Residue messages for data-in phases.
		 */
		test	DFCNTRL, DIRECTION jz target_ITloop;
		test	SSTAT1, REQINIT	jnz .;
		test	DATA_COUNT_ODD, 0x1 jz target_ITloop;
		test	SCSIRATE, WIDEXFER jz target_ITloop;
		/*
		 * Issue an Ignore Wide Residue Message.
		 */
		mvi	P_MESGIN|BSYO call change_phase;
		mvi	MSG_IGN_WIDE_RESIDUE call target_outb;
		mvi	1 call target_outb;
		jmp	target_ITloop;
	} else {
		jmp	ITloop;
	}

if ((ahc->flags & AHC_INITIATORROLE) != 0) {
/*
 * Command phase.  Set up the DMA registers and let 'er rip.
 */
p_command:
	test	SEQ_FLAGS, NOT_IDENTIFIED jz p_command_okay;
	mvi	PROTO_VIOLATION call set_seqint;
p_command_okay:

	if ((ahc->features & AHC_ULTRA2) != 0) {
		bmov	HCNT[0], SCB_CDB_LEN,  1;
		bmov	HCNT[1], ALLZEROS, 2;
		mvi	SG_CACHE_PRE, LAST_SEG;
	} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
		bmov	STCNT[0], SCB_CDB_LEN, 1;
		bmov	STCNT[1], ALLZEROS, 2;
	} else {
		mov	STCNT[0], SCB_CDB_LEN;
		clr	STCNT[1];
		clr	STCNT[2];
	}
	add	NONE, -13, SCB_CDB_LEN;
	mvi	SCB_CDB_STORE jnc p_command_embedded;
p_command_from_host:
	if ((ahc->features & AHC_ULTRA2) != 0) {
		bmov	HADDR[0], SCB_CDB_PTR, 4;
		mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
	} else {
		if ((ahc->features & AHC_CMD_CHAN) != 0) {
			bmov	HADDR[0], SCB_CDB_PTR, 4;
			bmov	HCNT, STCNT, 3;
		} else {
			mvi	DINDEX, HADDR;
			mvi	SCB_CDB_PTR call bcopy_4;
			mov	SCB_CDB_LEN call set_hcnt;
		}
		mvi	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET);
	}
	jmp	p_command_xfer;
p_command_embedded:
	/*
	 * The data fifo seems to require 4 byte aligned
	 * transfers from the sequencer.  Force this to
	 * be the case by clearing HADDR[0] even though
	 * we aren't going to touch host memory.
	 */
	clr	HADDR[0];
	if ((ahc->features & AHC_ULTRA2) != 0) {
		mvi	DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION);
		bmov	DFDAT, SCB_CDB_STORE, 12;
	} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
		if ((ahc->flags & AHC_SCB_BTT) != 0) {
			/*
			 * On the 7895 the data FIFO will
			 * get corrupted if you try to dump
			 * data from external SCB memory into
			 * the FIFO while it is enabled.  So,
			 * fill the fifo and then enable SCSI
			 * transfers.
			 */
			mvi	DFCNTRL, (DIRECTION|FIFORESET);
		} else {
			mvi	DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
		}
		bmov	DFDAT, SCB_CDB_STORE, 12;
		if ((ahc->flags & AHC_SCB_BTT) != 0) {
			mvi	DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFOFLUSH);
		} else {
			or	DFCNTRL, FIFOFLUSH;
		}
	} else {
		mvi	DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
		call	copy_to_fifo_6;
		call	copy_to_fifo_6;
		or	DFCNTRL, FIFOFLUSH;
	}
p_command_xfer:
	and	SEQ_FLAGS, ~NO_CDB_SENT;
	if ((ahc->features & AHC_DT) == 0) {
		test	SSTAT0, SDONE jnz . + 2;
		test    SSTAT1, PHASEMIS jz . - 1;
		/*
		 * Wait for our ACK to go-away on its own
		 * instead of being killed by SCSIEN getting cleared.
		 */
		test	SCSISIGI, ACKI jnz .;
	} else {
		test	DFCNTRL, SCSIEN jnz .;
	}
	test	SSTAT0, SDONE jnz p_command_successful;
	/*
	 * Don't allow a data phase if the command
	 * was not fully transferred.
	 */
	or	SEQ_FLAGS, NO_CDB_SENT;
p_command_successful:
	and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .;
	jmp	ITloop;

/*
 * Status phase.  Wait for the data byte to appear, then read it
 * and store it into the SCB.
 */
p_status:
	test	SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation;
p_status_okay:
	mov	SCB_SCSI_STATUS, SCSIDATL;
	or	SCB_CONTROL, STATUS_RCVD;
	jmp	ITloop;

/*
 * Message out phase.  If MSG_OUT is MSG_IDENTIFYFLAG, build a full
 * identify 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 sequencer from testing the MK_MESSAGE bit
 * on an SCB that might not be for the current nexus. (For example, a
 * BDR message in response 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:
	/* Turn on ATN for the retry */
	if ((ahc->features & AHC_DT) == 0) {
		or	SCSISIGO, ATNO, LASTPHASE;
	} else {
		mvi	SCSISIGO, ATNO;
	}
p_mesgout:
	mov	SINDEX, MSG_OUT;
	cmp	SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
	test	SCB_CONTROL,MK_MESSAGE	jnz host_message_loop;
p_mesgout_identify:
	or	SINDEX, MSG_IDENTIFYFLAG|DISCENB, SCB_LUN;
	test	SCB_CONTROL, DISCENB jnz . + 2;
	and	SINDEX, ~DISCENB;
/*
 * 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:
	test	SCB_CONTROL,TAG_ENB jz  p_mesgout_onebyte;
	mov	SCSIDATL, SINDEX;	/* Send the identify message */
	call	phase_lock;
	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
	and	SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
	call	phase_lock;
	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
	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:
	cmp	SINDEX, HOST_MSG	jne p_mesgout_onebyte;
	jmp	host_message_loop;

p_mesgout_onebyte:
	mvi	CLRSINT1, CLRATNO;
	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.
 */
	call	phase_lock;
	cmp	LASTPHASE, P_MESGOUT	je p_mesgout_retry;

p_mesgout_done:
	mvi	CLRSINT1,CLRATNO;	/* Be sure to turn ATNO off */
	mov	LAST_MSG, MSG_OUT;
	mvi	MSG_OUT, MSG_NOOP;	/* No message left */
	jmp	ITloop;

/*
 * Message in phase.  Bytes are read using Automatic PIO mode.
 */
p_mesgin:
	mvi	ACCUM		call inb_first;	/* read the 1st message byte */

	test	A,MSG_IDENTIFYFLAG	jnz mesgin_identify;
	cmp	A,MSG_DISCONNECT	je mesgin_disconnect;
	cmp	A,MSG_SAVEDATAPOINTER	je mesgin_sdptrs;
	cmp	ALLZEROS,A		je mesgin_complete;
	cmp	A,MSG_RESTOREPOINTERS	je mesgin_rdptrs;
	cmp	A,MSG_IGN_WIDE_RESIDUE	je mesgin_ign_wide_residue;
	cmp	A,MSG_NOOP		je mesgin_done;

/*
 * Pushed message loop to allow the kernel to
 * run its 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:
	mvi	HOST_MSG_LOOP call set_seqint;
	call	phase_lock;
	cmp	RETURN_1, EXIT_MSG_LOOP	je ITloop + 1;
	jmp	host_message_loop;

mesgin_ign_wide_residue:
if ((ahc->features & AHC_WIDE) != 0) {
	test	SCSIRATE, WIDEXFER jz mesgin_reject;
	/* Pull the residue byte */
	mvi	ARG_1	call inb_next;
	cmp	ARG_1, 0x01 jne mesgin_reject;
	test	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 2;
	test	DATA_COUNT_ODD, 0x1	jz mesgin_done;
	mvi	IGN_WIDE_RES call set_seqint;
	jmp	mesgin_done;
}

mesgin_proto_violation:
	mvi	PROTO_VIOLATION call set_seqint;
	jmp	mesgin_done;
mesgin_reject:
	mvi	MSG_MESSAGE_REJECT	call mk_mesg;
mesgin_done:
	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
	jmp	ITloop;

/*
 * We received a "command complete" message.  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, requeue
 * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting
 * RETURN_1 to SEND_SENSE.
 */
mesgin_complete:

	/*
	 * If ATN is raised, we still want to give the target a message.
	 * Perhaps there was a parity error on this last message byte.
	 * Either way, the target should take us to message out phase
	 * and then attempt to complete the command again.  We should use a
	 * critical section here to guard against a timeout triggering
	 * for this command and setting ATN while we are still processing
	 * the completion.
	test	SCSISIGI, ATNI jnz mesgin_done;
	 */

	/*
	 * If we are identified and have successfully sent the CDB,
	 * any status will do.  Optimize this fast path.
	 */
	test	SCB_CONTROL, STATUS_RCVD jz mesgin_proto_violation;
	test	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted;

	/*
	 * If the target never sent an identify message but instead went
	 * to mesgin to give an invalid message, let the host abort us.
	 */
	test	SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation;

	/*
	 * If we received good status but never successfully sent the
	 * cdb, abort the command.
	 */
	test	SCB_SCSI_STATUS,0xff	jnz complete_accepted;
	test	SEQ_FLAGS, NO_CDB_SENT jnz mesgin_proto_violation;

complete_accepted:
	/*
	 * See if we attempted to deliver a message but the target ignored us.
	 */
	test	SCB_CONTROL, MK_MESSAGE jz . + 2;
	mvi	MKMSG_FAILED call set_seqint;

	/*
	 * Check for residuals
	 */
	test	SCB_SGPTR, SG_LIST_NULL jnz check_status;/* No xfer */
	test	SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
	test	SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;
check_status:
	test	SCB_SCSI_STATUS,0xff	jz complete;	/* Good Status? */
upload_scb:
	or	SCB_SGPTR, SG_RESID_VALID;
	mvi	DMAPARAMS, FIFORESET;
	mov	SCB_TAG		call dma_scb;
	test	SCB_SCSI_STATUS, 0xff	jz complete;	/* Just a residual? */
	mvi	BAD_STATUS call set_seqint;		/* let driver know */
	cmp	RETURN_1, SEND_SENSE	jne complete;
	call	add_scb_to_free_list;
	jmp	await_busfree;
complete:
	mov	SCB_TAG call complete_post;
	jmp	await_busfree;
}

complete_post:
	/* Post the SCBID in SINDEX and issue an interrupt */
	call	add_scb_to_free_list;
	mov	ARG_1, SINDEX;
	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
		mov	A, SDSCB_QOFF;
	} else {
		mov	A, QOUTPOS;
	}
	mvi	QOUTFIFO_OFFSET call post_byte_setup;
	mov	ARG_1 call post_byte;
	if ((ahc->features & AHC_QUEUE_REGS) == 0) {
		inc 	QOUTPOS;
	}
	mvi	INTSTAT,CMDCMPLT ret;

if ((ahc->flags & AHC_INITIATORROLE) != 0) {
/*
 * Is it a disconnect message?  Set a flag in the SCB to remind us
 * and await the bus going free.  If this is an untagged transaction
 * store the SCB id for it in our untagged target table for lookup on
 * a reselection.
 */
mesgin_disconnect:
	/*
	 * If ATN is raised, we still want to give the target a message.
	 * Perhaps there was a parity error on this last message byte
	 * or we want to abort this command.  Either way, the target
	 * should take us to message out phase and then attempt to
	 * disconnect again.
	 * XXX - Wait for more testing.
	test	SCSISIGI, ATNI jnz mesgin_done;
	 */
	test	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT
		jnz mesgin_proto_violation;
	or	SCB_CONTROL,DISCONNECTED;
	if ((ahc->flags & AHC_PAGESCBS) != 0) {
		call	add_scb_to_disc_list;
	}
	test	SCB_CONTROL, TAG_ENB jnz await_busfree;
	mov	ARG_1, SCB_TAG;
	mov	SAVED_LUN, SCB_LUN;
	mov	SCB_SCSIID	call set_busy_target;
	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.
 * Ack the message as soon as possible.  For chips without S/G pipelining,
 * we can only ack the message after SHADDR has been saved.  On these
 * chips, SHADDR increments with every bus transaction, even PIO.
 */
mesgin_sdptrs:
	if ((ahc->features & AHC_ULTRA2) != 0) {
		mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
		test	SEQ_FLAGS, DPHASE	jz ITloop;
	} else {
		test	SEQ_FLAGS, DPHASE	jz mesgin_done;
	}

	/*
	 * If we are asked to save our position at the end of the
	 * transfer, just mark us at the end rather than perform a
	 * full save.
	 */
	test	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz mesgin_sdptrs_full;
	or	SCB_SGPTR, SG_LIST_NULL;
	if ((ahc->features & AHC_ULTRA2) != 0) {
		jmp	ITloop;
	} else {
		jmp	mesgin_done;
	}

mesgin_sdptrs_full:

	/*
	 * 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) {
		bmov	SCB_DATAPTR, SHADDR, 4;
		if ((ahc->features & AHC_ULTRA2) == 0) {
			mov	NONE,SCSIDATL;	/*dummy read from latch to ACK*/
		}
		bmov	SCB_DATACNT, SCB_RESIDUAL_DATACNT, 8;
	} else {
		mvi	DINDEX, SCB_DATAPTR;
		mvi	SHADDR call bcopy_4;
		mov	NONE,SCSIDATL;	/*dummy read from latch to ACK*/
		mvi	SCB_RESIDUAL_DATACNT call bcopy_8;
	}
	jmp	ITloop;

/*
 * 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.  We also reset/reallocate the FIFO to make
 * sure we have a clean start for the next data or command phase.
 */
mesgin_rdptrs:
	and	SEQ_FLAGS, ~DPHASE;		/*
						 * We'll reload them
						 * the next time through
						 * the dataphase.
						 */
	or	SXFRCTL0, CLRSTCNT|CLRCHN;
	jmp	mesgin_done;

/*
 * Index into our Busy Target table.  SINDEX and DINDEX are modified
 * upon return.  SCBPTR may be modified by this action.
 */
set_busy_target:
	shr	DINDEX, 4, SINDEX;
	if ((ahc->flags & AHC_SCB_BTT) != 0) {
		mov	SCBPTR, SAVED_LUN;
		add	DINDEX, SCB_64_BTT;
	} else {
		add	DINDEX, BUSY_TARGETS;
	}
	mov	DINDIR, ARG_1 ret;

/*
 * 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:
	/*
	 * Determine whether a target is using tagged or non-tagged
	 * transactions by first looking at the transaction stored in
	 * the busy target array.  If there is no untagged transaction
	 * for this target or the transaction is for a different lun, then
	 * this must be a tagged transaction.
	 */
	shr	SINDEX, 4, SAVED_SCSIID;
	and	SAVED_LUN, MSG_IDENTIFY_LUNMASK, A;
	if ((ahc->flags & AHC_SCB_BTT) != 0) {
		add	SINDEX, SCB_64_BTT;
		mov	SCBPTR, SAVED_LUN;
		if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
			add	NONE, -SCB_64_BTT, SINDEX;
			jc	. + 2;
			mvi	INTSTAT, OUT_OF_RANGE;
			nop;
			add	NONE, -(SCB_64_BTT + 16), SINDEX;
			jnc	. + 2;
			mvi	INTSTAT, OUT_OF_RANGE;
			nop;
		}
	} else {
		add	SINDEX, BUSY_TARGETS;
		if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
			add	NONE, -BUSY_TARGETS, SINDEX;
			jc	. + 2;
			mvi	INTSTAT, OUT_OF_RANGE;
			nop;
			add	NONE, -(BUSY_TARGETS + 16), SINDEX;
			jnc	. + 2;
			mvi	INTSTAT, OUT_OF_RANGE;
			nop;
		}
	}
	mov	ARG_1, SINDIR;
	cmp	ARG_1, SCB_LIST_NULL	je snoop_tag;
	if ((ahc->flags & AHC_PAGESCBS) != 0) {
		mov	ARG_1 call findSCB;
	} else {
		mov	SCBPTR, ARG_1;
	}
	if ((ahc->flags & AHC_SCB_BTT) != 0) {
		jmp setup_SCB_id_lun_okay;
	} else {
		/*
		 * We only allow one untagged command per-target
		 * at a time.  So, if the lun doesn't match, look
		 * for a tag message.
		 */
		mov	A, SCB_LUN;
		cmp	SAVED_LUN, A	je setup_SCB_id_lun_okay;
		if ((ahc->flags & AHC_PAGESCBS) != 0) {
			/*
			 * findSCB removes the SCB from the
			 * disconnected list, so we must replace
			 * it there should this SCB be for another
			 * lun.
			 */
			call	cleanup_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, we must search for 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:
	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
		or	SEQ_FLAGS, 0x80;
	}
	mov	NONE,SCSIDATL;		/* ACK Identify MSG */
	call	phase_lock;
	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
		or	SEQ_FLAGS, 0x1;
	}
	cmp	LASTPHASE, P_MESGIN	jne not_found;
	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
		or	SEQ_FLAGS, 0x2;
	}
	cmp	SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
get_tag:
	if ((ahc->flags & AHC_PAGESCBS) != 0) {
		mvi	ARG_1	call inb_next;	/* tag value */
		mov	ARG_1	call findSCB;
	} else {
		mvi	ARG_1	call inb_next;	/* tag value */
		mov	SCBPTR, ARG_1;
	}

/*
 * Ensure that the SCB the tag points to is for
 * an SCB transaction to the reconnecting target.
 */
setup_SCB:
	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
		or	SEQ_FLAGS, 0x4;
	}
	mov	A, SCB_SCSIID;
	cmp	SAVED_SCSIID, A	jne not_found_cleanup_scb;
	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
		or	SEQ_FLAGS, 0x8;
	}
setup_SCB_id_okay:
	mov	A, SCB_LUN;
	cmp	SAVED_LUN, A	jne not_found_cleanup_scb;
setup_SCB_id_lun_okay:
	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
		or	SEQ_FLAGS, 0x10;
	}
	test	SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
	and	SCB_CONTROL,~DISCONNECTED;
	test	SCB_CONTROL, TAG_ENB	jnz setup_SCB_tagged;
	if ((ahc->flags & AHC_SCB_BTT) != 0) {
		mov	A, SCBPTR;
	}
	mvi	ARG_1, SCB_LIST_NULL;
	mov	SAVED_SCSIID	call	set_busy_target;
	if ((ahc->flags & AHC_SCB_BTT) != 0) {
		mov	SCBPTR, A;
	}
setup_SCB_tagged:
	clr	SEQ_FLAGS;	/* make note of IDENTIFY */
	call	set_transfer_settings;
	/* See if the host wants to send a message upon reconnection */
	test	SCB_CONTROL, MK_MESSAGE jz mesgin_done;
	mvi	HOST_MSG	call mk_mesg;
	jmp	mesgin_done;

not_found_cleanup_scb:
	if ((ahc->flags & AHC_PAGESCBS) != 0) {
		call	cleanup_scb;
	}
not_found:
	mvi	NO_MATCH call set_seqint;
	jmp	mesgin_done;

mk_mesg:
	if ((ahc->features & AHC_DT) == 0) {
		or	SCSISIGO, ATNO, LASTPHASE;
	} else {
		mvi	SCSISIGO, ATNO;
	}
	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:
	mvi	PERR_DETECTED call set_seqint;
	jmp	inb_next_wait;
inb_next:
	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.
	 */
	test	SSTAT1, REQINIT	jz inb_next_wait;
	test	SSTAT1, SCSIPERR jnz inb_next_wait_perr;
inb_next_check_phase:
	and	LASTPHASE, PHASE_MASK, SCSISIGI;
	cmp	LASTPHASE, P_MESGIN jne mesgin_phasemis;
inb_first:
	mov	DINDEX,SINDEX;
	mov	DINDIR,SCSIBUSL	ret;		/*read byte directly from bus*/
inb_last:
	mov	NONE,SCSIDATL ret;		/*dummy read from latch to ACK*/
}

if ((ahc->flags & AHC_TARGETROLE) != 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 preceding 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 wait at 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;
}

/*
 * Locate a disconnected SCB by SCBID.  Upon return, SCBPTR and SINDEX will
 * be set to the position of the SCB.  If the SCB cannot be found locally,
 * it will be paged in from host memory.  RETURN_2 stores the address of the
 * preceding SCB in the disconnected list which can be used to speed up
 * removal of the found SCB from the disconnected list.
 */
if ((ahc->flags & AHC_PAGESCBS) != 0) {
BEGIN_CRITICAL;
findSCB:
	mov	A, SINDEX;			/* Tag passed in SINDEX */
	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL je findSCB_notFound;
	mov	SCBPTR, DISCONNECTED_SCBH;	/* Initialize SCBPTR */
	mvi	ARG_2, SCB_LIST_NULL;		/* Head of list */
	jmp	findSCB_loop;
findSCB_next:
	cmp	SCB_NEXT, SCB_LIST_NULL je findSCB_notFound;
	mov	ARG_2, SCBPTR;
	mov	SCBPTR,SCB_NEXT;
findSCB_loop:
	cmp	SCB_TAG, A	jne findSCB_next;
rem_scb_from_disc_list:
	cmp	ARG_2, SCB_LIST_NULL	je rHead;
	mov	DINDEX, SCB_NEXT;
	mov	SINDEX, SCBPTR;
	mov	SCBPTR, ARG_2;
	mov	SCB_NEXT, DINDEX;
	mov	SCBPTR, SINDEX ret;
rHead:
	mov	DISCONNECTED_SCBH,SCB_NEXT ret;
END_CRITICAL;
findSCB_notFound:
	/*
	 * We didn't find it.  Page in the SCB.
	 */
	mov	ARG_1, A; /* Save tag */
	mov	ALLZEROS call get_free_or_disc_scb;
	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
	mov	ARG_1	jmp dma_scb;
}

/*
 * Prepare the hardware to post a byte to host memory given an
 * index of (A + (256 * SINDEX)) and a base address of SHARED_DATA_ADDR.
 */
post_byte_setup:
	mov	ARG_2, SINDEX;
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		mvi	DINDEX, CCHADDR;
		mvi	SHARED_DATA_ADDR call	set_1byte_addr;
		mvi	CCHCNT, 1;
		mvi	CCSCBCTL, CCSCBRESET ret;
	} else {
		mvi	DINDEX, HADDR;
		mvi	SHARED_DATA_ADDR call	set_1byte_addr;
		mvi	1	call set_hcnt;
		mvi	DFCNTRL, FIFORESET ret;
	}

post_byte:
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		bmov	CCSCBRAM, SINDEX, 1;
		or	CCSCBCTL, CCSCBEN|CCSCBRESET;
		test	CCSCBCTL, CCSCBDONE jz .;
		clr	CCSCBCTL ret;
	} else {
		mov	DFDAT, SINDEX;
		or	DFCNTRL, HDMAEN|FIFOFLUSH;
		jmp	dma_finish;
	}

phase_lock_perr:
	mvi	PERR_DETECTED call set_seqint;
phase_lock:
	/*
	 * If there is a parity error, wait for the kernel to
	 * see the interrupt and prepare our message response
	 * before continuing.
	 */
	test	SSTAT1, REQINIT jz phase_lock;
	test	SSTAT1, SCSIPERR jnz phase_lock_perr;
phase_lock_latch_phase:
	if ((ahc->features & AHC_DT) == 0) {
		and	SCSISIGO, PHASE_MASK, SCSISIGI;
	}
	and	LASTPHASE, PHASE_MASK, SCSISIGI ret;

if ((ahc->features & AHC_CMD_CHAN) == 0) {
set_hcnt:
	mov	HCNT[0], SINDEX;
clear_hcnt:
	clr	HCNT[1];
	clr	HCNT[2] ret;

set_stcnt_from_hcnt:
	mov	STCNT[0], HCNT[0];
	mov	STCNT[1], HCNT[1];
	mov	STCNT[2], HCNT[2] ret;

bcopy_8:
	mov	DINDIR, SINDIR;
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_TARGETROLE) != 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:
	shr	ARG_2, 2, A;
	shl	A, 6;

/*
 * Setup addr assuming that A + (ARG_2 * 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:
	add     DINDIR, A, SINDIR;
	mov     A, ARG_2;
	adc	DINDIR, A, SINDIR;
	clr	A;
	adc	DINDIR, A, SINDIR;
	adc	DINDIR, A, SINDIR ret;

/*
 * Either post or fetch an SCB from host memory based on the
 * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
 */
dma_scb:
	mov	A, SINDEX;
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		mvi	DINDEX, CCHADDR;
		mvi	HSCB_ADDR call set_64byte_addr;
		mov	CCSCBPTR, SCBPTR;
		test	DMAPARAMS, DIRECTION jz dma_scb_tohost;
		if ((ahc->flags & AHC_SCB_BTT) != 0) {
			mvi	CCHCNT, SCB_DOWNLOAD_SIZE_64;
		} else {
			mvi	CCHCNT, SCB_DOWNLOAD_SIZE;
		}
		mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
		cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
		jmp	dma_scb_finish;
dma_scb_tohost:
		mvi	CCHCNT, SCB_UPLOAD_SIZE;
		if ((ahc->features & AHC_ULTRA2) == 0) {
			mvi	CCSCBCTL, CCSCBRESET;
			bmov	CCSCBRAM, SCB_BASE, SCB_UPLOAD_SIZE;
			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
			test	CCSCBCTL, CCSCBDONE jz .;
		} else if ((ahc->bugs & AHC_SCBCHAN_UPLOAD_BUG) != 0) {
			mvi	CCSCBCTL, CCARREN|CCSCBRESET;
			cmp	CCSCBCTL, ARRDONE|CCARREN jne .;
			mvi	CCHCNT, SCB_UPLOAD_SIZE;
			mvi	CCSCBCTL, CCSCBEN|CCSCBRESET;
			cmp	CCSCBCTL, CCSCBDONE|CCSCBEN jne .;
		} else {
			mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
			cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
		}
dma_scb_finish:
		clr	CCSCBCTL;
		test	CCSCBCTL, CCARREN|CCSCBEN jnz .;
		ret;
	} else {
		mvi	DINDEX, HADDR;
		mvi	HSCB_ADDR call set_64byte_addr;
		mvi	SCB_DOWNLOAD_SIZE call set_hcnt;
		mov	DFCNTRL, DMAPARAMS;
		test	DMAPARAMS, DIRECTION	jnz dma_scb_fromhost;
		/* Fill it with the SCB data */
copy_scb_tofifo:
		mvi	SINDEX, SCB_BASE;
		add	A, SCB_DOWNLOAD_SIZE, SINDEX;
copy_scb_tofifo_loop:
		call	copy_to_fifo_8;
		cmp	SINDEX, A jne copy_scb_tofifo_loop;
		or	DFCNTRL, HDMAEN|FIFOFLUSH;
		jmp	dma_finish;
dma_scb_fromhost:
		mvi	DINDEX, SCB_BASE;
		if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0) {
			/*
			 * The PCI module will only issue a PCI
			 * retry if the data FIFO is empty.  If the
			 * host disconnects in the middle of a
			 * transfer, we must empty the fifo of all
			 * available data to force the chip to
			 * continue the transfer.  This does not
			 * happen for SCSI transfers as the SCSI module
			 * will drain the FIFO as data are made available.
			 * When the hang occurs, we know that a multiple
			 * of 8 bytes is in the FIFO because the PCI
			 * module has an 8 byte input latch that only
			 * dumps to the FIFO when HCNT == 0 or the
			 * latch is full.
			 */
			clr	A;
			/* Wait for at least 8 bytes of data to arrive. */
dma_scb_hang_fifo:
			test	DFSTATUS, FIFOQWDEMP jnz dma_scb_hang_fifo;
dma_scb_hang_wait:
			test	DFSTATUS, MREQPEND jnz dma_scb_hang_wait;
			test	DFSTATUS, HDONE	jnz dma_scb_hang_dma_done;
			test	DFSTATUS, HDONE	jnz dma_scb_hang_dma_done;
			test	DFSTATUS, HDONE	jnz dma_scb_hang_dma_done;
			/*
			 * The PCI module no longer intends to perform
			 * a PCI transaction.  Drain the fifo.
			 */
dma_scb_hang_dma_drain_fifo:
			not	A, HCNT;
			add	A, SCB_DOWNLOAD_SIZE+SCB_BASE+1;
			and	A, ~0x7;
			mov	DINDIR,DFDAT;
			cmp	DINDEX, A jne . - 1;
			cmp	DINDEX, SCB_DOWNLOAD_SIZE+SCB_BASE
				je	dma_finish_nowait;
			/* Restore A as the lines left to transfer. */
			add	A, -SCB_BASE, DINDEX;
			shr	A, 3;
			jmp	dma_scb_hang_fifo;
dma_scb_hang_dma_done:
			and	DFCNTRL, ~HDMAEN;
			test	DFCNTRL, HDMAEN jnz .;
			add	SEQADDR0, A;
		} else {
			call	dma_finish;
		}
		call	dfdat_in_8;
		call	dfdat_in_8;
		call	dfdat_in_8;
dfdat_in_8:
		mov	DINDIR,DFDAT;
dfdat_in_7:
		mov	DINDIR,DFDAT;
		mov	DINDIR,DFDAT;
		mov	DINDIR,DFDAT;
		mov	DINDIR,DFDAT;
		mov	DINDIR,DFDAT;
dfdat_in_2:
		mov	DINDIR,DFDAT;
		mov	DINDIR,DFDAT ret;
	}

copy_to_fifo_8:
	mov	DFDAT,SINDIR;
	mov	DFDAT,SINDIR;
copy_to_fifo_6:
	mov	DFDAT,SINDIR;
copy_to_fifo_5:
	mov	DFDAT,SINDIR;
copy_to_fifo_4:
	mov	DFDAT,SINDIR;
	mov	DFDAT,SINDIR;
	mov	DFDAT,SINDIR;
	mov	DFDAT,SINDIR 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:
	test	DFSTATUS,HDONE	jz dma_finish;
dma_finish_nowait:
	/* Turn off DMA */
	and	DFCNTRL, ~HDMAEN;
	test	DFCNTRL, HDMAEN jnz .;
	ret;

/*
 * Restore an SCB that failed to match an incoming reselection
 * to the correct/safe state.  If the SCB is for a disconnected
 * transaction, it must be returned to the disconnected list.
 * If it is not in the disconnected state, it must be free.
 */
cleanup_scb:
	if ((ahc->flags & AHC_PAGESCBS) != 0) {
		test	SCB_CONTROL,DISCONNECTED jnz add_scb_to_disc_list;
	}
add_scb_to_free_list:
	if ((ahc->flags & AHC_PAGESCBS) != 0) {
BEGIN_CRITICAL;
		mov	SCB_NEXT, FREE_SCBH;
		mvi	SCB_TAG, SCB_LIST_NULL;
		mov	FREE_SCBH, SCBPTR ret;
END_CRITICAL;
	} else {
		mvi	SCB_TAG, SCB_LIST_NULL ret;
	}

if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
set_hhaddr:
	or	DSCOMMAND1, HADDLDSEL0;
	and	HADDR, SG_HIGH_ADDR_BITS, SINDEX;
	and	DSCOMMAND1, ~HADDLDSEL0 ret;
}

if ((ahc->flags & AHC_PAGESCBS) != 0) {
get_free_or_disc_scb:
BEGIN_CRITICAL;
	cmp	FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
return_error:
	mvi	NO_FREE_SCB call set_seqint;
	mvi	SINDEX, SCB_LIST_NULL	ret;
dequeue_disc_scb:
	mov	SCBPTR, DISCONNECTED_SCBH;
	mov	DISCONNECTED_SCBH, SCB_NEXT;
END_CRITICAL;
	mvi	DMAPARAMS, FIFORESET;
	mov	SCB_TAG	jmp dma_scb;
BEGIN_CRITICAL;
dequeue_free_scb:
	mov	SCBPTR, FREE_SCBH;
	mov	FREE_SCBH, SCB_NEXT ret;
END_CRITICAL;

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.
 */
BEGIN_CRITICAL;
	mov	SCB_NEXT, DISCONNECTED_SCBH;
	mov	DISCONNECTED_SCBH, SCBPTR ret;
END_CRITICAL;
}
set_seqint:
	mov	INTSTAT, SINDEX;
	nop;
return:
	ret;
@


1.24
log
@s/psuedo/pseudo/ in comments.
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.23 2022/06/27 22:41:29 andvar Exp $"
d448 1
a448 1
		 * Put tag in connonical location since not
@


1.23
log
@fix various typos in comments.
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.22 2022/01/25 22:01:34 andvar Exp $"
d60 1
a60 1
 * use byte 27 of the SCB as a psuedo-next pointer and to thread a list
@


1.22
log
@fix various typos in comments.
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.21 2021/09/03 22:33:17 andvar Exp $"
d1651 1
a1651 1
	 * See if we attempted to deliver a message but the target ingnored us.
@


1.21
log
@s/existant/existent/ in comments and messages, plus few more similar fixes.
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.20 2019/06/04 10:15:22 msaitoh Exp $"
d1479 1
a1479 1
 * indentify message sequence and send it to the target.  The host may
d1489 1
a1489 1
 * sequence while protecting the seqencer from testing the MK_MESSAGE bit
d1491 1
a1491 1
 * BDR message in responce to a bad reselection would leave us pointed to
d1699 1
a1699 1
 * a reselction.
@


1.20
log
@ Fix typo (s/recevie/receive/).
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.19 2014/10/18 08:33:28 snj Exp $"
d754 1
a754 1
	 * so we don't end up referencing a non-existant page.
@


1.19
log
@src is too big these days to tolerate superfluous apostrophes.  It's
"its", people!
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.18 2006/11/25 16:48:32 christos Exp $"
d1643 1
a1643 1
	 * If we recevied good status but never successfully sent the
@


1.19.20.1
log
@Sync with HEAD
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.20 2019/06/04 10:15:22 msaitoh Exp $"
d1643 1
a1643 1
	 * If we received good status but never successfully sent the
@


1.18
log
@spell precede; from Zafer
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.17 2005/12/11 12:22:18 christos Exp $"
d322 1
a322 1
		 * run it's own target mode message state engine.
d1448 1
a1448 1
		 * Wait for our ACK to go-away on it's own
d1570 1
a1570 1
 * run it's own message state engine.  To avoid an
@


1.18.98.1
log
@update from HEAD
@
text
@d43 1
a43 1
VERSION = "$NetBSD$"
d322 1
a322 1
		 * run its own target mode message state engine.
d1448 1
a1448 1
		 * Wait for our ACK to go-away on its own
d1570 1
a1570 1
 * run its own message state engine.  To avoid an
@


1.17
log
@merge ktrace-lwp.
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.15.2.1 2005/03/04 16:43:50 skrll Exp $"
d2005 1
a2005 1
	/* Wait for preceeding I/O session to complete. */
@


1.17.20.1
log
@Sync with head.
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.18 2006/11/25 16:48:32 christos Exp $"
d2005 1
a2005 1
	/* Wait for preceding I/O session to complete. */
@


1.17.22.1
log
@sync with head.
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.17 2005/12/11 12:22:18 christos Exp $"
d2005 1
a2005 1
	/* Wait for preceding I/O session to complete. */
@


1.16
log
@nuke trailing whitespace
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.15 2003/05/03 18:11:31 wiz Exp $"
@


1.16.4.1
log
@sync with head.
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.16 2005/02/27 00:27:23 perry Exp $"
d2005 1
a2005 1
	/* Wait for preceding I/O session to complete. */
@


1.15
log
@DMA, not dma nor Dma.
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.14 2003/04/19 19:26:10 fvdl Exp $"
d59 1
a59 1
 * create yet another SCB waiting for selection.  The solution used here is to 
d61 1
a61 1
 * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB indexes, 
d104 1
a104 1
	 * We have at least one queued SCB now and we don't have any 
d282 1
a282 1
		
d399 1
a399 1
	} 
d411 1
a411 1
 * We carefully toggle SPIOEN to allow us to return the 
d479 1
a479 1
		
d497 1
a497 1
		test	SEQ_FLAGS, NO_DISCONNECT jz target_disconnect; 
d518 1
a518 1
		
d556 1
a556 1
tqinfifo_has_space:	
d618 1
a618 1
	
d686 1
a686 1
	
d793 1
a793 1
 * be transferred using memory write and invalidate PCI transactions.  
d1005 1
a1005 1
		adc	SCB_RESIDUAL_SGPTR[2], -1; 
d1144 1
a1144 1
		 * data fifo is full, but the PCI input latch is not empty, 
d1160 1
a1160 1
		 * Advance the scatter-gather pointers if needed 
d1415 1
a1415 1
		bmov	DFDAT, SCB_CDB_STORE, 12; 
d1430 1
a1430 1
		bmov	DFDAT, SCB_CDB_STORE, 12; 
d1486 1
a1486 1
 * 
d1609 1
a1609 1
 * process this information.  In the case of a non zero status byte, we 
d1613 1
a1613 1
 * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting 
d1634 1
a1634 1
	test	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted; 
d2038 1
a2038 1
	
d2112 1
a2112 1
phase_lock:     
@


1.15.10.1
log
@sync with -current
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.16 2005/02/27 00:27:23 perry Exp $"
d59 1
a59 1
 * create yet another SCB waiting for selection.  The solution used here is to
d61 1
a61 1
 * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB indexes,
d104 1
a104 1
	 * We have at least one queued SCB now and we don't have any
d282 1
a282 1

d399 1
a399 1
	}
d411 1
a411 1
 * We carefully toggle SPIOEN to allow us to return the
d479 1
a479 1

d497 1
a497 1
		test	SEQ_FLAGS, NO_DISCONNECT jz target_disconnect;
d518 1
a518 1

d556 1
a556 1
tqinfifo_has_space:
d618 1
a618 1

d686 1
a686 1

d793 1
a793 1
 * be transferred using memory write and invalidate PCI transactions.
d1005 1
a1005 1
		adc	SCB_RESIDUAL_SGPTR[2], -1;
d1144 1
a1144 1
		 * data fifo is full, but the PCI input latch is not empty,
d1160 1
a1160 1
		 * Advance the scatter-gather pointers if needed
d1415 1
a1415 1
		bmov	DFDAT, SCB_CDB_STORE, 12;
d1430 1
a1430 1
		bmov	DFDAT, SCB_CDB_STORE, 12;
d1486 1
a1486 1
 *
d1609 1
a1609 1
 * process this information.  In the case of a non zero status byte, we
d1613 1
a1613 1
 * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting
d1634 1
a1634 1
	test	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted;
d2038 1
a2038 1

d2112 1
a2112 1
phase_lock:
@


1.15.12.1
log
@sync with head.  xen and whitespace.  xen part is not finished.
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.15 2003/05/03 18:11:31 wiz Exp $"
d59 1
a59 1
 * create yet another SCB waiting for selection.  The solution used here is to
d61 1
a61 1
 * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB indexes,
d104 1
a104 1
	 * We have at least one queued SCB now and we don't have any
d282 1
a282 1

d399 1
a399 1
	}
d411 1
a411 1
 * We carefully toggle SPIOEN to allow us to return the
d479 1
a479 1

d497 1
a497 1
		test	SEQ_FLAGS, NO_DISCONNECT jz target_disconnect;
d518 1
a518 1

d556 1
a556 1
tqinfifo_has_space:
d618 1
a618 1

d686 1
a686 1

d793 1
a793 1
 * be transferred using memory write and invalidate PCI transactions.
d1005 1
a1005 1
		adc	SCB_RESIDUAL_SGPTR[2], -1;
d1144 1
a1144 1
		 * data fifo is full, but the PCI input latch is not empty,
d1160 1
a1160 1
		 * Advance the scatter-gather pointers if needed
d1415 1
a1415 1
		bmov	DFDAT, SCB_CDB_STORE, 12;
d1430 1
a1430 1
		bmov	DFDAT, SCB_CDB_STORE, 12;
d1486 1
a1486 1
 *
d1609 1
a1609 1
 * process this information.  In the case of a non zero status byte, we
d1613 1
a1613 1
 * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting
d1634 1
a1634 1
	test	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted;
d2038 1
a2038 1

d2112 1
a2112 1
phase_lock:
@


1.15.2.1
log
@Sync with HEAD.

Hi Perry!
@
text
@d43 1
a43 1
VERSION = "$NetBSD: aic7xxx.seq,v 1.16 2005/02/27 00:27:23 perry Exp $"
d59 1
a59 1
 * create yet another SCB waiting for selection.  The solution used here is to
d61 1
a61 1
 * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB indexes,
d104 1
a104 1
	 * We have at least one queued SCB now and we don't have any
d282 1
a282 1

d399 1
a399 1
	}
d411 1
a411 1
 * We carefully toggle SPIOEN to allow us to return the
d479 1
a479 1

d497 1
a497 1
		test	SEQ_FLAGS, NO_DISCONNECT jz target_disconnect;
d518 1
a518 1

d556 1
a556 1
tqinfifo_has_space:
d618 1
a618 1

d686 1
a686 1

d793 1
a793 1
 * be transferred using memory write and invalidate PCI transactions.
d1005 1
a1005 1
		adc	SCB_RESIDUAL_SGPTR[2], -1;
d1144 1
a1144 1
		 * data fifo is full, but the PCI input latch is not empty,
d1160 1
a1160 1
		 * Advance the scatter-gather pointers if needed
d1415 1
a1415 1
		bmov	DFDAT, SCB_CDB_STORE, 12;
d1430 1
a1430 1
		bmov	DFDAT, SCB_CDB_STORE, 12;
d1486 1
a1486 1
 *
d1609 1
a1609 1
 * process this information.  In the case of a non zero status byte, we
d1613 1
a1613 1
 * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting
d1634 1
a1634 1
	test	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted;
d2038 1
a2038 1

d2112 1
a2112 1
phase_lock:
@


1.14
log
@Newer aic sequencer assembler. From FreeBSD, written by Justin Gibbs.
Ported to NetBSD by Pascal Renauld at Network Storage Solutions, Inc.
Some Makefile changes by me.

The 79xx code isn't yet used, driver for that to follow shortly.
@
text
@d43 1
a43 1
VERSION = "$NetBSD$"
d600 1
a600 1
		 * perspective of the initiator.  The dma
d1260 1
a1260 1
	 * the dma code again.  In the case of ULTRA2 adapters,
@


1.13
log
@precede, not preceed.
@
text
@a0 2
/*	$NetBSD: aic7xxx.seq,v 1.12 2001/06/19 13:45:57 wiz Exp $	*/

d4 2
a5 1
 * Copyright (c) 1994-2000 Justin Gibbs.
d14 8
a21 2
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
d24 2
a25 1
 * the GNU Public License ("GPL").
d27 6
a32 5
 * 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
d35 4
a38 4
 * 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.
d40 1
a40 1
 * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.seq,v 1.94 2000/02/09 21:25:00 gibbs Exp $
d43 4
a46 3
/*
 * #ifdef __NetBSD__
 */
a48 8
/*
 * Assembler can't handle ifdef.
 *
 * #else
 * #include <dev/aic7xxx/aic7xxx.reg>
 * #include <cam/scsi/scsi_message.h>
 * #endif
*/
d68 7
a74 12
reset:
	clr	SCSISIGO;		/* De-assert BSY */
	mvi	MSG_OUT, MSG_NOOP;	/* No message to send */
	and	SXFRCTL1, ~BITBUCKET;
	/* Always allow reselection */
	and	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE;
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		/* Ensure that no DMA operations are in progress */
		clr	CCSGCTL;
		clr	CCSCBCTL;
	}

d78 7
a84 2
	if ((ahc->features & AHC_QUEUE_REGS) == 0) {
		mov	A, QINPOS;
d86 1
a87 5
	if ((ahc->features & AHC_QUEUE_REGS) == 0) {
		and	SEQCTL, ~PAUSEDIS;
	}
	test	SSTAT0, SELDO|SELDI	jnz selection;
	test	SCSISEQ, ENSELO	jnz poll_for_work;
a88 6
		/*
		 * 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.
		 */
a89 3
		test	SSTAT0, SELDO|SELDI	jnz selection;
		test	SCSISEQ, ENSELO	jnz poll_for_work;
		xor	SBLKCTL,SELBUSB;	/* Toggle back */
d91 1
a91 1
	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
d94 1
a96 2
		mov	NONE, SNSCB_QOFF;
		inc	QINPOS;
d98 1
a98 1
		or	SEQCTL, PAUSEDIS;
a99 2
		inc	QINPOS;
		and	SEQCTL, ~PAUSEDIS;
d101 1
d103 5
a107 6
/*
 * 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.
 */
d110 1
a110 7
	}

dequeue_scb:
	add	A, -1, QINPOS;
	mvi	QINFIFO_OFFSET call fetch_byte;

	if ((ahc->flags & AHC_PAGESCBS) == 0) {
d112 1
a112 1
		mov	SCBPTR, RETURN_2;
d114 2
d117 3
a119 3
/*
 * DMA the SCB from host ram into the current SCB location.
 */
d121 1
a121 18
	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) {
		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];
	}
	mov	SCB_RESID_SGCNT, SCB_SGCOUNT;

start_scb:
d123 4
a126 2
	 * Place us on the waiting list in case our selection
	 * doesn't win during bus arbitration.
d128 8
d138 7
d147 1
a147 1
	 * Pull the first entry off of the waiting SCB list.
a150 1
	jmp	poll_for_work;
d152 8
a159 30
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:
	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) {
		and	A, TID, SCB_TCL;	/* Get target ID */
		and	SCSIID_ULTRA2, OID;	/* Clear old target */
		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:
	mov	SCSISEQ, SINDEX ret;
d161 8
a168 20
/*
 * 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:
	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) {
		bmov	SCSIRATE, SCB_SCSIRATE, 2 ret;
	} else {
		mov	SCSIRATE, SCB_SCSIRATE ret;
d171 6
a176 1
selection:
a177 1
	mvi	CLRSINT0, CLRSELDI;
d179 2
a180 2
	if ((ahc->flags & AHC_TARGETMODE) != 0) {
		if ((ahc->flags & AHC_INITIATORMODE) != 0) {
d183 1
a190 1
		mvi	CLRSINT1, CLRBUSFREE;
d196 1
a196 1
		or	SEQ_FLAGS, CMDPHASE_PENDING;
d201 1
a201 1
			mvi	TMODE_CMDADDR call set_32byte_addr;
d205 1
a205 1
			mvi	TMODE_CMDADDR call set_32byte_addr;
d210 6
a215 3
		and	SAVED_TCL, SELID_MASK, SELID;
		if ((ahc->features & AHC_CMD_CHAN) != 0) {
			mov	CCSCBRAM, SAVED_TCL;
d217 6
a222 1
			mov	DFDAT, SAVED_TCL;
a223 2

		/* The Target ID we were selected at */
d225 1
a225 7
			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;
			}
d227 1
a227 7
			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;
			}
a229 3
		/* No tag yet */
		mvi	INITIATOR_TAG, SCB_LIST_NULL;

d233 2
a248 1
		/* XXX May need to be more lax here for older initiators... */
d256 1
d272 1
a272 1
		jnc	ident_messages_done;
d274 2
a275 1
		jc	ident_messages_done;
d300 1
a300 12
		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;
a302 10
		/* If ring buffer is full, return busy or queue full */
		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:	
d309 4
a312 2
		or	SEQ_FLAGS, TARG_CMD_PENDING|IDENTIFY_SEEN;
		test	SCSISIGI, ATNI	jnz target_mesgout_pending_msg;
d314 6
a319 7
		
/*
 * 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:
d321 2
a322 1
		 * Drive REQ on the bus by enabling SCSI PIO.
d324 3
a326 2
		or	SXFRCTL0, SPIOEN;
		/* Wait for the byte */
d328 1
a328 4
		/* Prevent our read from triggering another REQ */
		and	SXFRCTL0, ~SPIOEN;
		/* Save latched contents */
		mov	DINDEX, SCSIDATL ret;
d331 1
a331 1
if ((ahc->flags & AHC_INITIATORMODE) != 0) {
d338 8
a345 1
	and	SAVED_TCL, SELID_MASK, SELID;
d348 1
a348 1
		or	SAVED_TCL, SELBUSB;
d350 1
a350 8
	or	SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
	mvi	CLRSINT1,CLRBUSFREE;
	or	SIMODE1, ENBUSFREE;		/*
						 * We aren't expecting a
						 * bus free, so interrupt
						 * the kernel driver if it
						 * happens.
						 */
d354 74
d436 1
a436 2
	and	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE;
	mvi	CLRSINT0, CLRSELDO;
d439 4
a442 2
	mov	SAVED_TCL, SCB_TCL;
	if ((ahc->flags & AHC_TARGETMODE) != 0) {
d445 8
d459 1
a459 1
		mvi	CLRSINT1,CLRBUSFREE;
d464 1
a464 2
		and	A, LID, SCB_TCL;
		or	A, MSG_IDENTIFYFLAG call target_outb;
d472 1
a472 2
		mov	SCB_INITIATOR_TAG call target_outb;
		mov	INITIATOR_TAG, SCB_INITIATOR_TAG;
d480 3
a482 1

d498 2
d503 1
a503 3
		mov	RETURN_1, ALLZEROS;
		call	complete_target_cmd;
		cmp	RETURN_1, CONT_MSG_LOOP jne .;
d505 3
a507 1
		mov	SCB_TAG	 call dma_scb;
d512 1
d514 2
a516 1
target_mesgout_pending_msg:
d529 4
d539 18
d576 1
a576 1
		add	SINDEX, TARG_SCSIRATE, A;
d580 1
a581 1
		or	SXFRCTL0, SPIOEN;
d599 5
a603 2
		 * Data direction flags are from the
		 * perspective of the initiator.
d605 2
a606 7
		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;
d612 1
a612 1
		mov	SCB_TARGET_STATUS call target_outb;
d635 1
a635 3
			mvi	HCNT[0], 28;
			clr	HCNT[1];
			clr	HCNT[2];
d643 1
a643 1
if ((ahc->flags & AHC_INITIATORMODE) != 0) {
d645 1
a645 9
	mvi	SPIOEN call	initialize_channel;

	/*
	 * We aren't expecting a bus free, so interrupt
	 * the kernel driver if it happens.
	 */
	mvi	CLRSINT1,CLRBUSFREE;
	or	SIMODE1, ENBUSFREE;

d652 2
a653 1
	or	SEQ_FLAGS, IDENTIFY_SEEN;
d660 1
d672 1
a672 1
	mvi	INTSTAT,BAD_PHASE;
d678 3
d684 1
a684 1
	mvi	INTSTAT, BAD_PHASE;
d694 1
d705 3
a707 1
		and	SXFRCTL0, ~(FAST20);
d711 40
a750 1
	clr	SEQ_FLAGS ret;
d752 39
d792 3
a794 2
 * 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.
d796 27
a822 17
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.
		 */
		bmov	HADDR, SHADDR, 4;
		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;
	}
	and	DATA_COUNT_ODD, 0x1, SCB_RESID_DCNT[0];
	jmp	data_phase_loop;
d825 3
a834 4
	call	assert;			/*
					 * Ensure entering a data
					 * phase is okay - seen identify, etc.
					 */
d836 2
a837 1
		mvi	CCSGADDR, CCSGADDR_MAX;
d839 1
a839 1
	test	SEQ_FLAGS, DPHASE	jnz data_phase_reinit;
d841 11
a851 1
	/* We have seen a data phase */
d856 4
a859 3
	 * 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.
d861 4
d867 1
d871 5
d877 2
a878 1
	and	DATA_COUNT_ODD, 0x1, SCB_DATACNT[0];
d888 3
a890 6
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		bmov	SG_COUNT, SCB_SGCOUNT, 5;
	} else {
		mvi	DINDEX, SG_COUNT;
		mvi	SCB_SGCOUNT	call bcopy_5;
	}
d892 6
a897 9
data_phase_loop:
/* Guard against overruns */
	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.
 */
d899 2
a900 5
	and	DMAPARAMS, ~(HDMAEN|SDMAEN);
	if ((ahc->features & AHC_ULTRA2) != 0) {
		bmov	HCNT, ALLONES, 3;
	} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
		bmov	STCNT, ALLONES, 3;
d902 1
a902 3
		mvi	STCNT[0], 0xFF;
		mvi	STCNT[1], 0xFF;
		mvi	STCNT[2], 0xFF;
d904 4
a908 2
/* If we are the last SG block, tell the hardware. */
	cmp	SG_COUNT,0x01 jne data_phase_wideodd;
d910 182
a1091 1
		or	SG_CACHEPTR, LAST_SEG;
d1093 4
a1096 3
		if ((ahc->flags & AHC_TARGETMODE) != 0) {
			test	SSTAT0, TARGET jz . + 2;
			test	DMAPARAMS, DIRECTION jz data_phase_wideodd;
d1098 8
d1107 2
a1108 4
	}
data_phase_wideodd:
	if ((ahc->features & AHC_ULTRA2) != 0) {
		mov	SINDEX, ALLONES;
d1110 22
a1131 7
		test	SSTAT0, SDONE jnz .;/* Wait for preload to complete */
data_phase_dma_loop:
		test	SSTAT0,	SDONE jnz data_phase_dma_done;
		test	SSTAT1,PHASEMIS	jz data_phase_dma_loop;	/* ie. underrun */
	} else {
		mov	DMAPARAMS  call dma;
	}
d1133 20
a1152 3
data_phase_dma_done:
/* Go tell the host about any overruns */
	test	SXFRCTL1,BITBUCKET jnz data_phase_overrun;
d1154 31
a1184 19
/* See if we completed this segment */
	test	STCNT[0], 0xff	jnz data_phase_finish;
	test	STCNT[1], 0xff	jnz data_phase_finish;
	test	STCNT[2], 0xff	jnz data_phase_finish;

/*
 * Advance the scatter-gather pointers if needed 
 */
sg_advance:
	dec	SG_COUNT;	/* one less segment to go */

	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.
 */
a1185 1
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
d1187 4
a1190 1
		 * Do we have any prefetch left???
d1192 22
a1213 1
		cmp	CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail;
d1215 1
a1215 19
		/*
		 * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
		 */
		add	A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
		mvi	A, CCSGADDR_MAX;
		jc	. + 2;
		shl	A, 3, SG_COUNT;
		mov	CCHCNT, A;
		bmov	CCHADDR, SG_NEXT, 4;
		mvi	CCSGCTL, CCSGEN|CCSGRESET;
		test	CCSGCTL, CCSGDONE jz .;
		and	CCSGCTL, ~CCSGEN;
		test	CCSGCTL, CCSGEN jnz .;
		mvi	CCSGCTL, CCSGRESET;
prefetched_segs_avail:
		bmov 	HADDR, CCSGRAM, 8;
	} else {
		mvi	DINDEX, HADDR;
		mvi	SG_NEXT	call bcopy_4;
d1217 1
a1217 3
		mvi	HCNT[0],SG_SIZEOF;
		clr	HCNT[1];
		clr	HCNT[2];
d1219 4
a1222 1
		or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
d1224 2
a1225 1
		call	dma_finish;
d1227 12
a1238 10
		/*
		 * 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;
	}
d1240 2
a1241 3
	/* Track odd'ness */
	test	HCNT[0], 0x1 jz . + 2;
	xor	DATA_COUNT_ODD, 0x1;
d1243 1
a1243 2
	if ((ahc->features & AHC_ULTRA2) == 0) {
		/* Load STCNT as well.  It is a mirror of HCNT */
d1249 26
d1277 10
a1286 7
/* Advance the SG pointer */
	clr	A;			/* add sizeof(struct scatter) */
	add	SG_NEXT[0],SG_SIZEOF;
	adc	SG_NEXT[1],A;

	if ((ahc->flags & AHC_TARGETMODE) != 0) {
		test	SSTAT0, TARGET jnz data_phase_loop;
a1287 2
	test	SSTAT1, REQINIT jz .;
	test	SSTAT1,PHASEMIS	jz data_phase_loop;
d1289 9
a1297 4
	/* Ensure the last seg is visable at the shaddow layer */
	if ((ahc->features & AHC_ULTRA2) != 0) {
		mov	DFCNTRL, DMAPARAMS;
		test	SSTAT0, SDONE jnz .;/* Wait for preload to complete */
d1300 25
a1324 11
data_phase_finish:
	if ((ahc->features & AHC_ULTRA2) != 0) {
		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) {
		bmov	SCB_RESID_DCNT, STCNT, 3;
d1326 3
a1328 3
		mov	SCB_RESID_DCNT[0],STCNT[0];
		mov	SCB_RESID_DCNT[1],STCNT[1];
		mov	SCB_RESID_DCNT[2],STCNT[2];
d1330 6
a1335 1
	mov	SCB_RESID_SGCNT, SG_COUNT;
d1338 2
d1343 1
a1343 1
	if ((ahc->flags & AHC_TARGETMODE) != 0) {
d1348 2
a1349 1
		 * initiator before changing phase.
d1353 8
d1362 2
a1363 48
	}
	jmp	ITloop;

data_phase_overrun:
	if ((ahc->features & AHC_ULTRA2) != 0) {
		call	ultra2_dmafinish;
		or	SXFRCTL0, CLRSTCNT|CLRCHN;
	}
/*
 * Turn off BITBUCKET mode and notify the host
 */
	and	SXFRCTL1, ~BITBUCKET;
	mvi	INTSTAT,DATA_OVERRUN;
	jmp	ITloop;

ultra2_dmafinish:
	if ((ahc->features & AHC_ULTRA2) != 0) {
		test	DFCNTRL, DIRECTION jnz ultra2_dmafifoempty;
		and	DFCNTRL, ~SCSIEN;
		test	DFCNTRL, SCSIEN jnz .;
ultra2_dmafifoflush:
		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.
		 */
		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;

ultra2_dmafifoempty:
		/* Don't clobber an inprogress host data transfer */
		test	DFSTATUS, MREQPEND	jnz ultra2_dmafifoempty;

ultra2_dmahalt:
		and     DFCNTRL, ~(SCSIEN|HDMAEN);
		test	DFCNTRL, HDMAEN jnz .;
		ret;
d1366 1
a1366 1
if ((ahc->flags & AHC_INITIATORMODE) != 0) {
d1371 3
a1373 1
	call	assert;
d1375 2
a1376 2
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		mov	HCNT[0], SCB_CMDLEN;
d1378 23
a1400 2
		if ((ahc->features & AHC_ULTRA2) == 0) {
			bmov	STCNT, HCNT, 3;
d1402 25
a1426 11
		add	NONE, -17, SCB_CMDLEN;
		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.
		 */
		bmov	HADDR[0], ALLZEROS, 1;
		if ((ahc->features & AHC_ULTRA2) != 0) {
			mvi	DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION);
d1430 6
a1435 4
		bmov   DFDAT, SCB_CMDSTORE, 16; 
		jmp	cmd_loop;
dma_cmd_data:
		bmov	HADDR, SCB_CMDPTR, 4;
d1437 4
a1440 4
		mvi	DINDEX, HADDR;
		mvi	SCB_CMDPTR	call bcopy_5;
		clr	HCNT[1];
		clr	HCNT[2];
d1442 10
a1451 6

	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);
d1453 1
a1453 1
		mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
d1455 1
a1455 3
cmd_loop:
	test	SSTAT0, SDONE jnz . + 2;
	test    SSTAT1, PHASEMIS jz cmd_loop;
d1457 2
a1458 2
	 * Wait for our ACK to go-away on it's own
	 * instead of being killed by SCSIEN getting cleared.
d1460 2
a1461 1
	test	SCSISIGI, ACKI jnz .;
d1471 4
a1474 3
	call	assert;

	mov	SCB_TARGET_STATUS, SCSIDATL;
d1491 1
a1491 1
 * BDR message in response to a bad reselection would leave us pointed to
d1502 6
a1507 1
	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
a1511 25
	mov	FUNCTION1, SCB_TCL;
	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.
		 */
		test	HS_MAILBOX, 0xF0	jz . + 2;
		mvi	INTSTAT, UPDATE_TMSG_REQ;
		nop;
	}
	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) {
		test	SCB_TCL, 0x80		jz . + 2; /* target > 7 */
		mov	SINDEX, TARGET_MSG_REQUEST[1];
	}
	test	SINDEX, A	jnz host_message_loop;
d1513 3
a1515 4
	and	SINDEX,LID,SCB_TCL;	/* lun */
	and	A,DISCENB,SCB_CONTROL;	/* mask off disconnect privledge */
	or	SINDEX,A;		/* or in disconnect privledge */
	or	SINDEX,MSG_IDENTIFYFLAG;
d1565 1
d1578 1
a1578 1
	mvi	INTSTAT, HOST_MSG_LOOP;
d1583 17
a1603 2

mesgin_complete:
d1605 1
a1605 1
 * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO,
d1612 3
a1614 8
 * 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.
d1616 25
d1642 22
a1663 5
/*
 * First check for residuals
 */
	test	SCB_RESID_SGCNT,0xff	jnz upload_scb;
	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Good Status? */
d1665 1
d1668 2
a1669 4
check_status:
	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Just a residual? */
	mvi	INTSTAT,BAD_STATUS;			/* let driver know */
	nop;
d1671 1
a1671 11
	/* This SCB becomes the next to execute as it will retrieve sense */
	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
	mov	SCB_TAG		call dma_scb;
add_to_waiting_list:
	mov	SCB_NEXT,WAITING_SCBH;
	mov	WAITING_SCBH, SCBPTR;
	/*
	 * Prepare our selection hardware before the busfree so we have a
	 * high probability of winning arbitration.
	 */
	call	start_selection;
a1672 1

a1673 8
	/* If we are untagged, clear our address up in host ram */
	test	SCB_CONTROL, TAG_ENB jnz complete_queue;
	mov	A, SAVED_TCL;
	/* fvdl - let ahc_intr clear this to avoid race conditions */
	/* mvi	UNTAGGEDSCB_OFFSET call post_byte_setup; */
	/* mvi	SCB_LIST_NULL call post_byte; */

complete_queue:
d1694 1
a1694 1
if ((ahc->flags & AHC_INITIATORMODE) != 0) {
d1697 3
a1699 1
 * and await the bus going free.
d1702 11
d1714 7
a1720 1
	call	add_scb_to_disc_list;
d1729 3
d1734 21
a1754 1
	test	SEQ_FLAGS, DPHASE	jz mesgin_done;
d1757 2
a1758 2
	 * The SCB SGPTR becomes the next one we'll download,
	 * and the SCB DATAPTR becomes the current SHADDR.
a1762 1
		bmov	SCB_SGCOUNT, SG_COUNT, 5;
d1764 4
a1767 1
		bmov	SCB_DATACNT, SCB_RESID_DCNT, 3;
a1768 3
		mvi	DINDEX, SCB_SGCOUNT;
		mvi	SG_COUNT call bcopy_5;
	
d1770 3
a1772 2
		mvi	SHADDR		call bcopy_4;
		mvi	SCB_RESID_DCNT	call bcopy_3;
d1774 1
a1774 1
	jmp	mesgin_done;
d1780 2
a1781 1
 * code do the rest.
d1789 1
d1793 14
d1812 22
a1833 2
	if ((ahc->features & AHC_WIDE) != 0) {
		and	A,0x0f;		/* lun in lower four bits */
d1835 11
a1845 1
		and	A,0x07;		/* lun in lower three bits */
d1847 1
a1847 4
	or      SAVED_TCL,A;		/* SAVED_TCL should be complete now */

	mvi	ARG_2, SCB_LIST_NULL;	/* SCBID of prev SCB in disc List */
	call	get_untagged_SCBID;
d1850 23
a1872 1
		test	SEQ_FLAGS, SCBPTR_VALID	jz use_retrieveSCB;
d1874 1
a1874 8
	/*
	 * 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.
	 */
	call	rem_scb_from_disc_list;
	jmp	setup_SCB;
d1878 4
a1881 4
 * 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.
d1884 3
a1887 1
snoop_tag_loop:
d1889 3
d1893 3
d1898 7
a1904 1
	mvi	ARG_1	call inb_next;	/* tag value */
d1906 4
a1909 6
	/*
	 * Ensure that the SCB the tag points to is for
	 * an SCB transaction to the reconnecting target.
	 */
use_retrieveSCB:
	call	retrieveSCB;
d1911 15
a1925 2
	mov	A, SAVED_TCL;
	cmp	SCB_TCL, A	jne not_found_cleanup_scb;
d1928 11
a1938 1
	or	SEQ_FLAGS,IDENTIFY_SEEN;	/* make note of IDENTIFY */
a1941 1
	and	SCB_CONTROL, ~MK_MESSAGE;
d1946 3
a1948 4
	test	SCB_CONTROL, DISCONNECTED jz . + 3;
	call	add_scb_to_disc_list;
	jmp	not_found;
	call	add_scb_to_free_list;
d1950 1
a1950 1
	mvi	INTSTAT, NO_MATCH;
a1952 8
/*
 * [ 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.
 */
d1954 5
a1958 1
	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
d1977 1
a1977 1
	mvi	INTSTAT, PERR_DETECTED;
d1999 1
a1999 1
if ((ahc->flags & AHC_TARGETMODE) != 0) {
d2016 1
a2016 1
	 * we must waitat least a data release delay plus
a2038 1
mesgin_phasemis:
d2040 5
a2044 70
 * We expected to receive another byte, but the target changed phase
 */
	mvi	INTSTAT, MSGIN_PHASEMIS;
	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:
	mov	DFCNTRL,SINDEX;
dma_loop:
	test	SSTAT0,DMADONE	jnz dma_dmadone;
	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:
	test	DFCNTRL,DIRECTION	jnz dma_fifoempty;
dma_fifoflush:
	test	DFSTATUS,FIFOEMP	jz dma_fifoflush;

dma_fifoempty:
	/* Don't clobber an inprogress host data transfer */
	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:
	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;
	}
	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
return:
	ret;

/*
 * Assert that if we've been reselected, then we've seen an IDENTIFY
 * message.
 */
assert:
	test	SEQ_FLAGS,IDENTIFY_SEEN	jnz return;	/* seen IDENTIFY? */

	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.
d2046 2
d2049 5
a2053 7
	mov	SCBPTR,SINDEX;			/* Initialize SCBPTR */
	cmp	ARG_1, SCB_LIST_NULL	jne findSCB_by_SCBID;
	mov	A, SAVED_TCL;
	mvi	SCB_TCL	jmp findSCB_loop;	/* &SCB_TCL -> SINDEX */
findSCB_by_SCBID:
	mov	A, ARG_1;			/* Tag passed in ARG_1 */
	mvi	SCB_TAG	jmp findSCB_loop;	/* &SCB_TAG -> SINDEX */
d2055 1
a2056 1
	cmp	SCB_NEXT, SCB_LIST_NULL je notFound;
a2057 1
	dec	SINDEX;		/* Last comparison moved us too far */
d2059 1
a2059 23
	cmp	SINDIR, A	jne findSCB_next;
	mov	SINDEX, SCBPTR 	ret;
notFound:
	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:
	test	SEQ_FLAGS, SCBPTR_VALID	jz retrieve_from_host;
	mov	SCBPTR	call findSCB;	/* Continue the search */
	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.
 */
a2060 1
/* Remove this SCB from the disconnection list */
d2063 1
d2069 7
a2075 7

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.
 */
	mov	ALLZEROS	call	get_free_or_disc_scb;
a2076 1
	/* Jump instead of call as we want to return anyway */
d2078 1
a2078 44

/*
 * 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:
	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host;
	mvi	ARG_1, SCB_LIST_NULL;
	mov	DISCONNECTED_SCBH call findSCB;
	cmp	SINDEX, SCB_LIST_NULL	je get_SCBID_from_host;
	or	SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */
	test	SCB_CONTROL, TAG_ENB	jnz . + 2;
	mov	ARG_1, SCB_TAG	ret;
	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:
	mov	ARG_2, SINDEX;
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		mvi	DINDEX, CCHADDR;
		mvi	SCBID_ADDR call set_1byte_addr;
		mvi	CCHCNT, 1;
		mvi	CCSGCTL, CCSGEN|CCSGRESET;
		test	CCSGCTL, CCSGDONE jz .;
		mvi	CCSGCTL, CCSGRESET;
		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;
	}
d2082 1
a2082 1
 * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR.
d2088 1
a2088 1
		mvi	SCBID_ADDR call	set_1byte_addr;
d2093 2
a2094 4
		mvi	SCBID_ADDR call	set_1byte_addr;
		mvi	HCNT[0], 1;
		clr	HCNT[1];
		clr	HCNT[2];
a2109 5
get_SCBID_from_host:
	mov	A, SAVED_TCL;
	mvi	UNTAGGEDSCB_OFFSET call fetch_byte;
	mov	RETURN_1,  RETURN_2 ret;

d2111 1
a2111 1
	mvi	INTSTAT, PERR_DETECTED;
d2121 3
a2123 1
	and	SCSISIGO, PHASE_MASK, SCSISIGI;
d2127 6
d2138 2
d2153 1
a2153 1
if ((ahc->flags & AHC_TARGETMODE) != 0) {
d2179 1
a2179 1
 * Setup addr assuming that A + (ARG_1 * 256) is an
d2194 1
a2194 1
 * Either post or fetch and SCB from host memory based on the
d2204 5
a2208 1
		mvi	CCHCNT, SCB_64BYTE_SIZE;
d2213 2
a2214 2
		mvi	CCHCNT, SCB_32BYTE_SIZE;
		if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
d2216 1
a2216 1
			bmov	CCSCBRAM, SCB_CONTROL, SCB_32BYTE_SIZE;
d2219 6
d2236 1
a2236 3
		mvi	HCNT[0], SCB_32BYTE_SIZE;
		clr	HCNT[1];
		clr	HCNT[2];
d2241 2
a2242 2
		mvi	SINDEX, SCB_CONTROL;
		add	A, SCB_32BYTE_SIZE, SINDEX;
d2244 1
a2244 7
		mov	DFDAT,SINDIR;
		mov	DFDAT,SINDIR;
		mov	DFDAT,SINDIR;
		mov	DFDAT,SINDIR;
		mov	DFDAT,SINDIR;
		mov	DFDAT,SINDIR;
		mov	DFDAT,SINDIR;
d2247 1
d2249 54
a2302 7
		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;
a2303 2
		mov     DINDEX,SINDEX;
dfdat_in_7_continued:
d2309 1
d2314 12
d2333 1
d2339 10
d2351 1
d2355 1
d2360 7
d2369 1
d2373 1
d2377 2
a2378 1
dma_up_scb:
d2380 2
a2381 3
	mov	SCB_TAG		call dma_scb;
unlink_disc_scb:
	mov	DISCONNECTED_SCBH, SCB_NEXT ret;
d2385 1
a2385 1
}
d2393 1
d2396 7
@


1.12
log
@`response', not `responce'
@
text
@d1 1
a1 1
/*	$NetBSD: aic7xxx.seq,v 1.11 2000/03/15 02:09:11 fvdl Exp $	*/
d517 1
a517 1
		/* Wait for preceeding I/O session to complete. */
@


1.12.2.1
log
@Merge Aug 24 -current into the kqueue branch.
@
text
@d1 1
a1 1
/*	$NetBSD: aic7xxx.seq,v 1.13 2001/08/20 12:00:52 wiz Exp $	*/
d517 1
a517 1
		/* Wait for preceding I/O session to complete. */
@


1.11
log
@New ahc driver, a port of Justin Gibbs' FreeBSD driver. This adds
support for the U2W chips, and U160 controllers.
@
text
@d1 1
a1 1
/*	$NetBSD$	*/
d1064 1
a1064 1
 * BDR message in responce to a bad reselection would leave us pointed to
@


1.11.6.1
log
@Catch up to -current.
@
text
@d1 1
a1 1
/*	$NetBSD: aic7xxx.seq,v 1.11 2000/03/15 02:09:11 fvdl Exp $	*/
d1064 1
a1064 1
 * BDR message in response to a bad reselection would leave us pointed to
@


1.11.6.2
log
@Catch up with -current.
@
text
@d1 1
a1 1
/*	$NetBSD: aic7xxx.seq,v 1.11.6.1 2001/06/21 20:04:10 nathanw Exp $	*/
d517 1
a517 1
		/* Wait for preceding I/O session to complete. */
@


1.10
log
@port-i386/5706: I had no problem with the previous patch, but someone
else has. Rolling it back while we try and figure it out.
@
text
@d1 1
a1 1
/*	$NetBSD: aic7xxx.seq,v 1.9 1998/07/03 23:45:10 mjacob Exp $	*/
d3 2
a4 2
/*+M***********************************************************************
 *Adaptec 274x/284x/294x device driver for Linux and FreeBSD.
d6 2
a7 3
 *Copyright (c) 1994 John Aycock
 *  The University of Calgary Department of Computer Science.
 *  All rights reserved.
d9 8
a16 3
 *FreeBSD, Twin, Wide, 2 command per target support, tagged queuing,
 *SCB paging and other optimizations:
 *Copyright (c) 1994, 1995, 1996 Justin Gibbs. All rights reserved.
d18 2
a19 15
 *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.
 *2. Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *3. All advertising materials mentioning features or use of this software
 *   must display the following acknowledgement:
 *     This product includes software developed by the University of Calgary
 *     Department of Computer Science and its contributors.
 *4. Neither the name of the University nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
d21 11
a31 11
 *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.
d33 2
a34 13
 * from	Id: aic7xxx.seq,v 1.42 1996/06/09 17:29:11 gibbs Exp
 *
 *-M************************************************************************/

VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.8 1998/04/16 07:07:37 leo Exp $"

#if defined(__NetBSD__)
#include "../../ic/aic7xxxreg.h"
#include "../../scsipi/scsi_message.h"
#elif defined(__FreeBSD__)
#include "../../dev/aic7xxx/aic7xxx_reg.h"
#include "../../scsi/scsi_message.h"
#endif
d37 1
a37 4
 * We can't just use ACCUM in the sequencer code because it
 * must be treated specially by the assembler, and it currently
 * looks for the symbol 'A'.  This is the only register defined in
 * the assembler's symbol space.
d39 10
a48 1
A = ACCUM
d50 3
a52 1
/* After starting the selection hardware, we check for reconnecting targets
d61 5
a65 4
 * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB offsets, 
 * SCB_LIST_NULL is 0xff which is out of range.  The kernel driver must
 * add an entry to this list everytime a request sense occurs.  The sequencer
 * will automatically consume the entries.
a67 4
/*
 * We assume that the kernel driver may reset us at any time, even in the
 * middle of a DMA, so clear DFCNTRL too.
 */
d69 11
a79 13
	clr	DFCNTRL
	clr	SCSISIGO		/* De-assert BSY */
/*
 * We jump to start after every bus free.
 */
start:
	and	FLAGS,0x0f		/* clear target specific flags */
	mvi	SCSISEQ,ENRSELI		/* Always allow reselection */
	clr	SCSIRATE		/*
					 * We don't know the target we will
					 * connect to, so default to narrow
					 * transfers to avoid parity problems.
					 */
d81 36
a116 15
	/*
	 * Are we a twin channel device?
	 * For fairness, we check the other bus first,
	 * since we just finished a transaction on the
	 * current channel.
	 */
	test	FLAGS,TWIN_BUS	jz start2
	xor	SBLKCTL,SELBUSB			/* Toggle to the other bus */
	test	SSTAT0,SELDI	jnz reselect
	xor	SBLKCTL,SELBUSB			/* Toggle to the original bus */
start2:
	test	SSTAT0,SELDI	jnz reselect
	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting
	mov	A, QCNTMASK
	test	QINCNT,A	jz poll_for_work
d120 37
a156 51
 * SCBs in the list of SCBs awaiting selection.  Set the SCB
 * pointer from the FIFO so we see the right bank of SCB 
 * registers.
 */
	mov	SCBPTR,QINFIFO

/*
 * See if there is not already an active SCB for this target.  This code
 * locks out on a per target basis instead of target/lun.  Although this
 * is not ideal for devices that have multiple luns active at the same
 * time, it is faster than looping through all SCB's looking for active
 * commands.  It may be benificial to make findscb a more general procedure
 * to see if the added cost of the search is negligible.  This code also 
 * assumes that the kernel driver will clear the active flags on board 
 * initialization, board reset, and a target SELTO.  Tagged commands
 * don't set the active bits since you can queue more than one command
 * at a time.  We do, however, look to see if there are any non-tagged
 * I/Os in progress, and requeue the command if there are.  Tagged and
 * non-tagged commands cannot be mixed to a single target.
 */

test_busy:
	mov	FUNCTION1,SCB_TCL
	mov	A,FUNCTION1
	test	SCB_TCL,0x88	jz test_a	/* Id < 8 && A channel */

	test	ACTIVE_B,A	jnz requeue
	test	SCB_CONTROL,TAG_ENB	jnz start_scb
	/* Mark the current target as busy */
	or	ACTIVE_B,A
	jmp	start_scb

/* Place the currently active SCB back on the queue for later processing */
requeue:
	mov	QINFIFO, SCBPTR
	jmp	poll_for_work

/*
 * Pull the first entry off of the waiting for selection list
 * We don't have to "test_busy" because only transactions that
 * have passed that test can be in the waiting_scb list.
 */
start_waiting:
	mov	SCBPTR,WAITING_SCBH
	jmp	start_scb2

test_a:
	test	ACTIVE_A,A jnz requeue
	test	SCB_CONTROL,TAG_ENB jnz start_scb
	/* Mark the current target as busy */
	or	ACTIVE_A,A
d159 13
a171 8
	mov	SCB_NEXT,WAITING_SCBH
	mov	WAITING_SCBH, SCBPTR
start_scb2:
	and	SINDEX,0xf7,SBLKCTL	/* Clear the channel select bit */
	and	A,0x08,SCB_TCL		/* Get new channel bit */
	or	SINDEX,A
	mov	SBLKCTL,SINDEX		/* select channel */
	mov	SCB_TCL	call initialize_scsiid
a172 6
/*
 * Enable selection phase as an initiator, and do automatic ATN
 * after the selection.  We do this now so that we can overlap the
 * rest of our work to set up this target with the arbitration and
 * selection bus phases.
 */
d174 43
a216 2
	mvi	SCSISEQ,0x58		/* ENSELO|ENAUTOATNO|ENRSELI */

d218 1
a218 6
 * As soon as we get a successful selection, the target should go
 * into the message out phase since we have ATN asserted.  Prepare
 * the message to send.
 *
 * Messages are stored in scratch RAM starting with a length byte
 * followed by the message itself.
d220 188
d409 1
a409 33
mk_identify:
	and	A,DISCENB,SCB_CONTROL	/* mask off disconnect privledge */

	and	MSG0,0x7,SCB_TCL	/* lun */
	or	MSG0,A			/* or in disconnect privledge */
	or	MSG0,MSG_IDENTIFYFLAG
	mvi	MSG_LEN, 1

/*
 * 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.
 */
mk_tag:
	test	SCB_CONTROL,TAG_ENB jz  mk_message
	mvi	DINDEX, MSG1
	and	DINDIR,0x23,SCB_CONTROL
	mov	DINDIR,SCB_TAG

	add	MSG_LEN,COMP_MSG0,DINDEX	/* update message length */

/*
 * Interrupt the driver, and allow it to tweak the message buffer
 * if it asks.
 */
mk_message:
	test	SCB_CONTROL,MK_MESSAGE  jz wait_for_selection

	mvi     INTSTAT,AWAITING_MSG

wait_for_selection:
	test	SSTAT0,SELDO	jnz select 
	test	SSTAT0,SELDI	jz wait_for_selection

d412 1
a412 2
 * reselected, but haven't seen an IDENTIFY message from the target
 * yet.
d414 17
a430 5
reselect:
	clr	MSG_LEN		/* Don't have anything in the mesg buffer */
	mov	SELID		call initialize_scsiid
	or	FLAGS,RESELECTED
	jmp	select2
d433 1
a433 1
 * After the selection, remove this SCB from the "waiting for selection"
d438 196
a633 15
select:
	mov	WAITING_SCBH,SCB_NEXT
	or	FLAGS,SELECTED
select2:
/*
 * Set CLRCHN here before the target has entered a data transfer mode -
 * with synchronous SCSI, if you do it later, you blow away some
 * data in the SCSI FIFO that the target has already sent to you.
 */
	or	SXFRCTL0,CLRCHN
/*
 * Initialize SCSIRATE with the appropriate value for this target.
 */
	call	ndx_dtr
	mov	SCSIRATE,SINDIR
d635 5
a639 30
/*
 * Initialize Ultra mode setting.
 */
	mov	FUNCTION1,SCSIID
	mov	A,FUNCTION1
	and	SINDEX,0xdf,SXFRCTL0		/* default to Ultra disabled */
	test	SCSIID, 0x80	 jnz ultra_b	/* Target ID > 7 */
	test	SBLKCTL, SELBUSB jnz ultra_b	/* Second channel device */
	test	ULTRA_ENB,A	 jz  set_sxfrctl0
	or	SINDEX, ULTRAEN  jmp set_sxfrctl0
ultra_b:
	test	ULTRA_ENB_B,A	 jz  set_sxfrctl0
	or	SINDEX, ULTRAEN

set_sxfrctl0:
	mov	SXFRCTL0,SINDEX

	mvi	SCSISEQ,ENAUTOATNP		/*
						 * ATN on parity errors
						 * for "in" phases
						 */
	mvi	CLRSINT1,CLRBUSFREE
	mvi	CLRSINT0,0x60			/* CLRSELDI|CLRSELDO */
/*
 * Main loop for information transfer phases.  If BSY is false, then
 * we have a bus free condition, expected or not.  Otherwise, wait
 * for the target to assert REQ before checking MSG, C/D and I/O
 * for the bus phase.
 *
 */
d641 29
a669 2
	test	SSTAT1,BUSFREE	jnz p_busfree
	test	SSTAT1,REQINIT	jz ITloop
d671 14
a684 20
	and	A,PHASE_MASK,SCSISIGI
	mov	LASTPHASE,A
	mov	SCSISIGO,A

	cmp	ALLZEROS,A	je p_dataout
	cmp	A,P_DATAIN	je p_datain
	cmp	A,P_COMMAND	je p_command
	cmp	A,P_MESGOUT	je p_mesgout
	cmp	A,P_STATUS	je p_status
	cmp	A,P_MESGIN	je p_mesgin

	mvi	INTSTAT,BAD_PHASE	/* unknown phase - signal driver */
	jmp	ITloop			/* Try reading the bus again. */

p_dataout:
	mvi	DMAPARAMS,0x7d			/*
						 * WIDEODD|SCSIEN|SDMAEN|HDMAEN|
						 * DIRECTION|FIFORESET
						 */
	jmp	data_phase_init
d691 28
a718 9
	mov	STCNT0,SCB_RESID_DCNT0
	mov	STCNT1,SCB_RESID_DCNT1
	mov	STCNT2,SCB_RESID_DCNT2
	jmp	data_phase_loop

p_datain:
	mvi	DMAPARAMS,0x79		/*
					 * WIDEODD|SCSIEN|SDMAEN|HDMAEN|
					 * !DIRECTION|FIFORESET
d720 4
a723 2
data_phase_init:
	call	assert
d725 31
a755 3
	test	FLAGS, DPHASE	jnz data_phase_reinit
	call	sg_scb2ram
	or	FLAGS, DPHASE		/* We have seen a data phase */
d759 1
a759 1
	test	SG_COUNT, 0xff jnz data_phase_inbounds
d766 11
a776 5
	or	SXFRCTL1,BITBUCKET
	mvi	STCNT0,0xff
	mvi	STCNT1,0xff
	mvi	STCNT2,0xff

d778 11
a788 3
/* If we are the last SG block, don't set wideodd. */
	cmp	SG_COUNT,0x01 jne data_phase_wideodd
	and	DMAPARAMS, 0xbf		/* Turn off WIDEODD */
d790 10
a799 1
	mov	DMAPARAMS  call dma
d801 1
d803 1
a803 1
	test	SXFRCTL1,BITBUCKET jnz data_phase_overrun
d805 4
a808 2
/* Exit if we had an underrun */
	test	SSTAT0,SDONE	jz data_phase_finish /* underrun STCNT != 0 */
d814 1
a814 7
	dec	SG_COUNT	/* one less segment to go */

	test	SG_COUNT, 0xff	jz data_phase_finish /* Are we done? */

	clr	A			/* add sizeof(struct scatter) */
	add	SG_NEXT0,SG_SIZEOF,SG_NEXT0
	adc	SG_NEXT1,A,SG_NEXT1
d816 1
d825 74
a898 65
	clr	HCNT2
	clr	HCNT1
	mvi	HCNT0,SG_SIZEOF

	mov	HADDR0,SG_NEXT0
	mov	HADDR1,SG_NEXT1
	mov	HADDR2,SG_NEXT2
	mov	HADDR3,SG_NEXT3

	or	DFCNTRL,0xd			/* HDMAEN|DIRECTION|FIFORESET */

/*
 * 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:
	test	DFSTATUS,HDONE	jz dma_finish
	/* Turn off DMA preserving WIDEODD */
	and	DFCNTRL,WIDEODD
dma_finish2:
	test	DFCNTRL,HDMAENACK jnz dma_finish2

/*
 * Copy data from FIFO into SCB data pointer and data count.  This assumes
 * that the struct scatterlist has this structure (this and sizeof(struct
 * scatterlist) == 12 are asserted in aic7xxx.c for the Linux driver):
 *
 *	struct scatterlist {
 *		char *address;		four bytes, little-endian order
 *		...			four bytes, ignored
 *		unsigned short length;	two bytes, little-endian order
 *	}
 *
 *
 * In FreeBSD, the scatter list entry is only 8 bytes.
 * 
 * struct ahc_dma_seg {
 *       physaddr addr;                  four bytes, little-endian order
 *       long    len;                    four bytes, little endian order
 * };
 */

	mov	HADDR0,DFDAT
	mov	HADDR1,DFDAT
	mov	HADDR2,DFDAT
	mov	HADDR3,DFDAT
/*
 * For Linux, we must throw away four bytes since there is a 32bit gap
 * in the middle of a struct scatterlist.
 */
#ifdef __linux__
	mov	NONE,DFDAT
	mov	NONE,DFDAT
	mov	NONE,DFDAT
	mov	NONE,DFDAT
#endif
	mov	HCNT0,DFDAT
	mov	HCNT1,DFDAT
	mov	HCNT2,DFDAT

/* Load STCNT as well.  It is a mirror of HCNT */
	mov	STCNT0,HCNT0
	mov	STCNT1,HCNT1
	mov	STCNT2,HCNT2
        test    SSTAT1,PHASEMIS  jz data_phase_loop
d901 3
d909 25
a933 5
	mov	SCB_RESID_DCNT0,STCNT0
	mov	SCB_RESID_DCNT1,STCNT1
	mov	SCB_RESID_DCNT2,STCNT2
	mov	SCB_RESID_SGCNT, SG_COUNT
	jmp	ITloop
d936 4
d943 38
a980 3
	and	SXFRCTL1,0x7f		/* ~BITBUCKET */
	mvi	INTSTAT,DATA_OVERRUN
	jmp	ITloop
d982 1
d987 1
a987 1
	call	assert
d989 50
a1038 18
/*
 * Load HADDR and HCNT.
 */
	mov	HADDR0, SCB_CMDPTR0
	mov	HADDR1, SCB_CMDPTR1
	mov	HADDR2, SCB_CMDPTR2
	mov	HADDR3, SCB_CMDPTR3
	mov	HCNT0, SCB_CMDLEN
	clr	HCNT1
	clr	HCNT2

	mov	STCNT0, HCNT0
	mov	STCNT1, HCNT1
	mov	STCNT2, HCNT2

	mvi	0x3d		call dma	# SCSIEN|SDMAEN|HDMAEN|
						#   DIRECTION|FIFORESET
	jmp	ITloop
d1045 4
a1048 2
	mvi	SCB_TARGET_STATUS	call inb_first
	jmp	mesgin_done
d1051 22
a1072 2
 * Message out phase.  If there is not an active message, but the target
 * took us into this phase anyway, build a no-op message and send it.
d1074 2
d1077 33
a1109 4
	test	MSG_LEN, 0xff	jnz  p_mesgout_start
	mvi	MSG_NOOP	call mk_mesg	/* build NOP message */

p_mesgout_start:
d1111 2
a1112 12
 * Set up automatic PIO transfer from MSG0.  Bit 3 in
 * SXFRCTL0 (SPIOEN) is already on.
 */
	mvi	SINDEX,MSG0
	mov	DINDEX,MSG_LEN

/*
 * When target asks for a byte, drop ATN if it's the last one in
 * the message.  Otherwise, keep going until the message is exhausted.
 *
 * Keep an eye out for a phase change, in case the target issues
 * a MESSAGE REJECT.
d1114 20
a1133 13
p_mesgout_loop:
	test	SSTAT1,PHASEMIS	jnz p_mesgout_phasemis
	test	SSTAT0,SPIORDY	jz p_mesgout_loop
	test	SSTAT1,PHASEMIS	jnz p_mesgout_phasemis
	cmp	DINDEX,1	jne p_mesgout_outb	/* last byte? */
	mvi	CLRSINT1,CLRATNO			/* drop ATN */
p_mesgout_outb:
	dec	DINDEX
	or	CLRSINT0, CLRSPIORDY
	mov	SCSIDATL,SINDIR
	
p_mesgout4:
	test	DINDEX,0xff	jnz p_mesgout_loop
d1136 1
a1136 1
 * If the next bus phase after ATN drops is a message out, it means
d1139 2
a1140 3
p_mesgout_snoop:
	test	SSTAT1,BUSFREE	jnz p_mesgout_done
	test	SSTAT1,REQINIT	jz p_mesgout_snoop
a1141 8
	test	SSTAT1,PHASEMIS	jnz p_mesgout_done

	or	SCSISIGO,ATNO			/* turn on ATNO */

	jmp	ITloop

p_mesgout_phasemis:
	mvi	CLRSINT1,CLRATNO	/* Be sure to turn ATNO off */
d1143 4
a1146 2
	clr	MSG_LEN			/* no active msg */
	jmp	ITloop
d1152 1
a1152 2
	mvi	A		call inb_first	/* read the 1st message byte */
	mov	REJBYTE,A			/* save it for the driver */
d1154 21
a1174 21
	test	A,MSG_IDENTIFYFLAG	jnz mesgin_identify
	cmp	A,MSG_DISCONNECT	je mesgin_disconnect
	cmp	A,MSG_SAVEDATAPOINTER	je mesgin_sdptrs
	cmp	ALLZEROS,A		je mesgin_complete
	cmp	A,MSG_RESTOREPOINTERS	je mesgin_rdptrs
	cmp	A,MSG_EXTENDED		je mesgin_extended
	cmp	A,MSG_MESSAGE_REJECT	je mesgin_reject

rej_mesgin:
/*
 * We have no idea what this message in is, and there's no way
 * to pass it up to the kernel, so we issue a message reject and
 * hope for the best.  Since we're now using manual PIO mode to
 * read in the message, there should no longer be a race condition
 * present when we assert ATN.  In any case, rejection should be a
 * rare occurrence - signal the driver when it happens.
 */
	or	SCSISIGO,ATNO			/* turn on ATNO */
	mvi	INTSTAT,SEND_REJECT		/* let driver know */

	mvi	MSG_MESSAGE_REJECT	call mk_mesg
d1177 2
a1178 2
	call	inb_last			/*ack & turn auto PIO back on*/
	jmp	ITloop
d1183 15
a1197 16
 * We got a "command complete" message, so put the SCB_TAG into QUEUEOUT,
 * and trigger a completion interrupt.  Check status for non zero return
 * and interrupt driver if needed.  This allows the driver to interpret
 * errors only when they occur instead of always uploading the scb.  If
 * the status is SCSI_CHECK, the driver will download a new scb requesting
 * sense to replace the old one, modify the "waiting for selection" SCB list
 * and set RETURN_1 to SEND_SENSE.  If RETURN_1 is set to SEND_SENSE the
 * sequencer imediately jumps to main loop where it will run down the waiting
 * SCB list and process the sense request.  If the kernel driver does not
 * wish to request sense, it need only clear RETURN_1, and the command is
 * allowed to complete.  We don't bother to post to the QOUTFIFO in the
 * error case 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
d1199 1
a1199 1
	test	SCB_RESID_SGCNT,0xff	jz check_status
d1201 1
a1201 6
 * If we have a residual count, interrupt and tell the host.  Other
 * alternatives are to pause the sequencer on all command completes (yuck),
 * dma the resid directly to the host (slick, we may have space to do it now)
 * or have the sequencer pause itself when it encounters a non-zero resid 
 * (unecessary pause just to flag the command -yuck-, but takes one instruction
 * and since it shouldn't happen that often is good enough for our purposes).  
d1203 22
a1224 2
resid:
	mvi	INTSTAT,RESIDUAL
a1225 31
check_status:
	test	SCB_TARGET_STATUS,0xff	jz status_ok	/* Good Status? */
	mvi	INTSTAT,BAD_STATUS			/* let driver know */
	cmp	RETURN_1, SEND_SENSE	jne status_ok
	jmp	mesgin_done

status_ok:
/* First, mark this target as free. */
	test	SCB_CONTROL,TAG_ENB jnz test_immediate	/*
							 * Tagged commands
							 * don't busy the
							 * target.
							 */
	mov	FUNCTION1,SCB_TCL
	mov	A,FUNCTION1
	test	SCB_TCL,0x88 jz clear_a
	xor	ACTIVE_B,A
	jmp	test_immediate

clear_a:
	xor	ACTIVE_A,A

test_immediate:
	test    SCB_CMDLEN,0xff jnz complete  /* Immediate message complete */
/*
 * Pause the sequencer until the driver gets around to handling the command
 * complete.  This is so that any action that might require carefull timing
 * with the completion of this command can occur.
 */
	mvi	INTSTAT,IMMEDDONE
	jmp	start
d1227 27
a1253 40
	mov	QOUTFIFO,SCB_TAG
	mvi	INTSTAT,CMDCMPLT
	jmp	mesgin_done


/*
 * Is it an extended message?  Copy the message to our message buffer and
 * notify the host.  The host will tell us whether to reject this message,
 * respond to it with the message that the host placed in our message buffer,
 * or simply to do nothing.
 */
mesgin_extended:
	mvi	MSGIN_EXT_LEN	 call inb_next
	mvi	MSGIN_EXT_OPCODE call inb_next
	mov	A, MSGIN_EXT_LEN
	dec	A	/* Length counts the op code */
	mvi	SINDEX, MSGIN_EXT_BYTE0
mesgin_extended_loop:
	test	A, 0xFF		jz mesgin_extended_intr
	cmp	SINDEX, MSGIN_EXT_LASTBYTE je mesgin_extended_dump
	call	inb_next
	dec	A
/*
 * We pass the arg to inb in SINDEX, but DINDEX is the one incremented
 * so update SINDEX with DINDEX's value before looping again.
 */
	mov	DINDEX		jmp mesgin_extended_loop
mesgin_extended_dump:
/* We have no more storage space, so dump the rest */
	test	A, 0xFF		jz mesgin_extended_intr
	mvi	NONE		call inb_next
	dec	A
	jmp	mesgin_extended_dump
mesgin_extended_intr:
	mvi	INTSTAT,EXTENDED_MSG		/* let driver know */
	cmp	RETURN_1,SEND_REJ je rej_mesgin
	cmp	RETURN_1,SEND_MSG jne mesgin_done
/* The kernel has setup a message to be sent */
	or	SCSISIGO,ATNO			/* turn on ATNO */
	jmp	mesgin_done
d1255 1
d1261 3
a1263 18
	or	SCB_CONTROL,DISCONNECTED
	test	FLAGS, PAGESCBS jz mesgin_done
/*
 * 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.
 */
	mvi	SCB_PREV, SCB_LIST_NULL
	mvi	SEQCTL,0x50			/* PAUSEDIS|FASTMODE */
	mov	SCB_NEXT, DISCONNECTED_SCBH
	mov	DISCONNECTED_SCBH, SCBPTR
	cmp	SCB_NEXT,SCB_LIST_NULL je linkdone
	mov	SCBPTR,SCB_NEXT
	mov	SCB_PREV,DISCONNECTED_SCBH
	mov	SCBPTR,DISCONNECTED_SCBH
linkdone:
	mvi	SEQCTL,0x10			/* !PAUSEDIS|FASTMODE */
	jmp	mesgin_done
d1266 5
a1270 2
 * Save data pointers message?  Copy working values into the SCB,
 * usually in preparation for a disconnect.
d1273 21
a1293 2
	call	sg_ram2scb
	jmp	mesgin_done
d1302 2
a1303 2
	and	FLAGS,0xef			/*
						 * !DPHASE we'll reload them
d1305 1
d1307 1
a1307 1
	jmp	mesgin_done
d1315 21
a1335 9
	test	A,0x78	jnz rej_mesgin	/*!DiscPriv|!LUNTAR|!Reserved*/

	and	A,0x07			/* lun in lower three bits */
	or      SAVED_TCL,A,SELID          
	and     SAVED_TCL,0xf7
	and     A,SELBUSB,SBLKCTL	/* B Channel?? */
	or      SAVED_TCL,A
	call	inb_last		/* ACK */

d1338 2
a1339 2
 * If we get one, we use the tag returned to switch to find the proper
 * SCB.  With SCB paging, this requires using findSCB for both tagged
d1344 2
a1345 1
	mvi	ARG_1,SCB_LIST_NULL	/* Default to no-tag */
d1347 3
a1349 5
	test	SSTAT1,BUSFREE		jnz use_findSCB
	test	SSTAT1,REQINIT		jz snoop_tag_loop
	test	SSTAT1,PHASEMIS		jnz use_findSCB
	mvi	A			call inb_first
	cmp	A,MSG_SIMPLE_Q_TAG	jne use_findSCB
d1351 1
a1351 9
	mvi	ARG_1	call inb_next	/* tag value */
/*
 * See if the tag is in range.  The tag is < SCBCOUNT if we add
 * the complement of SCBCOUNT to the incomming tag and there is
 * no carry.
 */
	mov	A,COMP_SCBCOUNT	
	add	SINDEX,A,ARG_1
	jc	abort_tag
d1353 6
a1358 8
/*
 * Ensure that the SCB the tag points to is for an SCB transaction
 * to the reconnecting target.
 */
	test	FLAGS, PAGESCBS	jz index_by_tag
	call	inb_last			/* Ack Tag */
use_findSCB:
	mov	ALLZEROS	call findSCB	  /* Have to search */
d1360 20
a1379 26
	and	SCB_CONTROL,0xfb	  /* clear disconnect bit in SCB */
	or	FLAGS,IDENTIFY_SEEN	  /* make note of IDENTIFY */
	jmp	ITloop
index_by_tag:
	mov	SCBPTR,ARG_1
	mov	A,SAVED_TCL
	cmp	SCB_TCL,A		jne abort_tag
	test	SCB_CONTROL,TAG_ENB	jz  abort_tag
	call	inb_last			/* Ack Successful tag */
	jmp	setup_SCB

abort_tag:
	or	SCSISIGO,ATNO			/* turn on ATNO */
	mvi	INTSTAT,ABORT_TAG 		/* let driver know */
	mvi	MSG_ABORT_TAG	call mk_mesg	/* ABORT TAG message */
	jmp	mesgin_done

/*
 * Message reject?  Let the kernel driver handle this.  If we have an 
 * outstanding WDTR or SDTR negotiation, assume that it's a response from 
 * the target selecting 8bit or asynchronous transfer, otherwise just ignore 
 * it since we have no clue what it pertains to.
 */
mesgin_reject:
	mvi	INTSTAT, REJECT_MSG
	jmp	mesgin_done
a1385 16
 * Bus free phase.  It might be useful to interrupt the device
 * driver if we aren't expecting this.  For now, make sure that
 * ATN isn't being asserted and look for a new command.
 */
p_busfree:
	mvi	CLRSINT1,CLRATNO
	clr	LASTPHASE

/*
 * if this is an immediate command, perform a psuedo command complete to
 * notify the driver.
 */
	test	SCB_CMDLEN,0xff	jz status_ok
	jmp	start

/*
d1390 2
a1391 16
	mvi	SEQCTL,0x50			/* PAUSEDIS|FASTMODE */
	test	MSG_LEN,0xff	jz mk_mesg1	/* Should always succeed */
	
	/*
	 * Hmmm.  For some reason the mesg buffer is in use.
	 * Tell the driver.  It should look at SINDEX to find
	 * out what we wanted to use the buffer for and resolve
	 * the conflict.
	 */
	mvi	SEQCTL,0x10			/* !PAUSEDIS|FASTMODE */
	mvi	INTSTAT,MSG_BUFFER_BUSY

mk_mesg1:
	mvi	MSG_LEN,1		/* length = 1 */
	mov	MSG0,SINDEX		/* 1-byte message */
	mvi	SEQCTL,0x10	ret	/* !PAUSEDIS|FASTMODE */
d1408 3
a1410 1

d1412 1
a1412 2
	or	CLRSINT0, CLRSPIORDY
	mov	NONE,SCSIDATL			/*dummy read from latch to ACK*/
d1414 10
a1423 2
	test	SSTAT1,PHASEMIS	jnz mesgin_phasemis
	test	SSTAT0,SPIORDY	jz inb_next_wait /* wait for next byte */
d1425 2
a1426 3
	mov	DINDEX,SINDEX
	test	SSTAT1,PHASEMIS	jnz mesgin_phasemis
	mov	DINDIR,SCSIBUSL	ret		/*read byte directly from bus*/
d1428 2
a1429 1
	mov	NONE,SCSIDATL ret		/*dummy read from latch to ACK*/
d1431 40
d1475 2
a1476 2
	mvi	INTSTAT, MSGIN_PHASEMIS
	jmp	ITloop
d1485 5
a1489 4
	mov	DFCNTRL,SINDEX
dma1:
	test	SSTAT0,DMADONE	jnz dma3
	test	SSTAT1,PHASEMIS	jz dma1		/* ie. underrun */
d1499 8
a1506 5
dma3:
	test	SINDEX,DIRECTION	jnz dma5
dma4:
	test	DFSTATUS,FIFOEMP	jz dma4

d1511 14
a1524 5
dma5:
	/* disable DMA, but maintain WIDEODD */
	and	DFCNTRL,WIDEODD
dma6:
	test	DFCNTRL,0x38	jnz dma6  /* SCSIENACK|SDMAENACK|HDMAENACK */
d1526 1
a1526 12
	ret

/*
 * Common SCSI initialization for selection and reselection.  Expects
 * the target SCSI ID to be in the upper four bits of SINDEX, and A's
 * contents are stomped on return.
 */
initialize_scsiid:
	and	SINDEX,0xf0		/* Get target ID */
	and	A,0x0f,SCSIID
	or	SINDEX,A
	mov	SCSIID,SINDEX ret
d1533 1
a1533 2
	test	FLAGS,RESELECTED	jz return	/* reselected? */
	test	FLAGS,IDENTIFY_SEEN	jnz return	/* seen IDENTIFY? */
d1535 1
a1535 1
	mvi	INTSTAT,NO_IDENT 	ret	/* no - cause a kernel panic */
d1538 5
a1542 5
 * Locate the SCB matching the target ID/channel/lun in SAVED_TCL, and the tag
 * value in ARG_1.  If ARG_1 == SCB_LIST_NULL, we're looking for a non-tagged
 * SCB.  Have the kernel print a warning message if it can't be found, and
 * generate an ABORT/ABORT_TAG message to the target.  SINDEX should be
 * cleared on call.
d1545 37
a1581 13
	mov	A,SAVED_TCL
	mov	SCBPTR,SINDEX			/* switch to next SCB */
	mvi	SEQCTL,0x50			/* PAUSEDIS|FASTMODE */
	cmp	SCB_TCL,A	jne findSCB1 /* target ID/channel/lun match? */
	test	SCB_CONTROL,DISCONNECTED jz findSCB1 /*should be disconnected*/
	test	SCB_CONTROL,TAG_ENB jnz findTaggedSCB
	cmp	ARG_1,SCB_LIST_NULL je foundSCB
	jmp	findSCB1
findTaggedSCB:
	mov	A, ARG_1			/* Tag passed in ARG_1 */
	cmp	SCB_TAG,A	jne findSCB1	/* Found it? */
foundSCB:
	test	FLAGS,PAGESCBS	jz foundSCB_ret
d1583 5
a1587 12
	cmp	SCB_NEXT,SCB_LIST_NULL je unlink_prev
	mov	SAVED_LINKPTR, SCB_PREV
	mov	SCBPTR, SCB_NEXT
	mov	SCB_PREV, SAVED_LINKPTR
	mov	SCBPTR, SINDEX
unlink_prev:
	cmp	SCB_PREV,SCB_LIST_NULL	je rHead/* At the head of the list */
	mov	SAVED_LINKPTR, SCB_NEXT
	mov	SCBPTR, SCB_PREV
	mov	SCB_NEXT, SAVED_LINKPTR
	mov	SCBPTR, SINDEX
	mvi	SEQCTL,0x10	ret		/* !PAUSEDIS|FASTMODE */
d1589 1
a1589 42
	mov	DISCONNECTED_SCBH,SCB_NEXT
foundSCB_ret:
	mvi	SEQCTL,0x10	ret		/* !PAUSEDIS|FASTMODE */

findSCB1:
	mvi	SEQCTL,0x10			/* !PAUSEDIS|FASTMODE */
	inc	SINDEX
	mov	A,SCBCOUNT
	cmp	SINDEX,A	jne findSCB

	mvi	INTSTAT,NO_MATCH		/* not found - signal kernel */
	cmp	RETURN_1,SCB_PAGEDIN je return
	or	SCSISIGO,ATNO			/* assert ATNO */
	cmp	ARG_1,SCB_LIST_NULL jne find_abort_tag
	mvi	MSG_ABORT	call mk_mesg
	jmp	ITloop
find_abort_tag:
	mvi	MSG_ABORT_TAG	call mk_mesg
	jmp	ITloop

/*
 * Make a working copy of the scatter-gather parameters from the SCB.
 */
sg_scb2ram:
	mov	HADDR0, SCB_DATAPTR0
	mov	HADDR1, SCB_DATAPTR1
	mov	HADDR2, SCB_DATAPTR2
	mov	HADDR3, SCB_DATAPTR3
	mov	HCNT0, SCB_DATACNT0
	mov	HCNT1, SCB_DATACNT1
	mov	HCNT2, SCB_DATACNT2

	mov	STCNT0, HCNT0
	mov	STCNT1, HCNT1
	mov	STCNT2, HCNT2

	mov	SG_COUNT,SCB_SGCOUNT

	mov	SG_NEXT0, SCB_SGPTR0
	mov	SG_NEXT1, SCB_SGPTR1
	mov	SG_NEXT2, SCB_SGPTR2
	mov	SG_NEXT3, SCB_SGPTR3 ret
d1591 1
d1593 2
a1594 4
 * 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.
d1596 231
a1826 13
sg_ram2scb:
	test	FLAGS, DPHASE	jz return
	mov	SCB_SGCOUNT,SG_COUNT

	mov	SCB_SGPTR0,SG_NEXT0
	mov	SCB_SGPTR1,SG_NEXT1
	mov	SCB_SGPTR2,SG_NEXT2
	mov	SCB_SGPTR3,SG_NEXT3
	
	mov	SCB_DATAPTR0,SHADDR0
	mov	SCB_DATAPTR1,SHADDR1
	mov	SCB_DATAPTR2,SHADDR2
	mov	SCB_DATAPTR3,SHADDR3
d1829 2
a1830 1
 * Use the residual number since STCNT is corrupted by any message transfer
d1832 33
a1864 3
	mov	SCB_DATACNT0,SCB_RESID_DCNT0
	mov	SCB_DATACNT1,SCB_RESID_DCNT1
	mov	SCB_DATACNT2,SCB_RESID_DCNT2 ret
d1866 1
d1868 3
a1870 3
 * Add the array base TARG_SCRATCH to the target offset (the target address
 * is in SCSIID), and return the result in SINDEX.  The accumulator
 * contains the 3->8 decoding of the target ID on return.
d1872 2
a1873 6
ndx_dtr:
	shr	A,SCSIID,4
	test	SBLKCTL,SELBUSB	jz ndx_dtr_2
	or	A,0x08		/* Channel B entries add 8 */
ndx_dtr_2:
	add	SINDEX,TARG_SCRATCH,A ret
@


1.10.14.1
log
@Update thorpej_scsipi to -current as of a month ago
A i386 GENERIC kernel compiles without the siop, ahc and bha drivers
(will be updated later). i386 IDE/ATAPI and ncr work, as well as
sparc/esp_sbus. alpha should work as well (untested yet).
siop, ahc and bha will be updated once I've updated the branch to current
-current, as well as machine-dependant code.
@
text
@d1 1
a1 1
/*	$NetBSD: aic7xxx.seq,v 1.11 2000/03/15 02:09:11 fvdl Exp $	*/
d3 6
a8 2
/*
 * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
d10 3
a12 2
 * Copyright (c) 1994-2000 Justin Gibbs.
 * All rights reserved.
d14 15
a28 8
 * 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.
d30 11
a40 2
 * Alternatively, this software may be distributed under the terms of the
 * the GNU Public License ("GPL").
d42 1
a42 11
 * 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.
d44 11
a54 2
 * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.seq,v 1.94 2000/02/09 21:25:00 gibbs Exp $
 */
d57 4
a60 1
 * #ifdef __NetBSD__
d62 1
a62 10
#include <dev/microcode/aic7xxx/aic7xxx.reg>
#include <dev/scsipi/scsi_message.h>
/*
 * Assembler can't handle ifdef.
 *
 * #else
 * #include <dev/aic7xxx/aic7xxx.reg>
 * #include <cam/scsi/scsi_message.h>
 * #endif
*/
d64 1
a64 3
/*
 * A few words on the waiting SCB list:
 * After starting the selection hardware, we check for reconnecting targets
d73 4
a76 5
 * 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.
d79 4
d84 13
a96 11
	clr	SCSISIGO;		/* De-assert BSY */
	mvi	MSG_OUT, MSG_NOOP;	/* No message to send */
	and	SXFRCTL1, ~BITBUCKET;
	/* Always allow reselection */
	and	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE;
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		/* Ensure that no DMA operations are in progress */
		clr	CCSGCTL;
		clr	CCSCBCTL;
	}

d98 15
a112 36
	call	clear_target_state;
	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;
	}
	test	SSTAT0, SELDO|SELDI	jnz selection;
	test	SCSISEQ, ENSELO	jnz poll_for_work;
	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 */
	}
	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) {
		test	QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
		mov	NONE, SNSCB_QOFF;
		inc	QINPOS;
	} else {
		or	SEQCTL, PAUSEDIS;
		cmp	KERNEL_QINPOS, A je poll_for_work_loop;
		inc	QINPOS;
		and	SEQCTL, ~PAUSEDIS;
	}
d116 51
a166 37
 * 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) {
		mov	ALLZEROS	call	get_free_or_disc_scb;
	}

dequeue_scb:
	add	A, -1, QINPOS;
	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.
 */
	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
	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) {
		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];
	}
	mov	SCB_RESID_SGCNT, SCB_SGCOUNT;
d169 8
a176 13
	/*
	 * Place us on the waiting list in case our selection
	 * doesn't win during bus arbitration.
	 */
	mov	SCB_NEXT,WAITING_SCBH;
	mov	WAITING_SCBH, SCBPTR;
start_waiting:
	/*
	 * Pull the first entry off of the waiting SCB list.
	 */
	mov	SCBPTR, WAITING_SCBH;
	call	start_selection;
	jmp	poll_for_work;
d178 6
d185 31
a215 43
	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:
	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) {
		and	A, TID, SCB_TCL;	/* Get target ID */
		and	SCSIID_ULTRA2, OID;	/* Clear old target */
		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:
	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:
	or	SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX;
set_transfer_settings:
	if ((ahc->features & AHC_ULTRA) != 0) {
		test	SCB_CONTROL, ULTRAENB jz . + 2;
		or	SXFRCTL0, FAST20;
	} 
d217 2
a218 1
 * Initialize SCSIRATE with the appropriate value for this target.
d220 8
a227 188
	if ((ahc->features & AHC_ULTRA2) != 0) {
		bmov	SCSIRATE, SCB_SCSIRATE, 2 ret;
	} else {
		mov	SCSIRATE, SCB_SCSIRATE ret;
	}

selection:
	test	SSTAT0,SELDO	jnz select_out;
	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 */
		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;
	}
a228 1
if ((ahc->flags & AHC_INITIATORMODE) != 0) {
d231 2
a232 1
 * reselected, but haven't seen an IDENTIFY message from the target yet.
d234 5
a238 17
initiator_reselect:
	/* XXX test for and handle ONE BIT condition */
	and	SAVED_TCL, SELID_MASK, SELID;
	if ((ahc->features & AHC_TWIN) != 0) {
		test	SBLKCTL, SELBUSB	jz . + 2;
		or	SAVED_TCL, SELBUSB;
	}
	or	SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
	mvi	CLRSINT1,CLRBUSFREE;
	or	SIMODE1, ENBUSFREE;		/*
						 * We aren't expecting a
						 * bus free, so interrupt
						 * the kernel driver if it
						 * happens.
						 */
	jmp	ITloop;
}
d241 1
a241 1
 * After the selection, remove this SCB from the "waiting SCB"
d246 15
a260 181
select_out:
	/* Turn off the selection hardware */
	and	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE;
	mvi	CLRSINT0, CLRSELDO;
	mov	SCBPTR, WAITING_SCBH;
	mov	WAITING_SCBH,SCB_NEXT;
	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:
	mvi	SPIOEN call	initialize_channel;
d262 30
a291 20
	/*
	 * We aren't expecting a bus free, so interrupt
	 * the kernel driver if it happens.
	 */
	mvi	CLRSINT1,CLRBUSFREE;
	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.
	 */
	mvi	MSG_OUT, MSG_IDENTIFYFLAG;
	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.
	 */
d293 2
a294 1
	call	phase_lock;
d296 20
a315 42
	mov	A, LASTPHASE;

	test	A, ~P_DATAIN	jz p_data;
	cmp	A,P_COMMAND	je p_command;
	cmp	A,P_MESGOUT	je p_mesgout;
	cmp	A,P_STATUS	je p_status;
	cmp	A,P_MESGIN	je p_mesgin;

	mvi	INTSTAT,BAD_PHASE;
	jmp	ITloop;			/* Try reading the bus again. */

await_busfree:
	and	SIMODE1, ~ENBUSFREE;
	mov	NONE, SCSIDATL;		/* Ack the last byte */
	and	SXFRCTL0, ~SPIOEN;
	test	SSTAT1,REQINIT|BUSFREE	jz .;
	test	SSTAT1, BUSFREE jnz poll_for_work;
	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.
	 */
	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) {
		bmov	SCSIRATE, ALLZEROS, 2;
	} else {
		clr	SCSIRATE;
		and	SXFRCTL0, ~(FAST20);
	}
	mvi	LASTPHASE, P_BUSFREE;
	/* clear target specific flags */
	clr	SEQ_FLAGS ret;
d322 9
a330 28
	if ((ahc->features & AHC_ULTRA2) != 0) {
		/*
		 * The preload circuitry requires us to
		 * reload the address too, so pull it from
		 * the shaddow address.
		 */
		bmov	HADDR, SHADDR, 4;
		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;
	}
	and	DATA_COUNT_ODD, 0x1, SCB_RESID_DCNT[0];
	jmp	data_phase_loop;

p_data:
	if ((ahc->features & AHC_ULTRA2) != 0) {
		mvi	DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
	} else {
		mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
	}
	test	LASTPHASE, IOI jnz . + 2;
	or	DMAPARAMS, DIRECTION;
	call	assert;			/*
					 * Ensure entering a data
					 * phase is okay - seen identify, etc.
d332 2
a333 4
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		mvi	CCSGADDR, CCSGADDR_MAX;
	}
	test	SEQ_FLAGS, DPHASE	jnz data_phase_reinit;
d335 3
a337 31
	/* We have seen a data phase */
	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) {
		bmov	HADDR, SCB_DATAPTR, 7;
	} else {
		mvi	DINDEX, HADDR;
		mvi	SCB_DATAPTR	call bcopy_7;
	}
	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) {
		bmov	SG_COUNT, SCB_SGCOUNT, 5;
	} else {
		mvi	DINDEX, SG_COUNT;
		mvi	SCB_SGCOUNT	call bcopy_5;
	}
d341 1
a341 1
	test	SG_COUNT, 0xff jnz data_phase_inbounds;
d348 5
a352 11
	or	SXFRCTL1,BITBUCKET;
	and	DMAPARAMS, ~(HDMAEN|SDMAEN);
	if ((ahc->features & AHC_ULTRA2) != 0) {
		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;
	}
d354 3
a356 11
/* If we are the last SG block, tell the hardware. */
	cmp	SG_COUNT,0x01 jne data_phase_wideodd;
	if ((ahc->features & AHC_ULTRA2) != 0) {
		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;
	}
d358 1
a358 10
	if ((ahc->features & AHC_ULTRA2) != 0) {
		mov	SINDEX, ALLONES;
		mov	DFCNTRL, DMAPARAMS;
		test	SSTAT0, SDONE jnz .;/* Wait for preload to complete */
data_phase_dma_loop:
		test	SSTAT0,	SDONE jnz data_phase_dma_done;
		test	SSTAT1,PHASEMIS	jz data_phase_dma_loop;	/* ie. underrun */
	} else {
		mov	DMAPARAMS  call dma;
	}
a359 1
data_phase_dma_done:
d361 1
a361 1
	test	SXFRCTL1,BITBUCKET jnz data_phase_overrun;
d363 2
a364 4
/* See if we completed this segment */
	test	STCNT[0], 0xff	jnz data_phase_finish;
	test	STCNT[1], 0xff	jnz data_phase_finish;
	test	STCNT[2], 0xff	jnz data_phase_finish;
d370 7
a376 1
	dec	SG_COUNT;	/* one less segment to go */
a377 1
	test	SG_COUNT, 0xff	jz data_phase_finish; /* Are we done? */
d386 65
a450 74
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		/*
		 * Do we have any prefetch left???
		 */
		cmp	CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail;

		/*
		 * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
		 */
		add	A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
		mvi	A, CCSGADDR_MAX;
		jc	. + 2;
		shl	A, 3, SG_COUNT;
		mov	CCHCNT, A;
		bmov	CCHADDR, SG_NEXT, 4;
		mvi	CCSGCTL, CCSGEN|CCSGRESET;
		test	CCSGCTL, CCSGDONE jz .;
		and	CCSGCTL, ~CCSGEN;
		test	CCSGCTL, CCSGEN jnz .;
		mvi	CCSGCTL, CCSGRESET;
prefetched_segs_avail:
		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 */
	test	HCNT[0], 0x1 jz . + 2;
	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 */
	clr	A;			/* add sizeof(struct scatter) */
	add	SG_NEXT[0],SG_SIZEOF;
	adc	SG_NEXT[1],A;

	if ((ahc->flags & AHC_TARGETMODE) != 0) {
		test	SSTAT0, TARGET jnz data_phase_loop;
	}
	test	SSTAT1, REQINIT jz .;
	test	SSTAT1,PHASEMIS	jz data_phase_loop;

	/* Ensure the last seg is visable at the shaddow layer */
	if ((ahc->features & AHC_ULTRA2) != 0) {
		mov	DFCNTRL, DMAPARAMS;
		test	SSTAT0, SDONE jnz .;/* Wait for preload to complete */
	}
a452 3
	if ((ahc->features & AHC_ULTRA2) != 0) {
		call	ultra2_dmafinish;
	}
d458 5
a462 25
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		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];
	}
	mov	SCB_RESID_SGCNT, SG_COUNT;

	if ((ahc->features & AHC_ULTRA2) != 0) {
		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;
	}
	jmp	ITloop;
a464 4
	if ((ahc->features & AHC_ULTRA2) != 0) {
		call	ultra2_dmafinish;
		or	SXFRCTL0, CLRSTCNT|CLRCHN;
	}
d468 3
a470 38
	and	SXFRCTL1, ~BITBUCKET;
	mvi	INTSTAT,DATA_OVERRUN;
	jmp	ITloop;

ultra2_dmafinish:
	if ((ahc->features & AHC_ULTRA2) != 0) {
		test	DFCNTRL, DIRECTION jnz ultra2_dmafifoempty;
		and	DFCNTRL, ~SCSIEN;
		test	DFCNTRL, SCSIEN jnz .;
ultra2_dmafifoflush:
		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.
		 */
		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;

ultra2_dmafifoempty:
		/* Don't clobber an inprogress host data transfer */
		test	DFSTATUS, MREQPEND	jnz ultra2_dmafifoempty;

ultra2_dmahalt:
		and     DFCNTRL, ~(SCSIEN|HDMAEN);
		test	DFCNTRL, HDMAEN jnz .;
		ret;
	}
a471 1
if ((ahc->flags & AHC_INITIATORMODE) != 0) {
d476 1
a476 1
	call	assert;
d478 18
a495 50
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		mov	HCNT[0], SCB_CMDLEN;
		bmov	HCNT[1], ALLZEROS, 2;
		if ((ahc->features & AHC_ULTRA2) == 0) {
			bmov	STCNT, HCNT, 3;
		}
		add	NONE, -17, SCB_CMDLEN;
		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.
		 */
		bmov	HADDR[0], ALLZEROS, 1;
		if ((ahc->features & AHC_ULTRA2) != 0) {
			mvi	DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION);
		} else {
			mvi	DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
		}
		bmov   DFDAT, SCB_CMDSTORE, 16; 
		jmp	cmd_loop;
dma_cmd_data:
		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 {
		mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
	}
cmd_loop:
	test	SSTAT0, SDONE jnz . + 2;
	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.
	 */
	test	SCSISIGI, ACKI jnz .;
	and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .;
	jmp	ITloop;
d502 2
a503 1
	call	assert;
d505 7
a511 2
	mov	SCB_TARGET_STATUS, SCSIDATL;
	jmp	ITloop;
d513 1
d515 2
a516 22
 * 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.
d518 3
a520 36
p_mesgout_retry:
	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
p_mesgout:
	mov	SINDEX, MSG_OUT;
	cmp	SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
	test	SCB_CONTROL,MK_MESSAGE	jnz host_message_loop;
	mov	FUNCTION1, SCB_TCL;
	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.
		 */
		test	HS_MAILBOX, 0xF0	jz . + 2;
		mvi	INTSTAT, UPDATE_TMSG_REQ;
		nop;
	}
	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) {
		test	SCB_TCL, 0x80		jz . + 2; /* target > 7 */
		mov	SINDEX, TARGET_MSG_REQUEST[1];
	}
	test	SINDEX, A	jnz host_message_loop;
p_mesgout_identify:
	and	SINDEX,LID,SCB_TCL;	/* lun */
	and	A,DISCENB,SCB_CONTROL;	/* mask off disconnect privledge */
	or	SINDEX,A;		/* or in disconnect privledge */
	or	SINDEX,MSG_IDENTIFYFLAG;
d522 5
a526 2
 * 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.
d528 13
a540 20
p_mesgout_tag:
	test	SCB_CONTROL,TAG_ENB jz  p_mesgout_onebyte;
	mov	SCSIDATL, SINDEX;	/* Send the identify message */
	call	phase_lock;
	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
	and	SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
	call	phase_lock;
	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
	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:
	cmp	SINDEX, HOST_MSG	jne p_mesgout_onebyte;
	jmp	host_message_loop;

p_mesgout_onebyte:
	mvi	CLRSINT1, CLRATNO;
	mov	SCSIDATL, SINDEX;
d543 1
a543 1
 * If the next bus phase after ATN drops is message out, it means
d546 3
a548 2
	call	phase_lock;
	cmp	LASTPHASE, P_MESGOUT	je p_mesgout_retry;
d550 8
d559 2
a560 4
	mvi	CLRSINT1,CLRATNO;	/* Be sure to turn ATNO off */
	mov	LAST_MSG, MSG_OUT;
	mvi	MSG_OUT, MSG_NOOP;	/* No message left */
	jmp	ITloop;
d566 2
a567 1
	mvi	ACCUM		call inb_first;	/* read the 1st message byte */
d569 21
a589 21
	test	A,MSG_IDENTIFYFLAG	jnz mesgin_identify;
	cmp	A,MSG_DISCONNECT	je mesgin_disconnect;
	cmp	A,MSG_SAVEDATAPOINTER	je mesgin_sdptrs;
	cmp	ALLZEROS,A		je mesgin_complete;
	cmp	A,MSG_RESTOREPOINTERS	je mesgin_rdptrs;
	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:
	mvi	INTSTAT, HOST_MSG_LOOP;
	call	phase_lock;
	cmp	RETURN_1, EXIT_MSG_LOOP	je ITloop + 1;
	jmp	host_message_loop;
d592 2
a593 2
	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
	jmp	ITloop;
d598 16
a613 15
 * 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.
d615 1
a615 1

d617 6
a622 1
 * First check for residuals
d624 3
a626 5
	test	SCB_RESID_SGCNT,0xff	jnz upload_scb;
	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Good Status? */
upload_scb:
	mvi	DMAPARAMS, FIFORESET;
	mov	SCB_TAG		call dma_scb;
d628 35
a662 16
	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Just a residual? */
	mvi	INTSTAT,BAD_STATUS;			/* let driver know */
	nop;
	cmp	RETURN_1, SEND_SENSE	jne complete;
	/* This SCB becomes the next to execute as it will retrieve sense */
	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
	mov	SCB_TAG		call dma_scb;
add_to_waiting_list:
	mov	SCB_NEXT,WAITING_SCBH;
	mov	WAITING_SCBH, SCBPTR;
	/*
	 * Prepare our selection hardware before the busfree so we have a
	 * high probability of winning arbitration.
	 */
	call	start_selection;
	jmp	await_busfree;
d664 35
a698 28
complete:
	/* If we are untagged, clear our address up in host ram */
	test	SCB_CONTROL, TAG_ENB jnz complete_queue;
	mov	A, SAVED_TCL;
	/* fvdl - let ahc_intr clear this to avoid race conditions */
	/* mvi	UNTAGGEDSCB_OFFSET call post_byte_setup; */
	/* mvi	SCB_LIST_NULL call post_byte; */

complete_queue:
	mov	SCB_TAG call complete_post;
	jmp	await_busfree;
}

complete_post:
	/* Post the SCBID in SINDEX and issue an interrupt */
	call	add_scb_to_free_list;
	mov	ARG_1, SINDEX;
	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
		mov	A, SDSCB_QOFF;
	} else {
		mov	A, QOUTPOS;
	}
	mvi	QOUTFIFO_OFFSET call post_byte_setup;
	mov	ARG_1 call post_byte;
	if ((ahc->features & AHC_QUEUE_REGS) == 0) {
		inc 	QOUTPOS;
	}
	mvi	INTSTAT,CMDCMPLT ret;
a699 1
if ((ahc->flags & AHC_INITIATORMODE) != 0) {
d705 18
a722 3
	or	SCB_CONTROL,DISCONNECTED;
	call	add_scb_to_disc_list;
	jmp	await_busfree;
d725 2
a726 5
 * 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.
d729 2
a730 21
	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) {
		bmov	SCB_SGCOUNT, SG_COUNT, 5;
		bmov	SCB_DATAPTR, SHADDR, 4;
		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;
	}
	jmp	mesgin_done;
d739 2
a740 2
	and	SEQ_FLAGS, ~DPHASE;		/*
						 * We'll reload them
a741 1
						 * the dataphase.
d743 1
a743 1
	jmp	mesgin_done;
d751 9
a759 21
	if ((ahc->features & AHC_WIDE) != 0) {
		and	A,0x0f;		/* lun in lower four bits */
	} else {
		and	A,0x07;		/* lun in lower three bits */
	}
	or      SAVED_TCL,A;		/* SAVED_TCL should be complete now */

	mvi	ARG_2, SCB_LIST_NULL;	/* SCBID of prev SCB in disc List */
	call	get_untagged_SCBID;
	cmp	ARG_1, SCB_LIST_NULL	je snoop_tag;
	if ((ahc->flags & AHC_PAGESCBS) != 0) {
		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.
	 */
	call	rem_scb_from_disc_list;
	jmp	setup_SCB;
d762 2
a763 2
 * If we get one, we use the tag returned to find the proper
 * SCB.  With SCB paging, this requires using search for both tagged
d768 1
a768 2
snoop_tag:
	mov	NONE,SCSIDATL;		/* ACK Identify MSG */
d770 5
a774 3
	call	phase_lock;
	cmp	LASTPHASE, P_MESGIN	jne not_found;
	cmp	SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
d776 9
a784 1
	mvi	ARG_1	call inb_next;	/* tag value */
d786 8
a793 6
	/*
	 * Ensure that the SCB the tag points to is for
	 * an SCB transaction to the reconnecting target.
	 */
use_retrieveSCB:
	call	retrieveSCB;
d795 26
a820 20
	mov	A, SAVED_TCL;
	cmp	SCB_TCL, A	jne not_found_cleanup_scb;
	test	SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
	and	SCB_CONTROL,~DISCONNECTED;
	or	SEQ_FLAGS,IDENTIFY_SEEN;	/* make note of IDENTIFY */
	call	set_transfer_settings;
	/* See if the host wants to send a message upon reconnection */
	test	SCB_CONTROL, MK_MESSAGE jz mesgin_done;
	and	SCB_CONTROL, ~MK_MESSAGE;
	mvi	HOST_MSG	call mk_mesg;
	jmp	mesgin_done;

not_found_cleanup_scb:
	test	SCB_CONTROL, DISCONNECTED jz . + 3;
	call	add_scb_to_disc_list;
	jmp	not_found;
	call	add_scb_to_free_list;
not_found:
	mvi	INTSTAT, NO_MATCH;
	jmp	mesgin_done;
d827 16
d847 16
a862 2
	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
	mov	MSG_OUT,SINDEX ret;
d879 1
a879 3
inb_next_wait_perr:
	mvi	INTSTAT, PERR_DETECTED;
	jmp	inb_next_wait;
d881 2
a882 1
	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
d884 2
a885 10
	/*
	 * If there is a parity error, wait for the kernel to
	 * see the interrupt and prepare our message response
	 * before continuing.
	 */
	test	SSTAT1, REQINIT	jz inb_next_wait;
	test	SSTAT1, SCSIPERR jnz inb_next_wait_perr;
inb_next_check_phase:
	and	LASTPHASE, PHASE_MASK, SCSISIGI;
	cmp	LASTPHASE, P_MESGIN jne mesgin_phasemis;
d887 3
a889 2
	mov	DINDEX,SINDEX;
	mov	DINDIR,SCSIBUSL	ret;		/*read byte directly from bus*/
d891 1
a891 2
	mov	NONE,SCSIDATL ret;		/*dummy read from latch to ACK*/
}
a892 40
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;
}
	
d897 2
a898 2
	mvi	INTSTAT, MSGIN_PHASEMIS;
	jmp	ITloop;
d907 4
a910 5
	mov	DFCNTRL,SINDEX;
dma_loop:
	test	SSTAT0,DMADONE	jnz dma_dmadone;
	test	SSTAT1,PHASEMIS	jz dma_loop;	/* ie. underrun */
dma_phasemis:
d920 5
a924 8
dma_checkfifo:
	test	DFCNTRL,DIRECTION	jnz dma_fifoempty;
dma_fifoflush:
	test	DFSTATUS,FIFOEMP	jz dma_fifoflush;

dma_fifoempty:
	/* Don't clobber an inprogress host data transfer */
	test	DFSTATUS, MREQPEND	jnz dma_fifoempty;
d929 5
a933 14
dma_dmadone:
	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;
	}
	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
d935 12
a946 1
	ret;
d953 2
a954 1
	test	SEQ_FLAGS,IDENTIFY_SEEN	jnz return;	/* seen IDENTIFY? */
d956 1
a956 1
	mvi	INTSTAT,NO_IDENT 	ret;	/* no - tell the kernel */
d959 5
a963 5
 * 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.
d966 13
a978 37
	mov	SCBPTR,SINDEX;			/* Initialize SCBPTR */
	cmp	ARG_1, SCB_LIST_NULL	jne findSCB_by_SCBID;
	mov	A, SAVED_TCL;
	mvi	SCB_TCL	jmp findSCB_loop;	/* &SCB_TCL -> SINDEX */
findSCB_by_SCBID:
	mov	A, ARG_1;			/* Tag passed in ARG_1 */
	mvi	SCB_TAG	jmp findSCB_loop;	/* &SCB_TAG -> SINDEX */
findSCB_next:
	mov	ARG_2, SCBPTR;
	cmp	SCB_NEXT, SCB_LIST_NULL je notFound;
	mov	SCBPTR,SCB_NEXT;
	dec	SINDEX;		/* Last comparison moved us too far */
findSCB_loop:
	cmp	SINDIR, A	jne findSCB_next;
	mov	SINDEX, SCBPTR 	ret;
notFound:
	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:
	test	SEQ_FLAGS, SCBPTR_VALID	jz retrieve_from_host;
	mov	SCBPTR	call findSCB;	/* Continue the search */
	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:
d980 12
a991 5
	cmp	ARG_2, SCB_LIST_NULL	je rHead;
	mov	DINDEX, SCB_NEXT;
	mov	SCBPTR, ARG_2;
	mov	SCB_NEXT, DINDEX;
	mov	SCBPTR, SINDEX ret;
d993 42
a1034 1
	mov	DISCONNECTED_SCBH,SCB_NEXT ret;
a1035 1
retrieve_from_host:
d1037 4
a1040 2
 * 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.
d1042 13
a1054 231
	mov	ALLZEROS	call	get_free_or_disc_scb;
	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
	/* Jump instead of call as we want to return anyway */
	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:
	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host;
	mvi	ARG_1, SCB_LIST_NULL;
	mov	DISCONNECTED_SCBH call findSCB;
	cmp	SINDEX, SCB_LIST_NULL	je get_SCBID_from_host;
	or	SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */
	test	SCB_CONTROL, TAG_ENB	jnz . + 2;
	mov	ARG_1, SCB_TAG	ret;
	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:
	mov	ARG_2, SINDEX;
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		mvi	DINDEX, CCHADDR;
		mvi	SCBID_ADDR call set_1byte_addr;
		mvi	CCHCNT, 1;
		mvi	CCSGCTL, CCSGEN|CCSGRESET;
		test	CCSGCTL, CCSGDONE jz .;
		mvi	CCSGCTL, CCSGRESET;
		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:
	mov	ARG_2, SINDEX;
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		mvi	DINDEX, CCHADDR;
		mvi	SCBID_ADDR call	set_1byte_addr;
		mvi	CCHCNT, 1;
		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) {
		bmov	CCSCBRAM, SINDEX, 1;
		or	CCSCBCTL, CCSCBEN|CCSCBRESET;
		test	CCSCBCTL, CCSCBDONE jz .;
		clr	CCSCBCTL ret;
	} else {
		mov	DFDAT, SINDEX;
		or	DFCNTRL, HDMAEN|FIFOFLUSH;
		jmp	dma_finish;
	}

get_SCBID_from_host:
	mov	A, SAVED_TCL;
	mvi	UNTAGGEDSCB_OFFSET call fetch_byte;
	mov	RETURN_1,  RETURN_2 ret;

phase_lock_perr:
	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.
	 */
	test	SSTAT1, REQINIT jz phase_lock;
	test	SSTAT1, SCSIPERR jnz phase_lock_perr;
phase_lock_latch_phase:
	and	SCSISIGO, PHASE_MASK, SCSISIGI;
	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:
	shr	ARG_2, 2, A;
	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:
	add     DINDIR, A, SINDIR;
	mov     A, ARG_2;
	adc	DINDIR, A, SINDIR;
	clr	A;
	adc	DINDIR, A, SINDIR;
	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:
	mov	A, SINDEX;
	if ((ahc->features & AHC_CMD_CHAN) != 0) {
		mvi	DINDEX, CCHADDR;
		mvi	HSCB_ADDR call set_64byte_addr;
		mov	CCSCBPTR, SCBPTR;
		test	DMAPARAMS, DIRECTION jz dma_scb_tohost;
		mvi	CCHCNT, SCB_64BYTE_SIZE;
		mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
		cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
		jmp	dma_scb_finish;
dma_scb_tohost:
		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 {
			mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
			cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
		}
dma_scb_finish:
		clr	CCSCBCTL;
		test	CCSCBCTL, CCARREN|CCSCBEN jnz .;
		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;
	}

d1057 1
a1057 2
 * Wait for DMA from host memory to data FIFO to complete, then disable
 * DMA and wait for it to acknowledge that it's off.
d1059 3
a1061 33
dma_finish:
	test	DFSTATUS,HDONE	jz dma_finish;
	/* Turn off DMA */
	and	DFCNTRL, ~HDMAEN;
	test	DFCNTRL, HDMAEN jnz .;
	ret;

add_scb_to_free_list:
	if ((ahc->flags & AHC_PAGESCBS) != 0) {
		mov	SCB_NEXT, FREE_SCBH;
		mvi	SCB_TAG, SCB_LIST_NULL;
		mov	FREE_SCBH, SCBPTR ret;
	} else {
		mvi	SCB_TAG, SCB_LIST_NULL ret;
	}

if ((ahc->flags & AHC_PAGESCBS) != 0) {
get_free_or_disc_scb:
	cmp	FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
return_error:
	mvi	SINDEX, SCB_LIST_NULL	ret;
dequeue_disc_scb:
	mov	SCBPTR, DISCONNECTED_SCBH;
dma_up_scb:
	mvi	DMAPARAMS, FIFORESET;
	mov	SCB_TAG		call dma_scb;
unlink_disc_scb:
	mov	DISCONNECTED_SCBH, SCB_NEXT ret;
dequeue_free_scb:
	mov	SCBPTR, FREE_SCBH;
	mov	FREE_SCBH, SCB_NEXT ret;
}
a1062 1
add_scb_to_disc_list:
d1064 3
a1066 3
 * 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.
d1068 6
a1073 2
	mov	SCB_NEXT, DISCONNECTED_SCBH;
	mov	DISCONNECTED_SCBH, SCBPTR ret;
@


1.9
log
@Patches from Justin that handle some REV B AIC7880 problems
@
text
@d1 1
a1 1
/*	$NetBSD: aic7xxx.seq,v 1.8 1998/04/16 07:07:37 leo Exp $	*/
d908 3
a910 3
dma_loop:
	test	SSTAT0,DMADONE	jnz dma_checkfifo
	test	SSTAT1,PHASEMIS	jz dma_loop		/* ie. underrun */
d920 5
a924 8
dma_checkfifo:
	test	DFCNTRL,DIRECTION	jnz dma_fifoempty
dma_fifoflush:
	test	DFSTATUS,FIFOEMP	jz dma_fifoflush
 
dma_fifoempty:
	/* Don't clobber an inprogress host data transfer */ 
	test	DFSTATUS, MREQPEND	jnz dma_fifoempty
d926 1
a926 1
 * Now shut the DMA enables off and make sure that the DMA enables are
d929 5
a933 12
dma_dmadone:
	and	DFCNTRL, 0xc7	/* ~(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.
	 */
	mov	NONE, DFDAT
	test	DFCNTRL, HDMAEN jnz dma_halt
@


1.8
log
@Backout endian changes here (per discussion with Justin) and move them to
aic7xxx.c
@
text
@d1 1
a1 1
/*	$NetBSD: aic7xxx.seq,v 1.6 1996/10/08 03:04:06 gibbs Exp $	*/
d46 1
a46 1
VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.6 1996/10/08 03:04:06 gibbs Exp $"
d908 3
a910 3
dma1:
	test	SSTAT0,DMADONE	jnz dma3
	test	SSTAT1,PHASEMIS	jz dma1		/* ie. underrun */
d920 8
a927 5
dma3:
	test	SINDEX,DIRECTION	jnz dma5
dma4:
	test	DFSTATUS,FIFOEMP	jz dma4

d929 1
a929 1
 * Now shut the DMA enables off and make sure that the DMA enables are 
d932 12
a943 5
dma5:
	/* disable DMA, but maintain WIDEODD */
	and	DFCNTRL,WIDEODD
dma6:
	test	DFCNTRL,0x38	jnz dma6  /* SCSIENACK|SDMAENACK|HDMAENACK */
@


1.7
log
@Make it possible to interface to big-endian cpu's.
@
text
@a427 6
#if BIG_ENDIAN_CPU
	mov	HADDR3,DFDAT
	mov	HADDR2,DFDAT
	mov	HADDR1,DFDAT
	mov	HADDR0,DFDAT
#else
a431 1
#endif
a441 6
#if BIG_ENDIAN_CPU
	mov	HCNT2,DFDAT	/* LEO: MSB unused */
	mov	HCNT2,DFDAT
	mov	HCNT1,DFDAT
	mov	HCNT0,DFDAT
#else
a444 1
#endif
a480 6
#if BIG_ENDIAN_CPU
	mov	HADDR3, SCB_CMDPTR0
	mov	HADDR2, SCB_CMDPTR1
	mov	HADDR1, SCB_CMDPTR2
	mov	HADDR0, SCB_CMDPTR3
#else
a484 1
#endif
a1016 9
#if BIG_ENDIAN_CPU
	mov	HADDR3, SCB_DATAPTR0
	mov	HADDR2, SCB_DATAPTR1
	mov	HADDR1, SCB_DATAPTR2
	mov	HADDR0, SCB_DATAPTR3
	mov	HCNT0, SCB_DATACNT3
	mov	HCNT1, SCB_DATACNT2
	mov	HCNT2, SCB_DATACNT1
#else
a1023 1
#endif
a1030 6
#if BIG_ENDIAN_CPU
	mov	SG_NEXT3, SCB_SGPTR0
	mov	SG_NEXT2, SCB_SGPTR1
	mov	SG_NEXT1, SCB_SGPTR2
	mov	SG_NEXT0, SCB_SGPTR3 ret
#else
a1034 1
#endif
a1045 10
#if BIG_ENDIAN_CPU
	mov	SCB_SGPTR3,SG_NEXT0
	mov	SCB_SGPTR2,SG_NEXT1
	mov	SCB_SGPTR1,SG_NEXT2
	mov	SCB_SGPTR0,SG_NEXT3
	mov	SCB_DATAPTR3,SHADDR0
	mov	SCB_DATAPTR2,SHADDR1
	mov	SCB_DATAPTR1,SHADDR2
	mov	SCB_DATAPTR0,SHADDR3
#else
d1050 1
a1054 2
#endif
	
a1058 5
#if BIG_ENDIAN_CPU
	mov	SCB_DATACNT3,SCB_RESID_DCNT0
	mov	SCB_DATACNT2,SCB_RESID_DCNT1
	mov	SCB_DATACNT1,SCB_RESID_DCNT2 ret
#else
a1061 1
#endif
@


1.6
log
@dev/microcode/aic7xxx/aic7xxx.seq,
dev/microcode/aic7xxx_seq.h,
dev/ic/aic7xxxreg.h:
  Remove intrinsic knowledge about SDTR and WDTR messages and replace it
  with a generic message system that allows the kernel driver to handle
  SDTR, WDTR and any other type of extended message it chooses too.  This
  makes the sequencer code much simpler, makes extended message handling
  debuggable since the bulk of the work is in the kernel driver, and saves
  lots of instruction space.

  Regen microcode header file.

dev/ic/aic7xxx.c, dev/ic/aic7xxxvar.h:
  Add code to handle WDTR and SDTR negotiation in light of the changes in
  the message interface to the sequencer.  Don't reject targets that
  negotiate async by sending an SDTR with a 0 offset.  Use an sdtr message
  with 0,0 to negotiate async when a target suggests a period that is too
  long for us to handle.  Some tape and cdrom drives don't like us doing
  the message reject that we did in the past.

  Fix a problem with handing the QUEUE FULL condition.

  Fix a race condition (most likely the cause of the SCB paging problems) that
  might allow the sequencer to get unpaused before the condition that caused
  it to be paused (a SEQINT) was handled.

  Race condition pointed out by Doug Ledford <dledford@@dialnet.net> and
  by "Dan Willis" <dan@@plutotech.com>.

dev/pci/ahc_pci.c:
  Add support for the 2940AU, an aic7860 based controller.

dev/pci/pcidevs.h, dev/pci/pcidevs_data.h:
  Add product IDs for the 2940AU, aic7860 and aic7855.

  Regen data file.

scsi/scsi_message.h:
  Add MSG_EXT_SDTR_LEN and MSG_EXT_WDTR_LEN - the length of bytes in these
  extended messages.

Thanks to Chuck Cranor <chuck@@maria.wustl.edu> for testing these changes
out for me.
@
text
@d1 1
a1 1
/*	$NetBSD: aic7xxx.seq,v 1.5 1996/08/10 08:36:58 mycroft Exp $	*/
d46 1
a46 1
VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.5 1996/08/10 08:36:58 mycroft Exp $"
d50 1
a50 1
#include "../../../scsi/scsi_message.h"
d428 6
d438 1
d449 6
d458 1
d495 6
d505 1
d1038 9
d1054 1
d1062 6
d1072 1
d1084 10
a1097 1
	
d1102 2
d1108 5
d1116 1
@


1.5
log
@Compile the sequencer statically.
@
text
@d1 1
a1 1
/*	$NetBSD: aic7xxx.seq,v 1.4 1996/07/10 22:51:23 explorer Exp $	*/
d46 1
a46 1
VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.4 1996/07/10 22:51:23 explorer Exp $"
d50 1
d53 1
a194 10
	test	SCB_CMDLEN,0xff jnz mk_identify	/* 0 Length Command? */

/*
 * The kernel has sent us an SCB with no command attached.  This implies
 * that the kernel wants to send a message of some sort to this target,
 * so we interrupt the driver, allow it to fill the message buffer, and
 * then go back into the arbitration loop
 */
	mvi     INTSTAT,AWAITING_MSG
	jmp     wait_for_selection
d201 1
a201 1
	or	MSG0,MSG_IDENTIFY
a203 1
	test	SCB_CONTROL,0xb0 jz  !message	/* WDTR, SDTR or TAG?? */
a207 1

d209 1
a210 1
	test	SCB_CONTROL,TAG_ENB jz mk_tag_done
d216 6
a221 1
mk_tag_done:
d223 1
a223 2
	test	SCB_CONTROL,0x90 jz !message	/* NEEDWDTR|NEEDSDTR */
	mov	DINDEX	call mk_dtr	/* build DTR message if needed */
a224 1
!message:
d511 1
a511 1
	mvi	MSG_NOP		call mk_mesg	/* build NOP message */
d569 1
a569 1
	test	A,MSG_IDENTIFY		jnz mesgin_identify
d571 1
a571 1
	cmp	A,MSG_SDPTRS		je mesgin_sdptrs
d573 1
a573 1
	cmp	A,MSG_RDPTRS		je mesgin_rdptrs
d575 1
a575 1
	cmp	A,MSG_REJECT		je mesgin_reject
d589 1
a589 1
	mvi	MSG_REJECT	call mk_mesg
d665 4
a668 5
 * Is it an extended message?  We only support the synchronous and wide data
 * transfer request messages, which will probably be in response to
 * WDTR or SDTR message outs from us.  If it's not SDTR or WDTR, reject it -
 * apparently this can be done after any message in byte, according
 * to the SCSI-2 spec.
d671 26
a696 38
	mvi	ARG_1		call inb_next	/* extended message length */
	mvi	REJBYTE_EXT	call inb_next	/* extended message code */

	cmp	REJBYTE_EXT,MSG_SDTR	je p_mesginSDTR
	cmp	REJBYTE_EXT,MSG_WDTR	je p_mesginWDTR
	jmp	rej_mesgin

p_mesginWDTR:
	cmp	ARG_1,2		jne rej_mesgin	/* extended mesg length=2 */
	mvi	ARG_1		call inb_next	/* Width of bus */
	mvi	INTSTAT,WDTR_MSG		/* let driver know */
	test	RETURN_1,0xff jz mesgin_done	/* Do we need to send WDTR? */
	cmp	RETURN_1,SEND_REJ je rej_mesgin /*
						 * Bus width was too large 
						 * Reject it.
						 */

/* We didn't initiate the wide negotiation, so we must respond to the request */
	and	RETURN_1,0x7f			/* Clear the SEND_WDTR Flag */
	mvi	DINDEX,MSG0
	mvi	MSG0	call mk_wdtr		/* build WDTR message */
	or	SCSISIGO,ATNO			/* turn on ATNO */
	jmp	mesgin_done

p_mesginSDTR:
	cmp	ARG_1,3		jne rej_mesgin	/* extended mesg length=3 */
	mvi	ARG_1		call inb_next	/* xfer period */
	mvi	A		call inb_next	/* REQ/ACK offset */
	mvi	INTSTAT,SDTR_MSG		/* call driver to convert */

	test	RETURN_1,0xff	jz mesgin_done  /* Do we need to mk_sdtr/rej */
	cmp	RETURN_1,SEND_REJ je rej_mesgin /*
						 * Requested SDTR too small
						 * Reject it.
						 */
	clr	ARG_1				/* Use the scratch ram rate */
	mvi	DINDEX, MSG0
	mvi     MSG0     call mk_sdtr
d770 5
a774 5
	test	SSTAT1,BUSFREE	jnz use_findSCB
	test	SSTAT1,REQINIT	jz snoop_tag_loop
	test	SSTAT1,PHASEMIS	jnz use_findSCB
	mvi	A		call inb_first
	cmp	A,MSG_SIMPLE_TAG jne use_findSCB
d934 1
a934 1

a1073 65

/*
 * If we need to negotiate transfer parameters, build the WDTR or SDTR message
 * starting at the address passed in SINDEX.  DINDEX is modified on return.
 * The SCSI-II spec requires that Wide negotiation occur first and you can
 * only negotiat one or the other at a time otherwise in the event of a message
 * reject, you wouldn't be able to tell which message was the culpret.
 */
mk_dtr:
	test	SCB_CONTROL,NEEDWDTR jnz  mk_wdtr_16bit
	mvi	ARG_1, MAXOFFSET	/* Force an offset of 15 or 8 if WIDE */

mk_sdtr:
	mvi	DINDIR,1		/* extended message */
	mvi	DINDIR,3		/* extended message length = 3 */
	mvi	DINDIR,1		/* SDTR code */
	call	sdtr_to_rate
	mov	DINDIR,RETURN_1		/* REQ/ACK transfer period */
	cmp	ARG_1, MAXOFFSET je mk_sdtr_max_offset
	and	DINDIR,0x0f,SINDIR	/* Sync Offset */

mk_sdtr_done:
	add	MSG_LEN,COMP_MSG0,DINDEX ret	/* update message length */

mk_sdtr_max_offset:
/*
 * We're initiating sync negotiation, so request the max offset we can (15 or 8)
 */
	/* Talking to a WIDE device? */
	test	SCSIRATE, WIDEXFER	jnz wmax_offset	
	mvi	DINDIR, MAX_OFFSET_8BIT
	jmp	mk_sdtr_done

wmax_offset:
	mvi	DINDIR, MAX_OFFSET_16BIT
	jmp	mk_sdtr_done

mk_wdtr_16bit:
	mvi	ARG_1,BUS_16_BIT
mk_wdtr:
	mvi	DINDIR,1		/* extended message */
	mvi	DINDIR,2		/* extended message length = 2 */
	mvi	DINDIR,3		/* WDTR code */
	mov	DINDIR,ARG_1		/* bus width */

	add	MSG_LEN,COMP_MSG0,DINDEX ret	/* update message length */
	
sdtr_to_rate:
	call	ndx_dtr			/* index scratch space for target */
	shr	A,SINDIR,0x4
	dec	SINDEX			/* Preserve SINDEX */
	and	A,0x7
	clr	RETURN_1
sdtr_to_rate_loop:
	test	A,0x0f	jz sdtr_to_rate_done
	add	RETURN_1,0x19
	dec	A	
	jmp	sdtr_to_rate_loop
sdtr_to_rate_done:
	shr	RETURN_1,0x2
	add	RETURN_1,0x19
	test	SXFRCTL0,ULTRAEN jz return
	shr	RETURN_1,0x1
return:
	ret
@


1.4
log
@Updates to aic7xxx driver ; from pr port-i386/2600
@
text
@d1 1
a1 1
/*	$NetBSD$	*/
d46 1
a46 1
VERSION AIC7XXX_SEQ_VER "$NetBSD$"
d49 1
a49 1
#include "../../../../dev/ic/aic7xxxreg.h"
@


1.3
log
@RCS Id police.
@
text
@d1 1
a1 1
/*	$NetBSD: $	*/
d42 2
d46 1
a46 1
VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.2 1996/05/16 03:51:45 mycroft Exp $"
d90 5
d269 17
a291 1

d315 1
d347 14
d367 3
d471 8
d538 1
d908 1
@


1.3.4.1
log
@Pulled up from rev 1.4, ahc fixes
@
text
@d1 1
a1 1
/*	$NetBSD: aic7xxx.seq,v 1.4 1996/07/10 22:51:23 explorer Exp $	*/
a41 2
 * from	Id: aic7xxx.seq,v 1.42 1996/06/09 17:29:11 gibbs Exp
 *
d44 1
a44 1
VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.4 1996/07/10 22:51:23 explorer Exp $"
a87 5
	clr	SCSIRATE		/*
					 * We don't know the target we will
					 * connect to, so default to narrow
					 * transfers to avoid parity problems.
					 */
a261 17
/*
 * Initialize Ultra mode setting.
 */
	mov	FUNCTION1,SCSIID
	mov	A,FUNCTION1
	and	SINDEX,0xdf,SXFRCTL0		/* default to Ultra disabled */
	test	SCSIID, 0x80	 jnz ultra_b	/* Target ID > 7 */
	test	SBLKCTL, SELBUSB jnz ultra_b	/* Second channel device */
	test	ULTRA_ENB,A	 jz  set_sxfrctl0
	or	SINDEX, ULTRAEN  jmp set_sxfrctl0
ultra_b:
	test	ULTRA_ENB_B,A	 jz  set_sxfrctl0
	or	SINDEX, ULTRAEN

set_sxfrctl0:
	mov	SXFRCTL0,SINDEX

d268 1
a291 1
	jmp	ITloop			/* Try reading the bus again. */
a322 14
/* Guard against overruns */
	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.
 */
	or	SXFRCTL1,BITBUCKET
	mvi	STCNT0,0xff
	mvi	STCNT1,0xff
	mvi	STCNT2,0xff

data_phase_inbounds:
a328 3
/* Go tell the host about any overruns */
	test	SXFRCTL1,BITBUCKET jnz data_phase_overrun

a429 8
data_phase_overrun:
/*
 * Turn off BITBUCKET mode and notify the host
 */
	and	SXFRCTL1,0x7f		/* ~BITBUCKET */
	mvi	INTSTAT,DATA_OVERRUN
	jmp	ITloop

a488 1
	test	SSTAT1,PHASEMIS	jnz p_mesgout_phasemis
a857 1
	test	SSTAT1,PHASEMIS	jnz mesgin_phasemis
@


1.3.4.2
log
@Pull up latest ahc driver.  Fixes several bugs.
@
text
@d1 1
a1 1
/*	$NetBSD: aic7xxx.seq,v 1.6 1996/10/08 03:04:06 gibbs Exp $	*/
d46 1
a46 1
VERSION AIC7XXX_SEQ_VER "$NetBSD: aic7xxx.seq,v 1.6 1996/10/08 03:04:06 gibbs Exp $"
d49 1
a49 2
#include "../../ic/aic7xxxreg.h"
#include "../../../scsi/scsi_message.h"
a51 1
#include "../../scsi/scsi_message.h"
d193 10
d209 1
a209 1
	or	MSG0,MSG_IDENTIFYFLAG
d212 1
d217 1
a218 1
	test	SCB_CONTROL,TAG_ENB jz  mk_message
d220 1
d226 1
a226 6
/*
 * Interrupt the driver, and allow it to tweak the message buffer
 * if it asks.
 */
mk_message:
	test	SCB_CONTROL,MK_MESSAGE  jz wait_for_selection
d228 2
a229 1
	mvi     INTSTAT,AWAITING_MSG
d231 1
d518 1
a518 1
	mvi	MSG_NOOP	call mk_mesg	/* build NOP message */
d576 1
a576 1
	test	A,MSG_IDENTIFYFLAG	jnz mesgin_identify
d578 1
a578 1
	cmp	A,MSG_SAVEDATAPOINTER	je mesgin_sdptrs
d580 1
a580 1
	cmp	A,MSG_RESTOREPOINTERS	je mesgin_rdptrs
d582 1
a582 1
	cmp	A,MSG_MESSAGE_REJECT	je mesgin_reject
d596 1
a596 1
	mvi	MSG_MESSAGE_REJECT	call mk_mesg
d672 5
a676 4
 * Is it an extended message?  Copy the message to our message buffer and
 * notify the host.  The host will tell us whether to reject this message,
 * respond to it with the message that the host placed in our message buffer,
 * or simply to do nothing.
d679 38
a716 26
	mvi	MSGIN_EXT_LEN	 call inb_next
	mvi	MSGIN_EXT_OPCODE call inb_next
	mov	A, MSGIN_EXT_LEN
	dec	A	/* Length counts the op code */
	mvi	SINDEX, MSGIN_EXT_BYTE0
mesgin_extended_loop:
	test	A, 0xFF		jz mesgin_extended_intr
	cmp	SINDEX, MSGIN_EXT_LASTBYTE je mesgin_extended_dump
	call	inb_next
	dec	A
/*
 * We pass the arg to inb in SINDEX, but DINDEX is the one incremented
 * so update SINDEX with DINDEX's value before looping again.
 */
	mov	DINDEX		jmp mesgin_extended_loop
mesgin_extended_dump:
/* We have no more storage space, so dump the rest */
	test	A, 0xFF		jz mesgin_extended_intr
	mvi	NONE		call inb_next
	dec	A
	jmp	mesgin_extended_dump
mesgin_extended_intr:
	mvi	INTSTAT,EXTENDED_MSG		/* let driver know */
	cmp	RETURN_1,SEND_REJ je rej_mesgin
	cmp	RETURN_1,SEND_MSG jne mesgin_done
/* The kernel has setup a message to be sent */
d790 5
a794 5
	test	SSTAT1,BUSFREE		jnz use_findSCB
	test	SSTAT1,REQINIT		jz snoop_tag_loop
	test	SSTAT1,PHASEMIS		jnz use_findSCB
	mvi	A			call inb_first
	cmp	A,MSG_SIMPLE_Q_TAG	jne use_findSCB
d954 1
a954 1
return:
d1094 65
@


1.2
log
@New version, with changes from Justin Gibbs and Noriyuki Soda.
@
text
@d1 2
d44 1
a44 1
VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 1.34 1996/05/10 16:11:01 gibbs Exp $"
@


1.1
log
@Add an Adaptec 2940 driver, by John Aycock and Justin Gibbs, ported to NetBSD by Stefan
Grefen, with several bug fixes by me.
@
text
@d1 82
a82 260
##+M#########################################################################
# Adaptec 274x/284x/294x device driver for Linux and FreeBSD.
#
# Copyright (c) 1994 John Aycock
#   The University of Calgary Department of Computer Science.
#   All rights reserved.
#
# Modifications/enhancements:
#   Copyright (c) 1994, 1995 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.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#      This product includes software developed by the University of Calgary
#      Department of Computer Science and its contributors.
# 4. Neither the name of the University nor the names of its contributors
#    may be used to endorse or promote products derived from this software
#    without specific prior written permission.
#
# 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, Twin, Wide, 2 command per target support, tagged queuing and other 
# optimizations provided by Justin T. Gibbs (gibbs@@FreeBSD.org)
#
##-M#########################################################################

VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 1.15 1995/04/27 17:44:27 gibbs Exp $"

SCBMASK		= 0x1f

SCSISEQ		= 0x00
ENRSELI		= 0x10
SXFRCTL0	= 0x01
SXFRCTL1	= 0x02
SCSISIGI	= 0x03
SCSISIGO	= 0x03
SCSIRATE	= 0x04
SCSIID		= 0x05
SCSIDATL	= 0x06
STCNT		= 0x08
STCNT+0		= 0x08
STCNT+1		= 0x09
STCNT+2		= 0x0a
CLRSINT0	= 0x0b
SSTAT0		= 0x0b
SELDO		= 0x40
SELDI		= 0x20
CLRSINT1	= 0x0c
SSTAT1		= 0x0c
SIMODE1		= 0x11
SCSIBUSL	= 0x12
SHADDR		= 0x14
SELID		= 0x19
SBLKCTL		= 0x1f
SEQCTL		= 0x60
A		= 0x64				# == ACCUM
SINDEX		= 0x65
DINDEX		= 0x66
ALLZEROS	= 0x6a
NONE		= 0x6a
SINDIR		= 0x6c
DINDIR		= 0x6d
FUNCTION1	= 0x6e
HADDR		= 0x88
HADDR+1		= 0x89
HADDR+2		= 0x8a
HADDR+3		= 0x8b
HCNT		= 0x8c
HCNT+0		= 0x8c
HCNT+1		= 0x8d
HCNT+2		= 0x8e
SCBPTR		= 0x90
INTSTAT		= 0x91
DFCNTRL		= 0x93
DFSTATUS	= 0x94
DFDAT		= 0x99
QINFIFO		= 0x9b
QINCNT		= 0x9c
QOUTFIFO	= 0x9d

SCSICONF_A	= 0x5a
SCSICONF_B	= 0x5b

#  The two reserved bytes at SCBARRAY+1[23] are expected to be set to
#  zero, and the reserved bit in SCBARRAY+0 is used as an internal flag
#  to indicate whether or not to reload scatter-gather parameters after
#  a disconnect.  We also use bits 6 & 7 to indicate whether or not to
#  initiate SDTR or WDTR repectively when starting this command.
#
SCBARRAY+0	= 0xa0

DISCONNECTED	= 0x04
NEEDDMA		= 0x08
SG_LOAD		= 0x10
TAG_ENB		= 0x20
NEEDSDTR	= 0x40
NEEDWDTR	= 0x80

SCBARRAY+1	= 0xa1
SCBARRAY+2	= 0xa2
SCBARRAY+3	= 0xa3
SCBARRAY+4	= 0xa4
SCBARRAY+5	= 0xa5
SCBARRAY+6	= 0xa6
SCBARRAY+7	= 0xa7
SCBARRAY+8	= 0xa8
SCBARRAY+9	= 0xa9
SCBARRAY+10	= 0xaa
SCBARRAY+11	= 0xab
SCBARRAY+12	= 0xac
SCBARRAY+13	= 0xad
SCBARRAY+14	= 0xae
SCBARRAY+15	= 0xaf
SCBARRAY+16	= 0xb0
SCBARRAY+17	= 0xb1
SCBARRAY+18	= 0xb2
SCBARRAY+19	= 0xb3
SCBARRAY+20	= 0xb4
SCBARRAY+21	= 0xb5
SCBARRAY+22	= 0xb6
SCBARRAY+23	= 0xb7
SCBARRAY+24	= 0xb8
SCBARRAY+25	= 0xb9
SCBARRAY+26	= 0xba
SCBARRAY+27	= 0xbb
SCBARRAY+28	= 0xbc
SCBARRAY+29	= 0xbd
SCBARRAY+30	= 0xbe

BAD_PHASE	= 0x01				# unknown scsi bus phase
CMDCMPLT	= 0x02				# Command Complete
SEND_REJECT	= 0x11				# sending a message reject
NO_IDENT	= 0x21				# no IDENTIFY after reconnect
NO_MATCH	= 0x31				# no cmd match for reconnect
MSG_SDTR	= 0x41				# SDTR message recieved
MSG_WDTR	= 0x51				# WDTR message recieved
MSG_REJECT	= 0x61				# Reject message recieved
BAD_STATUS	= 0x71				# Bad status from target
RESIDUAL	= 0x81				# Residual byte count != 0
ABORT_TAG	= 0x91				# Sent an ABORT_TAG message

#  The host adapter card (at least the BIOS) uses 20-2f for SCSI
#  device information, 32-33 and 5a-5f as well. As it turns out, the
#  BIOS trashes 20-2f, writing the synchronous negotiation results
#  on top of the BIOS values, so we re-use those for our per-target
#  scratchspace (actually a value that can be copied directly into
#  SCSIRATE).  The kernel driver will enable synchronous negotiation
#  for all targets that have a value other than 0 in the lower four
#  bits of the target scratch space.  This should work irregardless of
#  whether the bios has been installed. NEEDWDTR and NEEDSDTR are the top
#  two bits of the SCB control byte.  The kernel driver will set these
#  when a WDTR or SDTR message should be sent to the target the SCB's 
#  command references.
#
#  REJBYTE contains the first byte of a MESSAGE IN message, so the driver 
#  can report an intelligible error if a message is rejected.
#
#  FLAGS's high bit is true if we are currently handling a reselect;
#  its next-highest bit is true ONLY IF we've seen an IDENTIFY message
#  from the reselecting target.  If we haven't had IDENTIFY, then we have
#  no idea what the lun is, and we can't select the right SCB register
#  bank, so force a kernel panic if the target attempts a data in/out or
#  command phase instead of corrupting something.  FLAGS also contains
#  configuration bits so that we can optimize for TWIN and WIDE controllers
#  as well as the MAX_SYNC bit which we set when we want to negotiate for
#  10MHz irregardless of what the per target scratch space says.
#
#  Note that SG_NEXT occupies four bytes.
#
SYNCNEG		= 0x20

REJBYTE		= 0x31
DISC_DSB_A	= 0x32
DISC_DSB_B	= 0x33

MSG_LEN		= 0x34
MSG_START+0	= 0x35
MSG_START+1	= 0x36
MSG_START+2	= 0x37
MSG_START+3	= 0x38
MSG_START+4	= 0x39
MSG_START+5	= 0x3a
-MSG_START+0	= 0xcb				# 2's complement of MSG_START+0

ARG_1		= 0x4a				# sdtr conversion args & return
BUS_16_BIT	= 0x01
RETURN_1	= 0x4a

SIGSTATE	= 0x4b				# value written to SCSISIGO

# Linux users should use 0xc (12) for SG_SIZEOF
SG_SIZEOF	= 0x8 				# sizeof(struct ahc_dma)
#SG_SIZEOF	= 0xc 				# sizeof(struct scatterlist)
SCB_SIZEOF	= 0x13				# sizeof SCB to DMA (19 bytes)

SG_NOLOAD	= 0x4c				# load SG pointer/length?
SG_COUNT	= 0x4d				# working value of SG count
SG_NEXT		= 0x4e				# working value of SG pointer
SG_NEXT+0	= 0x4e
SG_NEXT+1	= 0x4f
SG_NEXT+2	= 0x50
SG_NEXT+3	= 0x51

SCBCOUNT	= 0x52				# the actual number of SCBs
FLAGS		= 0x53				# Device configuration flags
TWIN_BUS	= 0x01
WIDE_BUS	= 0x02
MAX_SYNC	= 0x08
ACTIVE_MSG	= 0x20
IDENTIFY_SEEN	= 0x40
RESELECTED	= 0x80

ACTIVE_A	= 0x54
ACTIVE_B	= 0x55
SAVED_TCL	= 0x56				# Temporary storage for the 
						# target/channel/lun of a
						# reconnecting target

# After starting the selection hardware, we return to the "poll_for_work"
# loop so that we can 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 31 of the SCB as a psuedo-next pointer and to thread a list
# of SCBs that are awaiting selection.  Since 0 is a valid SCB offset, 
# SCB_LIST_NULL is 0x10 which is out of range.  The kernel driver must
# add an entry to this list everytime a request sense occurs.  The sequencer
# will automatically consume the entries.

WAITING_SCBH	= 0x57				# head of list of SCBs awaiting
						# selection
WAITING_SCBT	= 0x58				# tail of list of SCBs awaiting
						# selection
SCB_LIST_NULL	= 0x10


#  Poll QINCNT for work - the lower bits contain
#  the number of entries in the Queue In FIFO.
#
d84 2
a85 1
	test	WAITING_SCBH,SCB_LIST_NULL jz start_waiting
d87 8
a94 4
	test	FLAGS,TWIN_BUS	jz start2	# Are we a twin channel device?
# For fairness, we check the other bus first, since we just finished a 
# transaction on the current channel.
	xor	SBLKCTL,0x08			# Toggle to the other bus
d96 1
a96 2
	test	SSTAT0,SELDO	jnz select
	xor	SBLKCTL,0x08			# Toggle to the original bus
d99 10
a108 10
	test	SSTAT0,SELDO	jnz select
	test	WAITING_SCBH,SCB_LIST_NULL jz start_waiting
	test	QINCNT,SCBMASK	jz poll_for_work

# We have at least one queued SCB now and we don't have any 
# SCBs in the list of SCBs awaiting selection.  Set the SCB
# pointer from the FIFO so we see the right bank of SCB 
# registers, then set SCSI options and set the initiator and
# target SCSI IDs.
#
d111 14
a124 35
# If the control byte of this SCB has the NEEDDMA flag set, we have
# yet to DMA it from host memory

test    SCBARRAY+0,NEEDDMA      jz test_busy    
	clr	HCNT+2
	clr	HCNT+1
	mvi	HCNT+0,SCB_SIZEOF

	mvi	DINDEX,HADDR      
	mvi	SCBARRAY+26     call bcopy_4
        
	mvi	DFCNTRL,0xd                     # HDMAEN|DIRECTION|FIFORESET

#  Wait for DMA from host memory to data FIFO to complete, then disable
#  DMA and wait for it to acknowledge that it's off.
#
	call	dma_finish

# Copy the SCB from the FIFO to  the SCBARRAY

	mvi	DINDEX, SCBARRAY+0
	call	bcopy_3_dfdat 
	call	bcopy_4_dfdat
	call	bcopy_4_dfdat
	call	bcopy_4_dfdat   
	call	bcopy_4_dfdat

# See if there is not already an active SCB for this target.  This code
# locks out on a per target basis instead of target/lun.  Although this
# is not ideal for devices that have multiple luns active at the same
# time, it is faster than looping through all SCB's looking for active
# commands.  It may be benificial to make findscb a more general procedure
# to see if the added cost of the search is negligible.  This code also 
# assumes that the kernel driver will clear the active flags on board 
# initialization, board reset, and a target's SELTO.
d127 1
a127 2
	test	SCBARRAY+0,0x20	jnz start_scb
	and	FUNCTION1,0x70,SCBARRAY+1
d129 1
a129 1
	test	SCBARRAY+1,0x88	jz test_a	# Id < 8 && A channel
d132 3
a134 1
	or	ACTIVE_B,A	# Mark the current target as busy
d137 1
a137 1
# Place the currently active back on the queue for later processing
d142 5
a146 1
# Pull the first entry off of the waiting for selection list
d149 1
a149 1
	jmp	start_scb
d152 4
a155 2
	test	ACTIVE_A,A	jnz requeue
	or	ACTIVE_A,A	# Mark the current target as busy
d158 1
a158 13
	and	SINDEX,0xf7,SBLKCTL  #Clear the channel select bit
	and	A,0x08,SCBARRAY+1    #Get new channel bit
	or	SINDEX,A	     
	mov	SBLKCTL,SINDEX	# select channel
	mov	SCBARRAY+1	call initialize_scsiid

# Enable selection phase as an initiator, and do automatic ATN
# after the selection.  We do this now so that we can overlap the
# rest of our work to set up this target with the arbitration and
# selection bus phases.
#
start_selection:
	or	SCSISEQ,0x48			# ENSELO|ENAUTOATNO
d160 6
a165 2
	clr	SG_NOLOAD
	and	FLAGS,0x3f	# !RESELECTING
d167 8
a174 18
#  As soon as we get a successful selection, the target should go
#  into the message out phase since we have ATN asserted.  Prepare
#  the message to send, locking out the device driver.  If the device
#  driver hasn't beaten us with an ABORT or RESET message, then tack
#  on an SDTR negotiation if required.
#
#  Messages are stored in scratch RAM starting with a flag byte (high bit
#  set means active message), one length byte, and then the message itself.
#
	mov	SCBARRAY+1	call disconnect	# disconnect ok?

	and	SINDEX,0x7,SCBARRAY+1		# lun
	or	SINDEX,A			# return value from disconnect
	or	SINDEX,0x80	call mk_mesg	# IDENTIFY message

	mov	A,SINDEX
	test	SCBARRAY+0,0xe0	jz  !message	# WDTR, SDTR or TAG??
	cmp	MSG_START+0,A	jne !message	# did driver beat us?
d176 32
a207 2
# Tag Message if Tag enabled in SCB control block.  Use SCBPTR as the tag
# value
d210 4
a213 5
	mvi	DINDEX, MSG_START+1
	test	SCBARRAY+0,TAG_ENB jz mk_tag_done
	and	A,0x23,SCBARRAY+0
	mov	DINDIR,A
	mov	DINDIR,SCBPTR
d215 1
a215 1
	add	MSG_LEN,-MSG_START+0,DINDEX	# update message length
d219 2
a220 1
	mov	DINDEX	call mk_dtr	# build DTR message if needed
d223 9
a231 6
	jmp	poll_for_work

#  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.
#
d233 1
d235 2
a236 2
	and	FLAGS,0x3f			# reselected, no IDENTIFY	
	or	FLAGS,RESELECTED jmp select2
d238 6
a243 5
# After the selection, remove this SCB from the "waiting for selection"
# list.  This is achieved by simply moving our "next" pointer into
# WAITING_SCBH and setting our next pointer to null so that the next
# time this SCB is used, we don't get confused.
#
d245 2
a246 3
	or	SCBARRAY+0,NEEDDMA
	mov	WAITING_SCBH,SCBARRAY+30
	mvi	SCBARRAY+30,SCB_LIST_NULL
d248 26
a273 15
	call	initialize_for_target
	mvi	SCSISEQ,ENRSELI
	mvi	CLRSINT0,0x60			# CLRSELDI|CLRSELDO
	mvi	CLRSINT1,0x8			# CLRBUSFREE

#  Main loop for information transfer phases.  If BSY is false, then
#  we have a bus free condition, expected or not.  Otherwise, wait
#  for the target to assert REQ before checking MSG, C/D and I/O
#  for the bus phase.
#
#  We can't simply look at the values of SCSISIGI here (if we want
#  to do synchronous data transfer), because the target won't assert
#  REQ if it's already sent us some data that we haven't acknowledged
#  yet.
#
d275 2
a276 2
	test	SSTAT1,0x8	jnz p_busfree	# BUSFREE
	test	SSTAT1,0x1	jz ITloop	# REQINIT
d278 3
a280 1
	and	A,0xe0,SCSISIGI			# CDI|IOI|MSGI
d283 5
a287 5
	cmp	A,0x40		je p_datain
	cmp	A,0x80		je p_command
	cmp	A,0xc0		je p_status
	cmp	A,0xa0		je p_mesgout
	cmp	A,0xe0		je p_mesgin
d289 1
a289 1
	mvi	INTSTAT,BAD_PHASE		# unknown - signal driver
d292 15
a306 40
	mvi	0		call scsisig	# !CDO|!IOO|!MSGO
	call	assert
	call	sg_load

	mvi	DINDEX,HADDR
	mvi	SCBARRAY+19	call bcopy_4

#	mvi	DINDEX,HCNT	# implicit since HCNT is next to HADDR
	mvi	SCBARRAY+23	call bcopy_3

	mvi	DINDEX,STCNT
	mvi	SCBARRAY+23	call bcopy_3

# If we are the last SG block, don't set wideodd.
	test    SCBARRAY+18,0xff jnz p_dataout_wideodd
	mvi	0x3d		call dma	# SCSIEN|SDMAEN|HDMAEN|
						#   DIRECTION|FIFORESET
	jmp	p_dataout_rest

p_dataout_wideodd:
	mvi	0xbd		call dma	# WIDEODD|SCSIEN|SDMAEN|HDMAEN|
						#   DIRECTION|FIFORESET

p_dataout_rest:
#  After a DMA finishes, save the final transfer pointer and count
#  back into the SCB, in case a device disconnects in the middle of
#  a transfer.  Use SHADDR and STCNT instead of HADDR and HCNT, since
#  it's a reflection of how many bytes were transferred on the SCSI
#  (as opposed to the host) bus.
#
	mvi	DINDEX,SCBARRAY+23
	mvi	STCNT		call bcopy_3

	mvi	DINDEX,SCBARRAY+19
	mvi	SHADDR		call bcopy_4

	call	sg_advance
	mov	SCBARRAY+18,SG_COUNT		# residual S/G count

	jmp	ITloop
d309 5
a313 1
	mvi	0x40		call scsisig	# !CDO|IOO|!MSGO
a314 1
	call	sg_load
d316 3
a318 2
	mvi	DINDEX,HADDR
	mvi	SCBARRAY+19	call bcopy_4
d320 15
a334 2
#	mvi	DINDEX,HCNT	# implicit since HCNT is next to HADDR
	mvi	SCBARRAY+23	call bcopy_3
d336 1
a336 14
	mvi	DINDEX,STCNT
	mvi	SCBARRAY+23	call bcopy_3

# If we are the last SG block, don't set wideodd.
	test	SCBARRAY+18,0xff jnz p_datain_wideodd
	mvi	0x39		call dma	# SCSIEN|SDMAEN|HDMAEN|
						#   !DIRECTION|FIFORESET
	jmp	p_datain_rest
p_datain_wideodd:
	mvi	0xb9		call dma	# WIDEODD|SCSIEN|SDMAEN|HDMAEN|
						#   !DIRECTION|FIFORESET
p_datain_rest:
	mvi	DINDEX,SCBARRAY+23
	mvi	STCNT		call bcopy_3
d338 33
a370 5
	mvi	DINDEX,SCBARRAY+19
	mvi	SHADDR		call bcopy_4

	call	sg_advance
	mov	SCBARRAY+18,SG_COUNT		# residual S/G count
d372 54
d428 3
a430 4
#  Command phase.  Set up the DMA registers and let 'er rip - the
#  two bytes after the SCB SCSI_cmd_length are zeroed by the driver,
#  so we can copy those three bytes directly into HCNT.
#
a431 1
	mvi	0x80		call scsisig	# CDO|!IOO|!MSGO
d434 14
a447 8
	mvi	DINDEX,HADDR
	mvi	SCBARRAY+7	call bcopy_4

#	mvi	DINDEX,HCNT	# implicit since HCNT is next to HADDR
	mvi	SCBARRAY+11	call bcopy_3

	mvi	DINDEX,STCNT
	mvi	SCBARRAY+11	call bcopy_3
d453 4
a456 3
#  Status phase.  Wait for the data byte to appear, then read it
#  and store it into the SCB.
#
d458 2
a459 1
	mvi	0xc0		call scsisig	# CDO|IOO|!MSGO
d461 4
a464 6
	mvi	SCBARRAY+14	call inb_first
	jmp	p_mesgin_done

#  Message out phase.  If there is no active message, but the target
#  took us into this phase anyway, build a no-op message and send it.
#
d466 2
a467 5
	mvi	0xa0		call scsisig	# CDO|!IOO|MSGO
	mvi	0x8		call mk_mesg	# build NOP message

	clr     STCNT+2
	clr     STCNT+1
d469 6
a474 4
#  Set up automatic PIO transfer from MSG_START.  Bit 3 in
#  SXFRCTL0 (SPIOEN) is already on.
#
	mvi	SINDEX,MSG_START+0
d477 15
a491 32
#  When target asks for a byte, drop ATN if it's the last one in
#  the message.  Otherwise, keep going until the message is exhausted.
#  (We can't use outb for this since it wants the input in SINDEX.)
#
#  Keep an eye out for a phase change, in case the target issues
#  a MESSAGE REJECT.
#
p_mesgout2:
	test	SSTAT0,0x2	jz p_mesgout2	# SPIORDY
	test	SSTAT1,0x10	jnz p_mesgout6	# PHASEMIS

	cmp	DINDEX,1	jne p_mesgout3	# last byte?
	mvi	CLRSINT1,0x40			# CLRATNO - drop ATN

#  Write a byte to the SCSI bus.  The AIC-7770 refuses to automatically
#  send ACKs in automatic PIO or DMA mode unless you make sure that the
#  "expected" bus phase in SCSISIGO matches the actual bus phase.  This
#  behaviour is completely undocumented and caused me several days of
#  grief.
#
#  After plugging in different drives to test with and using a longer
#  SCSI cable, I found that I/O in Automatic PIO mode ceased to function,
#  especially when transferring >1 byte.  It seems to be much more stable
#  if STCNT is set to one before the transfer, and SDONE (in SSTAT0) is
#  polled for transfer completion - for both output _and_ input.  The
#  only theory I have is that SPIORDY doesn't drop right away when SCSIDATL
#  is accessed (like the documentation says it does), and that on a longer
#  cable run, the sequencer code was fast enough to loop back and see
#  an SPIORDY that hadn't dropped yet.
#
p_mesgout3:
	mvi	STCNT+0, 0x01	
d493 11
d505 1
a505 4
p_mesgout4:
	test	SSTAT0,0x4	jz p_mesgout4	# SDONE
	dec	DINDEX
	test	DINDEX,0xff	jnz p_mesgout2
d507 1
a507 10
#  If the next bus phase after ATN drops is a message out, it means
#  that the target is requesting that the last message(s) be resent.
#
p_mesgout5:
	test	SSTAT1,0x8	jnz p_mesgout6	# BUSFREE
	test	SSTAT1,0x1	jz p_mesgout5	# REQINIT

	and	A,0xe0,SCSISIGI			# CDI|IOI|MSGI
	cmp	A,0xa0		jne p_mesgout6
	mvi	0x10		call scsisig	# ATNO - re-assert ATN
d511 4
a514 3
p_mesgout6:
	mvi	CLRSINT1,0x40			# CLRATNO - in case of PHASEMIS
	and	FLAGS,0xdf			# no active msg
d517 3
a519 8
#  Message in phase.  Bytes are read using Automatic PIO mode, but not
#  using inb.  This alleviates a race condition, namely that if ATN had
#  to be asserted under Automatic PIO mode, it had to beat the SCSI
#  circuitry sending an ACK to the target.  This showed up under heavy
#  loads and really confused things, since ABORT commands wouldn't be
#  seen by the drive after an IDENTIFY message in until it had changed
#  to a data I/O phase.
#
d521 60
a580 25
	mvi	0xe0		call scsisig	# CDO|IOO|MSGO
	mvi	A		call inb_first	# read the 1st message byte
	mvi	REJBYTE,A			# save it for the driver

	cmp	ALLZEROS,A	jne p_mesgin1

#  We got a "command complete" message, so put the SCB pointer
#  into the Queue Out, and trigger a completion interrupt.
#  Check status for non zero return and interrupt driver if needed
#  This allows the driver to interpret errors only when they occur
#  instead of always uploading the scb.  If the status is SCSI_CHECK,
#  the driver will download a new scb requesting sense to replace
#  the old one, modify the "waiting for selection" SCB list and set 
#  RETURN_1 to 0x80.  If RETURN_1 is set to 0x80 the sequencer imediately
#  jumps to main loop where it will run down the waiting SCB list.
#  If the kernel driver does not wish to request sense, it need
#  only clear RETURN_1, and the command is allowed to complete.  We don't 
#  bother to post to the QOUTFIFO in the error case 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
	test	SCBARRAY+15,0xff	jnz resid
	test	SCBARRAY+16,0xff	jnz resid
	test	SCBARRAY+17,0xff	jnz resid
d583 4
a586 4
	test	SCBARRAY+14,0xff	jz status_ok	# 0 Status?
	mvi	INTSTAT,BAD_STATUS			# let driver know
	test	RETURN_1, 0x80	jz status_ok
	jmp	p_mesgin_done
d589 7
a595 3
#  First, mark this target as free.
	test	SCBARRAY+0,0x20	jnz complete		# Tagged command
	and	FUNCTION1,0x70,SCBARRAY+1
d597 1
a597 1
	test	SCBARRAY+1,0x88 jz clear_a
d599 1
a599 1
	jmp	complete
d604 9
d614 1
a614 1
	mov	QOUTFIFO,SCBPTR
d616 1
a616 12
	jmp	p_mesgin_done

# If we have a residual count, interrupt and tell the host.  Other
# alternatives are to pause the sequencer on all command completes (yuck),
# dma the resid directly to the host (slick, but a ton of instructions), or
# have the sequencer pause itself when it encounters a non-zero resid 
# (unecessary pause just to flag the command -- yuck, but takes few instructions
# and since it shouldn't happen that often is good enough for our purposes).  

resid:
	mvi	INTSTAT,RESIDUAL
	jmp	check_status
a617 11
#  Is it an extended message?  We only support the synchronous and wide data
#  transfer request messages, which will probably be in response to
#  WDTR or SDTR message outs from us.  If it's not SDTR or WDTR, reject it -
#  apparently this can be done after any message in byte, according
#  to the SCSI-2 spec.
#
p_mesgin1:
	cmp	A,1		jne p_mesgin2	# extended message code?
	
	mvi	ARG_1		call inb_next	# extended message length
	mvi	A		call inb_next	# extended message code
d619 14
a632 3
	cmp	A,1		je p_mesginSDTR	# Syncronous negotiation message
	cmp	A,3		je p_mesginWDTR # Wide negotiation message
	jmp	p_mesginN
d635 15
a649 13
	cmp	ARG_1,2		jne p_mesginN	# extended mesg length = 2
	mvi	A		call inb_next	# Width of bus
	mvi	INTSTAT,MSG_WDTR		# let driver know
	test	RETURN_1,0x80	jz p_mesgin_done# Do we need to send WDTR?

# We didn't initiate the wide negotiation, so we must respond to the request
	and	RETURN_1,0x7f			# Clear the SEND_WDTR Flag
	or	FLAGS,ACTIVE_MSG
	mvi	DINDEX,MSG_START+0
	mvi	MSG_START+0	call mk_wdtr	# build WDTR message	
	or	SINDEX,0x10,SIGSTATE		# turn on ATNO
	call	scsisig
	jmp	p_mesgin_done
d652 45
a696 29
	cmp	ARG_1,3		jne p_mesginN	# extended mesg length = 3
	mvi	ARG_1		call inb_next	# xfer period
	mvi	A		call inb_next	# REQ/ACK offset
	mvi	INTSTAT,MSG_SDTR		# call driver to convert

	test	RETURN_1,0xc0	jz p_mesgin_done# Do we need to mk_sdtr or rej?
	test	RETURN_1,0x40	jnz p_mesginN	# Requested SDTR too small - rej
	or	FLAGS,ACTIVE_MSG
	mvi	DINDEX, MSG_START+0
	mvi     MSG_START+0     call mk_sdtr
	or	SINDEX,0x10,SIGSTATE		# turn on ATNO
	call	scsisig
	jmp	p_mesgin_done

#  Is it a disconnect message?  Set a flag in the SCB to remind us
#  and await the bus going free.
#
p_mesgin2:
	cmp	A,4		jne p_mesgin3	# disconnect code?

	or	SCBARRAY+0,0x4			# set "disconnected" bit
	jmp	p_mesgin_done

#  Save data pointers message?  Copy working values into the SCB,
#  usually in preparation for a disconnect.
#
p_mesgin3:
	cmp	A,2		jne p_mesgin4	# save data pointers code?

d698 1
a698 1
	jmp	p_mesgin_done
d700 20
a719 6
#  Restore pointers message?  Data pointers are recopied from the
#  SCB anyway at the start of any DMA operation, so the only thing
#  to copy is the scatter-gather values.
#
p_mesgin4:
	cmp	A,3		jne p_mesgin5	# restore pointers code?
d721 1
a721 13
	call	sg_scb2ram
	jmp	p_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.
#
p_mesgin5:
	test	A,0x80		jz p_mesgin6	# identify message?

	test	A,0x78		jnz p_mesginN	# !DiscPriv|!LUNTAR|!Reserved

	and	A,0x07				# lun in lower three bits
d724 1
a724 1
	and     A,0x08,SBLKCTL			# B Channel??
d726 36
a761 2
	call	inb_last			# ACK
	mov	ALLZEROS	call findSCB    
d763 2
a764 5
	and	SCBARRAY+0,0xfb			# clear disconnect bit in SCB
	or	FLAGS,IDENTIFY_SEEN		# make note of IDENTIFY

	call	sg_scb2ram			# implied restore pointers
						#   required on reselect
d766 2
a767 7
get_tag:
	mvi	A		call inb_first
	cmp	A,0x20  	jne return	# Simple Tag message?
	mvi	A		call inb_next
	call			inb_last
	test	A,0xf0		jnz abort_tag	# Tag in range?
	mov	SCBPTR,A
d769 5
a773 3
	cmp	SCBARRAY+1,A		jne abort_tag
	test	SCBARRAY+0,TAG_ENB	jz  abort_tag
	ret
d775 27
a801 32
	or	SINDEX,0x10,SIGSTATE		# turn on ATNO
	call	scsisig
	mvi	INTSTAT,ABORT_TAG 		# let driver know
	mvi	0xd		call mk_mesg	# ABORT TAG message
	ret

#  Message reject?  Let the kernel driver handle this.  If we have an 
#  outstanding WDTR or SDTR negotiation, assume that it's a response from 
#  the target selecting 8bit or asynchronous transfer, otherwise just ignore 
#  it since we have no clue what it pertains to.
#
p_mesgin6:
	cmp	A,7		jne p_mesgin7	# message reject code?

	mvi	INTSTAT, MSG_REJECT
	jmp	p_mesgin_done

#  [ ADD MORE MESSAGE HANDLING HERE ]
#
p_mesgin7:

#  We have no idea what this message in is, and there's no way
#  to pass it up to the kernel, so we issue a message reject and
#  hope for the best.  Since we're now using manual PIO mode to
#  read in the message, there should no longer be a race condition
#  present when we assert ATN.  In any case, rejection should be a
#  rare occurrence - signal the driver when it happens.
#
p_mesginN:
	or	SINDEX,0x10,SIGSTATE		# turn on ATNO
	call	scsisig
	mvi	INTSTAT,SEND_REJECT		# let driver know
d803 5
a807 14
	mvi	0x7		call mk_mesg	# MESSAGE REJECT message

p_mesgin_done:
	call	inb_last			# ack & turn auto PIO back on
	jmp	ITloop


#  Bus free phase.  It might be useful to interrupt the device
#  driver if we aren't expecting this.  For now, make sure that
#  ATN isn't being asserted and look for a new command.
#
p_busfree:
	mvi	CLRSINT1,0x40			# CLRATNO
	clr	SIGSTATE
d810 7
a816 17
#  Instead of a generic bcopy routine that requires an argument, we unroll
#  the two cases that are actually used, and call them explicitly.  This
#  not only reduces the overhead of doing a bcopy by 2/3rds, but ends up
#  saving space in the program since you don't have to put the argument 
#  into the accumulator before the call.  Both functions expect DINDEX to
#  contain the destination address and SINDEX to contain the source 
#  address.
bcopy_3:
	mov	DINDIR,SINDIR
	mov	DINDIR,SINDIR
	mov	DINDIR,SINDIR	ret

bcopy_4:
	mov	DINDIR,SINDIR
	mov	DINDIR,SINDIR
	mov	DINDIR,SINDIR
	mov	DINDIR,SINDIR	ret
d818 8
a825 21
bcopy_3_dfdat:
	mov	DINDIR,DFDAT
	mov	DINDIR,DFDAT
	mov	DINDIR,DFDAT	ret

bcopy_4_dfdat:
	mov	DINDIR,DFDAT
	mov	DINDIR,DFDAT
	mov	DINDIR,DFDAT
	mov	DINDIR,DFDAT	ret

#  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:
	mvi	SEQCTL,0x50			# PAUSEDIS|FASTMODE
	test	FLAGS,ACTIVE_MSG jnz mk_mesg1	# active message?

	or	FLAGS,ACTIVE_MSG		# if not, there is now
	mvi	MSG_LEN,1			# length = 1
	mov	MSG_START+0,SINDEX		# 1-byte message
d828 19
a846 1
	mvi	SEQCTL,0x10	ret		# !PAUSEDIS|FASTMODE
d848 6
a853 17
#  Carefully read data in Automatic PIO mode.  I first tried this using
#  Manual PIO mode, but it gave me continual underrun errors, probably
#  indicating that I did something wrong, but I feel more secure leaving
#  Automatic PIO on all the time.
#
#  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.
#
a854 2
	clr	STCNT+2
	clr	STCNT+1
d856 3
a858 1
	mov	DINDIR,SCSIBUSL	ret		# read byte directly from bus
d860 6
a865 10
inb_next:
	mov	DINDEX,SINDEX			# save SINDEX

        mvi     STCNT+0,1			# xfer one byte
	mov	NONE,SCSIDATL			# dummy read from latch to ACK
inb_next1:
	test	SSTAT0,0x4	jz inb_next1	# SDONE
inb_next2:
	test	SSTAT0,0x2	jz inb_next2	# SPIORDY - wait for next byte
	mov	DINDIR,SCSIBUSL	ret		# read byte directly from bus
d867 6
a872 12
inb_last:
	mvi	STCNT+0,1			# ACK with dummy read
	mov	NONE,SCSIDATL
inb_last1:
	test	SSTAT0,0x4	jz inb_last1	# wait for completion
	ret

#  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.
#
d876 11
a886 11
dma2:
	test	SSTAT0,0x1	jnz dma3	# DMADONE
	test	SSTAT1,0x10	jz dma1		# PHASEMIS, ie. underrun

#  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.
#
d888 1
a888 1
	test	SINDEX,0x4	jnz dma5	# DIRECTION
d890 1
a890 1
	test	DFSTATUS,0x1	jz dma4		# !FIFOEMP
d892 4
a895 5
#  Now shut the DMA enables off, and copy STCNT (ie. the underrun
#  amount, if any) to the SCB registers; SG_COUNT will get copied to
#  the SCB's residual S/G count field after sg_advance is called.  Make
#  sure that the DMA enables are actually off first lest we get an ILLSADDR.
#
d897 2
a898 1
	clr	DFCNTRL				# disable DMA
d900 1
a900 9
	test	DFCNTRL,0x38	jnz dma6	# SCSIENACK|SDMAENACK|HDMAENACK

	mvi	DINDEX,SCBARRAY+15
	mvi	STCNT		call bcopy_3

	ret

dma_finish:
	test	DFSTATUS,0x8	jz dma_finish	# HDONE
a901 3
	clr	DFCNTRL				# disable DMA
dma_finish2:
	test	DFCNTRL,0x8	jnz dma_finish2	# HDMAENACK
d904 5
a908 4
#  Common SCSI initialization for selection and reselection.  Expects
#  the target SCSI ID to be in the upper four bits of SINDEX, and A's
#  contents are stomped on return.
#
d910 1
a910 1
	and	SINDEX,0xf0		# Get target ID
d915 4
a918 24
initialize_for_target:
#  Turn on Automatic PIO mode now, before we expect to see a REQ
#  from the target.  It shouldn't hurt anything to leave it on.  Set
#  CLRCHN here before the target has entered a data transfer mode -
#  with synchronous SCSI, if you do it later, you blow away some
#  data in the SCSI FIFO that the target has already sent to you.
#
	clr	SIGSTATE 

	mvi	SXFRCTL0,0x8a			# DFON|SPIOEN|CLRCHN

#  Initialize scatter-gather pointers by setting up the working copy
#  in scratch RAM.
#
	call	sg_scb2ram

#  Initialize SCSIRATE with the appropriate value for this target.
#
	call	ndx_dtr
	mov	SCSIRATE,SINDIR	ret

#  Assert that if we've been reselected, then we've seen an IDENTIFY
#  message.
#
d920 2
a921 17
	test	FLAGS,RESELECTED	jz return	# reselected?
	test	FLAGS,IDENTIFY_SEEN	jnz return	# seen IDENTIFY?

	mvi	INTSTAT,NO_IDENT 	ret	# no - cause a kernel panic

#  Find out if disconnection is ok from the information the BIOS has left
#  us.  The tcl from SCBARRAY+1 should be in SINDEX; A will
#  contain either 0x40 (disconnection ok) or 0x00 (disconnection not ok)
#  on exit.
#
#  To allow for wide or twin busses, we check the upper bit of the target ID
#  and the channel ID and look at the appropriate disconnect register. 
#
disconnect:
	and	FUNCTION1,0x70,SINDEX		# strip off extra just in case
	mov	A,FUNCTION1
	test	SINDEX, 0x88	jz disconnect_a
d923 1
a923 2
	test	DISC_DSB_B,A	jz disconnect1	# bit nonzero if DISabled
	clr	A		ret
d925 7
a931 12
disconnect_a:
	test	DISC_DSB_A,A	jz disconnect1	# bit nonzero if DISabled
	clr	A		ret

disconnect1:
	mvi	A,0x40		ret

#  Locate the SCB matching the target ID/channel/lun in SAVED_TCL and switch 
#  the SCB to it.  Have the kernel print a warning message if it can't be 
#  found, and generate an ABORT message to the target.  SINDEX should be
#  cleared on call.
#
d934 29
a962 5
	mov	SCBPTR,SINDEX			# switch to new SCB
	cmp	SCBARRAY+1,A	jne findSCB1	# target ID/channel/lun match?
	test	SCBARRAY+0,0x4	jz findSCB1	# should be disconnected
	test	SCBARRAY+0,TAG_ENB jnz get_tag
	ret
d965 1
d970 9
a978 2
	mvi	INTSTAT,NO_MATCH		# not found - signal kernel
	mvi	0x6		call mk_mesg	# ABORT message
d980 3
a982 6
	or	SINDEX,0x10,SIGSTATE		# assert ATNO
	call	scsisig
	ret

#  Make a working copy of the scatter-gather parameters in the SCB.
#
d984 25
a1008 11
	mov	SG_COUNT,SCBARRAY+2

	mvi	DINDEX,SG_NEXT
	mvi	SCBARRAY+3	call bcopy_4

	mvi	SG_NOLOAD,0x80
	test	SCBARRAY+0,0x10	jnz return	# don't reload s/g?
	clr	SG_NOLOAD	 ret

#  Copying RAM values back to SCB, for Save Data Pointers message.
#
d1010 2
a1011 1
	mov	SCBARRAY+2,SG_COUNT
d1013 22
a1034 94
	mvi	DINDEX,SCBARRAY+3
	mvi	SG_NEXT		call bcopy_4

	and	SCBARRAY+0,0xef,SCBARRAY+0
	test	SG_NOLOAD,0x80	jz return	# reload s/g?
	or	SCBARRAY+0,SG_LOAD	 ret

#  Load a struct scatter if needed 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 the above DMA, assumes a little-endian host data storage.
#
sg_load:
	test	SG_COUNT,0xff	jz return	# SG being used?
	test	SG_NOLOAD,0x80	jnz return	# don't reload s/g?

	clr	HCNT+2
	clr	HCNT+1
	mvi	HCNT+0,SG_SIZEOF

	mvi	DINDEX,HADDR
	mvi	SG_NEXT		call bcopy_4

	mvi	DFCNTRL,0xd			# HDMAEN|DIRECTION|FIFORESET

#  Wait for DMA from host memory to data FIFO to complete, then disable
#  DMA and wait for it to acknowledge that it's off.
#

	call	dma_finish

#  Copy data from FIFO into SCB data pointer and data count.  This assumes
#  that the struct scatterlist has this structure (this and sizeof(struct
#  scatterlist) == 12 are asserted in aic7xxx.c):
#
#	struct scatterlist {
#		char *address;		/* four bytes, little-endian order */
#		...			/* four bytes, ignored */
#		unsigned short length;	/* two bytes, little-endian order */
#	}
#

# Not in FreeBSD.  the scatter list entry is only 8 bytes.
# 
# struct ahc_dma_seg {
#       physaddr addr;                  /* four bytes, little-endian order */
#       long    len;                    /* four bytes, little endian order */   
# };
#

	mvi	DINDEX, SCBARRAY+19
	call	bcopy_4_dfdat

# For Linux, we must throw away four bytes since there is a 32bit gap
# in the middle of a struct scatterlist
#	mov	NONE,DFDAT
#	mov	NONE,DFDAT
#	mov	NONE,DFDAT
#	mov	NONE,DFDAT

	call	bcopy_3_dfdat		#Only support 24 bit length.
	ret

#  Advance the scatter-gather pointers only IF NEEDED.  If SG is enabled,
#  and the SCSI transfer count is zero (note that this should be called
#  right after a DMA finishes), then move the working copies of the SG
#  pointer/length along.  If the SCSI transfer count is not zero, then
#  presumably the target is disconnecting - do not reload the SG values
#  next time.
#
sg_advance:
	test	SG_COUNT,0xff	jz return	# s/g enabled?

	test	STCNT+0,0xff	jnz sg_advance1	# SCSI transfer count nonzero?
	test	STCNT+1,0xff	jnz sg_advance1
	test	STCNT+2,0xff	jnz sg_advance1

	clr	SG_NOLOAD			# reload s/g next time
	dec	SG_COUNT			# one less segment to go

	clr	A				# add sizeof(struct scatter)
	add	SG_NEXT+0,SG_SIZEOF,SG_NEXT+0
	adc	SG_NEXT+1,A,SG_NEXT+1
	adc	SG_NEXT+2,A,SG_NEXT+2
	adc	SG_NEXT+3,A,SG_NEXT+3	ret

sg_advance1:
	mvi	SG_NOLOAD,0x80	ret		# don't reload s/g next time

#  Add the array base SYNCNEG to the target offset (the target address
#  is in SCSIID), and return the result in SINDEX.  The accumulator
#  contains the 3->8 decoding of the target ID on return.
#
d1037 2
a1038 2
	test	SBLKCTL,0x08	jz ndx_dtr_2
	or	A,0x08		# Channel B entries add 8
d1040 1
a1040 1
	add	SINDEX,SYNCNEG,A
d1042 7
a1048 9
	and	FUNCTION1,0x70,SCSIID		# 3-bit target address decode
	mov	A,FUNCTION1	ret

#  If we need to negotiate transfer parameters, build the WDTR or SDTR message
#  starting at the address passed in SINDEX.  DINDEX is modified on return.
#  The SCSI-II spec requires that Wide negotiation occur first and you can
#  only negotiat one or the other at a time otherwise in the event of a message
#  reject, you wouldn't be able to tell which message was the culpret.
#
d1050 2
a1051 3
	test	SCBARRAY+0,0xc0 jz return	# NEEDWDTR|NEEDSDTR
	test	SCBARRAY+0,NEEDWDTR jnz  mk_wdtr_16bit
	or	FLAGS, MAX_SYNC		 # Force an offset of 15
d1054 3
a1056 3
	mvi	DINDIR,1			# extended message
	mvi	DINDIR,3			# extended message length = 3
	mvi	DINDIR,1			# SDTR code
d1058 3
a1060 3
	mov	DINDIR,RETURN_1			# REQ/ACK transfer period
	test	FLAGS, MAX_SYNC	jnz mk_sdtr_max_sync
	and	DINDIR,0xf,SINDIR		# Sync Offset
d1063 1
a1063 1
	add	MSG_LEN,-MSG_START+0,DINDEX ret	# update message length
d1065 11
a1075 4
mk_sdtr_max_sync:
# We're initiating sync negotiation, so request the max offset we can (15)
	mvi	DINDIR, 0x0f
	xor	FLAGS, MAX_SYNC
d1081 4
a1084 4
	mvi	DINDIR,1			# extended message
	mvi	DINDIR,2			# extended message length = 2
	mvi	DINDIR,3			# WDTR code
	mov	DINDIR,ARG_1			# bus width
d1086 1
a1086 1
	add	MSG_LEN,-MSG_START+0,DINDEX ret	# update message length
a1087 11
#  Set SCSI bus control signal state.  This also saves the last-written
#  value into a location where the higher-level driver can read it - if
#  it has to send an ABORT or RESET message, then it needs to know this
#  so it can assert ATN without upsetting SCSISIGO.  The new value is
#  expected in SINDEX.  Change the actual state last to avoid contention
#  from the driver.
#
scsisig:
	mov	SIGSTATE,SINDEX
	mov	SCSISIGO,SINDEX	ret

d1089 1
a1089 1
	call	ndx_dtr				# index scratch space for target
d1091 1
a1091 1
	dec	SINDEX				#Preserve SINDEX
d1096 1
a1096 1
	add	RETURN_1,0x18
d1101 3
a1103 2
	add	RETURN_1,0x18	ret

@
