*****************************************************************************
*  Program:  ClearSCSI.asm ©1990 by Jeff Lavin
*  Descrip:  Clear the SCSI bus.
*    Usage:  1> ClearSCSI <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
REQUEST_SENSE	equ	$03

* 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

* Bit defs for Mode Register (2)

ARBITRATE	equ	0
MONITOR_BSY	equ	2

* 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
INT_REQ	equ	4	;Interrupt request active
DMA_REQ	equ	6	;DMA request

* 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	DataBuf,256	;Block of SCSI data
	LABEL	Work_Sizeof

_LVODisable	equ	-$78
_LVOEnable	equ	-$7E
_LVOCloseLibrary	equ	-$19E
_LVORawDoFmt	equ	-$20A
_LVOOpenLibrary	equ	-$228

_LVOWrite	equ	-$30
_LVOOutput	equ	-$3C

_LVOFindConfigDev	equ	-$48

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

ClearSCSI	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)

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

	lea	SCSICmd(a5),a3	   ;Build our SCSI command
	movea.l	a3,a0
	move.b	#REQUEST_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	DataBuf(a5),a2
	bsr	GetData
	bsr	SCSIStat

5$	move.b	Unit(a5),d1	;Setup unit for 2nd try
	bsr	GenCmd
	beq	Error4

	lea	SCSICmd(a5),a3	   ;Build our SCSI command
	movea.l	a3,a0
	move.b	#REQUEST_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	DataBuf(a5),a2
	bsr	GetData
	bsr	SCSIStat

	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	Cleanup	;All OK, exit

6$	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	#REQUEST_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	DataBuf(a5),a2
	bsr	GetData
	bsr	SCSIStat
	bne.s	Error5

	lea	FmtArgs(a5),a1
	lea	DataBuf(a5),a0
	move.l	(a0),(a1)
	lea	SenseErr.msg(pc),a0
	bsr	Printf
	bra.s	Cleanup

Error5	addq.w	#4,Error(a5)	;"SCSI Error"
Error4	addq.w	#4,Error(a5)	;"CLtd controller not responding"
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>=7"
Instruct	bsr	ReportError	;"Usage: 1> ClearSCSI <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:     GetData()
* FUNCTION: Do pseudo-DMA data transfer from SCSI bus.
* INPUTS:   A2 = Data buffer.
*           A4 = NCR5380 base address.
* RETURN:   None
* SCRATCH:  None
*************************************************************************

GetData	move.l	a2,-(sp)
	SYS	Disable
	move.b	#PH_DIN,ncr_tcmd(a4)	;Data In phase
1$	btst	#BCBS_REQ,ncr_bstat(a4)
	beq.s	1$
	btst	#PHAS_MATCH,ncr_stat(a4)
	beq.s	11$

	move.b	#MONITOR_BSY,ncr_mode(a4)
	move.b	#0,ncr_reset(a4)		;Start DMA receive
	move.b	#DMA_REQ,d1
	lea	ncr_input(a4),a0
	lea	ncr_stat(a4),a1
2$	btst	d1,(a1)
	beq.s	3$
	move.b	(a0),(a2)+
3$	btst	d1,(a1)
	beq.s	4$
	move.b	(a0),(a2)+
4$	btst	d1,(a1)
	beq.s	5$
	move.b	(a0),(a2)+
5$	btst	d1,(a1)
	beq.s	6$
	move.b	(a0),(a2)+
6$	btst	d1,(a1)
	beq.s	7$
	move.b	(a0),(a2)+
7$	btst	d1,(a1)
	beq.s	8$
	move.b	(a0),(a2)+
8$	btst	d1,(a1)
	beq.s	9$
	move.b	(a0),(a2)+
9$	btst	d1,(a1)
	beq.s	10$
	move.b	(a0),(a2)+
10$	btst	#INT_REQ,(a1)
	beq.s	2$
	move.b	#ARBITRATE,ncr_mode(a4)
	tst.b	ncr_reset(a4)		;Dummy read
11$	SYS	Enable
	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

Usage.msg	db	'Clear the SCSI bus.',10
	db	'Usage: 1> ClearSCSI <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	'CLtd controller not responding.  Wrong unit number?',10,0
Error5.msg	db	'SCSI Error',10,0
SenseErr.msg	db	'Sense Error %lx',10,0

BannerStr	db	'Clear SCSI Channel V1.00 - ©1990 by Jeff Lavin',10,10,0

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

DT	dx.b	Work_Sizeof

	end
