.comment %
#########################################################
#							#
#	Hard Disk Certification or Read Validate	#
#	Program for the Kaypro 10 and the WD 1001	#
#	and 1002 Winchester Disk Controller.		#
#							#
#	Copyright (c) 1983 by Non-Linear Systems, Inc.	#
#	No Warranty is made, expressed or implied.	#
#							#
#	begun 09-Mar-83					#
#	by Matthew Sherman.				#
#							#
#=======================================================#
#							#
#	New Improved Version:	2.6	26-Oct-83	#
#	Current version:	2.5	19-Oct-83	#
#	New Current Version	2.4	Oct-83		#
#	Current version:	2.3	06-Mar-83	#
#	Previous Version:	2.2	20-Apr-83	#
#	Previous Version:	2.1	19-Apr-83	#
#	!!! RELEASE VERSION !!!				#
#	Release version:	1.0	20-Apr-83	#
#							#
#	Changes, version 2.6 - Recalibrate disk and one	#
#	 retry after any error. (P. Whalley)		#
#	Changes, version 2.5 - Added Pass counter and	#
#	 added conditional equates for hard disk reset	#
#	 logic. (M. Sherman)				#
#	Changes, version 2.4 - Inverted hard disk reset	#
#	 logic						#
#	Changes, version 2.3 - Added conditional	#
#	 equates for Western Digital 1002 board		#
#	 ( one less error status bit to test )		#
#	 and read retry/no read retry.			#
#	Changes:  Restructured control section,		#
#	 added IF structures and equates for first,	#
#	 range selections and auto format, exit.	#
#	 ( no keyboard entry required ) ( version 2.2 )	#
#	-Added one retry on read error.			#
#	 ( supposed to fix occasional noise glitch	#
#	   problem on read-Xebex controller does this	#
#	   automatically, Western Digital does not. )	#
#	-Error reporting, better handling of multiple	#
#	 drives.  ( version 2.0 )			#
#	-Proper disk select, proper handling of		#
#	 disk drive cut-out. ( version 1.1 )		#
#							#
#########################################################
%

.comment %
#########################
#	set up module	#
#########################
%

;
; version number
;
vers	equ	26
;

; console i/o equates:
;
bdos	equ	05	; bdos entry vector
stack	equ	0A000h	; stack location
lf	equ	0ah	; line feed
cr	equ	0dh	; carriage return
esc	equ	1bh	; ASCII esc code
clears	equ	1ah	; clear screen code
clreos	equ	17h	; clear to end of screen
clreol	equ	18h	; clear to end of line
lodcur	equ	'='	; load cursor command
offset	equ	' '	; cursor positioning offset
wboot	equ	0000h	; warm boot entry point

; conditional equates:

FALSE	equ	0000h		; false condition
TRUE	equ	NOT FALSE

AUTOF	equ	FALSE		; auto format, no keyboard
WD1001	equ	FALSE		; Western Digital 1001 Disk Controller Board.
WD1002	equ	TRUE		; Western Digital 1002 Disk Controller Board.
RETRY	equ	FALSE		; one retry on read error
CERTIFI	equ	FALSE		; format and test if true, read only if false
INVRES	equ	TRUE		; inverted hard disk reset if true
MULPASS	equ	FALSE		; multiple passes (forever, actually)
				; with pass count and count display.

; auto format equates:

firstd	equ	01		; first disk, auto format
lastd	equ	01		; last disk, auto format
drange	equ	lastd-firstd	; disk range (number of disks-1)
firstc	equ	00		; first cylinder, auto format
lastc	equ	305		; last cylinder, auto format
crange	equ	lastc-firstc	; cylinder range (number of cylinders-1)
firsth	equ	00		; first head to format
lasth	equ	03		; last head to format
hrange	equ	lasth-firsth	; head range (number of heads-1)

.Z80

page
; begin set up.
;
start:	ld	sp,stack
	xor	a
	ld	(lstsw),a	; turn off print switch
	call	print		; sign on.
	db	clears
	db	lf,lf,'Kaypro 10 Hard Disk '
IF CERTIFI
	db	'Certification '
ELSE
	db	'Read Validate '
ENDIF
	db	'Program Version '
	db	vers/10+'0','.',vers mod 10+'0'
	db	cr,lf,'( Copyright (c) 1983 by Non-Linear Systems, Inc. )'
	db	cr,lf
IF WD1001
	db	'(WD1001) '
ENDIF
IF WD1002
	db	'(WD1002) '
ENDIF
IF RETRY
	db	'one read retry on error'
ENDIF
IF INVRES
	db	' NEW INTERFACE CARD'
ELSE
	db	' OLD INTERFACE CARD'
ENDIF
IF MULPASS
	db	' LOOP FOREVER'
ELSE
	db	' ONE PASS ONLY'
ENDIF

	db	00

