*  Program:  SCSIcheck.asm ©1990 by Jeff Lavin
*  Descrip:  Check the SCSI bus for functional drive.
*    Usage:  1> SCSIcheck <unit>
*   Author:  Jeff Lavin
*  History:  05/23/90 V1.00 Created

SYS	macro
	jsr	_LVO\1(a6)
	endm

STRUCTURE	macro
\1	set	0
SOFFSET	set	\2
	endm

APTR	macro
\1	equ	SOFFSET
SOFFSET	set	SOFFSET+4
	endm

BPTR	macro
\1	equ	SOFFSET
SOFFSET	set	SOFFSET+4
	endm

LONG	macro
\1	equ	SOFFSET
SOFFSET	set	SOFFSET+4
	endm

WORD	macro
\1	equ	SOFFSET
SOFFSET	set	SOFFSET+2
	endm

BYTE	macro
\1	equ	SOFFSET
SOFFSET	set	SOFFSET+1
	endm

STRUCT	macro
\1	equ	SOFFSET
SOFFSET	set	SOFFSET+\2
	endm

LABEL	macro
\1	equ	SOFFSET
	endm

SysBase	equ	4	;AbsExecBase

cd_BoardAddr	equ	$20	;ConfigDev Structure

* AutoConfig Board Equates

MANF_CLTD	equ	1004	;Manufacturer number
PROD_12	equ	12	;Current product rev number
PROD_14	equ	14	;Earlier product rev number
CLTD_HOST	equ	7	;SCSI Unit number of Host Controller

* Class 00 SCSI Command Structure - (General form, specific commands vary)

sc_cmd	equ	0	;Command OpCode
sc_adrH	equ	1	;Logical Block Address (MSB)
sc_adrM	equ	2	;Logical Block Address
sc_adrL	equ	3	;Logical Block Address (LSB)
sc_count	equ	4	;Block Count
sc_reserv	equ	5	;Reserved

* NCR5380 SCSI Host Controller registers  - Offset = ([reg] * 2) + 1
* ------------------------------------------------------------------
ncr_data	equ	$01	;(0) Current SCSI Data           (R)
			;    Output Data                 (W)
ncr_icmd	equ	$03	;(1) Initiator Command           (R/W)
ncr_mode	equ	$05	;(2) Mode                        (R/W)
ncr_tcmd	equ	$07	;(3) Target Command              (R/W)
ncr_bstat	equ	$09	;(4) Current SCSI Bus Status     (R)
			;    Select Enable               (W)
ncr_stat	equ	$0B	;(5) Bus and Status              (R)
			;    Start DMA Send              (W)
ncr_input	equ	$0D	;(6) Input Data                  (R)
			;    Start DMA Target Receive    (W)
ncr_reset	equ	$0F	;(7) Reset Parity/Interrupts     (R)
			;    Start DMA Initiator Receive (W)
* SCSI Command OpCodes

REZERO_UNIT	equ	$01
REQ_SENSE	equ	$03
WRITE_BUF	equ	$13
READ_BUF	equ	$14

* Equates for Initiator Command Register (1)

ASS_NOT	equ	%00000000	;Assert nothing, i.e., free data bus
ASS_BUS	equ	%00000001	;Assert Data Bus
ASS_ATN	equ	%00000010	;Assert Attention
ASS_SEL	equ	%00000100	;Assert Select
ASS_BSY	equ	%00001000	;Assert Busy
ASS_ACK	equ	%00010000	;Assert Acknowledge
ASS_RST	equ	%10000000	;Assert Reset

* SCSI Information Transfer Phases (for Target Command Register (3))

ASS_IO	equ	%00000001	;Assert I/O
ASS_CD	equ	%00000010	;Assert C/D
ASS_MSG	equ	%00000100	;Assert MSG

PH_DOUT	equ	0	;Data Out Phase
PH_DIN	equ	ASS_IO	;Data In Phase
PH_CMD	equ	ASS_CD	;Command Phase
PH_STAT	equ	ASS_IO!ASS_CD	;Status Phase
PH_MOUT	equ	ASS_CD!ASS_MSG	;Message Out Phase
PH_MIN	equ	ASS_IO!ASS_CD!ASS_MSG ;Message In Phase

