;+++++++++++++++++++++++++++++++++++++++++++++++++
;++               D I S K T E S T               ++
;++      FLOPPY DISK DIAGNOSTIC   12-1-'80      ++
;++      TARBELL ELECTRONICS - VERSION 1.9      ++
;+++++++++++++++++++++++++++++++++++++++++++++++++
;THIS PROGRAM READS ALL DISK SECTORS
;AND PRINTS THE TRACK AND SECTOR NUMBER
;WHERE AN ERROR IS FOUND.
;RETRY COUNT CAN BE SET AT 0 - 9.
;NORMALLY SET RETRY COUNT AT 0.
;SETTING RETRY COUNT TO GREATER THAN ZERO WILL CAUSE THE
;PROGRAM TO TRY TO READ A SECTOR WHERE AN ERROR IS FOUND
;THAT MANY TIMES OVER BEFORE IT WILL PRINT AN ERROR MESSAGE.
;STEP RATE CAN BE SET TO SLOW (20MS.), MEDIUM (10MS.),
;OR FAST (6MS.).
;FULL TRACK SEEK SEEKS INSIDE TRACK BEFORE READING EACH TRACK
;UP TO THE MIDDLE TRACK, AND SEEKS TRACK ZERO BEFORE READING
;EACH TRACK PAST THE MIDDLE TRACK.
;CONTROL C TERMINATES TEST AND DOES A WARM BOOT.
;CONTROL D TERMINATES TEST AND STARTS OVER.
;CONTROL A AS A RESPONSE TO A PROMPT FOR A TEST
;PARAMETER, (SELECT STEP RATE, ETC.), WILL CAUSE
;THE PROGRAM TO START OVER WITH THE FIRST PROMPT.
;THIS PROGRAM INCLUDES DISK I/O ROUTINES AND CALLS CP/M
;ONLY FOR CONSOLE I/O.
;DISK WRITE ROUTINES HAVE BEEN INCLUDED IN THE SOURCE
;ALTHOUGH THEY ARE NOT USED BY THIS VERSION OF THE PROGRAM.
;WRITTEN BY MARC D. FARJEON USING DISK I/O ROUTINES
;FROM THE TARBELL CBIOS.

VN	EQU  19
FALSE	EQU  0		;DEFINE VALUE OF FALSE.
TRUE	EQU  NOT FALSE	;DEFINE VALUE OF TRUE.
STD	EQU  TRUE	;TRUE IF STANDARD DRIVE.
SINGLE	EQU  FALSE	;true if using single density board.
FAST	EQU  NOT STD	;TRUE IF FAST SEEK.
HLAB	EQU  8		;no headload @ beginning of seek = 0.
SEEKC	EQU  18H	;seek command with head load.
DISK	EQU  0F8H	;DISK BASE ADDRESS.
DCOM	EQU  DISK	;DISK COMMAND PORT.
DSTAT	EQU  DISK	;DISK STATUS PORT.
TRACK	EQU  DISK+1	;DISK TRACK PORT.
SECTP	EQU  DISK+2	;DISK SECTOR PORT.
DDATA	EQU  DISK+3	;DISK DATA PORT.
WAIT	EQU  DISK+4	;DISK WAIT PORT.
DCONT	EQU  DISK+4	;DISK CONTROL PORT.
TRACKS	EQU  77		;TRACKS PER DISK.
SECTS	EQU  26		;SECTORS PER TRACK.
DOS     EQU  5		;BDOS ENTRY POINT.
CREAD	EQU  1		;CODE FOR CONSOLE READ.
CWRITE	EQU  2		;CODE FOR CONSOLE WRITE.
CPB	EQU  9		;CODE FOR PRINT BUFFER.
CSTAT	EQU  11		;CODE FOR STATUS CHECK.
DRINT	EQU  25		;CODE FOR DRIVE INTERROGATE.
;
	ORG  100H	;STARTING ADDRESS.
;
	LXI  H,0	;CLEAR H AND L.
	DAD  SP		;H AND L = SP.
	SHLD CPMS	;SAVE IT.
	LXI  H,OMSG	;GET MESSAGE ADDRESS.
	CALL CPRINT	;PRINT MESSAGE.
