;---------------------------------------------------------------;
;---------------------------------------------------------------;
;       SCSI DRIVER - for AMSCSI hardware			;
;       NCR 53C80 - SCSI ctrl chip				;
;	Rich Frantz		24-may-87			;	
;		Version 0.1					;
;	This routine should be called from a DOS handler	;
;	it should be noted that all command data should be	;
;	checked for validity before calling. Also I have 	;
;	not finished the error handling.			;
;---------------------------------------------------------------;
;---------------------------------------------------------------;

; equates

NCR	equ	$800001		;SCSI ctrl chip			
M250	equ	$1E000		;250 mSEC dly			
U200	equ	$64		;200 uSEC dly
SELlp	equ	$100		;Selection timeout
LUv	equ	$1		;select drive 0
TarIDv	equ	$1		;target I D 
InitIDv	equ	$8		;Initiator I D
;
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;            REMOVE THIS START UP ROUTINE WHEN USING 
;	AS A REAL AMIGA DRIVER - THE CALLING ROUTINE SHOULD
;	CALL ReadSCSI: , WriteSCSI: , or FormatSCSI: DIRECTLY
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;            equates for test routines ( START )
;
Data_Len	equ	$100
Sect		equ	$140000	;bytes not sectors		
Loop		equ	100
Data_Len1	equ	$300
Sect1		equ	$200

;---------------------------------------------------------------;
;	a0 = IO_DATA - MUST BE SETUP BY CALLER			;
;	a6 = DEVICE POINTER - MUST BE SETUP BY CALLER		;
;   	a4 = SCSI buffer pointer				;
;       a5 = unassigned 					;
; 	d0 = IO_LENGTH ( in bytes) - MUST BE SETUP BY CALLER	;
;       d1 = IO_OFFSET - MUST BE SETUP BY CALLER		;
;	d4 = unassigned - used by driver			;
;       d5 = unassigned - used by driver			;
;	d6 = Tid - setup by this driver	(target I D)		;
;	d7 = Iid - setup by this driver (iniator I D)		;
;---------------------------------------------------------------;

START:	; this starts the program by fake setting the regs
	move.l #Loop,d2		;set loop counter
	move.b #TarIDv,TarID	;set up traget ID
	move.b #InitIDv,InitID	;set up initiator ID
	move.b #LUv,LU		;set up logical unit #
STLP:	move.l #Data_Buf,a0	;set up IO_DATA
	move.l #Data_Len,d0	;set up IO_LENGTH
	move.l #Sect,d1		;set up IO_OFFSET
	bsr WriteSCSI		;go write it
	move.l #Data_Len1,d0	;get new IO_LENGTH
	move.l #Sect1,d1	;get new IO_OFFSET
	bsr ReadSCSI		;go read it
	subq.l #1,d2		;dec counter
	bne.s STLP
	rts			;done
ReadSCSI:	;read data from the SCSI 
	movem.l d4/d5/d6/d7/a4/a5,-(sp)	;save regs
	bsr Build_CMD			;build the scsi cmd packet
	btst.b #0,ERROR			;check for any error
	bne.s ReadSCSI_End		;if so, exit
	move.b #$08,CMDBUF		;set cmd to read
	bsr SELph			;select the target

ReadSCSI_End:	
	movem.l (sp)+,d4/d5/d6/d7/a4/a5	;restore regs
	rts				;return to caller

WriteSCSI:	;send data to the SCSI
	movem.l d4/d5/d6/d7/a4/a5,-(sp)	;save regs
	bsr Build_CMD			;build the scsi cmd packet
	btst.b #0,ERROR			;check for any error
	bne.s WriteSCSI_End		;if so, exit
	move.b #$0A,CMDBUF		;set cmd to write
	bsr SELph			;do it

WriteSCSI_End:
	movem.l (sp)+,d4/d5/d6/d7/a4/a5	;restoer regs
	rts				;return to caller

FormatSCSI:
	movem.l d4/d5/d6/d7/a4/a5,-(sp)	;save the regs
	move.w #$0400,CMDBUF		;set the cmd
	move.l #0,CMDBUF+2		;clr the cmd packet
	bsr Unit_No			;set I D's & L U #
	btst.b #0,ERROR			;check for any error
	bne.s FormatSCSI_End		;if so, end
	bsr SELph			;do it