* Bit defs for Bus Status Register (4)

BCBS_DBP	equ	0	;Data Bus Parity Status
BCBS_SEL	equ	1	;Select Phase Status
BCBS_IO	equ	2	;I/O Direction
BCBS_CD	equ	3	;Control / Data Status
BCBS_MSG	equ	4	;Message Phase
BCBS_REQ	equ	5	;Data Request Status
BCBS_BSY	equ	6	;SCSI Bus Busy Status
BCBS_RST	equ	7	;SCSI Bus Reset Status

* Equates for Status Register (5)

PHAS_MATCH	equ	3	;Phase match bit def

* Program variables

               STRUCTURE	WorkArea,0
	APTR	InitialSP
	APTR	DosBase
	APTR	ExpBase
	BPTR	stdout
	WORD	Error	;Offset into error msg table
	WORD	Unit	;SCSI unit number <0-7>
	STRUCT	FmtArgs,4*4	;Format args
	STRUCT	SCSICmd,6	;SCSI command structure
	STRUCT	OutBuf,120	;Output buffer
	STRUCT	ScsiData,1024	;Block of SCSI data
	LABEL	Work_Sizeof

_LVOCloseLibrary	equ	-$19E
_LVORawDoFmt	equ	-$20A
_LVOOpenLibrary	equ	-$228

_LVOWrite	equ	-$30
_LVOOutput	equ	-$3C

_LVOFindConfigDev	equ	-$48

	errfile	'ram:assem.output'
	exeobj
	objfile	'SCSIcheck'

SCSIcheck	lea	DT(pc),a5
	movea.l	a5,a1	;Clear BSS section
	move.w	#(Work_Sizeof/4)-1,d1
1$	clr.l	(a1)+
	dbra	d1,1$

	move.l	d0,d2	;Save cmd line
	movea.l	a0,a2
	move.l	sp,InitialSP(a5)
	movea.l	SysBase,a6
	lea	DosName(pc),a1
	moveq	#0,d0
	SYS	OpenLibrary	;Open dos.library
	move.l	d0,DosBase(a5)
	beq	Exit

	movea.l	d0,a6
	SYS	Output
	move.l	d0,stdout(a5)
	beq	Cleanup

	lea	BannerStr(pc),a0
	bsr	Puts

	move.l	d2,d0	;Get cmd line
	movea.l	a2,a0
	lea	0(a0,d0.w),a1
2$	cmpi.b	#' ',-(a1)	;Eat trailing garbage
	dbhi	d0,2$
	blt	Instruct	;If no args, error
	clr.b	1(a1)	;Null terminate the string

3$	move.b	(a0)+,d0
	beq	Instruct	;No args
	cmpi.b	#' ',d0	;Skip spaces
	beq.s	3$
	cmpi.b	#'?',d0	;Instruct?
	beq	Instruct	;"Usage:..."
	subi.b	#$30,d0	;Convert to number
	blt	Error1	;Not a number
	cmpi.b	#7,d0
	bgt	Error1	;Out of range
	move.b	d0,Unit(a5)

	movea.l	SysBase,a6
	lea	ExpName(pc),a1	;Open expansion.library
	moveq	#0,d0	;Any version
	SYS	OpenLibrary
	move.l	d0,ExpBase(a5)
	beq	Error2

	movea.l	d0,a6
	suba.l	a0,a0
	move.l	#1004,d3
	moveq	#14,d2
	move.l	d3,d0
	move.l	d2,d1
	SYS	FindConfigDev
	tst.l	d0
	bne.s	4$

	suba.l	a0,a0
	move.l	d3,d0
	moveq	#12,d2
	move.l	d2,d1
	SYS	FindConfigDev
	tst.l	d0
	beq	Error3