IF AUTOF

	call	print
	db	cr,lf,lf,'(Auto Select, disk(s) '
	db	00
	ld	hl,firstd
	call	outdec
	call	print
	db	'-'
	db	00
	ld	hl,lastd
	call	outdec
	call	print
	db	', cylinder(s) '
	db	00
	ld	hl,firstc
	call	outdec
	call	print
	db	'-'
	db	00
	ld	hl,lastc
	call	outdec
	call	print
	db	' head(s) '
	db	00
	ld	hl,firsth
	call	outdec
	call	print
	db	'-'
	db	00
	ld	hl,lasth
	call	outdec
	call	print
	db	' )',cr,lf
	db	00
	ld	a,firstd
	ld	(fdsk),a
	ld	a,drange
	ld	(dskrng),a
	ld	hl,firstc
	ld	(fstcyl),hl
	ld	hl,crange
	ld	(cylrng),hl
	ld	a,firsth
	ld	(fhead),a
	ld	a,hrange
	ld	(hrng),a
	jp	cerdsk

ELSE

gdsks:	call	print
	db	esc,lodcur,6+offset,offset
	db	'First disk, last disk? (0-3,0-3)',clreol
	db	cr,lf,' ( or an ESC to quit )'
	db	esc,lodcur,6+offset,33+offset,00
	call	gettwo		; get two 16 bit numbers
	jp	z,valdsk	; validate numbers, range
	ld	a,(gotc)
	cp	esc
	jp	z,mpexit	; exit, they want to quit.
	call	ientry		; invalid entry
	jr	gdsks

mpexit:	call	print
	db	cr,lf,lf,00
	jp	wboot

; display invalid entry message at top of screen
;
ientry:	call	print
	db	esc,lodcur,offset,offset
	db	'Invalid entry',clreol,00
	ret

; clear top line
;
clrtop:	call	print
	db	esc,lodcur,offset,offset,clreol,00
	ret

; get two 16-bit numbers, return them in hl and de (hl is 1st, de is second)

gettwo:	xor	a
	ld	(rollo),a	; initialize rollover
	call	getnum
	ret	nz
	ld	a,(rollo)
	or	a
	ret	nz
	ld	hl,(number)
	push	hl
	call	getnum
	ld	de,(number)
	pop	hl
	ret	nz
	ld	a,(rollo)
	or	a
	ret

; get a number
getnum:	ld	hl,numlst
	ld	(hl),'.'
	inc	hl
	call	getinp
	ret	c	; illegal, exit
	cp	','
	jr	nz,getnm2
	ld	a,0ffh
	or	a
	ret		; illegal, 1st number can't be a comma!
getnm2:	ld	(hl),a	; 1st number
	inc	hl
	call	getinp
	ret	c	; illegal, exit
	cp	','
	jr	z,maknum
	ld	(hl),a	; second number
	inc	hl
	call	getinp
	ret	c	; illegal, exit
	cp	','
	jr	z,maknum
	ld	(hl),a	; third number
	inc	hl
	call	getinp
	ret	c	; illegal, exit
	cp	','
	jr	z,maknum
	ld	(hl),a	; fourth number
	inc	hl
	call	getinp
	ret	c	; illegal, exit
	cp	','
	jr	z,maknum
	ld	(hl),a	; fifth number
	call	getinp
	ret	c
	cp	','
	ret	nz	; entered more than five numbers, illegal!
	inc	hl
	jr	maknum

mul16:	or	a
	ret	z
mul162:	add	hl,de
	ret	c		; rollover, exit
	djnz	mul162
	ret

maknum:	ld	de,0
	ld	(number),de
	dec	hl
	ld	a,(hl)
	cp	'.'
	ret	z
	push	hl
	ld	de,1
	call	makit
	pop	hl
	dec	hl
	ld	a,(hl)
	cp	'.'
	ret	z
	push	hl
	ld	de,10
	call	makit
	pop	hl
	dec	hl
	ld	a,(hl)
	cp	'.'
	ret	z
	push	hl
	ld	de,100
	call	makit
	pop	hl
	dec	hl
	ld	a,(hl)
	cp	'.'
	ret	z
	push	hl
	ld	de,1000
	call	makit
	pop	hl
	dec	hl
	ld	a,(hl)
	cp	'.'
	ret	z
	push	hl
	ld	de,10000
	call	makit
	pop	hl
	xor	a
	or	a
	ret

makit:	ld	hl,0
	ld	b,a
	call	mul16
	jr	c,makite
	ld	de,(number)
	add	hl,de
	ld	(number),hl
	ret	nc
makite:	ld	a,(0ffh)
	ld	(rollo),a
	ret

getinp:	push	hl
	push	de
	push	bc
	call	getd
	cp	' '
	call	nc,putd	; echo it if it isn't a special character
	pop	bc
	pop	de
	pop	hl
	ld	a,(gotc)
	cp	','
	ret	z
	cp	cr
	jr	z,ntok
	sub	'0'
	ret	c	; illegal
	cp	10
	jr	c,setok
	scf
	ret
