TITLE	'CPM+ DISK IO MODULE FOR XCOMP HARD DISK CONTROLLER'
;
;This is a self contained module to link in a Xcomp controler to CPM+. It
;is setup for a 10 Mg byte miniscribe at the moment
;

;	John Monahan		(201) 783 1548
;
	; DEFINE LOGICAL VALUES:
TRUE		EQU	-1
FALSE		EQU	NOT TRUE
;
	; DETERMINE IF BANK SELECTING:
BANKED		EQU	TRUE		;FULL BLOWN VERSION
;
;
BELL	EQU	07H
CR	EQU	0DH
LF	EQU	0AH
MAXCYL	EQU	500			;<---WILL ALLOW A MAX OF ONLY 8 MBYTES
LZONE	EQU	656
;
;   DRIVE COMMANDS
;
SKCMD	EQU	3		;SEEK
SKOUT	EQU	1		;SEEK OUTWARD
NOPC	EQU	40H		;NO PRE-COMPENSATION
LOWRT	EQU	80H		;LOW WRITE CURRENT
VSA	EQU	8		;SEEK VERIFY START ADDRESS
VCA	EQU	1BH		;SEEK VERIFY COMMAND ADDRESS
;
;   DRIVE STATUS
;
READY	EQU	1		;DRIVE READY
WRTFLT	EQU	2		;WRITE FAULT
TK00	EQU	4		;TRACK ZERO
RAWINDX	EQU	20H		;RAW INDEX
;
;   CONTROLLER COMMANDS
;
BANK0	EQU	0		;BANK 0 SELECT
BANK1	EQU	1		;BANK 1 SELECT
DBENB	EQU	2		;DATA BUFFER ENABLE
CBENB	EQU	4		;COMPARE BUFFER ENABLE
START	EQU	8		;START COMMAND
;
;   DRIVE/CONTROLLER I/O
;
CBASE	EQU	70H		;BASE ADR OF THE CONTROLLER
DRCSR	EQU	CBASE		;DRIVE COMMAND/STATUS
EXTCMD	EQU	CBASE+1		;EXTENDED COMMNAND REGISTER
LOSC	EQU	CBASE+2		;SEEK COUNT, LSB
HISC	EQU	CBASE+3		;  * MSB
CTCSR	EQU	CBASE+4		;CONTROLLER COMMAND/STATUS
CTBFR	EQU	CBASE+5		;CONTROLLER BUFFER ADDRESS
CTDP	EQU	CBASE+6		;CONTROLLER DATA PORT


	; DEFINE PUBLIC LABELS:
	PUBLIC	DPH0			;DISK PARAMETER HEADERS

	; DEFINE EXTERNAL LABELS:
	EXTRN	@ADRV,@RDRV
	EXTRN	@DMA,@TRK,@SECT
	EXTRN	@CBNK
	EXTRN	@DBNK			;BANK FOR DMA OPERATION
	EXTRN	@ERMDE			;BDOS ERROR MODE
	EXTRN	?WBOOT			;WARM BOOT VECTOR
	EXTRN	?PMSG			;PRINT MESSAGE @<HL> UP TO 00, SAVES
					; [BC] AND [DE]
	EXTRN	?PDERR			;PRINT BIOS DISK ERROR HEADER
	EXTRN	?CONIN,?CONO		;CONSOLE IN AND OUT
	EXTRN	?CONST			;CONSOLE STATUS
	EXTRN	?BNKSL			;SELECT PROCESSOR MEMORY BANK

	EXTRN	?SMSG			;MY ROUTINE TO SPEAK A MESSAGE
					;YOU CAN REMOVE REFERENCES TO THIS

	; INCLUDE CP/M 3.0 DISK DEFINITION MACROS:
	MACLIB	CPM3

	; INCLUDE Z-80 MACRO LIBRARY:
	MACLIB	Z80


	IF	BANKED
	DSEG			;PUT IN OP SYS BANK IF BANKING
	ELSE
	CSEG			;ELSE KEEP IN COMMON MEMORY
	ENDIF

	; EXTENDED DISK PARAMETER HEADER FOR DRIVE 0:
	DW	HDWRT		;HARD DISK WRITE ROUTINE
	DW	HDRD		;HARD DISK READ ROUTINE
	DW	HDLOGIN		;HARD DISK LOGIN PROCEDURE
	DW	HDINIT		;HARD DISK DRIVE INITIALIZATION ROUTINE
	DB	0		;RELATIVE DRIVE 0 ON THIS CONTROLLER
	DB	0		;MEDIA TYPE:
				;  HI BIT SET : DRIVE NEEDS RECALIBRATING