4$	movea.l	d0,a4	;Ptr to ConfigDev structure
	movea.l	cd_BoardAddr(a4),a4
	movea.l	a6,a1
	movea.l	SysBase,a6
	SYS	CloseLibrary
	clr.l	ExpBase(a5)

	lea	FmtArgs(a5),a1
	move.l	a4,(a1)
	move.w	d2,4(a1)
	lea	Controller.msg(pc),a0
	bsr	Printf

	move.b	Unit(a5),d1	;Setup unit
	bsr	GenCmd
	beq	Error4

	lea	SCSICmd(a5),a3	;Build our SCSI command
	movea.l	a3,a0
	move.b	#WRITE_BUF,(a0)+	;Bit 0-4 = OpCode
	move.b	Unit(a5),d0
	lsl.b	#7,d0	;Shift device ID to a bit number
	move.b	d0,(a0)+	;Bit 5-7 = Logical unit number
	clr.b	(a0)+	; Not used for this command
	clr.b	(a0)+	; Not used for this command
	clr.b	(a0)+	; Not used for this command
	clr.b	(a0)	; Not used for this command
	bsr	ScsiOutCmd

	lea	ScsiData(a5),a2	;Fill buffer with pattern
	movea.l	a2,a0
	move.l	#$AAAAAAAA,d0
	move.w	#(1024/4)-1,d1
5$	move.l	d0,(a0)+
	dbra	d1,5$

	bsr	WriteData	;Write data out to Adaptec
	bsr	ScsiStat
	tst.b	d0
	bne	Error5

	move.b	Unit(a5),d1	;Setup unit
	bsr	GenCmd
	beq	Error4

	lea	SCSICmd(a5),a3	;Build our SCSI command
	movea.l	a3,a0
	move.b	#READ_BUF,(a0)+	;Bit 0-4 = OpCode
	move.b	Unit(a5),d0
	lsl.b	#7,d0	;Shift device ID to a bit number
	move.b	d0,(a0)+	;Bit 5-7 = Logical unit number
	clr.b	(a0)+	; Not used for this command
	clr.b	(a0)+	; Not used for this command
	clr.b	(a0)+	; Not used for this command
	clr.b	(a0)	; Not used for this command
	bsr	ScsiOutCmd

	lea	ScsiData(a5),a2	;Clear Buffer
	movea.l	a2,a0
	moveq	#0,d0
	move.w	#(1024/4)-1,d1
6$	move.l	d0,(a0)+
	dbra	d1,6$

	bsr	ReadData	;Read data in from Adaptec
	bsr	ScsiStat
	tst.b	d0
	bne	Error5

	lea	ScsiData(a5),a0	;Compare to pattern
	move.l	#$AAAAAAAA,d0
	move.w	#(1024/4)-1,d1
7$	cmp.l	(a0)+,d0
	dbne	d1,7$
	bne	Error6

	lea	AdaptecOK.msg(pc),a0
	bsr	Puts

	move.b	Unit(a5),d1	;Setup unit
	bsr	GenCmd
	beq	Error4

	lea	SCSICmd(a5),a3	 ;Build our SCSI command
	movea.l	a3,a0
	move.b	#REZERO_UNIT,(a0)+ ;Bit 0-4 = OpCode
	move.b	Unit(a5),d0
	lsl.b	#7,d0	;Shift device ID to a bit number
	move.b	d0,(a0)+	;Bit 5-7 = Logical unit number
	clr.b	(a0)+	; Not used for this command
	clr.b	(a0)+	; Not used for this command
	clr.b	(a0)+	; Not used for this command
	clr.b	(a0)	; Not used for this command
	bsr	ScsiOutCmd
	bsr	ScsiStat
	tst.b	d0
	beq.s	8$

	move.b	Unit(a5),d1	;Setup unit
	bsr	GenCmd
	beq.s	Error4

	lea	SCSICmd(a5),a3	;Build our SCSI command
	movea.l	a3,a0
	move.b	#REQ_SENSE,(a0)+	;Bit 0-4 = OpCode
	move.b	Unit(a5),d0
	lsl.b	#7,d0	;Shift device ID to a bit number
	move.b	d0,(a0)+	;Bit 5-7 = Logical unit number
	clr.b	(a0)+	; Not used for this command
	clr.b	(a0)+	; Not used for this command
	move.b	#4,(a0)+	;Number of sense bytes to return
	clr.b	(a0)	; Not used for this command
	bsr	ScsiOutCmd

	lea	ScsiData(a5),a2
	bsr	ReadData	;Get error code
	bsr	ScsiStat

	lea	FmtArgs(a5),a1
	moveq	#0,d0
	move.b	(a2),d0
	move.w	d0,(a1)
	move.b	Unit(a5),d0
	move.w	d0,2(a1)
	lea	UnitError.msg(pc),a0
	bra.s	9$

