	page
	IF	HARD
;	HDSEL -- Select appropriate hard disk drive.
;

HDSEL:
	CALL	HDALT		;Check for alterate sectors
	mvi	a,2
hdsl1:	sta	rept
	LDA	CIOPB+1		;Get unit #
	ORI	'0'
	STA	HDRDY1
	ANI	3		;Strip off ascii bias
	mov	b,a
	MVI	A,10h
HDSL0:	DCR	B
	JM	HDSLD
	RLC
	JMP	HDSL0

HDSLD:	MOV	B,A		;Save drive select
	LDA	CIOPB+1	
	ORI	H.DVS
	OUT	HDCTL		;Set select register
	MOV	A,B
	OUÔ	HDDATA
	IN	HDCTL
	ANI	1000_1000b
	cpi	80h
	JZ	HDSL2		;If drive ready
	lda	rept
	dcr	a
	jnz	hdsl1
	LXI	H,HDRDY
	CALL	PRINT
	LXI	B,500
	CALL	DELAY
	CALL	J.CST
	ANA	A
	JZ	HDSL1		;If not abort
	INR	A		;A = 0FFh +1 = 0
	INR	A		;A = 1
	RET

HDSL2:	LDA	CIOPB+1
	ORI	0000_0100B
	OUT	HDCTL
	CALL	BADMAP
	MVI	A,0ffh
	RC			;Error in reading bad map. A = 0ffh
	INR	A		;A + 1 = 0
	RET

HDRDY:	DB	CR,LF,'Hard disk unit '
HDRDY1:	DB	'x not ready!',0
	space	4,10

HDITAB:	DB	0, 0, 0, 0	;Set to one once bad map is read

;	BADMAP -- Read in bad map first time a drive is selected

BADMAP:	LHLD	CIOPB+1		;Pick up drive number in (l)
	MVI	H,0
	LXI	D,HDITAB	;Point to initilized table
	DAD	D
	MOV	A,M
	ORA	A		;Check for one
	RNZ			;Bad sector map allready read in
	MVI	M,1		;Set as read in

	LHLÄ	BUFADÒ		;Savå currenô buffeò address
	PUSH	H
	LHLD	CIOPB+4		;Save all CIOPB locations
	PUSH	H
	LHLD	CIOPB+2
	PUSH	H
	LHLD	CIOPB+0
	PUSH	H
	XCHG
	LXI	H,CIOPB+0
	MVI	M,H.RED		;Read a sector command
	XRA	A
	INX	H
	MOV	M,D		;Store unit #
	INX	H
	MOV	M,A		;Cylinder 0
	INX	H
	MVI	M,2		;Head 2
	INX	H
	INR	A
	MOV	M,A		;Sector 0 (1)

	LXI	H,HSTBUF	;Set DMA address to internal buffer
	SHLD	BUFADR

	CALL	HDSEEK
	lxi	h,numsec
	mov	b,m
	push	b
	mvi	m,1
	CALÌ	HDXFER
	pop	b
	lxi	h,numsec
	mov	m,b
	STC			;Set error flag
	JNZ	BADERR		;Return if error

	POP	H		;Restore info
	SHLD	CIOPB+0
	POP	H
	SHLD	CIOPB+2
	POP	H
	SHLD	CIOPB+4
	POP	H
	SHLD	BUFADR
	
	LXI	H,HSTBUF
ADDLR:	MOV	A,M		;Get an entry
	ORA	A
	JZ	HDSEL		;This is the return.  It will reselect the head
	CALL	ADDTO
	LXI	D,6		;Skip to next entry
	DAD	D
	JMP	ADDLR

BADERR:	POP	H
	POP	H
	POP	H
	POP	H
	STC
	RET

ADDTO:	PUSH	H		;Save address of current bad sector info
	MOV	B,H
	MOÖ	C,L
	LXI	D,HDBAD		;Start of bad map pointer
ADDL:	LHLD	HDPTR		;Pick up current pointer
	MOV	A,L		;Check if at end of table
	CMP	E
	JNZ	MORE
	MOV	A,H
	CMP	D
	JNZ	MORE
				;End of table so append
	LDA	CIOPB+1		;Pick up drive
	STAX	D		;Store in bad map table
	INX	D
	MVI	L,6		;Move 6 bytes
ATL:	LDAX	B
	STAX	D
	INX	B
	INX	D
	DCR	L
	JNZ	ATL
	XCHG
	SHLD	HDPTR		;Restore new bad map pointer
	POÐ	H
	RET

MORE:	PUSH	B		;Save current bad sector
	PUSH	D		;Save bad map pointer
	XCHG			;Move 
	LDA	CIOPB+1
	CMP	M
	INX	H
	JNZ	MISS

	MVI	E,3		;Try to match 3 bytes
MORAL:	LDAX	B
	CMP	M
	JNZ	MISS
	INX	B
	INX	H
	DCR	E
	JNZ	MORAL		;Still more to compare
	XTHL
	POP	D
	POP	B
	POP	H
	RET			;Current entry allready in bad map

MISS:	XCHG
	POP	D
	POP	B
	LXI	H,7		;Length of a bad map entry
	DAD	D
	XCHG
	JMP	ADDL

;
;	HDSEEK -- Seek to specified cylinder.
;
;	ENTRY	CIOPB+2 = cylinder.

HDSEEK:	LDA	ACTDSK
	MVI	D,0
	MOV	E,A
	LXI	H,HDCYL
	DAD	D
	MOV	A,M
	CMP	-1
	JNZ	HDSK3		;If not forced home