ntok:	ld	a,','
setok:	or	a
	ret


invdsk:	call	print
	db	esc,lodcur,offset,offset
	db	'Disk numbers too large (0 to 3, please)',clreol,00
	jp	gdsks

d2sml:	call	print
	db	esc,lodcur,offset,offset
	db	'Last disk number too small '
	db	'( greater than or equal to first number, please)',clreol,00
	jp	gdsks

valdsk:	push	hl
	push	de
	call	clrtop
	pop	de
	pop	hl
	xor	a
	cp	h		; 1st byte better be 0
	jp	nz,invdsk	; it wasn't
	cp	d		; also should be 0
	jp	nz,invdsk	; it wasn't
	ld	a,l
	cp	4		; better be less than 4
	jp	nc,invdsk	; it wasn't
	ld	(fdsk),a
	ld	a,e
	cp	l		; better be less than e
	jp	c,d2sml		; it wasn't
	cp	4
	jp	nc,invdsk
	sub	l
	ld	(drng),a
	jp	gheads

gheads:	call	print
	db	esc,lodcur,7+offset,offset,clreol
	db	lf,'First head, last head? (0-7,0-7)',clreol
	db	cr,lf,' ( or an ESC to start over )'
	db	esc,lodcur,8+offset,33+offset,00
	call	gettwo
	jr	z,valhds
	ld	a,(gotc)
	cp	esc
	jp	z,start	; they want to start over
	call	ientry
	jr	gheads

valhds:	push	hl
	push	de
	call	clrtop
	pop	de
	pop	hl
	xor	a
	cp	h
	jr	nz,invhds
	cp	d
	jr	nz,invhds
	ld	a,l
	cp	8
	jr	nc,invhds
	ld	(fhead),a
	ld	a,e
	cp	l
	jr	c,h2sml
	cp	8
	jr	nc,invhds
	sub	l
	ld	(hrng),a
	jp	gcylnd

invhds:	call	print
	db	esc,lodcur,offset,offset
	db	'Head numbers too large (0 to 7, please)',clreol,00
	jp	gheads

h2sml:	call	print
	db	esc,lodcur,offset,offset

	db	'Last head number too small '
	db	'(greater than or equal to first number, please)',clreol,00
	jp	gheads

gcylnd:	call	print
	db	esc,lodcur,9+offset,offset,clreol
	db	lf,'First cylinder, last cylinder? (0-305, 0-305)',clreol
	db	cr,lf,' ( or an ESC to start over )'
	db	esc,lodcur,10+offset,46+offset,00
	call	gettwo
	jr	z,valcyl
	ld	a,(gotc)
	cp	esc
	jp	z,start	; start over
	call	ientry
	jr	gcylnd

valcyl:	push	hl
	push	de
	call	clrtop
	pop	de
	pop	hl
	ld	a,h
	cp	01
	jr	c,valcy2
	jr	nz,invcyl
	ld	a,l
	cp	32h
	jr	nc,invcyl
valcy2:	ld	(fstcyl),hl	; first cylinder
	ld	a,d
	cp	01
	jr	c,valcy3
	jr	nz,invcyl
	ld	a,e
	cp	32h
	jr	nc,invcyl
valcy3:	ex	de,hl
	or	a
	sbc	hl,de
	jr	c,c2sml
	ld	(cylrng),hl
	jp	cerdsk

invcyl:	call	print
	db	esc,lodcur,offset,offset
	db	'Cylinder numbers too large '
	db	'(0 to 305, please)',clreol,00
	jp	gcylnd

c2sml:	call	print
	db	esc,lodcur,offset,offset
	db	'last cylinder number smaller than first cylinder number'
	db	clreol,00
	jp	gcylnd

ENDIF

.comment %
#########################################
#					#
#	Main Program Control Module	#
#					#
#########################################
%

cerdsk:	call	print
	db	esc,lodcur,11+offset,offset,clreol,lf,00
	ld	a,(fdsk)
	ld	(diskno),a
	ld	a,(drng)
	ld	(dskrng),a	; all control routines have
	ld	a,(fhead)	; resetable parameters
	ld	(headno),a
	call	hdcres		; reset controller, select drive.
if mulpass
	ld	hl,0
	ld	(pascnt),hl	; initialize pass counter
	call	print
	db	esc,'B','7'	; turn on status line preservation
	db	00
pfloop:	call	print
	db	cr,clreol
	db	00
	ld	a,(fdsk)
	ld	(diskno),a
	ld	a,(drng)
	ld	(dskrng),a	; all control routines have
	ld	a,(fhead)	; resetable parameters
	ld	(headno),a
	call	dsppas		; display pass count
	call	fmtdsk
	ld	hl,(pascnt)
	inc	hl
	ld	(pascnt),hl
	jr	pfloop		; pass forever loop
endif
	call	fmtdsk
	jp	cfexit