START:	LXI  SP,STACK	;LOAD STACK POINTER.
SELR:	LXI  H,SELMSG	;DRIVE SELECT MESSAGE.
	CALL CPRINT	;PRINT MESSAGE.
	CALL CONIN	;READ DRIVE.
	ANI  5FH	;CONVERT TO UPPER CASE.
	CALL BACK	;CHECK TO START OVER.
	CPI  'Q'	;WANT TO QUIT?
	JZ   EXITT	;YES
	CPI  'A'	;COMPARE
	JC   SELR	;FOR
	CPI  'E'	;CORRECT
	JNC  SELR	;DRIVE.
	STA  DRIVE	;SAVE IT.
RETR:	LXI  H,REMSG	;RETRY QUESTION.
	CALL CPRINT	;PRINT QUESTION.
	CALL CONIN	;READ RETRY COUNT.
	CALL BACK	;CHECK.
	CPI  '0'	;COMPARE
	JC   RETR	;FOR
	CPI  ':'	;CORRECT
	JNC  RETR	;NUMBER.
	ANI  0FH	;MASK OUT UPPER NYBBLE.
	ADI  1		;ADD ONE.
	STA  RTCNT	;STASH IT.

	IF   STD	;IF STANDARD DRIVE.
STPSET:	LXI  H,STPMSG	;STEP RATE MESSAGE.
	CALL CPRINT	;PRINT IT.
	CALL CONIN	;GET CHARACTER.

	IF   NOT SINGLE
	CPI  '0'	;CHECK LIMITS
	ENDIF

	IF   SINGLE
	CPI  '1'
	ENDIF

	JC   STPSET	;TO SMALL
	CPI  '4'
	JNC  STPSET
	CALL SETSTP	;SET STEP RATE.
	ENDIF

	LXI  H,LSMSG	;LONG SEEK QUESTION.
	CALL CPRINT	;PRINT IT.
	CALL CONIN	;READ RESPONSE.
	ANI  5FH	;MAKE UPPER CASE.
	CALL BACK	;CHECK.
	CPI  'Y'	;IS IT Y? IF SO,
	LXI  H,LST	;SET LOCATION LST TO
	MVI  M,1	;ONE FOR LONG SEEK.
	JZ   READY	;JUMP.
	MVI  M,0	;ZERO FOR SEQUENTIAL SEEK.
READY:	LXI  H,RMSG	;GET MESSAGE ADDRESS.
	CALL CPRINT	;PRINT MESSAGE.
	CALL CONIN	;READ RETURN.
	CALL BACK	;CHECK.
	CPI  13		;COMPARE FOR RETURN.
	JNZ  READY	;GO BACK IF NOT.
	LXI  H,CRLF	;OUTPUT A RETURN AND
	CALL CPRINT	;LINEFEED TO CRT.
	XRA  A		;SET A = 0.
	STA  TRK	;TRACK COUNTER = 0.
	STA  BCDTRK	;BCD TRACK COUNTER = 0.
	STA  BCRETO	;BCD READ ERRORS = 0.
	STA  BCRETH	;BCRETH = 0.
	STA  TLONTL	;READ ERROR FLAG = 0.
	STA  OTIT	;OTIT = 0.
	STA  OT		;OT = 0.
	INR  A		;SET A = 1.
	STA  SECT	;SECTOR COUNTER = 1.
	STA  BCDSEC	;BCD SECTOR COUNTER = 1.
	MVI  A,TRACKS	;SET A = TRACKS.
	STA  IT		;IT = TRACKS.
	CALL SELECT	;SELECT DRIVE.
	CALL HOME	;RESTORE.
	JMP  READFS	;START READING.

INRTRK:	CALL CONST	;CHECK CONSOLE INPUT STATUS.
	LDA  LST	;CHECK LOCATION LST
	DCR  A		;FOR LONG SEEK. IF ONE,
	JZ   LSTRK	;DO IT. IF NOT,
	LDA  TRK	;GET PRESENT TRACK NUMBER.
	INR  A		;INCREMENT TRACK NUMBER.
	CPI  TRACKS	;IF LAST TRACK,
	JZ   FINIS	;JUMP TO END ROUTINE.
	STA  TRK	;SAVE NEW TRACK NUMBER.
	CALL SEEK	;MOVE HEAD TO NEW TRACK.
	LDA  BCDTRK	;GET BCD TRACK NUMBER.
	ADI  1		;ADD ONE TO TRACK COUNT.
	DAA		;DECIMAL ADJUST.
	STA  BCDTRK	;STASH IT.
	MVI  A,1	;SET A = 1.
	STA  SECT	;SECTOR = 1 FOR NEW TRACK.
	STA  BCDSEC	;BCD SECTOR NUMBER = 1.