hdsk0:	IN	HDCTL
	ANI	0000_0001B
	JZ	HDSK3
	LDA	ACTDSK
	ORI	4+H.SOU
	OUT	HDCTL
	if	gbc20
	MVI	B,0
	endif
	if	gbc10
	mvi	b,0
	endif
	if	gbc26
	mvi	b,1
	endif
HDSK1:	IN	HDDATA
	DCR	B
	JNZ	HDSK1
HDSK2:	IN	HDCTL
	ANI	0000_0100B
	JNZ	HDSK2
	if	gbc26
	jmp	hdsk0
	endif
HDSK3:	MOV	C,A		;Save current cylinder
	LDA	CIOPB+2		;Get desired cylinder
	MOV	M,A		;Set current = desired
	SUB	C		;Desired - current
	STA	HDSKFG		;Save flag
	RZ			;If current cylinder = desired
	JC	HDSK4		;If outward
	MOV	B,A		;Move number of tracks
	LDA	CIOPB+1
	ORI	H.SIN
	OUT	HDCTL
	JMP	HDSTEP

HDSK4:	CMA
	INR	A
	MOV	B,A		;Save track count
	LDA	CIOPB+1
	ORI	H.SOU		;Direction = outwards
	OUT	HDCTL
HDSTEP:	IN	HDDATA
	DCR	B
	JNZ	HDSTEP		;If not all steps sent
	RET

;	HDXFER -- Transfer data to/from hard disk.
;
;	ENTRY	CIOPB+0 = command.

HDXFER:	XRA	A
	STA	SECOFF
	LXI	H,0
	SHLD	SECADD
	LDA	HDSKFG		;Check seek outstanding
	ORA	A
	JZ	HDX2		;If not seek
HDX1:	IN	HDCTL
	ANI	0000_0100B
	JNZ	HDX1

	if	gbc26
	lxi	b,32
	call	delay
	endif

HDX2:	LDA	CIOPB+1		;Pick up drive
	MOV	C,A
	INR	C		;Inc drive number
	MVI	A,10h
HDXSL:	DCR	C
	JZ	HDXDN
	RLC
	JMP	HDXSL

HDXDN:	MOV	C,A
	LDA	CIOPB+1
	ORI	H.DVS
	OUT	HDCTL		;Output to drive select
	LDA	CIOPB+3
	ORA	C
	OUT	HDDATA		;Select drive, head

	LXI	H,CIOPB+2
	MVI	A,H.CYL
	OUT	HDCTL
	MOV	A,M		;Cylinder
	OUT	HDDATA
	INX	H
	MVI	A,H.HED
	OUT	HDCTL
	MOV	A,M		;Head
	OUT	HDDATA
	INX	H
	MVI	A,H.SEC
	OUT	HDCTL
	LDA	SECOFF
	ADD	M		;Sector
	DCR	A
	OUT	HDDATA
	LHLD	BUFADR
	XCHG
	LHLD	SECADD
	DAD	D
	MVI	B,0
	JNC	HDX3
	MVI	B,1
HDX3:	IN	SELCHAN
	LDA	BUFADE
	ADD	B
	OUT	SELCHAN
	MOV	A,H
	OUT	SELCHAN
	MOV	A,L
	OUT	SELCHAN
	LDA	CIOPB+0
	MOV	B,A
	CPI	H.RED
	MVI	A,80H+SELBYT
	JZ	HDX4
	MVI	A,SELBYT
HDX4:	OUT	SELCHAN
	LDA	CIOPB+1
	ORA	B
	OUT	HDCTL
HDX5:	IN	HDCTL		;Get status
	ANI	1000_0000b
	JNZ	HDX5		;If transfer not complete
	IN	HDCTL
	ANI	0101_0000b	;Mask timeout and over run
	JNZ	HDERR
	lda	numsec
	dcr	a
	rz
	sta	numsec
	lxi	h,secoff
	inr	m
	lhld	secadd
	lxi	d,1024
	dad	d
	shld	secadd
	jmp	hdx2

HDERR:	XRA	A
	ORI	01h
	RET

HDALT:	LXI	H,HDBAD		;Point to start of bad map
ALTLOP:	XCHG
	LHLD	HDPTR		;Pick up end of bad map
	XCHG
	PUSH	H		;Save current bad map pointer
	MOV	A,D		;Check if at end of bad map
	CMP	H
	JNZ	ALMORE		;Still more to check
	MOV	A,E
	CMP	L
	JNZ	ALMORE
	POP	H
	RET			;All done with check

ALTSKP:	POP	H
	LXI	D,7		;Size of bad map entry
	DAD	D
	JMP	ALTLOP

ALMORE:	LXI	D,CIOPB+1	;Pick up current drive
	LDAX	D
	CMP	M		;Check if alternate drive matches
	JNZ	ALTSKP
	INX	H
	INX	D

	LDAX	D		;Pick up alternate cylinder
	CMP	M		;Check for match
	JNZ	ALTSKP
	INX	H
	INX	D

	LDAX	D
	CMP	M		;Check if head matches
	JNZ	ALTSKP
	INX	H
	INX	D

	LDAX	D		;Pick up sector
	DCR	A		;Dec by one. Sector start at zero
	CMP	M
	JNZ	ALTSKP
	INX	H

	LXI	D,CIOPB+2
	MOV	A,M		;Pick up alternate cyl
	STAX	D
	INX	H
	INX	D
	MOV	A,M		;Pick up alterate head
	STAX	D
	INX	H
	INX	D
	MOV	A,M		;Pick up alterante sector
	INR	A	
	STAX	D
	POP   	H		;Fix up stack
	RET			;Return with alternate info

rept	db	0
HDSKFG:	DB	0
SECOFF:	DB	0
SECADD:	DW	0
.hrd	ENDIF