; format a range of disk drives.
; entry parameters:	first disk in diskno,
;			number of additional disks in dskrng.
; exit parameters:	none.
; registers affected:	all
;
fmtdsk:	ld	hl,(fstcyl)
	ld	(fcyl),hl
	ld	hl,(cylrng)
	ld	(crng),hl
	call	hdseld		; select controller, select drive,
				; restore drive
	call	fmtcyl
	ld	a,(dskrng)
	or	a
	ret	z
	dec	a
	ld	(dskrng),a
	ld	a,(diskno)
	inc	a
	ld	(diskno),a
	jr	fmtdsk

fmtcyl:	ld	a,(fhead)
	ld	(headno),a
	ld	a,(hrng)
	ld	(hcount),a
	call	fmthds		; go do each head
	ld	hl,(crng)
	ld	a,h
	or	l
	ret	z
	dec	hl
	ld	(crng),hl
	ld	hl,(fcyl)
	inc	hl
	ld	(fcyl),hl	; go on to next
	jr	fmtcyl		; cylinder...

fmthds:	call	fmttrk
	ld	a,(hcount)
	or	a
	ret	z
	dec	a
	ld	(hcount),a
	ld	a,(headno)
	inc	a
	ld	(headno),a
	jr	fmthds

.comment %
#################################################
#						#
#	Track Format and Certification Module	#
#						#
#################################################
%

.comment %
#########################################################
#							#
#	equate tables for the Western Digital		#
#	Winchester Controller and the Kaypro 10		#
#							#
#########################################################
%

; port/register equates

wdbase	equ	80h		; base address for the controller
wddata	equ	wdbase+0		; data register
wderr	equ	wdbase+1		; error register (read)
wdpcmp	equ	wdbase+1		; write precomp. register (write)
wdscnt	equ	wdbase+2		; sector COUNT (read/write)
wdsnum	equ	wdbase+3		; sector NUMBER (read/write)
wdclow	equ	wdbase+4		; low byte, cylinder number (r/w)
wdchi	equ	wdbase+5		; high byte, cylinder number (r/w)
wdsdh	equ	wdbase+6		; Size/Drive/Head (r/w)
wdsts	equ	wdbase+7		; status register
wdcmd	equ	wdbase+7		; command register

; masks, select information

crcdat	equ	00h		; crc in data field mask
eccdat	equ	80h		; ecc in data field mask
sec256	equ	00h		; 256 byte sector mask
sec512	equ	20h		; 512 byte sector mask
sec128	equ	60h		; 128 byte sector mask
dsel00	equ	00h		; drive 0 select mask,
dsel01	equ	08h		; drive 1,
dsel02	equ	10h		; drive 2,
dsel03	equ	18h		; drive 3.
hsel00	equ	00h		; head 0 select mask,
hsel01	equ	01h		; head 1,
hsel02	equ	02h		; head 2,
hsel03	equ	03h		; head 3,
hsel04	equ	04h		; head 4,
hsel05	equ	05h		; head 5,
hsel06	equ	06h		; head 6,
hsel07	equ	07h		; head 7.

hicmsk	equ	03h		; high cylinder mask

; status masks

; status register
bsymsk	equ	80h		; busy mask,
bsybit	equ	7		; busy bit
ready	equ	40h		; selected drive ready line.
wrtflt	equ	20h		; write fault mask
scmplt	equ	10h		; seek complete
datreq	equ	08h		; data request
corerr	equ	04h		; corrected error
hderr	equ	01h		; error occured

; error register
bbdet	equ	80h		; bad block detect
uncerr	equ	40h		; uncorrectable error
crcerr	equ	20h		; crc error - ID field
idnf	equ	10h		; ID not found
abocmd	equ	04h		; aborted command
tr0err	equ	02h		; track 0 error
damnf	equ	01h		; DAM not found

; commands:

hdrstr	equ	10h		; restore command, 35.0 uS step rate.
seek30	equ	06h		; 3.0 ms seek rate
hdseek	equ	76h		; seek command, 3.0 ms
hdread	equ	20h		; read, i/o mode, single, no ecc returned.
hdwrte	equ	30h		; write, ditto.
hdfmtt	equ	50h		; format track.

mulbit	equ	04h		; multiple read/write
longbt	equ	02h		; long read/write

; KayPro operational equates

bitport	equ	14h		; system bit port
hdcmsk	equ	11111101b	; hard disk controller reset bit(s) mask
invmsk	equ	00000010b	; hard disk controller isolate bit(s) mask
if invres
hdcsel	equ	00000000b	; hd controller select mask
reshdc	equ	00000010b	; hard disk controller reset mask
else
hdcsel	equ	00000010b
reshdc	equ	00000000b
endif
whdsel	equ	eccdat+sec512	; error correction, 512 byte sectors
msecfmt	equ	17		; 17 sectors formatted, 0-16
maxsec	equ	16		; 16 sectors used, 0-15
lndzne	equ	229		; landing zone
hdssiz	equ	512		; sector size