READFS:	CALL READ	;READ FIRST SECTOR.
INRSEC:	LDA  SECT	;GET PRESENT SECTOR NUMBER.
	INR  A		;INCREMENT SECTOR NUMBER.
	CPI  SECTS+1	;IF LAST SECTOR,
	JZ   INRTRK	;START NEW TRACK. IF NOT,
	STA  SECT	;SAVE NEW SECTOR NUMBER.
	ORA  A		;CLEAR FLAGS.
	LDA  BCDSEC	;GET BCD SECTOR NUMBER.
	ADI  1		;ADD ONE TO SECTOR COUNT.
	DAA		;DECIMAL ADJUST.
	STA  BCDSEC	;STASH IT.
	LDA  TLONTL	;CHECK FOR ERROR IN LAST
	ORA  A		;SECTOR READ. IF ERROR
	JNZ  READL	;READ WITH HEAD LOAD.
	CALL READN	;READ NEW SECTOR.
	JMP  INRSEC	;READ NEXT SECTOR.
READL:	CALL READ	;READ SECTOR.
	JMP  INRSEC	;READ NEXT SECTOR.
LSTRK:	LDA  OTIT	;CHECK TO READ OUTER OR
	INR  A		;INNER TRACK. INCREMENT
	STA  OTIT	;OTIT.  SAVE IT.
	ANI  1		;IF LSB IS HIGH,
	JNZ  ITRK	;READ INNER TRACK.
	LDA  OT		;GET OUTER TRACK NUMBER.
	INR  A		;INCREMENT TRACK.
	CPI  TRACKS	;IF LAST TRACK,
	JZ   FINIS	;DO QUIT ROUTINE.
	STA  OT		;SAVE IT.
	STA  TRK	;PUT IN TRACK COUNTER.
	CALL SEEK	;MOVE HEAD TO TRACK.
	LDA  BCDTRK	;GET BCD TRACK NUMBER.
	ADI  1		;ADD ONE TO TRACK COUNT.
	DAA		;DECIMAL ADJUST.
	STA  BCDTRK	;SAVE IT.
	MVI  A,1	;SET A = 1.
	STA  SECT	;SECTOR = 1 FOR NEW TRACK.
	STA  BCDSEC	;BCD SECTOR = 1 FOR NEW TRACK.
	JMP  READFS	;READ NEW TRACK.
ITRK:	LDA  TRK	;GET TRACK NUMBER.
	CPI  TRACKS/2+1	;IF < HALFWAY IN,
	JC   INMOST	;SEEK INSIDE TRACK.
	XRA  A		;SET A = 0.
	STA  TRK	;PUT IN TRACK COUNTER.
	CALL SEEK	;MOVE HEAD TO TRACK 0.
	JMP  LSTRK	;READ NEXT TRACK.
INMOST:	MVI  A,TRACKS-1	;SET A = TRACKS-1.
	STA  TRK	;PUT IN TRACK COUNTER.
	CALL SEEK	;MOVE HEAD TO TRACK.
	JMP  LSTRK	;READ NEXT TRACK.

ETS:	LDA  BCDTRK	;GET BCD TRACK NUMBER.
	RRC		;SHIFT
	RRC		;RIGHT
	RRC		;FOUR
	RRC		;BITS.
	ANI  0FH	;MASK OFF UPPER NYBBLE.
	ADI  '0'	;ADD ASCII FOR ZERO.
	CPI  '0'	;SEE IF MSD IS ZERO.
	JNZ  TLSD	;PRINT IT IF NOT ZERO.
	MVI  A,20H	;DO NOT PRINT IF ZERO.
TLSD:	STA  TRKM	;INSERT IN BUFFER.
	LDA  BCDTRK	;GET BCD TRACK AGAIN.
	ANI  0FH	;MASK OFF UPPER NYBBLE.
	ADI  '0'	;ADD ASCII FOR ZERO.
	STA  TRKL	;INSERT IN BUFFER.
	LDA  BCDSEC	;GET BCD SECTOR NUMBER.
	RRC		;SHIFT
	RRC		;RIGHT
	RRC		;FOUR
	RRC		;BITS.
	ANI  0FH	;MASK OFF UPPER NYBBLE.
	ADI  '0'	;ADD ASCII FOR ZERO.
	CPI  '0'	;CHECK FOR MSD OF ZERO.
	JNZ  SLSD	;PRINT IT IF NOT ZERO.
	MVI  A,20H	;DO NOT PRINT IF ZERO.
