; 31 Mar 86.. fsw 
;         * * * * * * * * * * * * * * * * * * * * * * *
;         *                                           *
;         *       SCSI/Floppy/Hard Disk Boot ROM      *
;         *                                           *
;         *       Copyright (C) 1983, 1984, 1985      *
;         *            AMPRO Computers, Inc.          *
;         *             All rights reserved.          *
;         *                                           *
;         * * * * * * * * * * * * * * * * * * * * * * *
;
;
;  Revision history:
;
;  Ver  Date      Who     Description
;  ---  --------  ---     -----------------------------------------
;  1.2  03/27/86  fsw     Changed scsi select routine to current bios
;                         3.6 routines. Added SCSI retries. Fixed 
;			  arbitration to provide exit if scsi reset 
;			  occures during arbitration time.
;
;  1.1  10/18/85  fsw     Corrected problem booting from 96 tpi disk.
;			  Added 5 tries for floppy disk reads and
;			  jmp for drive prameters initialization.
;
;  1.0   9/17/85  fsw	  Initial release.
;
;
;
;                            DESCRIPTION
;
; This ROM will boot from floppy, hard disk, or SCSI disk server,
; and is based on the orginal AMPRO boot ROM and the SCSI routines
; in Version 3 BIOS.  Arbration is used in the SCSI routines.
;
; No alteration to boot sector is required to boot from floppy.
; Booting from hard disk requires the use of the HGEN utility, to
; install a sector on the hard disk containing hard disk system
; configuration info, and to add code to the boot loader for hard
; disk configuration initialization.
;
; 			 THEORY OF OPERATION   
;
; The ROM on reset begins execution at 0000H.  It first relocates
; itself to 8000H then continues executation above 8000H.  
; The ROM then loads in a "boot loader" sector from floppy, hard disk,
; or other SCSI device.  
;
; The first attempt is to load a boot loader from side 0, track 0 
; sector one of floppy drive "A" (drive select "1"), if a floppy
; is present in that drive.  If the first byte of that sector is not
; an LXI H or MVI A op code, then an attempt to load a boot loader
; from an SCSI device is made.  SCSI ID 0 is used as the SCSI boot
; device if the board's ID is jumpered as 7.  Otherwise, SCSI ID 7
; is used for the attempt to boot from SCSI.  
;
; If the appropriate SCSI device is not able to 
; provide a legal boot loader (with LXI H or MVI A op code as first
; byte), then the process begins again, looping until a legal "boot
; loader" is obtained.  
;
; The boot loader is stored at 9000H, followed by a jump to that
; address.  The contents of the boot loader determine what happens
; next.  Normally the ROM is switched off first thing.
;
; This accommodates:
;
;       o  Hard disk drive spinup
;       o  User choice of boot device
;       o  Networking
;       o  Odd-ball applications!
;
;
; 				MISC NOTES
;
; Supports floppy drive step rate of 6 msec only for compatability
; with the 1770 or 1772 fdc. 
;
; Supports hard disk with 512 byte sectors only.  Each call to read
; track reads one AMPRO track of 16 sectors when booting from hard
; disk.
;
; Requires the use of SCSI self-initializing controllers, such as
; Adaptec ACB-4000, Shugart 1610-4, Xebec OWL drive/controller, etc.
;
;                                FEATURES
;
; 	Test for presence of 177x floppy disk controller and
; 	for 5380 SCSI controller. 
;
;	Test for presence of boot sector on the disk it has read. 
;
;	Test for properly formated floppy disk in drive.
;
;	If unable to boot from floppy disk will try SCSI, if
;	unable to boot from SCSI, starts over. This sequence 
;	will repeat untill sucessful.
;
;
;                           SCSI ID CONVENTIONS
;
; SCSI Master mode:  Set board SCSI ID to 7 (refer to board manual).
;       When the board's SCSI ID is 7, the boot ROM issues an SCSI
;       bus reset, and then uses the device at SCSI ID 0 as the
;	SCSI boot device, if floppy boot attempt fails.
;
; SCSI Slave mode:  Set board SCSI ID to anything other than 7.
;	When the board's SCSI ID is not 7, the boot ROM does not
;	issue an SCSI bus reset.  In this case the device at SCSI
;	ID 7 is used as the SCSI boot device if the floppy boot 
;	attempt fails.  This might be a disk server rather than
;	a disk controller.
;
; In all cases if will boot from floppy in drive "A" if a legal boot
;	sector is present (as defined above)
;********************************************************************
;
YES	EQU	1
NO	EQU	0