DPH0:	DPH	0,HD$DPB,0,

	; MAKE SURE DPB'S ARE IN COMMON MEMORY:
	CSEG

	; 256 BYTE SECTORS ON HARD DISK
HD$DPB:	DPB	256,32,1000,2048,1024,2,8000H

	IF	BANKED
	DSEG			;CAN SET BACK TO BANKED SEGMENT IF BANKING
	ENDIF

	;;;;; HDINIT:
HDINIT	RET			;DO NOT INITILIZE HARD DISK YET
;
	;;;;; HDLOGIN
;------	INITILIZE THE XCOMP HARD DISK CONTROLLER (MUST DOWNLOAD MICROCODE) ---
HDLOGIN:
	CALL	XTKZ	     	;RESTORE
	JNZ	RESERR
	LXI	H,SAYSIGNON
	CALL	?SMSG		;SPEAK MESSAGE
	XRA	A		;RETURN WITH NO ERROR
	RET

RESERR:	LXI	H,MSGH2		;RESTORE FAILED
	CALL	?PMSG
	ORI	1
	RET
;
	;;;;; HDWRT
	; ROUTINE WRITES 1 SECTOR TO THE DISK:
;
HDWRT:	XRA	A
	STA	ERFLG
	CALL	GETDMA		;GET DMA ADR, SET POINTERS

	IF	BANKED
	JMP	ADJBNNN

	CSEG

ADJBNNN:LDA	@CBNK
	PUSH	PSW
	LDA	@DBNK
	CALL	?BNKSL
	OUTIR
	POP	PSW
	CALL	?BNKSL
	JMP	ADJDDD

	DSEG

	ELSE
	OUTIR
	ENDIF

ADJDDD:	CALL	XWRT
	LDA	ERFLG
	RET

XWRT:	LXI	H,WTBL		;GET COMMAND TABLE
	CALL	DORW		;EXICUTE WR/RD COMMANDS
	JMP	XR1

	;;;;; HDRD
	; ROUTINE READS 1 PHYSICAL SECTOR FROM THE DRIVE:
;
HDRD:	XRA	A
	STA	ERFLG	     	;CLEAR THE ERROR FLAG
	CALL	XREAD	     	;READ A BLOCK
	CALL	GETDMA	     	;GET DMA ADR, SET POINTERS

	IF	BANKED
	JMP	ADJBNKS

	CSEG			;MUST HAVE THE FOLLOWING CODE IN COMMON
 
ADJBNKS:LDA	@CBNK
	PUSH 	PSW
	LDA	@DBNK		;MUST HAVE THIS CODE IN COMMON
	CALL	?BNKSL		;NOW DMA ADDRESS IS AT THE CORRECT BANK
	IN	CTDP	     	;PRIME DATA INPUT
	NOP
	INIR		 	;BLOCK INPUT
	POP	PSW
	CALL	?BNKSL
	JMP	ADJDON

	DSEG

	ELSE
	IN	CTDP	     	;PRIME DATA INPUT
	NOP
	INIR		 	;BLOCK INPUT
	ENDIF

ADJDON:	LDA	ERFLG	     	;ERROR FLAG
	RET
;
XREAD:	LXI	H,RTBL	     ;SEEK SECTOR COMMAND TABLE IS SENT TO CONTROLLER
	CALL	DORW	     ;SEND IT
XR1:	MVI	A,0
	RZ		     ;RIF READ/WRITE OK
	INR	A
	STA	ERFLG        ;SET ERROR FLAG
	RET	
;
DORW:	SHLD	CTA	     ;SAVE CMD TBL ADR
	CALL	XSEK	     ;SEEK TO NEW TRACK (IF REQUIRED)
	RNZ		     ;RIF SEEK FAILED
	CALL	XSEL	     ;HEAD SELECT
	LHLD	CTA
;
DO0:	MOV	A,M
	STA	RETRY        ;SET RETRY COUNT
	INX	H
	MOV	A,M
	OUT	CTCSR	     ;ENB CMP BFR
	INX	H
	MOV	A,M
	OUT	CTBFR	     ;SET CMP BFR ADR
	INX	H
	SHLD	CTA	     ;SAVE CMD TBL ADR