SLSD:	STA  SECM	;INSERT IN BUFFER.
	LDA  BCDSEC	;GET BCD SECTOR AGAIN.
	ANI  0FH	;LOOK AT LOWER NYBBLE.
	ADI  '0'	;ADD 30 HEX.
	STA  SECL	;INSERT IN BUFFER.
	LXI  H,TSMSG	;GET MESSAGE ADDRESS.
	CALL CPRINT	;PRINT IT.
	LXI  H,TLONTL	;GET TLONTL ADDRESS.
	MVI  M,55H	;SET TO 01010101.
	CALL CONST	;CHECK CONSOLE STATUS.
	RET		;RETURN.

ECOUNT:	LDA  BCRETO	;GET BCD READ ERROR COUNT.
	ADI  1		;ADD ONE TO READ ERROR COUNT.
	DAA		;DECIMAL ADJUST.
	STA  BCRETO	;SAVE IT.
	RNC		;SKIP IF NO CARRY.
	LDA  BCRETH	;GET HUNDREDS COUNT.
	ADI  1		;ADD ONE TO COUNT.
	DAA		;DECIMAL ADJUST.
	STA  BCRETH	;SAVE IT.
	RET		;RETURN.

FINIS:	LXI  H,REC	;POINT TO REC BUFFER.
	LDA  BCRETH	;GET READ ERROR MSD'S.
	RRC		;SHIFT
	RRC		;RIGHT
	RRC		;FOUR
	RRC		;BITS.
	ANI  0FH	;LOOK AT ONE DIGIT.
	JZ   RECK	;SUPPRESS LEADING ZERO.
	ADI  30H	;ADD ASCII FOR ZERO.
RECK:	MOV  M,A	;INSERT IN BUFFER.
	LDA  BCRETH	;GET MSD'S BACK.
	ANI  0FH	;MASK.
	JNZ  PECH	;PRINT IF NOT ZERO.
	CMP  M		;CHECK FOR PREVIOUS ZERO.
	JZ   RECH	;YES, SO DON'T PRINT.
PECH:	ADI  30H	;ADD ASCII.
RECH:	INX  H		;POINT TO NEXT DIGIT.
	MOV  M,A	;INSERT.
	LDA  BCRETO	;GET READ ERROR LSD'S.
	RRC		;SHIFT
	RRC		;RIGHT
	RRC		;FOUR
	RRC		;BITS.
	ANI  0FH	;MASK IT OFF.
	JNZ  PECT	;PRINT IF NOT ZERO.
	LDA  BCRETH	;LOOK AT MSD'S.
	ORA  A		;SEE IF ZERO.
	JZ   RECT	;IF SO, DON'T PRINT.
	XRA  A		;CLEAR A.
PECT:	ADI  30H	;ADD ASCII.
RECT:	INX  H		;INCREMENT POINTER.
	MOV  M,A	;STASH IN BUFFER.
	LDA  BCRETO	;GET BACK LSD'S.
	ANI  0FH	;MASK.
	ADI  30H	;ADD ASCII.
	INX  H		;POINT TO LAST DIGIT.
	MOV  M,A	;STASH IT.
	LXI  H,RECMSG	;GET ERROR MESSAGE.
	CALL CPRINT	;PRINT IT.
	JMP  SELR	;START AGAIN
;
EXITT:	CALL INTDRN	;INTERROGATE DRIVE NUMBER.
	LHLD CPMS	;GET OLD
	SPHL		;STACK BACK.
	RET		;GO BACK TO CPM.

CONST:	MVI  C,CSTAT	;SET C FOR STATUS CHECK.
	CALL DOS	;DO IT.
	ANI  1		;LOOK AT LSB.
	RZ		;RETURN IF ZERO.
	CALL CONIN	;GET CHARACTER.
	CPI  4		;CHECK FOR CONTROL D.
	RNZ		;RETURN IF NOT.
	LXI  H,AMSG	;IF CONTROL D,
	CALL CPRINT	;PRINT MESSAGE AND
	JMP  FINIS	;DO QUIT ROUTINE.