8$	lea	FmtArgs(a5),a1
	moveq	#0,d0
	move.b	Unit(a5),d0
	move.w	d0,(a1)
	lea	UnitOK.msg(pc),a0
9$	bsr	Printf
	bra.s	Cleanup

Error6	addq.w	#4,Error(a5)	;"ERROR in Adaptec controller RAM!"
Error5	addq.w	#4,Error(a5)	;"ERROR in Adaptec, if an Adaptec is used"
Error4	addq.w	#4,Error(a5)	;"Drive not responding.  Wrong unit number?"
Error3	addq.w	#4,Error(a5)	;"** NO CLtd Controller Found **"
Error2	addq.w	#4,Error(a5)	;"Couldn''t open expansion.library."
Error1	addq.w	#4,Error(a5)	;"SCSI unit must be >= 0 and <= 7."
Instruct	bsr	ReportError	;"Usage: 1> SCSIcheck <unit>"
Cleanup	movea.l	SysBase,a6
	move.l	ExpBase(a5),d0
	beq.s	1$
	movea.l	d0,a1
	SYS	CloseLibrary
1$	move.l	DosBase(a5),d0
	beq.s	Exit
	movea.l	d0,a1
	SYS	CloseLibrary
Exit	move.l	InitialSP(a5),sp
	moveq	#0,d0
	rts

********************************************************************
* NAME:     GenCmd()
* FUNCTION: Setup SCSI bus for command phase & generate a class 00
*           SCSI command.
* INPUTS:   D1 = SCSI Device number
*           A4 = NCR5380 base address
* RETURN:   D0 = 0 if successful, or error code.
* SCRATCH:  None
********************************************************************

GenCmd	move.l	#$40000,d0
1$	btst	#BCBS_BSY,ncr_bstat(a4)	;Wait for free bus
	beq.s	2$		;If this doesn't happen after a
	subq.l	#1,d0		; while, exit (rather than hang).
	bmi.s	4$
	bpl.s	1$
2$	move.b	#PH_DOUT,ncr_tcmd(a4)	;Setup 'Data Out' bus phase
	moveq	#1,d0		;Wait 1 bus settle delay
	lsl.b	d1,d0		;Shift device ID to a bit number
	bset.b	#CLTD_HOST,d0		;OR with ID of Host Controller
	move.b	d0,ncr_data(a4)		;Place ID on the data bus
	nop			;Wait 2 deskew delays
	move.b	#ASS_BUS!ASS_SEL,ncr_icmd(a4) ;Assert select
	move.l	#$40000,d0
3$	btst	#BCBS_BSY,ncr_bstat(a4)	;Wait for target to respond
	bne.s	5$		; by asserting busy.
	subq.l	#1,d0
	bpl.s	3$		;If this doesn't happen after a
4$	moveq	#0,d0		; while, exit (rather than hang).
	rts

5$	nop			;Wait 2 deskew delays
	move.b	#ASS_NOT,ncr_icmd(a4)	; and deassert select.
	moveq	#1,d0
	rts

*************************************************************************
* NAME:     ScsiOutCmd()
* FUNCTION: Send a class 00 command out the SCSI bus.
* INPUTS:   A3 = Ptr to SCSICmd structure
*           A4 = NCR5380 base address
* RETURN:   None
* SCRATCH:  None
*************************************************************************

ScsiOutCmd	move.l	a3,-(sp)
	tst.b	(a3)		;If opcode 00 (Test Unit Ready),
	beq.s	1$		; go directly to Data Out phase.
	move.b	#PH_CMD,ncr_tcmd(a4)	;Else, setup 'Command' phase
	bra.s	2$
1$	move.b	#PH_DOUT,ncr_tcmd(a4)	;Setup 'Data Out' phase