MSEC	EQU	167
CR	EQU	0DH
LF	EQU	0AH
CONT	EQU	0		; system control port
SID1	EQU	10H		; select side one of disk
SDEN	EQU	20H		; select single density
ROMOFF	EQU	40H		; turn rom off

BOOT	EQU	8000H		; origin of this program
RAM	EQU	9000H		; beginning of scratch ram
STACK	EQU	RAM		; stack pointer

CTCA	EQU	40H		; counter/timer
CTCA0	EQU	CTCA
CTCA1	EQU	CTCA+10H
CTCA2	EQU	CTCA+20H
CTCA3	EQU	CTCA+30H

SIO	EQU	80H		; serial io
SIOAD	EQU	SIO
SIOBD	EQU	SIO+8
SIOAC	EQU	SIO+4
SIOBC	EQU	SIO+12

CMND	EQU	0C0H
WTRK	EQU	CMND+1
WSEC	EQU	CMND+2
WDAT	EQU	CMND+3

STAT	EQU	CMND+4
RTRK	EQU	CMND+5
RSEC	EQU	CMND+6
RDAT	EQU	CMND+7

REST	EQU	08H
STEPI	EQU	58H
RDSEC	EQU	88H
RDID	EQU	0C8H
FI	EQU	0D0H

ERMSK	EQU	18H

ID	EQU	29H		; Little board SCSI id
;
;  NCR 5380 controller equates
;
NCRBASE	EQU	20H		; Base address of NCR 5380
NCRCSD	EQU	NCRBASE+0	; (R)  Current SCSI data register
NCRODR	EQU	NCRBASE+0	; (W)  Output data register
NCRICR	EQU	NCRBASE+1	; (RW) Initiator command register
NCRMR	EQU	NCRBASE+2	; (RW) Mode register
NCRTCR	EQU	NCRBASE+3	; (RW) Target command register
NCRCSBS	EQU	NCRBASE+4	; (R)  Current SCSI bus status
NCRSER	EQU	NCRBASE+4	; (W)  Select enable register
NCRBSR	EQU	NCRBASE+5	; (R)  Bus & status register
NCRSDS	EQU	NCRBASE+5	; (W)  Start DMA send
NCRIDR	EQU	NCRBASE+6	; (R)  Input data register
NCRSDTR	EQU	NCRBASE+6	; (W)  Start DMA target receive
NCRRPI	EQU	NCRBASE+7	; (R)  Reset parity/interrupt
NCRSDIR	EQU	NCRBASE+7	; (W)  Start DMA initiator receive
NCRDACK	EQU	NCRBASE+8	; (RW) DACK pseudo-DMA register

; Current SCSI bus status (NCRCSBS)

NCRRST		EQU	10000000B	; Reset
NCRBSY		EQU	01000000B	; Busy
NCRREQ		EQU	00100000B	; Request
NCRMSG		EQU	00010000B	; Message
NCRCD		EQU	00001000B	; Control/Data
NCRIO		EQU	00000100B	; Input/Output
NCRSEL		EQU	00000010B	; Select
NCRDBP		EQU	00000001B	; Data bus parity
;...............................................................

	ORG	BOOT

	JMP	START -BOOT	; power on or re-boot
	JMP	SETUP		; setup disk drive
	JMP	STEPIN		; step in one track
	JMP	SIDEONE		; select side one
	JMP	FDCLR		; clear the fdc
	JMP	SETDMA		; set new dmaadr (hl)
	JMP	READTRK		; read current track at dmaadr
	JMP	READSEC		; read current sector at dmaadr
	JMP	READID		; read next sector address
	JMP	SETPRAM		; pass the hard disk prameters to the
				; scsi controller, for dumb controllers.

DMAADR:	DS	2
IDSV:	DS	6
SELBYT:	DS	1
TRACK:	DS	1
SECTOR:	DS	1
STATUS:	DS	1
TWOSID:	DS	1
SBIAS:	DS	1

START:	DI
	XRA	A
	OUT	CONT		; clear system control register
	LXI	H,0
	LXI	D,BOOT
RELOC:	MOV	A,M
	STAX	D
	INX	H
	INX	D
	MOV	A,H
	CPI	16		; relocate 16 pages
	JNZ	RELOC -BOOT	; this code runs at address 0
	JMP	INITSYS		; this code runs relative to boot