CONIN:	MVI  C,CREAD	;SET C FOR CONSOLE READ.
	CALL DOS	;INPUT CHARACTER.
	CPI  3		;CHECK FOR CONTROL C.
	RNZ		;RETURN IF NOT.
	LXI  H,AMSG	;POINT TO MESSAGE.
	CALL CPRINT	;SEND IT.
	JMP  SELR	;RESTART FROM START.
;
CPRINT:	PUSH D		;SAVE D AND E.
	XCHG		;MESSAGE ADDRESS IN D AND E.
	MVI  C,CPB	;SET C FOR PRINT BUFFER.
	CALL DOS	;PRINT CONSOLE BUFFER.
	POP  D		;RESTORE D AND E.
	RET		;RETURN FROM CPRINT.
;
INTDRN:	MVI  C,DRINT	;CODE FOR DRIVE QUERY.
	CALL DOS	;GET LOGED IN DRIVE.
	MOV  A,E	;PUT IN A.
	CALL SELD	;SELECT IT.
	RET		;RETURN FROM INTDRN.

SELECT:	LDA  DRIVE	;GET DRIVE FOR TEST.
	SUI  'A'	;SUBTRACT ASCII FOR A.
	STA  DISKNO	;SAVE IT.
SELD:
	IF   SINGLE	;if using single density board.
	CMA		;INVERT SELECT CODE.
	ENDIF

	ADD  A		;PUT BITS
	ADD  A		;ONE AND TWO
	ADD  A		;AT FOUR
	ADD  A		;AND FIVE.

	IF   SINGLE	;if using single density board.
	ORI  2		;SET BIT ONE TO SET LATCH.
	ENDIF

	OUT  DCONT	;SELECT DRIVE.
	STA  LATCH	;SAVE CODE.
	RET		;RETURN FROM SELECT.

	IF   STD	;IF STANDARD DRIVE.
SETSTP:	LXI  H,STP+1	;POINT TO SEEK BYTE.
	MVI  M,SEEKC	;SET FOR FAST STEP.
	CALL POKE	;SET SEEK COMMAND.
	LXI  H,RES+1	;POINT TO RESTORE BYTE.
	MVI  M,HLAB	;SET FOR FAST STEP WITH HEADLOAD.
POKE:	CPI  '0'	;IS IT AN F?
	RZ		;RETURN.
	INR  M		;SET FOR MEDIUM STEP.
	CPI  '1'	;IS IT AN M?
	RZ		;GO BACK.
	INR  M		;SET FOR SLOW STEP.
	CPI  '2'	;IS IT AN S?
	RZ
	INR  M		;MUST WANT SLOWEST RATE
	RET		;RETURN.
	ENDIF

BACK:	CPI  1		;CHECK FOR 1.
	RNZ		;RETURN IF NOT.
	LXI  H,CRLF	;SEND RETURN
	CALL CPRINT	;AND LINEFEED.
	JMP  START	;START OVER.

HOME:
	IF   FAST	;IF FAST SEEK.
	LDA  LATCH	;GET SELECT CODE.
	ANI  0B2H	;SET FOR RESTORE.
	OUT  DCONT	;RESTORE DRIVE.
HLOOP:	IN   DSTAT	;READ STATUS.
	ANI  4		;LOOK AT TRACK 0 BIT.
	JZ   HLOOP	;WAIT UNTIL 0.
	LDA  LATCH	;GET CODE.
	OUT  DCONT	;CLEAR RESTORE.
	ENDIF

RES:	MVI  A,HLAB	;RESTORE COMMAND.
	OUT  DCOM	;SEND IT.
	IN   WAIT	;WAIT FOR HOME.
	IN   DSTAT	;READ STATUS.
	MOV  B,A	;SAVE IT.
	ANI  4		;CHECK FOR TRACK 0.
	JZ   RERR	;ERROR IF NOT TRACK 0.
	MOV  A,B	;GET STATUS BACK.
	ANI  91H	;LOOK AT ERROR BITS.
	RZ		;RETURN IF NO ERROR.
RERR:	LXI  H,HEMSG	;POINT TO MESSAGE.
	CALL CPRINT	;PRINT IT.
	JMP  SELR	;DO IT AGAIN.