2$	move.b	#ASS_BUS,ncr_icmd(a4)	;Assert the data bus
3$	btst	#BCBS_REQ,ncr_bstat(a4)	;Wait for target to
	beq.s	3$		; assert Request.
	btst	#PHAS_MATCH,ncr_stat(a4)	;If we're not still in the
	beq.s	5$		; right phase, exit.
	move.b	(a3)+,ncr_data(a4)	;OK, send a byte of command
	move.b	#ASS_BUS!ASS_ACK,ncr_icmd(a4) ;Send an ACK to target
4$	btst	#BCBS_REQ,ncr_bstat(a4)	;Wait for target to
	bne.s	4$		; deassert Request.
	move.b	#ASS_BUS,ncr_icmd(a4)	;Reassert the data bus
	bra.s	3$		; and send the next byte.

5$	move.b	#ASS_NOT,ncr_icmd(a4)	;We got here because of a glitch
	movea.l	(sp)+,a3		; or we sent all the cmd bytes.
	rts

*************************************************************************
* NAME:     ReadData()
* FUNCTION: Read data from the SCSI bus.
* INPUTS:   A2 = Ptr to data buffer.
*           A4 = NCR5380 base address.
* RETURN:   None
* SCRATCH:  None
*************************************************************************

ReadData	move.l	a2,-(sp)
	move.b	#PH_DIN,ncr_tcmd(a4)
1$	btst	#BCBS_REQ,ncr_bstat(a4)
	beq.s	1$
	btst	#PHAS_MATCH,ncr_stat(a4)
	beq.s	3$
	move.b	ncr_data(a4),(a2)+
	move.b	#ASS_ACK,ncr_icmd(a4)
2$	btst	#BCBS_REQ,ncr_bstat(a4)
	bne.s	2$
	move.b	#ASS_NOT,ncr_icmd(a4)
	bra.s	1$

3$	move.b	#ASS_NOT,ncr_icmd(a4)
	movea.l	(sp)+,a2
	rts

*************************************************************************
* NAME:     WriteData()
* FUNCTION: Send data to the SCSI bus.
* INPUTS:   A2 = Ptr to data buffer.
*           A4 = NCR5380 base address.
* RETURN:   None
* SCRATCH:  None
*************************************************************************

WriteData	move.l	a2,-(sp)
	move.b	#PH_DOUT,ncr_tcmd(a4)
	move.b	#ASS_BUS,ncr_icmd(a4)
1$	btst	#BCBS_REQ,ncr_bstat(a4)
	beq.s	1$
	btst	#PHAS_MATCH,ncr_stat(a4)
	beq.s	3$
	move.b	(a2)+,ncr_data(a4)
	move.b	#ASS_ACK!ASS_BUS,ncr_icmd(a4)
2$	btst	#BCBS_REQ,ncr_bstat(a4)
	bne.s	2$
	move.b	#ASS_BUS,ncr_icmd(a4)
	bra.s	1$

3$	move.b	#ASS_NOT,ncr_icmd(a4)
	movea.l	(sp)+,a2
	rts

*************************************************************************
* NAME:     ScsiStat()
* FUNCTION: Test the status of the SCSI bus.
* INPUTS:   A4 = NCR5380 base address
* RETURN:   D0 = Status
* SCRATCH:  None
*************************************************************************

ScsiStat	move.b	#PH_STAT,ncr_tcmd(a4)	;Assert I/O and C/D
1$	btst	#BCBS_REQ,ncr_bstat(a4)	;Wait for target to
	beq.s	1$		; assert Request.
	moveq	#0,d0
	move.b	ncr_data(a4),d0		;Get status byte
	move.b	#ASS_ACK,ncr_icmd(a4)	;Send an ACK to target
2$	btst	#BCBS_REQ,ncr_bstat(a4)	;Wait for target to
	bne.s	2$		; deassert Request.
	move.b	#ASS_NOT,ncr_icmd(a4)	;Drop bus
	move.b	#PH_MIN,ncr_tcmd(a4)	;Setup 'Message In' phase
3$	btst	#BCBS_REQ,ncr_bstat(a4)	;Wait for target to
	beq.s	3$		; assert Request.
	tst.b	ncr_data(a4)		;Dummy read (expected)
	move.b	#ASS_ACK,ncr_icmd(a4)	;Send an ACK to target