; copyright declaration

	DB	'SCSI Boot Rom Version 1.2 - (C) 1983,1984,1985,1986 '
	DB	'- AMPRO Computers, Inc. - All rights reserved. '

INITSYS:LXI	SP,STACK	; set local stack
	LXI	H,CTCTBL
CTCINT:	MOV	A,M
	INR	A
	JZ	SIOINT
	DCR	A
	OUT	CTCA0
	OUT	CTCA1
	INX	H
	JMP	CTCINT

CTCTBL:	DB	47H,13,255	;9600
;CTCTBL:	DB	3,0,255			;19200

SIOINT:	LXI	H,SIOTBL
SIOLP:	MOV	A,M
	INR	A
	JZ	CBOOT
	DCR	A
	OUT	SIOAC
	OUT	SIOBC
	INX	H
	JMP	SIOLP

SIOTBL:DB	4,46H,5,0EAH,3,0C1H,255		;9600
;SIOTBL:	DB	4,84H,5,06AH,3,0C1H,255		;19200

; cboot is the start of the read disk routines. 
;
; check little board id, if id = 7 do SCSI reset, if id not=7 
; no reset is issued.
;
; a 1 second time delay is invoked prior to SCSI reset to allow
; various SCSI devices do their powerup initialization before
; the SCSI reset is issued.
;
CBOOT:  IN	ID		; get little board SCSI id
	ani	07h		; mask off
	inr	a		; make 1 thru 8
	mov	b,a		; set 'b' for count
	xra	a
	stc			; set carry
setid:	ral
	dcr	b
	jnz	setid
	sta	myid
	cpi	80h		; scsi id 7
	mvi	a,10000000b	; target id=7
	jnz	resetnot
      	MVI	A,4		; 4 x .25 sec
	CALL	TIMER
	mvi	a,80h		; scsi reset
	out	ncricr		; set reset line high
holdit:	dcr	a
	jnz	holdit		; 50 usec min
	out	ncricr		; clear reset line
	in	ncrrpi		; and the interrupt line
	mvi	a,00000001b	; target id=0
;
; has target id in 'a'. if little board id=7 boot from device 0.
; if little board id not=7, boot from device 7.
;
resetnot:
	sta	target		; device to boot from
	xra	a
	out	ncricr		; clear the 5380 registers
	out	ncrmr
	out	ncrtcr
	out	ncrser
	in	ncrmr		; read the mode register
				; mode register should equal 0 if
				; 5380 is present.

	sta	scsi		; set scsi to zero or non-zero to 
				; show presence of ncr 5380

;
; everything on the SCSI buss should have completed initialization and
; SCSI reset may have been issued. 
;
; test for presence of 177x floppy disk controller.
;
	MVI	A,0AAH		; test for presence of fdc
	OUT	WSEC		; write pattern to sector reg
	MOV	B,A		; save in 'b'
	ANI	0FH		; set up loop value
CLOOP:	DCR	A		; give the 177x time for the sec reg
	JNZ	CLOOP		;   to set up.
	IN	RSEC		; read the sector reg
	CMP	B		; see if same as written to sec reg
	JZ	CBOOT2		; 
	JMP	try$hd$dvr	; do hard disk, no fdc present
;
; floppy disk controller present try to boot from floppy
;
	MVI	A,5
	STA	TRIES		; set up try count
CBOOT2:	CALL	SETUP
	CALL	READID
	CALL	ERROR
	JNZ	CBOOT2		; try to read id 5 times
	MVI	A,5
	STA	TRIES		; reset floppy try counter
	LDA	IDSV+2		; get sector #
	CPI	17
	MVI	A,0
	JC	CB0
	INR	A
CB0:	STA	TWOSID
	ORA	A
	RAL
	RAL
	RAL
	RAL
	STA	SBIAS		; 0 if single sided, 16 if 2 sided
	LXI	H,RAM
	SHLD	DMAADR
	INR	A		; sector 1 ( or 17 )
	OUT	WSEC
	CALL	READSEC
	CALL	ERROR
	LDA	TWOSID
	JNZ	CB0		; error occured try to read again
	lda	ram		; see if boot record there
	cpi	21h		; starts with lxi h,xxxx
	jz	ram
	cpi	3eh		; other choice is mvi a,xx
	jz	ram
	jmp	try$hd$dvr	; see if can do hard disk