fmttrk:	call	print
	db	cr,'Validating',00
	call	dspdch		; display drive, cylinder and head
	call	oprint		; operator interrupt?
	ld	a,2		; set retry counter to 2.
	ld	(rtrycnt),a
xfmttrk:

IF CERTIFI

	push	bc		; save registers.
	push	de
	push	hl

ENDIF

	ld	hl,(fcyl)
	ld	a,l		; select track
	out	(wdclow),a
	ld	a,h
	out	(wdchi),a
; initialize buffer
	ld	de,fmtbuf	; initialize buffer
	ld	hl,fmttbl
	ld	bc,0200h
	ldir

IF CERTIFI
	call	fmttr2		; format it
	and	hderr+corerr	; any error-correctable or not
	pop	hl		; restore registers.
	pop	de
	pop	bc
	jp	z,certify	; if no errors, then goto certify.
	call	hdcres		; home disk.
	ld	a,(rtrycnt)	; decrement retry counter and loop if not zero.
	dec	a
	ld	(rtrycnt),a
	jr	nz,xfmttrk
	call	fmterr		; call error handler.
	jp	certify

; set up sector registers
fmttr2:	ld	a,0		; first sector
	out	(wdsnum),a
	ld	a,msecfmt	; sector count
	out	(wdscnt),a
; issue format command
	ld	a,hdfmtt
	out	(wdcmd),a
; output buffer to controller
	ld	hl,fmtbuf
	ld	bc,0000h+wddata	; b=count, c=data port
	otir
	otir
	call	busy
	ret

; write track, excluding previously noted bad sectors

wrttrk:	ld	hl,fmtbuf
	in	a,(wdclow)
	xor	a
	out	(wdsnum),a
	out	(wdscnt),a
	ld	e,msecfmt

wrtlp3:	ld	a,(hl)
	or	a
	jr	nz,wrtlp2
	ld	a,hdwrte
	out	(wdcmd),a
wrtlp:	push	hl
	ld	hl,wrtbuf
	ld	bc,0080h
	otir
	otir
	call	busy
	pop	hl
	inc	hl
	inc	hl
	and	hderr+corerr+wrtflt
	jr	z,wrtlp2
	ld	(hl),80h
	call	wrterr
wrtlp2:	dec	e
	ret	z
	in	a,(wdclow)
	in	a,(wdsnum)
	inc	a
	out	(wdsnum),a
	jr	wrtlp3
wrtlp4:	inc	hl
	inc	hl
	jr	wrtlp3

cert1:	ld	a,2		; set retry counter to 2.
	ld	(rtrycnt),a
xcert1:	ld	a,0		; clear error flag.
	ld	(errflag),a
	push	bc		; save registers.
	push	de
	push	hl
	call	wrttrk
	pop	hl		; restore registers.
	pop	de
	pop	bc
	ld	a,(errflag)	; if no errors, then goto vrfyx.
	or	a
	jr	z,vrfyx
	call	hdcres		; home disk.
	ld	a,(rtrycnt)	; decrement retry counter and loop if not zero.
	dec	a
	ld	(rtrycnt),a
	jr	nz,xcert1
vrfyx:	call	verify
	ld	hl,wrtbuf
	ld	b,00
	ret

certify:call	vrfyx		; verify format
swrt1:	ld	(hl),0b6h	; B6D data pattern
	inc	hl
	ld	(hl),0dbh
	inc	hl
	ld	(hl),06dh
	inc	hl
	djnz	swrt1
	call	cert1
swrt2:	ld	(hl),0b6h	; B6D9 data pattern
	inc	hl
	ld	(hl),0d9h
	inc	hl
	djnz	swrt2
	call	cert1
swrt3:	ld	(hl),0		; 0000 data pattern
	inc	hl
	ld	(hl),0
	inc	hl
	djnz	swrt3
	call	cert1
swrt4:	ld	(hl),0aah	; AAAA data pattern
	inc	hl
	ld	(hl),0aah
	inc	hl
	djnz	swrt4
	call	cert1
swrt5:	ld	(hl),00h	; 00AA data pattern
	inc	hl
	ld	(hl),0aah
	inc	hl
	djnz	swrt5
	call	cert1
swrt8:	ld	(hl),0a0h	; A0A0 data pattern
	inc	hl
	ld	(hl),0a0h
	inc	hl
	djnz	swrt8
	call	cert1
swrt9:	ld	(hl),0ffh	; FFFF data pattern
	inc	hl
	ld	(hl),0ffh
	inc	hl
	djnz	swrt9
	call	cert1
swrtA:	ld	(hl),055h	; 5555 data pattern
	inc	hl
	ld	(hl),055h
	inc	hl
	djnz	swrtA
	call	cert1