;
	LXI	H,RCA	     ;REAL TK ADR
	MVI	B,3
DO1:	MOV	A,M	     
	OUT	CTDP	     ;PUT HDR INFO INTO CMP BFR
	INX	H
	DCR	B
	JNZ	DO1
	LDA	@SECT
	OUT	CTDP	     ;SET SECT ADR FOR COMPARE
;
DO2:	CALL	XRDY	     ;DRIVE READY ?
	RNZ		     ;  RIF NO
	LHLD	CTA          ;CMD TBL ADR
	MOV	A,M	     ;A = CNTL BANK
	INX	H
	MOV	B,A
	OUT	CTCSR	     ;SLCT CNTL BANK
	MOV	A,M
	OUT	CTBFR	     ;SET START ADR
	INX	H
	MOV	A,B
	ORI	START
	OUT	CTCSR	     ;START R/W CMD
;
DO3:	CALL	WFD	     ;WAIT FOR READ/WRITE TO FINISH
	RC		     ;ABORT IF TIMEOUT
	ANA	M	     ;TEST CTLR STATUS (0=OK)
	MOV	B,A
	IN	DRCSR	     ;DRIVE STATUS
	ANI	WRTFLT
	CNZ	CLRDF        ;CIF CLEAR DRIVE FAULT
	ORA	B	     ;SET/CLEAR ERROR FLAG (0=OK)
	RZ		     ;RIF READ/WRITE OK
	LXI	H,RETRY
	DCR	M	     ;DECR RETRY COUNT
	JNZ	DO2	     ;JIF RETRY READ/WRITE
;
;   SET ERROR FLAG
;
SEF:	MVI	A,1	     ;A = ERROR FLAG
	ORA	A	     ;SET 8080 FLAGS
	RET		     ;TAKE ERROR EXIT
;
;	---WAIT FOR DONE---
;
WFD:	PUSH	H
	LXI	H,0		;TIMEOUT DELAY COUNT
;
WFD1:	IN	CTCSR	     ;CTLR STATUS
	RRC	
	JC	WFD2	     ;WAIT FOR DONE
	DCX	H
	MOV	A,H
	ORA	L
	JNZ	WFD1
;
	OUT	CTCSR
	POP	H
	MVI	A,1
	ORA	A
	STC
	RET
;
WFD2:	POP	H
	IN	CTCSR	     ;GET NON-CHANGING STATUS
	MOV	B,A	
	XRA	A
	OUT	CTCSR	     ;STOP CTLR
	MOV	A,B
	RET	
;	
;   SETS THE CONTROLLER BUFFER ADDRESS TO THE CORRECT
;   STARTING POINT. ALSO SETS B=256 & H/L=@DMA
;
GETDMA:	LHLD	@DMA
	MVI	C,CTDP	     ;FOR BLOCK I/O
	MVI	B,0	     ;B = COUNT (256 BYTES)
	MVI	A,DBENB
	OUT	CTCSR	     ;ENB DATA BFR
	MVI	A,0
GET1:	OUT	CTBFR	     ;SET CTLR DATA BFR ADR
	RET	
;
;	---REZERO---
;
XTKZ:	LXI	H,0
	SHLD	RCA
	CALL	TZT		;TEST IF TRK 0
	RZ
;
	LXI	H,511		;#OF CYLINDERS WE CAN COUNT ON CONTROLLER
	CALL	RTZ		;SEEK OUT
	RC			;ABORT DRIVE NOT READY
	RZ			;IS AT 0
	LXI	H,LZONE+10-511
	CALL	RTZ		;TRY SECOND PUMP
	RC
	RZ
	JMP	SEF		;ABORT RESTORE FAILED
;
;	SEEK OUTWARD
;
RTZ:	CALL	XRDY
	STC
	RNZ
	MOV	A,L
	OUT	LOSC		;SET LSB OF SEEK COUNT
	MOV	A,H
	OUT	HISC		;SET MSB
	MVI	A,SKOUT
	OUT	EXTCMD		;SET SEEK DIRECTION OUTWARD
	MVI	A,SKCMD
	OUT	DRCSR		;ISSUE SEEK
	CALL	WSC
	RC