SETUP:	MVI	A,'A'-40H
	OUT	CONT		; select a:, side 0
	STA	SELBYT		; save it
	CALL	FDCLR
	MVI	A,REST		; restore
	CALL	OUTCMD		; to fdc
	JMP	FDWAIT		; Wait for command to complete and ret
;
; Read current track at dmaadr
;
READTRK:
	lda	idsv+3		; sector size
	cpi	3
	lxi	h,skwtbl1	; 96 tpi disk
	jz	rtloop
	LXI	H,SKWTBL	; sector skew table
RTLOOP:	mvi	a,5		;
	sta	tries		; set floppy try counter
	MOV	C,M
	INX	H
	INR	C
	RZ			; finished
	DCR	C
	LDA	SBIAS
	ADD	C
	STA	SECTOR
	OUT	WSEC
RSAGN:	PUSH	H		; save skew table pointer
	CALL	READSEC		; read the sector
	CALL	ERROR		; check for errors
	JNZ	RSAGN1		; try to read the sector again
	LHLD	DMAADR		; get current dmaadr address
	SHLD	DMA		; save current dma addr
	POP	H		; restore skew table pointer
	JMP	RTLOOP		; next sector
;
; if error on read sector, restores old dma address and attempts
; to read the same sector again.
;
RSAGN1:	LHLD	DMA		; get old dmaadr dma address
	SHLD	DMAADR		; restore dma address
	POP	H		; restore stack
	JMP	RSAGN
;
SKWTBL:	DB	1,2,3,4,5,6,7,8,9,10,255
skwtbl1:db	1,2,3,4,5,255
;
; read id twice, once to see if disk in drive, the second for
; the id value.
;
READID:	IN	STAT
	IN	RDAT
	MVI	A,RDID
	CALL	OUTCMD
	call	fdwait		; See if will time out
	CPI	255		; no floppy
	sta	status		; if timeout error, set error status
	rz			; if timeout just return
	IN	STAT		; clear fdc reg
	IN	RDAT
	LXI	H,IDSV
	MVI	A,RDID
	CALL	OUTCMD
	CALL	RD
	MOV	A,B
	STA	STATUS
	RET

READSEC:IN	STAT		; clear status
	IN	RDAT		; clear any trash
	LHLD	DMAADR
	CALL	FDCLR
	MVI	A,RDSEC		; read sector command
	CALL	OUTCMD		; to fdc
	CALL	RD		; read loop
	MOV	A,B
	STA	STATUS
	SHLD	DMAADR
	RET

RD:	IN	STAT
	MOV	B,A		; save status
	RAR
	RNC			; return when fdc not busy
	RAR
	JNC	RD		; wait for drq
	IN	RDAT
	MOV	M,A
	INX	H
	JMP	RD

STEPIN:	LDA	TWOSID
	ORA	A
	JZ	STPI
	CALL	SIDEONE
	LXI	H,TRACK
	DCR	M
	LXI	H,MSEC		; wait one millisecond
	JMP	WT		; wt returns to caller
STPI:	MVI	A,STEPI		; step in command
	CALL	OUTCMD
STLP:	IN	STAT
	RAR
	JC	STLP
	RET

FDCLR:	MVI	A,FI
	OUT	CMND
	MVI	A,0
CL:	DCR	A
	JNZ	CL
	IN	STAT
	IN	RDAT
	RET

OUTCMD:	OUT	CMND
	MVI	A,19
OC0:	DCR	A
	JNZ	OC0		; wait 66.5 usec for fdc to set-up
	RET

SETDMA:	SHLD	DMAADR
	SHLD	DMA
	RET

SIDEONE:LDA	SELBYT
	ORI	SID1
	OUT	CONT
	RET
;
; floppy error checking enters here.  Returns with zero flag NZ if
; error, or Z if no error.  Floppy routines must set STATUS for error
; conditions.
;
ERROR:	LDA	STATUS
	ANI	ERMSK
	RZ
	PUSH	H
	LXI	H,TRIES		; point to tries
	DCR	M		; tries -1
	POP	H
	RNZ			; try again
	LDA	SCSI		; get 5380 present byte
	ANA	A
	JZ	HD$DVR		; Try hard disk, have timed out on floppy
				; disk at least 5 times
;
; floppy error falls thru to here also hard disk errors enter here to
; start the hole proceedure over and over again.
;
ERROR1:	XRA	A		; make sure prom is turned on
	OUT	CONT
	JMP	0		; jmp to prom