FormatSCSI_End:
	movem.l (sp)+,d4/d5/d6/d7/a4/a5	;restore th regs
	rts				; return to caller

Request_Sense:
	movem.l d4/d5/d6/d7/a4/a5,-(sp)	;save regs
	move.w #$0300,CMDBUF		;set the cmd
	move.l #0,CMDBUF+2		;clr the rest of the packet
	bsr Unit_No			set I D's & L U #
	btst.b #0,ERROR			;check for any error
	bne.s Request_End		;if so exit
	bsr SELph			;do it

Request_End:
	movem.l (sp)+,d4/d5/d6/d7/a4/a5	;restore regs
	rts
Build_CMD:	;build the scsi cmd packet
	move.l d1,d4			;get the start sect #
	move.l d0,d5			;get the #bytes to transfere
	asr.l #8,d5			;shift 8 palces to get sectors
	and.l #$FF,d5			;must be less than 1 byte
	asl.l #8,d5			;move to upper byte in word
	move.w d5,CMDBUF+4		;place in packet
	asr.l #8,d4			;convert to sectors
	and.l #$000FFFFF,d4		;mask out L U #
	move.l d4,CMDBUF		;place in packet
Unit_No:	; add the logical unit # & get I D's
	move.l #$01,d5			;load d0
	move.l #$0,d4			;clear d4
	move.b TarID,d4			;get TarID
	subq.l #$01,d4			;-1
	bmi ID_ERROR			;oops ! TARGET ID NO GOOD
	asl.l d4,d5			;shift TarID palces
	move.b d5,d6			;store in target I D
	move.l #$01,d5			;same for the Initiator (us)
	move.l #$0,d4			;clear d4
	move.b InitID,d4		;get InitID 
	subq.l #$01,d4			;-1
	bmi ID_ERROR			;oops ! INIT ID NO GOOD
	asl.l d4,d5			
	move.b d5,d7			;store in Init I D
;	get Logical Unit # (drive #)
	move.l #$0,d5			;clear d5
	move.b LU,d5			;get the drive number
	asl.l #$5,d5			;shift to LUN
	and.l #$E0,d5			;mask all other bits
	or.b d5,CMDBUF+1		;put it in the packet
	rts				;packet complete	

ID_ERROR:
	move.b #$01,ERROR		;set the drive not ready error
	rts	
	
SELph:		;select the target
	move.l #SELlp,d4	;set SEL loop value
SELph1:	move.b #0,NCR+2		;clr NCR
	move.b #0,NCR+4
	move.b #0,NCR+8
ARB:	move.b d7,NCR		;ld init I D
	or.b #1,NCR+4		;set ARB bit
NFREE:	subq.l #$01,d4		;dec SEL loop
	beq SEL_ERROR		;stuck in the loop
	btst.b #6,NCR+2		;check arib in proc.
	beq NFREE		;wait
	nop			;delay for bus settle
	nop
	nop
	nop
	btst.b #5,NCR+2		;check lost arb
	bne.s SELph1		;try again
;
;check for higher priority
;
	move.l #0,d5		;lcr d5
	move.b NCR,d5		;load scsi data
	sub.b d7,d5		;sub our id
	beq.s WIN		;if = we are the only bidder
	sub.b d7,d5		;sub again
	bmi.s WIN		;if - we are the highest
	bra.s SELph1		;try again
WIN:	btst.b #5,NCR+2		;check lost arb bit
	bne.s SELph1		;if lost , retry
	move.b #$0c,NCR+2	;set SEL
	nop			;delay
	nop
	nop
	nop
	move.b d7,NCR		;load init I D
	or.b d6,NCR		;load target I D
	move.b #5,NCR+2		;set SEL & Data Bus
	and.b #$FE,NCR+4	;clr ARB
	and.b #$F7,NCR+2	;clr BSY
	move.l #M250,d5		;load timer