;
TZT:	IN	DRCSR		;GET DRIVE STATUS
	ANI	TK00
	XRI	TK00
	RET
;
;
;	---SEEK---
;
XSEK:	MVI	A,3
	STA	SKRTC	     ;SET SEEK RETRY COUNT
;
XSEK1:	LHLD	@TRK	     ;REQUESTED TRACK
	MVI	B,1
	CALL	DRS	     ;SHIFT H/L RIGHT ONCE 
	LXI	D,MAXCYL
	CALL	TSUB
	RAL
	JNC	SEF		;ABORT IF INVALID ADDRESS
XSEK2:	XCHG			;[DE] = NEW CYLINDER ADDRESS
	LHLD	RCA		;LOAD UP CURRENT REAL ADR
	XCHG
	SHLD	RCA		;SAVE NEW ADDRESS
	XCHG
	CALL	SUBT		;GET DIFFERENCE
	RZ			;RETURN IF SAME
	MVI	B,1		;DIR = OUT
	RAL
	JNC	XSEK3		;OK SEEK OUTWARD
;
	MVI	B,3		;SEEK INWARD
	MOV	A,L
	CMA			;MAKE SEEK POSITIVE
	MOV	L,A
	MOV	A,H
	CMA
	MOV	H,A
	INX	H
;
XSEK3:	MOV	A,B		;GO TO SEEKING INWARD
	STA	SKDIR
	LXI	D,512
	CALL	TSUB
	RAL
	JNC	XSEK4		;JIF DOUBLE PUMP IS REQ
	CALL	PSK		;DO PARTIAL SEEK
	RNZ
	JMP	XSEK5
;
XSEK4:	DCX	D		;[DE] =511
	CALL	SUBT		;[HL]-[DE]
	SHLD	RSKNT		;SAVE RESIDUAL COUNT
	XCHG
	CALL	PSK
	RNZ			;ABORT IF SEEK FAILED
	LHLD	RSKNT
	CALL	PSK		;SEND THE REST
	RNZ
;
;				;SEEK VERIFY
XSEK5:	MVI	A,3
	STA	VSRTC		;SET RETRY COUNT
	MVI	A,CBENB
	OUT	CTCSR		;ENABLE BANK ZERO CMP BFR
	MVI	A,VCA
	OUT	CTBFR		;SET CMP BFR ADR
	LHLD	RCA		;REAL (CURR) CYL ADR
	MOV	A,L
	OUT	CTDP		;SET CYL ADR, LSB
	MOV	A,H
	OUT	CTDP		;SET CYL ADR, MSB
XSEK6:	MVI	A,VSA
	OUT	CTBFR		;SET M/CODE START ADR
	MVI	A,START
	OUT	CTCSR		;START VERIFY
	CALL	WFD		;WAIT FOR DONE
	ANI	0CH		;TEST CTLR STATUS
	RZ			;RIF VERIFY OK
	LXI	H,VSRTC
	DCR	M		;DECR RETRY COUNT
	JNZ	XSEK6		;JIF RETRY SEEK VERIFY
				; VERIFY FAILED
	CALL	XTKZ		;RESTORE
	LXI	H,SKRTC
	DCR	M		;DECR RETRY COUNT
	JNZ	XSEK1		;JIF RETRY SEEK
				; UNRECOVERABLE SEEK ERROR
	ORI	1		;SET ERROR FLAG
	RET			;ABORT
;
;   PARTIAL SEEK
;
PSK:	CALL	XRDY		;DRIVE READY ?
	RNZ			;ABORT, DRIVE NOT RDY
	MOV	A,L
	OUT	LOSC		;SET SEEK COUNT, LSB
	MOV	A,H
	OUT	HISC		;   * MSB
	LDA	SKDIR
	OUT	EXTCMD		;SET SEEK DIRECTION
	MVI	A,3
	OUT	DRCSR		;ISSUE SEEK CMD
;
;	 ---> FALL THRU TO 'WSC' <---
;
;	---WAIT FOR SEEK COMPLETE---
;
;
WSC:	PUSH	H		;SAVE REGS
	PUSH	B
	LXI	H,0		;TIME-OUT DELAY COUNT
	MVI	B,6		;XCOMP HAD 3 NEED 6 FOR 5MHz