;
WAIT:	LXI	H,100 * MSEC
WT:	DCX	H
	MOV	A,H
	ORA	L
	JNZ	WT
	RET
;
;  This routine will wait for a for the FDC to go not busy, showing
;  completion of a command.  After 1 seconds time out, a FORCE INTERRUPT
;  command will be issued to the FDC.  Routines will come through here
;  at least 5 times before final error occures.  Total time out 5 seconds.
;
FDWAIT:
	LXI	H,TIMEOUT	; Point to timeout location
	MVI	M,3		; Set major loops for timeout 
				; 3 = about 1 sec.
DLOOP:	IN	STAT		; Get FDC status
	RAR			; test busy bit
	RNC			; .  zero status if busy non-active
	RAR			; see if byte to be read
	JC	FREAD		; read the byte
	DCX	H		; See if enough minor loops
	MOV	A,H		; .  (Approx 34,000 times)
	ORA	L		; .
	JNZ	DLOOP		; Not done with minor loop
	LXI	H,TIMEOUT	; Decrement major loop counter
	DCR	M		; .  (timeout loop count)
	JNZ	DLOOP		; .
	CALL	FDCLR		; force fdc clear
	XRA	A		; Set A to 0FFH and status to NZ
	OUT	CONT		; deselect so floppy select light
	DCR	A		; will go out
	RET			;
;
FREAD:	IN	RDAT		; get the data byte from the fdc
	JMP	DLOOP		; just loop till command over
;
; timer entered with 'a' equal to number of major loops wanted
; each major loop = aprox .25 sec
;
TIMER:	LXI	H,TIMEOUT
	MOV	M,A		; save major loop value
TIMER0:	LXI	H,41668
TIMER1:	DCX	H		; 1.5
	MOV	A,L		; 1.0
	ORA	H		; 1.0
	JNZ	TIMER1		; 2.5
				;total =  6 usec X 41668
	LXI	H,TIMEOUT
	DCR	M
	RZ
	JMP	TIMER0
;
;---------------------------------------------------------------
; Send bytes to console until zero encountered
; use to insert messages when debuging
; format is
;	call	puts
;	db	'your message',0; must terminate with '0'
;
;PUTS:	pop	h		; 'hl' has address of msg
;	mov	c,m		; get char
;	inx	h
;CONOUT:
;	MVI	A,01H		; Check "all sent" bit in register 1
;	OUT	84H		; .
;	IN	84H		; .
;	ANI	01H		; "ALL SENT" is bit 0
;	JZ	CONOUT		;TRANSMIT BUFFER NOT READY
;	MOV	A,C		;CHARACTER TO REGISTER A
;	OUT	80H
;	mov	a,m
;	ana	a		; returns with char in 'a', null = end
;	jnz	puts+1
;	pchl			; hl has return address
;-----------------------------------------------------------------
;
; if no boot sector is present on the floppy disk, floppy timeout
; has occured, floppy read error, or no floppy disk controller is 
; present, entry to the hard disk (scsi) driver is here. 
; 
; starts with 5 second timer to prevent hammering the scsi buss 
; with possible scsi resets.
;
TRY$HD$DVR:
	MVI	A,20		; 20 x .25 seconds = 5 sec
	CALL	TIMER
;
;  Hard disk driver
;
HD$DVR:	LXI	SP,STACK	; reset stack

	lxi	h,hdtrk
	shld	boot+19		; patch readtrk
	xra	a		; zero 'a'
	lxi	h,ram		; address to read sector
	mov	m,a		; clear anything at this address 
	sta	hdsect		; zero hard disk sector
	shld	dmaadr
	call	scsi$rd		; read boot sector
	lda	ram
	cpi	21h		; should be lxi h,xxxx
	jz	ram
	cpi	3eh		; other choice is mvi a,xx
	jz	ram
	jmp	error1		; no boot sector go try floppy again
;
; Read hard disk system to memory. Do 16 sectors at a time. One AMPRO
; hd track. The boot loader will call this routine twice.
; First pass read sectors 0 thru 15, second pass read 16 thru 31 for
; 32 sectors total.
;
HDTRK:	call	scsi$rd
	cpi	0ffh		; see if return has timeout error
	jz	error1		; start all over
	lhld	dmaadr
	lxi	d,512		; update dma address
	dad	d
	shld	dmaadr
	lda	hdsect		; see if two tracks (32 sectors)
	inr	a		; next sector to read
	cpi	32		; have already read 512x32 bytes
	rz			;
	cpi	16		; see if one "track" has been read
	sta	hdsect		; update sector new sector, the boot
				; loader reads two tracks.
	rz			; return to boot
	jmp	hdtrk		; loop till through