swrtC:	ld	(hl),055h	; 55FF data pattern
	inc	hl
	ld	(hl),0ffh
	inc	hl
	djnz	swrtC
	call	cert1
swrtD:	ld	(hl),0f5h	; F5F5 data pattern
	inc	hl
	ld	(hl),0f5h
	inc	hl
	djnz	swrtD
	call	cert1
fmtE5:	ld	hl,wrtbuf	; fill with E5's,
	ld	bc,0		; thus -
swrtF:	ld	(hl),0e5h	; leaving it ready for use.
	inc	hl
	ld	(hl),0e5h
	inc	hl
	djnz	swrtF
	in	a,(wdclow)
	xor	a
	out	(wdsnum),a
	out	(wdscnt),a
	ld	e,msecfmt
	ld	hl,fmtbuf
fillin:	ld	a,(hl)
	or	a
	jr	z,filn2		; good block if zero,
	ld	a,hdwrte+longbt	; else a bad block, use long write.
	out	(wdcmd),a
	call	fillbx
	xor	a		; now give it a bad ecc
	out	(wddata),a
	inc	a
	out	(wddata),a
	out	(wddata),a
	out	(wddata),a
	call	busy
	jr	filn3
filn2:	ld	a,hdwrte	; good block,
	out	(wdcmd),a	; do a normal write
	call	fillp
filn3:	dec	e
	ret	z
	inc	hl
	inc	hl
	in	a,(wdclow)	; clear hrdw data strobe
	in	a,(wdsnum)
	inc	a
	out	(wdsnum),a
	jr	fillin

fillp:	call	fillbx
	call	busy
	ret

fillbx:	push	hl
	ld	hl,wrtbuf
	ld	bc,0080h
	otir
	otir
	pop	hl
	ret

ENDIF

; read track verify routine

verify:	ld	a,2		; set retry counter to 2.
	ld	(rtrycnt),a
yverify:ld	a,0		; clear error flag.
	ld	(errflag),a
	push	bc		; save registers.
	push	de
	push	hl
	ld	de,fmtbuf	; initialize buffer.
	ld	hl,fmttbl
	ld	bc,200h
	ldir
	pop	hl		; restore registers.
	pop	de
	pop	bc
	push	bc		; save registers.
	push	de
	push	hl
	call	xverify
	pop	hl		; restore registers.
	pop	de
	pop	bc
	ld	a,(errflag)	; if no errors, then return.
	or	a
	ret	z
	call	hdcres		; home disk.
	ld	a,(rtrycnt)	; decrement retry counter and loop if not zero.
	dec	a
	ld	(rtrycnt),a
	jr	nz,yverify
	ret

xverify:call	seldsk		; make sure we're selected before we start,
	xor	a		; initialize:
	out	(wdsnum),a	; sector count,
	out	(wdscnt),a	; multiple read count (none),
	ld	e,msecfmt	; number to read in e,
	ld	hl,fmtbuf	; pointer to sector header table
vfylp:	ld	a,(hl)		; get good/bad header byte from table
	or	a		; good header ?
	jr	nz,vfylp2	; if not, don't bother to retry sector,
	in	a,(wdclow)	; else clear hardware drq line
	ld	a,hdread
	out	(wdcmd),a	; issue a read command,
	call	busy		; wait until it's done,
	and	hderr+corerr	; any errors?
	jr	z,vfylp2	; no, continue,
IF RETRY
	in	a,(wdclow)	; else clear hardware drq
	ld	a,hdread	; and prepare for first retry
	out	(wdcmd),a
	call	busy
	and	hderr+corerr
	jr	z,vfylp2	; continue if retry good (a acc. = 0)
ENDIF
	ld	(hl),80h	; else set bad sector flag,
	call	vfyerr		; and tell everyone about it.
vfylp2:	inc	hl		; point to next header in table
	inc	hl
	dec	e		; last sector?
	ret	z		; exit if so,
	in	a,(wdsnum)	; else
	inc	a
	out	(wdsnum),a	; update sector number
	jr	vfylp		; and try again.

busy:	in	a,(wdsts)
	bit	bsybit,a
	jr	nz,busy
	ret

fmterr:	push	hl
	push	de
	push	bc
	call	print
	db	cr,'format error',00
	call	dspdch
	call	dsperr
	call	dspsdh
	call	print
	db	cr,lf,00
	call	oprint
	jr	erexit

vfyerr:	push	hl
	push	de
	push	bc
	ld	a,1		; set error flag.
	ld	(errflag),a
	ld	a,(rtrycnt)	; if retry counter is not 1, then goto erexit.
	cp	1
	jp	nz,erexit
	call	print
	db	cr,'verify error',00
	call	dspdch
	call	dspsec
	call	dsperr
	call	dspsdh		; display sdh register
	call	print
	db	cr,lf,00
	call	oprint
	jr	erexit