4$	btst	#BCBS_REQ,ncr_bstat(a4)	;Wait for target to
	bne.s	4$		; deassert Request.
	move.b	#ASS_NOT,ncr_icmd(a4)	;Drop bus
	rts     

*********************************************************************
* NAME:     Printf()
* FUNCTION: Print formatted strings to stdout.
* INPUTS:   A0 = Format string.
*           A1 = Ptr to arguments.
* RETURN:   None
* SCRATCH:  D0-D1/A0-A1
*********************************************************************

Printf	movem.l	a2-a3/a6,-(sp)
	lea	KPutChar(pc),a2	;Byte fill routine
	lea	OutBuf(a5),a3
	move.l	a3,-(sp)
	movea.l	SysBase,a6
	SYS	RawDoFmt	;Do it!
	movea.l	(sp)+,a0
	bsr.s	Puts
	movem.l	(sp)+,a2-a3/a6
	rts

KPutChar	move.b	d0,(a3)+
	rts

*************************************************************************
* NAME:     Puts()
* FUNCTION: Writes a message to stdout.
* INPUTS:   A0 = Ptr to message.
* RETURN:   None
* SCRATCH:  D0-D1/A0-A1
*************************************************************************

Puts	movem.l	d2-d4/a6,-(sp)
	move.l	stdout(a5),d4
	bsr.s	WriteMsg
	movem.l	(sp)+,d2-d4/a6
	rts

*************************************************************************
* NAME:     WriteMsg()
* FUNCTION: Common subroutine used by FPrintf, Printf, and Puts.
* INPUTS:   A0 = Ptr to message.
*           D4 = FileHandle.
* RETURN:   None
* SCRATCH:  D0-D1/A0-A1
*************************************************************************

WriteMsg	move.l	a0,d3
1$	tst.b	(a0)+
	bne.s	1$
	exg	a0,d3
	sub.l	a0,d3
	subq.l	#1,d3	;Length
	move.l	a0,d2	;Buffer
	move.l	d4,d1	;FileHandle
	movea.l	DosBase(a5),a6
	SYS	Write
	rts

*************************************************************************
* NAME:     ReportError()
* FUNCTION: Sends the appropriate error message to stdout.
* INPUTS:   Error = Offset from ErrStr.tbl.
* RETURN:   None
* SCRATCH:  D0-D1/A0-A1
*************************************************************************

ReportError	move.w	Error(a5),d0
	movea.l	ErrStr.tbl(pc,d0.w),a0
	bra.s	Puts

ErrStr.tbl	dl	Usage.msg
	dl	Error1.msg
	dl	Error2.msg
	dl	Error3.msg
	dl	Error4.msg
	dl	Error5.msg
	dl	Error6.msg

Usage.msg	db	'Check the SCSI bus for functional drive.',10
	db	'Usage: 1> SCSIcheck <unit>',10
	db	'Where <unit> is a number from 0 to 7.',10,0
Error1.msg	db	'SCSI unit must be >= 0 and <= 7.',10,0
Error2.msg	db	'Couldn''t open expansion.library.',10,0
Error3.msg	db	'** NO CLtd Controller Found **',10,0
Error4.msg	db	'Drive not responding.  Wrong unit number?',10,0
Error5.msg	db	'ERROR in Adaptec, if an Adaptec is used',10,0
Error6.msg	db	'ERROR in Adaptec controller RAM!',10,0

BannerStr	db	'SCSI System Check V1.00 - ©1990 by Jeff Lavin',10,10,0
Controller.msg	db	'CLtd Controller found @ $%lx, Product Code %d',10,10
	db	'Checking Adaptec...',10,0
AdaptecOK.msg	db	'Adaptec & Ram OK',10,10
	db	'Checking Drive...',10,0
UnitOK.msg	db	'UNIT %d, OK',10,10,0
UnitError.msg	db	10,'** ERROR CODE %x  in UNIT %d **',10,10,0

DosName	db	'dos.library',0
ExpName	db	'expansion.library',0
	cnop	0,4

DT	dx.b	Work_Sizeof

	end