;
;
;  SCSI return sense data command (Cmd 03)
;

SCSI$STAT$CMD:
  	DB 	3		; 00 - REQUEST SENSE COMMAND
  	DB 	0		; 01 - LOGICAL UNIT
  	DB 	0		; 02 - RESERVED
  	DB 	0		; 03 - RESERVED
  	DB 	4		; 04 - NUMBER OF BYTES
  	DB 	0		; 05 - RESERVED

;
;  SCSI read/write command (Cmd 08/0A)
;

SCSI$RD$CMD:	EQU	08H		; 08 IS READ DATA

SCSI$RW$CMD:	DB 	SCSI$RD$CMD	; 00 - 08=Read, 0A=Write
HIGH$ADDR: 	DB 	0		; 01 - High address
MED$ADDR:	DB 	0		; 02 - Middle address
LOW$ADDR:	DB 	0		; 03 - Low address
  		DB 	1		; 04 - Number of sectors
STEP$RATE:	DB 	0		; 05 - Step rate (Xebec)


;
;  Init scsi controller prameters
;
;  When called  HL = address of scsi command
;               DE = address of prameter table
;
SETPRAM:
	SHLD	CMDPTR		; set address of scsi command
	XCHG			; 'de' to 'hl'
	SHLD	DATPTR		; save address of prameters to pass
	JMP	SCSI$CMD
;
;  Read from the hard disk
;
SCSI$RD:
	LXI	H,SCSI$RW$CMD	; Get command string
	SHLD	CMDPTR		; Save the command pointer
	CALL	BLD$SCSI$SCTR	; Build SCSI sector address
;
;  Exits with status in A.  0FFH = timeout error
;
SCSI$CMD:
	LXI	H,TRIES		; set up retry count
	MVI	M,2

SCSI$CMD$RETRY:
	CALL 	SELECT 		; Perform the SCSI operation
	LDA 	STATUS		; Get the return status
	STA	ERFLAG		; save error
	CPI	0FFH		; Timeout?
	JZ	SCSI$DONE	; Yes, go save timeout status
	ANI 	2		; Check for SCSI error status
	JZ	SCSI$DONE	; No error -- finish up

	LHLD	CMDPTR
	SHLD	SAVE$CMDPTR	; save old command pointer

	LXI	H,SCSI$STAT$CMD	; get sense
	SHLD	CMDPTR		; set command pointer

	LHLD	DMAADR
	SHLD	SAVE$DMA	; save the current

	LXI	H,MESSAGE	; message area
	SHLD	DMAADR

	CALL	SELECT		; do operation
	LXI	H,TRIES
	DCR	M		; tries -1

	LHLD	SAVE$DMA	; get dma address back
	SHLD	DMAADR		; restore dma address

	LHLD	SAVE$CMDPTR
	SHLD	CMDPTR		; restore command pointer

	JNZ	SCSI$CMD$RETRY	;
	MVI	A,0FFH

SCSI$DONE:
	ORA	A		; Set Z/NZ for user
	RET			; and return
;
;
;  Build 2-byte SCSI sector number starting with 00
;
;  NOTE:  This routine uses only a block number starting with 00
;         and read a maximun of ffh sectors.
;
BLD$SCSI$SCTR:
	lda	hdsect
	sta	low$addr
	ret
;
;  Select controller, and fall through to phase if selected ok.
;
;
busbsy:	equ	40h
;
SELECT:	xra	a
	out	ncricr		; Clear initiator command register
	out	ncrtcr		; .  and target command register

clear$arbit:
	xra	a
	out	ncrmr		; .
	in	ncrrpi		; reset interrupts

arbitrate:
	lda	my$id		; Assert my ID (the initiator)
	out	ncrodr		; .
	mvi	a,1		; start arbitration
	out	ncrmr		; .

in$progress:
	in	ncricr		; Wait for "arbitration in 
	ani	40h		; .  progress" bit
	jnz	arbitrate$won	; we have argitration
	in	ncrbsr
	ani	10h		; see if scsi reset has occured 
	jz	in$progress
	jmp	clear$arbit