;
;READ WITHOUT HEAD LOAD.
;
READN:	LDA  RTCNT	;SET RETRY COUNT.
	STA  ERCNT
	LDA  SECT	;GET SECTOR NUMBER
	LXI  H,DIB	;AND STARTING ADDRESS.
	OUT  SECTP	;SET SECTOR NUMBER INTO 1771.
	MVI  A,88H	;GET CODE FOR READ W/O HLD.
	JMP  READE	;READ A SECTOR.
;
; READ THE SECTOR AT SECT, FROM THE PRESENT TRACK.
; USE STARTING ADDRESS AT DIB.
;
READ:	LDA  RTCNT	;GET RETRY COUNT.
RRETRY:	STA  ERCNT	;STORE IN ERROR CTR.
	LDA  SECT	;GET SECTOR NUMBER.
	LXI  H,DIB	;GET STARTING ADR.
	OUT  SECTP	;SET SECTOR INTO 1771.
	MVI  A,8CH	;CODE FOR READ W/O HD LD.
READE:	OUT  DCOM	;SEND COMMAND TO 1771.
RLOOP:	IN   WAIT	;WAIT FOR DRQ OR INTRQ.
	ORA  A		;SET FLAGS.
	JP   RDDONE	;DONE IF INTRQ.
	IN   DDATA	;READ A DATA BYTE FROM DISK.
	MOV  M,A	;PUT BYTE INTO MEMORY.
	INX  H		;INCREMENT MEMORY POINTER.
	JMP  RLOOP	;KEEP READING.
RDDONE:	LXI  H,TLONTL	;GET TLONTL ADDRESS.
	MVI  M,0	;SET TO ZERO.
	IN   DSTAT	;READ DISK STATUS.
	ANI  9DH	;LOOK AT ERROR BITS.
	RZ		;RETURN IF NONE.
	MOV  D,A	;SAVE ERROR BITS.
	LDA  ERCNT	;GET ERROR COUNT.
	DCR  A		;DECREMENT COUNT.
	JNZ  RRETRY	;TRY TO READ AGAIN.
	CALL ECOUNT	;COUNT AN ERROR.
	LXI  H,RDMSG	;PRINT "READ ".
ERMSG:	CALL CPRINT	;PRINT ORIGIN MESSAGE.
ERMSG1:	MOV  A,D	;GET ERROR BITS.
	ANI  80H	;IF BIT 7 HIGH,
	LXI  H,NRMSG	;"NOT READY".
	CNZ  CPRINT
	MOV  A,D	;GET ERROR BITS.
	ANI  10H	;IF BIT 4 IS HIGH,
	LXI  H,RNMSG	;PRINT "RECORD NOT FOUND"
	CNZ  CPRINT
	MOV  A,D	;GET ERROR BITS.
	ANI  8		;IF BIT 3 IS HIGH,
	LXI  H,CRCMSG	;PRINT "CRC ERROR".
	CNZ  CPRINT
	MOV  A,D	;GET ERROR BITS.
	ANI  4		;IF BIT 2 IS HIGH,
	LXI  H,LDMSG	;PRINT "LOST DATA".
	CNZ  CPRINT
	MOV  A,D	;GET ERROR BITS.
	ANI  1		;IF BIT 1 IS HIGH,
	LXI  H,BSYMSG	;PRINT "BUSY".
	CNZ  CPRINT
	LXI  H,ERRMSG	;PRINT "ERROR."
	CALL CPRINT
	JMP  ETS	;JUMP TO ERROR PRINT ROUTINE.
;
; ERCHK - CHECK FOR RECORD NOT FOUND ERROR.
;
ERCHK:	MOV  D,A	;SAVE ERROR BITS IN D.
	ANI  10H	;IF RECORD NOT FOUND,
	JNZ  CHKSK	;DO A CHECK ON SEEK.
	MOV  A,D	;OTHERWISE RESTORE BITS
	ORA  A		;SET FLAGS,
	RET		;AND RETURN.
;CHECK FOR SEEK TO CORRECT TRACK,
;AND CHANGE IF NECESSARY.
CHKSK:	MVI  A,0C4H	;SEND COMMAND TO 1771
	OUT  DCOM	;TO READ ADDRESS.
	IN   WAIT	;WAIT FOR DRQ OR INTRQ.
	IN   DDATA	;READ THE TRACK ADDRESS.
	MOV  B,A	;SAVE IN REGISTER B.