loop1:	btst.b #6,NCR+8		;test BSY
	bne.s SLECT		;if busy , selected
	subq #1,d5		;dec timer
	bne.s loop1		;loop
	move.b #0,NCR		;clr I D's
	move.l #U200,d5		;load timer
loop2:	btst.b #6,NCR		;test BSY
	bne.s SLECT		;if busy , selected
	subq #1,d5		;dec timer
	bne.s loop2		;loop
	bra SEL_ERROR		;selection failed
SLECT:	and.b #$FB,NCR+2	;clr SEL
; NextPhase
NextPhase:	;Check the SCSI bus phase
	btst.b #7,NCR+8		;check RESET
	bne RSTph		;reset phase
	btst.b #6,NCR+8		;check BUSY
	beq Done		;bus free
	btst.b #3,NCR+8		;check C/D
	beq.s Dat		;data in/out phase
	btst.b #4,NCR+8		;check MSG
	bne.s Message		;Message in/out phase
	btst.b #2,NCR+8		;check I/O
	bne.s STATph		;stat phase
	bra.s CMDph		;command phase
Message:	;check to see if it is message in or out
	btst.b #2,NCR+8		;check I/O
	bne.s MSGINph		
	bra.s MSGOUTph
Dat:		;check to see if it is data in or out
	btst.b #2,NCR+8		;check I/O
	bne.s RDATph
	bra.s SDATph	
Done:		;did we end normally ?
	btst.b #1,STAT		;do we have an error
	bne Return_ERROR
	 rts			;return to caller

RSTph:		;reset phase
	bra.s Done		;done
CMDph:		;send command
	move.l #CMDBUF,a4	;set the cmd buffer add
	move.b #$02,NCR+6	;set TCR to command phase
	bra.s Send		;send the command

STATph:		;get status byte
	move.l #STAT,a4		;set the stat buffer add
	move.b #$03,NCR+6	;set TCR to status phase
	bra.s Rec		;get the stat

MSGINph: 	;get message from target
	move.l #MSGINBUF,a4	;set the message in buffer add
	move.b #$07,NCR+6	;set TCR to message in phase
	bra.s Rec		;get the message

MSGOUTph:	;send message to target
	bra NextPhase		;error try again

RDATph:		;receive data fron target
	move.l a0,a4		;set the data buffer add
	move.b #$01,NCR+6 	;set TCR to receive data
	bra.s Rec		;get the data

SDATph:		;send data to target
	move.l a0,a4		;set the data buffer add
	move.b #$00,NCR+6	;set TCR to send data
	
Send:		;send x characters to the target
	move.b (a4)+,NCR	;set the byte
Send1:	btst.b #5,NCR+8		;test for REQ
	beq.s Send1		;wait for REQ
	or.b #$10,NCR+2		;set ACK
Send2:	btst.b #5,NCR+8		;test REQ
	bne.s Send2		;wait for /REQ
	andi.b #$EF,NCR+2	;clr ACK
	btst.b #3,NCR+10	;test pahse match
	bne.s Send	
	bra NextPhase

Rec:		;receive x characters form the target
Rec1:	btst.b #5,NCR+8		;test REQ
	beq.s Rec1		;wait for REQ
	move.b NCR,(a4)+	;get byte from target
	or.b #$10,NCR+2		;set ACK
Rec2:	btst.b #5,NCR+10	;test REQ
	bne.s Rec2		;wait for /REQ
	andi.b #$EF,NCR+2
	btst.b #3,NCR+10	;test for phase match
	bne.s Rec	
	bra NextPhase

SEL_ERROR:
	move.b #$01,ERROR	;set drive not ready error
	rts

Return_ERROR:
	;must be finnished
	rts			;return to caller

CMDBUF	dc.b $0A,$00,$00,$00,$01,$00	;command string (write 1 block)

STAT		ds.b $01	;status word

MSGINBUF	ds.b $04	;message buffer

TarID		ds.b $01	;target ID location

InitID		ds.b $01	;initiator ID location

ERROR		ds.b $01	;error flag

LU		ds.b $01	;logical unit #

Data_Buf	ds.b $500	;data buffer (reduce the size when real)

	end