arbitrate$won:
	nop			; Arbritration delay
	nop
	in	ncricr		; Check for lost arbitration
	ani	20h		; .
	jnz	clear$arbit	; We lost -- start over

	lda	my$id
	mov	b,a
	in	ncrcsd		; See if we're the highest priority
	sub	b		; .  remove my addr
	sub	b		; .  compare my addr to bus data
	jm	i$win		; We win if result < 0
	jmp	clear$arbit	; .  otherwise we lose -- start over

i$win:
	in	ncricr		; Check again for lost arbitration
	ani	20h		; .  (just in case)
	jnz	clear$arbit	; We lost -- start over

	mvi	a,08h		; set assert BSY bit in icr
	out	ncricr		; .

	in	ncricr
	ori	04h		; OR in SEL to ICR
	out	ncricr
	nop			; wait one bus clear delay

	lda	my$id		; Select target: get our ID,
	mov	b,a		; .
	lda	target		; .  or in target ID
	ora	b		; .
	out	ncrodr		; .  and send to NCR chip

	mvi	a,0dh		; Assert data bus
	out	ncricr		; .  (along with SEL & BSY)

	in	ncrmr		; Reset arbitration bit
	ani	0feh		; .
	out	ncrmr		; .

	mvi	a,05h		; Release BSY, keep SEL	
	out	ncricr		; .  and assert data bus
	nop

	lxi	b,6000h		; 250 ms loop (1M cycles)
stim:
	in	ncrcsbs		; Wait for BSY
	ani	busbsy		; .
	jnz	select$ok	; Got him!

	dcr	c
	jnz	stim		; inner loop:  41*256 = 10496 cycles
	dcr	b
	jnz	stim		; outer loop: 10510*96 = 1M cycles

	xra	a		; Select timeout -- clear bus
	out	ncrodr

	dcr	a		; set 'a' to 0ffh
	sta	status		; Save status timeout.
	jmp	all$done	; and clear the registers

select$ok:
	xra	a		; Set good status

all$done:
	mov	b,a		; temp save status
	mvi	a,01h		; Release SEL
	out	ncricr		; .
	xra	a		; Remove address from data bus
	out	ncricr		; .
	mov	a,b		; Get status back
	ora	a		; Set status
	rnz
	dcr	a		; set 'a' to ffh
	sta	status		; clear scsi status to timeout..

; SCSI.011

; * * * * *  
; *  --------\	NOTE: we fall through if we successfully
; *  --------/	selected the controller!!
; * * * * *

	MVI	A,00000110B	; Set DMA mode and Monitor Busy  
	OUT	NCRMR		; .

SCSI$RDY:
; Wait for either a 5380 "Interrupt" or a REQ from Target.
; The REQ is needed since it may have come too soon after
; selection to register an Interrupt.
	IN	NCRBSR		; Check for "Interrupt"
	ANI	00010000B	; .
	JNZ	SCSI$INT
	IN 	NCRCSBS		; Check for REQ
	ANI	NCRREQ		; .
	JZ	SCSI$RDY	; Wait for Interrupt or REQ
	JMP	PHASE		; Process phase vector

SCSI$INT:
;  Determine cause of 5380 "Interrupt".  Either phase
;  changed, busy dropped, or bus was reset.  If bits 2 and 3
;  of the NCRBSR are not 0's when the Interrupt flag (bit 4)
;  is set, then it is either a loss of BUSY or an SCSI RESET.
	XRA	A
	OUT 	NCRICR		; Release data bus
	IN	NCRBSR		; Read 5380 Bus and Stat Reg
	ANI	00001100B	; Keep interesting bits
	JNZ	SCSI$EXIT	; Reset or Busy Loss: Exit
				; 00 --> Process phase vector

PHASE:
; DMA mode and Monitor Busy must be cleared prior to clearing
; of the 5380 Interrupt Flag.  Then mode register is restored.
; Otherwise the interrupt flag may not clear and the DMA Mode 
; may not be useable.  
	XRA	A		; Clear 5380 Mode Register
	OUT	NCRMR		; .
	IN	NCRRPI		; Reset interrupts
	MVI	A,00000110B	; Set DMA mode and Monitor Busy
	OUT	NCRMR		; .
	IN 	NCRCSBS		; Update phase...
      	ANI 	00011100B	; Mask all but phase bits, clear carry bit
      	RAR			; Rotate over for target
	MOV	E,A		; . (Save for use with jump table)
      	RAR			; .
      	OUT 	NCRTCR		; Set phase
      	MVI 	D,0		; E is already set (6 ins ago)
      	LXI 	H,PHASE$TABLE	; Get phase jump table base
      	DAD 	D		; Add offset for this phase
	MOV	A,M		; Get phase pointer into HL
	INX	H		; .
	MOV	H,M		; .
	MOV	L,A		; Pointer is now together
  	MVI 	D,01000000B	; DMA request mask (used by RSCSI and WRSCSI)
      	PCHL			; Go to it!