CHKS2:	IN   WAIT	;WAIT FOR INTRQ.
	JM   CHKS2	;NOT IF DRQ.
	MOV  A,B	;UPDATE TRACK REGITER.
	OUT  TRACK
	LDA  TRK	;GET REQUIRED TRACK NO.
	CALL SEEK	;MOVE THE HEAD TO IT.
	MOV  A,D	;GET ERROR BITS.
	ORA   A		;SET FLAGS.
	RET		;RETURN FROM ERCHK.
;
; MOVE THE HEAD TO THE TRACK IN REGISTER A.
;
SEEK:	PUSH B		;SAVE B&C.
	MOV  B,A	;SAVE DESTINATION TRACK.
	LDA  RTCNT	;GET RETRY COUNT.
SRETRY:	STA  SERCNT	;STORE IN ERROR COUNTER.
	IN   TRACK	;READ PRESENT TRACK NO.
	MOV  C,A	;SAVE IN C.
	MOV  A,C	;DELAY.
	CMP  B		;SAME AS NEW TRACK NO.?
	MOV  A,B	;RESTORE A FROM B.
	JNZ  NOTHR	;JUMP IF NOT THERE.
THERE:	POP  B		;RESTORE B&C.
	ORA  A		;RESET FLAGS.
	RET		;RETURN FROM SEEK.

NOTHR:	IF   STD	;IF NOT FAST SEEK,
	OUT  DDATA	;TRACK TO DATA REGISTER.
BUSY:	IN   DSTAT	;READ DISK STATUS.
	RRC		;LOOK AT BIT 0.
	JC   BUSY	;WAIT TILL NOT BUSY.
STP:	MVI  A,SEEKC	;SEEK COMMAND.
	OUT  DCOM	;ISSUE SEEK COMMAND.
	IN   WAIT	;WAIT FOR INTRQ.
	IN   DSTAT	;READ STATUS.
	ANI  91H	;LOOK AT BITS.
	JZ  THERE	;OK IF ZERO.
	ENDIF

	IF   FAST	;IF FAST SEEK,
	MVI  A,40H	;IF CARRY = 1,
	JC   SDIR	;STEP IN.
	MVI  A,60H	;OTHERWISE, OUT.
SDIR:	OUT  DCOM	;ISSUE STEP DIRECTION.
	MVI  A,20	;DELAY LOOP COUNT.
DLOOP:	DCR  A		;DECREMENT COUNTER.
	JNZ  DLOOP
	MOV  A,C	;GET PRESENT TRACK.
	SUB  B		;FIGURE TRACKS TO STEP.
	JP   STEP	;IF NEGATIVE,
	CMA		;FIGURE THE
	INR  A		;TWO'S COMPLEMENT.
STEP:	MOV  C,A	;GET DIFFERENCE.
	MVI  A,1	;PERSCI STEP COMMAND.
STEP1:	OUT  DCONT	;STEP PERSCI (E-14).
	DCR  C		;COUNT THE STEP.
	JNZ  STEP1	;STEP UNTIL C = 0.
	IN   WAIT	;CLEAR 1771.
	IN   DSTAT
	MOV  A,B	;GET DEST. TRACK.
	OUT  TRACK	;UPDATE TRACK REG.
	LDA  DISKNO	;GET DISK NUMBER.
	RLC!RLC!RLC!RLC	;SHIFT LEFT 4 BITS.
	ANI  30H	;LOOK AT BITS 4 AND 5.
	CMA		;INVERT.
	MOV  B,A	;SAVE IN B.
	ANI  72H	;MAKE COMMAND TO
	OUT  DCONT	;SWITCH WAIT FOR
	IN   WAIT	;SEEK COMPLETE.
	MOV  A,B	;RESTORE ORIG. BITS.
	ANI  0F2H	;SWITCH WAIT BACK.
	OUT  DCONT
	XRA  A		;MAKE GOOD RETURN.
	POP  B		;RESTORE  B&C.
	RET
	ENDIF

	IF   STD	;IF NOT FAST SEEK,
	PUSH H		;SAVE H&L.
	LXI  H,SECNT	;GET ADR OF SEEK ERR CTR.
	INR  M		;ONE MORE SEEK ERROR.
	POP  H		;RESTORE H&L.
	LDA  ERCNT	;GET ERROR COUNT.
	DCR  A		;DECREMENT COUNT.
	JNZ  SRETRY	;RETRY SEEK.
	POP  B		;RESTORE B&C.
	LXI  H,SKMSG	;PRINT "SEEK ".
	IN   DSTAT	;READ DISK STATUS.
	ANI  81H	;LOOK AT ERROR BITS.
	MOV  D,A	;PUT IN REG D.
	CALL ERMSG	;DO ERROR MESSAGES.
	RET		;RETURN FROM SEEK.
	ENDIF