;
WSC1:	IN	DRCSR		;DRIVE STATUS
	RAL
	JC	WSC2		;JIF SEEK DONE
	DCX	H		;DECR DELAY COUNT
	MOV	A,H
	ORA	L
	JNZ	WSC1		;JIF CON'T WAITING
	DCR	B
	JNZ	WSC1
				; TIME-OUT ERROR
	POP	B		;RESTORE REGS
	POP	H
	MVI	A,1
	ORA	A		;SET CPM ERROR FLAG
	STC			;SET INTERNAL ERROR FLAG
	RET
;
WSC2:	POP	B		;RESTORE REGS
	POP	H
	XRA	A		;SET FLAG = OK
	RET
;
;
;
;	---CLEAR DRIVE FAULT---
;
CLRDF:	XRA	A
	OUT	EXTCMD	     ;DE-SELECT THE DRIVE (FALL THRU TO 'XSEL' TO
			     ;RE-SELECT THE DRIVE)
;
;	---HEAD SELECT---
;
XSEL:	LDA	@TRK         ;REQUESTED TRACK
	ANI	1	     ;2 HEADS
	STA	RHD	     ;SAVE REAL HEAD #
	ADD	A	     ;SHIFT HEAD # LEFT TWICE FOR H/W
	ADD	A
	ORI	1	     ;TO MAINTAIN DRIVE SLCT
	OUT	EXTCMD	     ;SELECT HEAD 0 OR 1
	RET	
;
;	---DRIVE READY TEST---
;
XRDY:	IN	DRCSR	     ;DRIVE STATUS
	ANI	1	     ;DRIVE RDY BIT
	XRI	1	     ;  MAKE IT LO-TRUE
	RZ		     ;RIF DRIVE READY
	LXI	H,MSGH1	     ;ERROR MSG
	CALL	?PMSG
	ORI	1	     ;SET ERROR FLAG
	RET	
;
;
;   SHIFTS THE CONTENTS OF H/L RIGHT 'N' TIMES.
;
DRS:	PUSH	PSW	     ;SAVE 'A'
DRS1:	MOV	A,H	     ;MSB
	ORA	A	     ;CLEAR CY
	RAR	
	MOV	H,A
	MOV	A,L	     ;LSB
	RAR	
	MOV	L,A
	DCR	B	     ;B = SHIFT COUNT
	JNZ	DRS1
	POP	PSW	     ;RESTORE 'A'
	RET	
;
SUBT:	MOV	A,L		;16 BIT SUBTRACTION
	SUB	E
	MOV	L,A
	MOV	A,H
	SBB	D
	MOV	H,A
	ORA	L
	MOV	A,H
	RET
;
TSUB:	PUSH	H		;SAVE [HL]
	CALL	SUBT
	POP	H
	RET
;
;------------------------- DATA STORAGE AREA -------------------------------
;
;	--- COMMAND TABLE TO SEEK SECTOR/TRACK ---
;
;
WTBL:	DB	5	     ;RETRY COUNT
	DB	5	     ;CMP BFR ENB
	DB	0E6H	     ;CMP BFR ADR
	DB	BANK1	     ;CNTL BANK
	DB	0D3H         ;START ADR
	DB	0EH	     ;STATUS MASK
;
RTBL:	DB	10	     ;RETRY COUNT
	DB	4	     ;CMP BFR ENB
	DB	0EAH	     ;CMP BFR ADR
	DB	BANK0	     ;CNTL BANK
	DB	0D7H	     ;START ADDRESS
	DB	0EH	     ;STATUS MASK
;
MSGH1:	DB	BELL,CR,LF,'Drive not Ready',0
MSGH2:	DB	BELL,CR,LF,'Restore Failed',0
SAYSIGNON: DB	'HHARD  DISK ON DRIVE AEE ',0
;
RCA:	DS	2	     ;REAL TRACK ADDRESS
RHD:	DS	1	     ;  HEAD
RSA:	DS	1	     ;  SECTOR
;
RETRY:	DS	1	     ;RETRY COUNT
CTA:	DS	2	     ;COMMAND TABLE ADDRESS
ERFLG:	DS	1	     ;ERROR FLAG
SKRTC:	DS	1		;SEEK RETRY COUNT
VSRTC:	DS	1		;SEEK VERIFY COUNT
SKDIR:	DS	1		;SEEK DIRECTION
RSKNT:	DS	2		;RESIDUAL SEEK COUNT
SPARE:	DS	2
;
;