wrterr:	push	hl
	push	de
	push	bc
	ld	a,1		; set error flag.
	ld	(errflag),a
	ld	a,(rtrycnt)	; if retry counter is not 1, then goto erexit.
	cp	1
	jr	nz,erexit
	call	print
	db	cr,'write error',00
	call	dspdch
	call	dspsec
	call	dsperr
	call	dspsdh
	call	print
	db	cr,lf,00
	call	oprint
erexit:	pop	bc
	pop	de
	pop	hl
	ret

cfexit:

IF AUTOF

	jp	0000		; warm boot

ENDIF

	call	print
	db	esc,lodcur,23+offset,offset
	db	'Another pass ? (y/n) ',clreos,00
	call	getd
	cp	' '
	call	nc,putd		; echo if not carry
	ld	a,(gotc)
	cp	'Y'
	jp	z,start
	cp	'y'
	jp	z,start
	cp	'N'
	jp	z,0000	; exit if 'N'
	cp	'n'
	jp	z,0000	; or 'n'
	jr	cfexit	; else loop.

; reset the controller, delay, enable it, do a slow restore to cyl 0.
;
hdcres:	in	a,(bitport)
	and	hdcmsk
	or	reshdc							; [plw]
	out	(bitport),a
	ld	bc,0			; 1.8 Milliseconds
hdrslp:	djnz	hdrslp
	dec	c
	jr	nz,hdrslp
hdseld:	in	a,(bitport)
	and	hdcmsk
	or	hdcsel							; [plw]
	out	(bitport),a						; [plw]
	push	bc		; save registers.
	push	de
	push	hl
	call	busy
	call	seldsk
	ld	a,hdrstr+seek30		; slow restore
	out	(wdcmd),a
	call	busy
	pop	hl		; restore registers.
	pop	de
	pop	bc
	ret

seldsk:	in	a,(bitport)	; make sure the
	and	invmsk		; isolate select bit(s),
	cp	hdcsel		; is the HDC selected?
	jr	nz,hdcres	; if not, go select, restore it
	call	busy		; wait until controller is ready,
	ld	a,(diskno)	; select the current disk,
	add	a,a
	add	a,a
	add	a,a
	ld	b,a
	ld	a,(headno)	; current head,
	or	b
	or	eccdat+sec512	; error detection and sector size
	out	(wdsdh),a
	call	busy		; wait until controller does it,
	and	ready		; is the drive itself ready?
	jr	z,notrdy	; not ready, wait on it,
seltrk:	push	hl		; else re-select track number, exit.
	ld	hl,(fcyl)
	ld	a,l
	out	(wdclow),a
	ld	a,h
	out	(wdchi),a
	pop	hl
	ret
notrdy:	call	print
	db	esc,'B','6'	; save current cursor position
	db	esc,lodcur,23+offset,offset
	db	'disk not ready, waiting 1 minute. '
	db	'( T minus 60 seconds and counting... )'
	db	00
	ld	hl,60
	ld	(cntdwn),hl
	ld	de,60		; 60X1=60 seconds
D1Sec:	ld	bc,4500		; 1 sec loop
seld2:	in	a,(wdsts)	; wait until the disk drive is ready,
	and	ready
	jp	nz,seld3		; it's ready, let's go
	push	de
	push	bc
	call	getsts
	pop	bc
	pop	de
	jp	nz,start
	dec	bc
	ld	a,b
	or	c
	jr	nz,seld2	; else drift thru delay loop
	push	de
	call	print
	db	esc,lodcur,23+offset,44+offset,00
	ld	hl,(cntdwn)
	dec	hl
	ld	(cntdwn),hl
	ld	a,l
	cp	10
	jr	nc,seld99
	push	hl		; we need a leading space
	ld	a,' '		; for numbers smaller than 10
	call	putd
	pop	hl
seld99:	call	outdec		; output the count down number
	call	print
	db	esc,lodcur,23+offset,70+offset,00
	pop	de
	dec	de		; 1 second gone...
	ld	a,d
	or	e
	jr	nz,D1Sec
	call	print		; if it isn't ready now it never will be.
	db	esc,lodcur,16+offset,60+offset,'!!! SELECT ERROR !!!'
	db	esc,lodcur,17+offset,60+offset,'Either that disk'
	db	esc,lodcur,18+offset,60+offset,'does not exist, or'
	db	esc,lodcur,19+offset,60+offset,'there has been an'
	db	esc,lodcur,20+offset,60+offset,'equipment malfunc-'
	db	esc,lodcur,21+offset,60+offset,'-tion.  Type any'
	db	esc,lodcur,22+offset,60+offset,'key to start over'
	db	00
	call	getd	; wait for them to hit a key,
	jp	start	; then start over.

seld3:	call	print	; clear delay display line,
	db	esc,lodcur,23+offset,offset,clreos
	db	esc,'C','6'	; restore cursor,
	db	00
	jp	seltrk	; let's go select track, exit!

	; display drive, cylinder and head numbers,
	; along with appropriate labels
	;