PHASE$TABLE:
  	DW	PHASE0
  	DW	PHASE1
	DW	PHASE2
  	DW	PHASE3
	DW 	PHASE4
	DW	PHASE5
	DW	PHASE6
	DW	PHASE7

PHASE0:				; Data out phase
	LHLD	DATPTR		; to pass prameters to scsi controller
	JMP	WSCSI		; Execute SCSI write routine

PHASE1:				; Data in phase ...
  	LHLD 	DMAADR		; Use data pointer
  	JMP 	RSCSI		; Execute SCSI read routine

PHASE2:				; Command out phase ...
  	LHLD 	CMDPTR		; Use command pointer
  	JMP 	WSCSI		; Execute SCSI write routine

PHASE3:				; Status in phase ...
  	LXI 	H,STATUS	; Use status pointer
  	JMP 	RSCSI		; Execute SCSI read routine

PHASE7:				; Message in phase ...
  	LXI 	H,MESSAGE	; Use message pointer
  	JMP 	RSCSI		; Execute SCSI read routine

; Currently unused phases

PHASE4:
PHASE5:
PHASE6:
SCSI$EXIT:
	XRA	A		; clean up 5380 and exit
	OUT	NCRTCR
	OUT	NCRMR
	IN	NCRRPI		; reset unterrupts
	RET

; Generalized SCSI read routine
; Initiator command reg is already ininialized by phase
;
RSCSI:	OUT 	NCRSDIR		; Start DMA initiator receive

; Wait for DMA request, keeping an eye on phase.  Note: we must do
; a check for DMA request before checking for a phase change, since
; a byte may be queued up waiting to be DACKed prior to the phase
; change.

RSCSI1:	SHLD	DATPTR		; Save (new) data ptr for multi-sector
  	IN 	NCRBSR
  	MOV 	C,A		; Keep for phase change checking
  	ANA 	D          	; Mask for DMA request
  	JZ 	RSCSI2

;
; All transfer is one byte at a time
; 
	IN	NCRDACK
	MOV	M,A
	INX	H
   	JMP 	RSCSI1		; Read until phase changes
;
; This code skipped when data is being transferred ...
;
RSCSI2:	MOV 	A,C		; Check phase
	ANI 	00010000B     	; .
	JZ 	RSCSI1		; Wait for DMA request
	JMP 	SCSI$INT	; Process "interrupt" flag
;
; Generalized SCSI write routine
;
WSCSI:	MVI 	A,1		; Assert data bus
  	OUT 	NCRICR
  	OUT 	NCRSDS		; Start DMA send
;
; Wait for DMA request, keeping an eye on phase.  Note that the NCR
; will not issue an ACK, nor will it generate DMA requests once the
; phase changes, so it is best to treat DMA request checking as a
; higher priority than phase change checking.
;
WSCSI1:	SHLD	DATPTR		; Save (new) data ptr
WSCI11:	IN 	NCRBSR
  	MOV 	C,A		; Save status for use below
  	ANA 	D          	; Check for DMA request
  	JZ 	WSCSI2
	mov	a,m
	out	ncrdack
	inx	h
    	JMP 	WSCSI1		; Write more bytes until phase changes
;
; This code skipped when data is being transferred ...
;
WSCSI2:	MOV 	A,C		; Check phase
    	ANI 	00010000B       ; .
    	JZ	WSCSI1		; Wait for dma request
    	JMP 	SCSI$INT	; Process "interrupt"
;
; Tempory storage
;
TRIES:		DB	5	; number of tries
DMA:		DS	2	; save for dma address
TIMEOUT:	DS	1
MYID:		DS	1
TARGET:		DS	1
ERFLAG:		DS	1
HDSECT:		DS	1
DATPTR:		DS	2
CMDPTR:		DS	2
MESSAGE:	DS	4
SCSI:		DS	1
SAVE$DMA:	DS	2
SAVE$CMDPTR:	DS	2

	END