;
; MESSAGES.
;
NRMSG:	DB   'Not Ready $'
RNMSG:	DB   'Record Not Found $'
CRCMSG:	DB   'CRC $'
LDMSG:	DB   'Lost Data $'
BSYMSG:	DB   'Busy $'
WPMSG:	DB   'Protect $'
WFMSG:	DB   'Fault $'
ERRMSG:	DB   'ERROR. $'
HEMSG:	DB   13,10,'Home Error. ',13,10,36
RDMSG:	DB   13,10,'Read $'
WTMSG:	DB   13,10,'Write $'
SKMSG:	DB   13,10,'Seek $'
TSMSG:	DB   13,10,'Track  '
TRKM:	DB   0
TRKL:	DB   0
	DB   '     Sector  '
SECM:	DB   0
SECL:	DB   0
CRLF:	DB   13,10,36
OMSG:	DB   13,10,'Tarbell Disk Diagnostic '
	DB   13,10

	IF   STD
	DB   'Standard '
	ENDIF

	IF   FAST
	DB   'Fast Seek '
	ENDIF

	DB   'Version ',VN/10+48,46
	DB   VN MOD 10+48,13,10
	DB   TRACKS/10+48,TRACKS MOD 10+48
	DB   ' Tracks    '
	DB   SECTS/10+48,SECTS MOD 10+48
	DB   ' Sectors ',13,10

	IF   SINGLE
	DB   13,10,'For Tarbell Single Density Board$'
	ENDIF

	IF   NOT SINGLE
	DB	13,10,'For Tarbell Double Density Board$'
	ENDIF
;
SELMSG:	DB   13,10,10,'Select Drive. (A/B/C/D or Q to quit) $'
REMSG:	DB   13,10,'How Many Retrys? (0-9) $'
STPMSG:	DB   13,10,'Select Step Rate.'

	IF   NOT SINGLE
	DB   '(3ms = 0, 6ms = 1, 10ms = 2, 15ms = 3)?  $'
	ENDIF

	IF   SINGLE
	DB   '(6ms = 1, 10ms = 2, 20ms = 3)?  $'
	ENDIF

LSMSG:	DB   13,10,'Full Track Seek? (Y/N) $'
RMSG:	DB   13,10,'To Start Test Type Return. $'
RECMSG:	DB   13,10
REC:	DB   0,0,0,0
	DB   '  Read Errors Detected. ',13,10,36
AMSG:	DB   13,10,'Aborting Test. ',13,10,36
GMSG:	DB   13,10,10,36
;
WECNT:	DB   0		;WRITE ERROR COUNT.
SECNT:	DB   0		;SEEK ERROR COUNT.
BCRETH:	DB   0		;MSD'S OF READ ERROR COUNT.
BCRETO:	DB   0		;LSD'S OF READ ERROR COUNT.
DRIVE:	DB   0		;DRIVE.
LATCH:	DB   0		;LATCH CODE.
DISKNO:	DB   0		;DISK NUMBER.
TRK:	DB   0		;TRACK NUMBER.
SECT:	DB   0		;SECTOR NUMBER.
BCDTRK:	DB   0		;TRACK NUMBER IN BCD.
BCDSEC:	DB   0		;SECTOR NUMBER IN BCD.
RTCNT:	DB   0		;RETRY COUNT.
ERCNT:	DB   0		;ERROR COUNT FOR RETRIES.
SERCNT:	DB   0		;SEEK RETRY COUNTER.
TLONTL:	DB   0		;READ ERROR FLAG.
LST:	DB   0		;LONG SEEK FLAG.
OT:	DB   0		;LST OUTER TRACK.
IT:	DB   0		;LST INNER TRACK.
OTIT:	DB   0		;OTIT.
CPMS:	DW   0		;OLD STACK POINTER.
DIB:	DS   128	;DISK INPUT BUFFER.
	DS   64		;STACK AREA.
STACK:	END