dspdch:	call	print
	db	' drive ',00
	ld	a,(diskno)
	ld	l,a
	ld	h,0
	call	outdec
	call	print
	db	' cylinder ',00
	ld	hl,(fcyl)
	call	outdec
	call	print
	db	' head ',00
	ld	a,(headno)
	ld	l,a
	ld	h,0
	call	outdec
	ret

	; display sector number
	;
dspsec:	call	print
	db	' sector ',00
	in	a,(wdsnum)
	ld	l,a
	ld	h,0
	call	outdec
	ret

oprint:	call	getsts		; anyone type anything?
	jp	z,seldsk	; no, return thru seldsk
	call	print		; interrupt!!!
	db	esc,'B','6'	; save cursor position
	db	esc,lodcur,16+offset,60+offset,'!!! INTERRUPTED !!!'
	db	esc,lodcur,17+offset,60+offset,'Please type any key'
	db	esc,lodcur,18+offset,60+offset,'to continue,'
	db	esc,lodcur,19+offset,60+offset,'or an ESC to return'
	db	esc,lodcur,20+offset,60+offset,'to the main menu'
	db	esc,lodcur,21+offset,60+offset,'and start all over'
	db	esc,lodcur,22+offset,60+offset,'from the beginning '
	db	00
	call	getd
	cp	esc	; interrupted by an esc?
	jp	z,start	; if so, interrupted out completely, start over,
	call	print	; else erase message,
	db	esc,lodcur,16+offset,60+offset,clreol
	db	esc,lodcur,17+offset,60+offset,clreol
	db	esc,lodcur,18+offset,60+offset,clreol
	db	esc,lodcur,19+offset,60+offset,clreol
	db	esc,lodcur,20+offset,60+offset,clreol
	db	esc,lodcur,21+offset,60+offset,clreol
	db	esc,lodcur,22+offset,60+offset,clreol
	db	esc,'C','6'	; return cursor to last position,
	db	00
	jp	seldsk	; select disk, resume certification.

dsppas:	call	print
	db	esc,'B','6'	; save current cursor position
	db	esc,lodcur,24+offset,40+offset	; middle of status line
	db	'Pass Number '
	db	00
	ld	hl,(pascnt)
	call	outdec
	call	print
	db	clreol,esc,'C','6'	; clear rest of line, return cursor.
	db	00
	ret	; exit

dspsdh:	call	busy
	in	a,(wdsdh)
	ld	hl,sdhtbl
	ld	b,8
dssdh1:	rla
	ld	(hl),'0'
	jr	nc,dssdh2
	ld	(hl),'1'
dssdh2:	inc	hl
	djnz	dssdh1
	call	print
	db	' SDH = '
sdhtbl:	db	00,00,00,00,00,00,00,00
	db	00
	ret

getd:	call	getsts
	jr	z,getd
	ld	(gotc),a
	ret

putd:	ld	e,a
	ld	c,06
	call	bdos
	ret

getsts:	ld	e,0ffh
	ld	c,06
	call	bdos
	or	a
	ret

print:	pop	hl
	ld	a,(hl)
	inc	hl
	push	hl
	or	a
	ret	z
	call	putd
	jr	print

lstit:	ld	c,05
	call	bdos
	ret

include	DSPERR.MAC
include	OUTDEC.MAC

; format skew table, skip 4: smallest is 4 sectors, largest is 4 + index gap.
;
fmttbl:	db	00,00,00,07,00,14,00,04,00,11
	db	00,01,00,08,00,15,00,05,00,12
	db	00,02,00,09,00,16,00,06,00,13
	db	00,03,00,10

ldspce:	db	00h		; leading zero suppress flag
gotc:	db	00h		; character input by getc
fdsk:	db	00h		; first disk, per pass, constant.
drng:	db	00h		; disk range, per pass, constant
number:	dw	0000h
fhead:	db	00h		; first head, per cylinder, constant.
hrng:	db	00h		; head range, per cylinder, constant.
fstcyl:	dw	0000h		; first cylinder to format, per disk,
cylrng:	dw	0000h		; cylinder range, per disk, constants.
fcyl:	dw	0000h		; current cylinder to format
crng:	dw	0000h		; current format pass cylinder range count
diskno:	db	00h		; current disk to format
dskrng:	db	00h		; current format pass disk range count
headno:	db	00h		; current head to format
hcount:	db	00h		; current range count, this cylinder
errcnt:	db	00h
rollo:	db	00h
numlst:	dw	0000h
	dw	0000h
	dw	0000h
	dw	0000h
lstsw:	db	00	; list on/off switch, 00=list off
pascnt:	dw	0000h	; pass counter
cntdwn:	dw	0000h
fmtbuf:	defw	0000
redbuf	equ	fmtbuf+512
wrtbuf	equ	redbuf+512
rtrycnt	equ	wrtbuf+512	; retry counter.
errflag	equ	rtrycnt+1	; error flag.

END
