	
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*	        DISKETTE MAINTENANCE UTILITY		    *
*        TO FORMAT, VERIFY AND COPY AMPRO DISKETTES	    *
*    COPYRIGHT (C) 1983, 1984, 1985 AMPRO COMPUTERS, INC.   *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

; assemble with asm.com or equivalent. there is no z80 code.

VER	EQU	33

; REVISION HISTORY:
;
; Version 3.3 12 Dec 85 Changed SETUP and READY routines to 
;                       work better with microfloppies.
;                       Added interrupt disable/enable so 
;                       can be used with future s/w rtc code.
;
; Version 3.2 24 Jun 85	Changed cmds to FDC to use proper step
;			rate if 2.x+ bios.
;
; Version 3.1 21 Feb 85 Changed prompt & display routine to get
;			real unit # (version 2.0+).  Removed
;			warning message of 12-dec-84.
;
; Version 3.0 12 Dec 84 Added warning message when used with
;			hard disk bios.
;
; Version 2.6 10 May 84 Cleaned up one more response.
;
; Version 2.5  8 May 84	Cleaned up the responses so that only
;			 characters of interest are acted upon.
;
; Version 2.4  5/3/84	Added 10 ms delay after any line feed.
;			Only ESC or CTLC will exit the program.
;
; Version 2.3  3/20/84  Increased restore timeout to 5 sec
;
; Version 2.2		Delayed step until after index pulse
;			 due to apparent 1770 problem resulting
;			 in improper stepping
;
; Version 2.1		Added motor on delay to restore command
;
; Version 2.0		Initial release

YES	EQU	1
NO	EQU	0

RAW	EQU	NO

; wd1770 equates
REST	EQU	00 		; slow restore
RDSEC	EQU	88H		; read sector command
WRSEC	EQU	0A8H		; write sector command
WRTRK	EQU	0F8H		; write track command
STEPI	EQU	58H		; step in command
RDID	EQU	0C8H		; read address command
MOT	EQU	80H		; motor on status
WPR	EQU	40H		; write protected status
SPU	EQU	20H		; disk at speed status (type i)
TYP	EQU	20H		; record type status (types ii and iii)
RNF	EQU	10H		; record not found status
CRC	EQU	8		; crc error status
TK0	EQU	4		; track zero status (type i)
LDT	EQU	4		; lost data status (types ii and iii)
IDX	EQU	2		; index status (type i)
DRQ	EQU	2		; data request status (types ii and iii)
BSY	EQU	1		; controller busy status

ERRII	EQU	WPR+RNF+CRC+LDT	; type ii error status mask
ERRIII	EQU	WPR+LDT		; type iii error status mask

CMND	EQU	0C0H		; fdc command register
WTRK	EQU	CMND+1		; write to track register
WSEC	EQU	WTRK+1		; write to sector register
WDAT	EQU	WSEC+1		; write to data register

STAT	EQU	WDAT+1		; fdc status register
RTRK	EQU	STAT+1		; read from track register
RSEC	EQU	RTRK+1		; read from sector register
RDAT	EQU	RSEC+1		; read from data register

CONT	EQU	0		; system control port
ROMOFF	EQU	40H		; turn rom off

SID0	EQU	0EFH		; mask side bit off
SID1	EQU	10H		; side bit in control register
SDEN	EQU	20H		; single density bit in control register
TRY	EQU	3		; tries
TRYHARD	EQU	10		; we try harder

MSEC	EQU	167		; constant for 1 ms delay

CTLC	EQU	3		; control c
CR	EQU	13		; carriage return
LF	EQU	10		; line feed
ESC	EQU	27		; escape key
UPCASE	EQU	5FH		; upper case mask

	ORG	100H

start:	
	lxi	sp,stack
	call	clrsc
	call	get$bios$vers
	mvi	a,16
	jnz	new$sys
	lhld	1
	mvi	l,5ch
	mov	a,m
	jmp	plugem
new$sys:
	push	psw
	mvi	a,0
	lxi	h,jplug1
	mov	m,a
	inx	h
	mov	m,a
	inx	h
	mov	m,a
	lxi	h,jplug2
	mov	m,a
	inx	h
	mov	m,a
	inx	h
	mov	m,a
	pop	psw
plugem:
	adi	'@'
	sta	plug1
	sta	plug2
	inr	a
	sta	cplug1
	sta	cplug2
	jmp	doit

*****

bdos	equ	5
cr	equ	13
lf	equ	10


DISPLAY$FDEV:
	lda	lb$vers
	cpi	20
	rm

	LXI	D,D$FDEV$HDR	; Print header
	mvi	c,9
	call	bdos

	mvi	a,0		; starting unit #
D$NEXT$FDEV:
	sta	unit
	call	lb$get$ldte	; Get address of unit id	
	MOV	A,M		; Get unit id
	CPI	01		; Floppy?
	JNZ	D$BUMP$PTR	; No -- go to the next device
	inx	h		; Get drive #
	mov	a,m		; .
	ani	03h		; mask out excess bits
	MOV	l,a		; update floppy device number
	mvi	h,0		; .
	dad	h		; x2
	xchg
	LXI	H,FNAMES	; .
	dad	d		; x2
	dad	d		; x4
	dad	d		; x6
	lxi	d,d$fname	;
	lxi	b,6		; 
	db	0edh,0b0h	; . (LDIR)
	lda	unit		;
	adi	'A'		;
	sta	d$current	;
	LXI	D,D$FDEV$LIN	; and output the line
	mvi	c,9
	call	bdos

D$BUMP$PTR:
	lda	unit
	inr	a		; Bump to next unit
	cpi	16		; Done yet?
	jm	D$NEXT$FDEV	; No -- go do the next one
	RET

unit:	db	0

D$FDEV$HDR:	DB	CR,LF,'FLOPPY DISK ASSIGNMENTS',CR,LF
		DB	'CP/M drive   '
		DB	'Floppy disk',CR,LF
		DB	'------------------------',CR,LF,'$'

D$FDEV$LIN:
		DB	' '
D$CURRENT:	DB	'x  '
D$EDISK:	DB	'   '
D$EBLANK:	DB	'         '
D$FNAME:	DB	'        '
		DB	CR,LF,'$'
D$FDEV$HLEN	EQU	$-D$FDEV$LIN	; Line length

FNAMES:		DB	'First '
		DB	'Second'
		DB	'Third '
		DB	'Fourth'

; end of DISPLAY$FDEV


TO$UPPER:
	CPI	'z'+1		; Convert to upper case
	JP	UPPER$ALREADY	; .
	CPI	'a'		; .
	JM	UPPER$ALREADY	; .
	ANI	5FH		; .
UPPER$ALREADY:
	RET			; and return


GET$BIOS$VERS:
	LHLD	1		; Get start of bios jump table
	LXI	D,LB$BIOS$TBL	; Move bios to local storage
	LXI	B,LB$LEN	; .  (length of bios area)
	DB	0edh,0b0h	; .  (LDIR)
	MVI	A,0		; Test CP/M version
	CALL	LB$GETNXT	; Get next jump table
	STA	LB$VERS		; Save bios version
	INX	H		; See if HL is 0FFFFh
	MOV	A,H		; .
	ORA	L		; .
	RZ			; If so, then old version
	DCX	H		; Fix HL as it has the table addr
	LXI	D,LB$XTBL	; Move extra table to local storage
	LXI	B,LB$XLEN	; .  (length of extra table)
	DB	0edh,0b0h	; .  (LDIR)
	MVI	A,0FFH		; Set NZ to indicate bios
	ORA	A		; ... version 2.1+
	RET			; ... and return.


; Replicated BIOS to make direct calls easier . . .

LB$BIOS$TBL:
LB$WBOOT	DS	3	; Warm boot
LB$CONST	DS	3	; Console status
LB$CONIN	DS	3	; Console input
LB$CONOUT	DS	3	; Console output
LB$LISTOUT	DS	3	; List output
LB$PUNCH	DS	3	; Punch output
LB$READER	DS	3	; Reader input
LB$HOMDSK	DS	3	; Home disk (move to track 00)
LB$SELDSK	DS	3	; Select disk drive
LB$SETTRK	DS	3	; Select track number
LB$SETSEC	DS	3	; Select sector number
LB$SETDMA	DS	3	; Set DMA address
LB$DSKREAD	DS	3	; Disk read
LB$DSKWRITE	DS	3	; Disk write
LB$LISTST	DS	3	; List status
LB$SECTRN	DS	3	; Sector translate routine
; AMPRO-specific BIOS calls
LB$GETNXT	DS	3	; Get bios ver & next tbl address
LB$GETEDSK	DS	3	; Get pointer to E-disk storage
LB$IOINIT	DS	3	; Set new I/O parameters
LB$SCSIDRV	DS	3	; SCSI direct driver
LB$LEN	EQU	$-LB$WBOOT	; Length of bios table

LB$XTBL:			; 'Extra' table definitions ...
LB$SWAP$DRV	DS	3	; Swap two logical drives
LB$GET$WDP	DS	3	; Set/get win drive parameters
LB$PHYTAB	DS	3	; Set/get phytab access
LB$GET$LDTE	DS	3	; Get logical device table entry
LB$RESERVED	DS	3	; Reserved entry
LB$XLEN	EQU	$-LB$XTBL	; Length of extra table

LB$VERS		DB	0	; Bios version number

doit:
	CALL	ILPRT		; print following text
	DB	CR,LF,'    AMPRO Copy/Format/Verify Utility'
	DB	CR,LF,' Copyright (C) 1984 AMPRO Computers, Inc.'
	DB	CR,LF,'              Version ',VER/10+'0','.'
	DB	VER MOD 10+'0','$'

ASK1:	CALL	ILPRT		; print following string
	DB	CR,LF,LF,'COPY, FORMAT or VERIFY?  (C, F or V)'
	DB	CR,LF,'Press <ESC> or ^C to quit. $'
ASK1A:	CALL	CONIN		; get response
	CPI	ESC
	JZ	EXIT		; exit to operation system
	CPI	3
	JZ	EXIT		; ctl c
	ANI	UPCASE		; force upper case
	CPI	'V'
	JZ	SETV		; if verify
	CPI	'C'
	JZ	SETC		; if copy
	CPI	'F'
	JNZ	ASK1A		; otherwise, do this again

; set function code and do it
SETF:	MVI	A,1
	STA	FUNC
	JMP	FORMAT
SETV:	MVI	A,2
	STA	FUNC
	JMP	VERIFY
SETC:	MVI	A,3
	STA	FUNC
	JMP	COPY

FORMAT:	CALL	CLRSC
	CALL	ILPRT
	DB	8,32,CR,LF,LF	; overwrite response and make new line
	DB	'FORMAT prepares a fresh diskette for data or program storage.',cr,lf,'$'
	call	display$fdev
	CALL	ASK5		; ask destination
	JC	ASK1		; restart if bad selection
	CALL	ASK2
	JC	ASK1
	CALL	ASK3
FORM0:	CALL	ASK6		; give warning
	JC	ASK1
	CALL	EXEC		; do it
	JMP	ASK1		; restart at end of format

VERIFY:	CALL	CLRSC
	CALL	ILPRT
	DB	8,32,CR,LF
	DB	CR,LF,'VERIFY checks the reliability of data on a disk.',cr,lf,'$'
	call	display$fdev
	CALL	ASK5		; ask for destination disk
	JC	ASK1
	CALL	EXEC		; do it
	JMP	ASK1

COPY:	CALL	CLRSC
	CALL	ILPRT
	DB	8,32,CR,LF
	DB	CR,LF,'COPY creates a duplicate of a disk.',cr,lf,'$'
	call	display$fdev
	CALL	ASK4		; ask for source
	JC	ASK1
	CALL	ASK5
	JC	ASK1
	CALL	ASK6		; give warning
	JC	ASK1
	CALL	EXEC		; do it
	JMP	ASK1

ASK2:	CALL	ILPRT		; print following string
	DB	CR,LF,LF,'Formats Available:'
	db	cr,lf,'     1.  Single side 48 tpi'
	db	cr,lf,'     2.  Double side 48 tpi'
	db	cr,lf,'     3.  Single side 96 tpi'
	db	cr,lf,'     4.  Double side 96 tpi'
	db	cr,lf,lf,'Choose one (1, 2, 3 or 4). $'
ASK2E:	CALL	CONIN		; get the answer
	CPI	ESC
	JZ	EXIT
	CPI	CTLC
	JZ	EXIT
	CPI	'1'		; less than 1?
	JC	ASK2E		; error return
	CPI	'5'		; more than 4?
	JNC	ASK2E
	push	psw
	call	conout
	pop	psw
	ANI	0FH		; mask hi order
	DCR	A		; 1 side = 0, two sides = 1
	JZ	SS48
	DCR	A
	JZ	DS48
	DCR	A
	JZ	SS96

DS96:	STA	TWOSID
	STA	TPI96
	MVI	A,16
	STA	SBIAS
	ORA	A
	RET

SS48:	STA	TWOSID
	STA	TPI96
	STA	SBIAS
	ORA	A
	RET

DS48:	STA	TPI96
	INR	A
	STA	TWOSID
	MVI	A,16
	STA	SBIAS
	ORA	A
	RET

SS96:	STA	SBIAS
	STA	TWOSID
	INR	A
	STA	TPI96
	ORA	A		; reset error flag
	RET

ASK3:
	LDA	TPI96
	ORA	A
	MVI	A,79
	JNZ	ASK3X
	MVI	A,39
ASK3X:	STA	MAXTRK
	ORA	A
	RET

ASK4:	CALL	ILPRT
	DB	CR,LF,LF,'Source drive? (A - '
plug1:	DB	'x) $'
ask4e:	call	conin
	cpi	esc
	jz	exit
	cpi	ctlc
	jz	exit
	ani	upcase
	cpi	'A'
	jc	ask4E
cplug1:	equ	$+1
	cpi	'E'
	JNC	ASK4E
	STA	ASK40
	push	psw
	call	conout
	pop	psw
	sui	'A'
jplug1:	jmp	ask40s
	call	lb$get$ldte
	mov	a,m
	cpi	01h
	jnz	ask4
	inx	h
	mov	a,m
	ani	03h
ask40s	STA	SDRIV		; source
ask40E:	CALL	ILPRT
	DB	CR,LF,LF,'Place source disk on drive '
ASK40:	DB	'a',CR,LF,'Press <RETURN> to continue, <ESC> to quit.$'
ask40h	CALL	CONIN
	CPI	CR
	RZ
	CPI	ESC
	JZ	EXIT
	CPI	CTLC
	JZ	EXIT
	JMP	ASK40h

ASK5:	CALL	ILPRT
	DB	CR,LF,LF,'Destination drive?  (A - '
plug2:	DB	'x) $'
ASK5E:	CALL	CONIN		; get the answer
	CPI	ESC
	JZ	EXIT
	CPI	CTLC
	JZ	EXIT
	ANI	UPCASE		; force upper case
	CPI	'A'		; first valid drive
	JC	ASK5e
cplug2:	equ	$+1
	CPI	'E'		; first invalid drive
	JNC	ASK5e
	sta	ask60
	push	psw
	call	conout
	pop	psw
	sui	'A'
jplug2:	jmp	ask50s
	call	lb$get$ldte
	mov	a,m
	cpi	1
	jnz	ask5
	inx	h
	mov	a,m
	ani	3
ask50s:	STA	DDRIV		; save drive designator
	CALL	SETSEL		; disk select bits
	ORA	A
	RET

ASK6:	CALL	ILPRT		; print the following string
	DB	CR,LF,LF,'Place destination disk on drive '
ask60	db	'x'
	DB	CR,LF,'Press <RETURN> to write, '
	DB	'<ESC> to quit.  $'
ASK6E:	CALL	CONIN		; get answer
	CPI	CR
	RZ
	CPI	ESC
	JZ	EXIT
	JMP	ASK6E

EXEC:	CALL	ILPRT
	DB	CR,LF,LF,'$'

	LDA	FUNC		; function 1=fmt, 2=vfy, 3=cpy
	CPI	1
	JZ	FMT
	CPI	2
	JZ	VFY		; else fall through to cpy

CPY:	MVI	A,0
	STA	HEAD
	MVI	A,39
	STA	MAXTRK
	LDA	DDRIV		; destination
	CALL	SETSEL
	CALL	SETUP
	LDA	SDRIV
	CALL	SETSEL
	CALL	SETUP
	MVI	A,TRY
	STA	TRIES
CP0:	CALL	READID
	JZ	CP1
	LXI	H,TRIES
	DCR	M
	JNZ	CP0
	CALL	ILPRT
	DB	CR,LF,LF,'COPY failed.  Cannot read source disk.',CR,LF,'$'
	RET
CP1:	LDA	IDSV+2		; sector #
	CPI	17		; two-sided if no carry
	MVI	A,0
	JC	CP2
	INR	A
CP2:	STA	TWOSID
	ORA	A		; reset carry
	RAL
	RAL
	RAL
	RAL
	STA	SBIAS
	XRA	A		; get a zero
	STA	TRACK
	CALL	SETDSK
CPYLP:	CALL	KEY
	LDA	HEAD
	ORA	A
	PUSH	PSW
	LDA	SDRIV
	CALL	SETSEL
	POP	PSW
	LDA	SELBYT
	JZ	CPL0
	ORI	SID1
CPL0:	OUT	CONT		; select destination
	STA	SELBYT
	LXI	H,RDMSG
	CALL	PRINT
	CALL	TMRPRT
cp14:	MVI	A,TRYHARD
	STA	TRIES
CP3:	CALL	READTRK
	JZ	CP4
	LXI	H,TRIES
	DCR	M
	JNZ	CP3
	CALL	ILPRT
	DB	CR,'                              '
	DB	CR,LF,'COPY failed.  Unrecoverable read error on source disk.',CR,LF,'$'
	call	report
	call	ilprt
	db	cr,lf,'Try again? (Y or N) $'
cf002z:	call	conin
	ani	upcase
	cpi	'N'
	rz
	cpi	'Y'
	jnz	cf002z
	call	ilprt
	db	cr,lf,lf,'$'
	jmp	cp14

CP4:	LDA	HEAD
	ORA	A
	PUSH	PSW
	LDA	DDRIV
	CALL	SETSEL
	POP	PSW
	LDA	SELBYT
	JZ	CPL1
	ORI	SID1
CPL1:	OUT	CONT
	STA	SELBYT
	LXI	H,WRMSG
	CALL	PRINT
	CALL	TMRPRT
	CALL	MAKETRK		; format destination
	MVI	A,TRY
	STA	TRIES
CP5:	CALL	WRITRK
	JZ	CP6
	LXI	H,TRIES
	DCR	M
	JNZ	CP5
	CALL	ILPRT
	DB	CR,'                              '
	DB	CR,LF,'COPY failed.  Cannot write destination disk.',CR,LF,'$'
	JMP	REPORT

CP6:	MVI	A,TRY
	STA	TRIES
CP7:	CALL	WRITETRK
	JZ	CP8
	LXI	H,TRIES
	DCR	M
	JNZ	CP7
	CALL	ILPRT
	DB	CR,'                              '
	DB	CR,LF,'COPY failed.  Unrecoverable write error on destination disk.',CR,LF,'$'
	JMP	REPORT
CP8:
	 IF	RAW
	LXI	H,VFMSG
	CALL	PRINT
	CALL	TMRPRT
	MVI	A,TRY
	STA	TRIES
CP9:	CALL	READTRK
	JZ	CP10
	LXI	H,TRIES
	DCR	M
	JNZ	CP9
	CALL	ILPRT
	DB	CR,'                              '
	DB	CR,LF,'COPY failed.  Unrecoverable read error on destination disk.',CR,LF,'$'
	JMP	REPORT
CP10:
	 ENDIF	; RAW
	LDA	TWOSID
	ORA	A
	LDA	TRACK
	JZ	CP11
	LDA	HEAD
	ORA	A
	LDA	TRACK
	JZ	CP12
CP11:	LXI	H,MAXTRK
	CMP	M
	JNZ	CP12
	CALL	ILPRT
	DB	CR,'                              '
	DB	CR,LF,'COPY complete.',CR,LF,LF,'$'
	 IF	RAW
	RET
	 ENDIF
	JMP	VFY

CP12:	INR	A
	STA	TRACK
	CALL	STEPIN
	LDA	TWOSID
	ORA	A
	JZ	CP13
	LDA	HEAD
	CMA
	ANI	1
	STA	HEAD
	JNZ	CP13
	LXI	H,TRACK
	INR	M
CP13:	LDA	SDRIV
	CALL	SETSEL
	CALL	STEPIN
	LDA	TRACK
	OUT	WTRK
	JMP	CPYLP

FMT:	LDA	DDRIV
	CALL	SETSEL
	CALL	SETUP
	XRA	A
	STA	TRACK
	STA	HEAD
NEXTRK:	CALL	KEY
	LXI	H,FRMSG
	CALL	PRINT
	CALL	TMRPRT
	CALL	MAKETRK
	MVI	A,TRY
	STA	TRIES
NT3:	CALL	WRITRK
	JZ	NT2
	LXI	H,TRIES
	DCR	M
	JNZ	NT3
	CALL	ILPRT
	DB	CR,'                              '
	DB	CR,LF,'FORMAT failed.',CR,LF,'$'
	CALL	REPORT
	RET
NT2:
	 IF	RAW
	LXI	H,VFMSG
	CALL	PRINT
	CALL	TMRPRT
	MVI	A,TRY
	STA	TRIES
NT4:	CALL	READTRK
	JZ	NT5
	LXI	H,TRIES
	DCR	M
	JNZ	NT4
	CALL	ILPRT
	DB	CR,'                              '
	DB	CR,LF,'FORMAT failed.  Bad destination disk.',CR,LF,'$'
	CALL	REPORT
	RET
NT5:
	 ENDIF	; RAW
	LDA	TWOSID
	ORA	A
	LDA	TRACK
	JZ	NT0
	LDA	HEAD
	ORA	A
	LDA	TRACK
	JZ	NT1
NT0:	LXI	H,MAXTRK
	CMP	M
	JNZ	NT1
	CALL	ILPRT
	DB	CR,'                              '
	DB	CR,LF,'FORMAT complete.',CR,LF,LF,'$'
	 IF	RAW
	RET			; finished
	 ENDIF
	JMP	VFY

NT1:	INR	A
	STA	TRACK
	CALL	STEPIN
	JMP	NEXTRK

VFY:	MVI	A,0
	STA	HEAD
	LDA	DDRIV
	CALL	SETSEL
	CALL	SETUP
	MVI	A,TRY
	STA	TRIES
V1:	CALL	READID
	JZ	V0
	LXI	H,TRIES
	DCR	M
	JNZ	V1
	CALL	ILPRT
	DB	CR,LF,'VERIFY failed.  Cannot read source disk.',cr,lf,'$'
	RET
V0:	LDA	IDSV+2
	CPI	17
	MVI	A,0
	JC	VF0
	INR	A
VF0:	STA	TWOSID
	ORA	A
	RAL
	RAL
	RAL
	RAL
	STA	SBIAS
	XRA	A
	STA	TRACK		; start with track 0
	CALL	SETDSK
VLOOP:	CALL	KEY
	LXI	H,VFMSG
	CALL	PRINT
	CALL	TMRPRT
	MVI	A,TRY
	STA	TRIES
VL1:	CALL	READTRK
	JZ	VL0
	LXI	H,TRIES
	DCR	M
	JNZ	VL1
	CALL	ILPRT
	DB	CR,'                              '
	DB	CR,LF,'VERIFY failed.  Unrecoverable read error.',CR,LF,'$'
	CALL	REPORT
	RET

VL0:	LDA	TWOSID
	ORA	A
	LDA	TRACK
	JZ	VF1
	LDA	HEAD
	ORA	A
	LDA	TRACK
	JZ	VF2
VF1:	LXI	H,MAXTRK
	CMP	M
	JNZ	VF2
	CALL	ILPRT
	DB	CR,'                              '
	DB	CR,LF,'VERIFY complete.',CR,LF,'$'
	RET
VF2:	INR	A
	STA	TRACK
	CALL	STEPIN
	JMP	VLOOP

SETDSK:	LDA	IDSV+3
	CPI	3
	JZ	SDSK
	MVI	A,39
	STA	MAXTRK
	XRA	A
	STA	SKEW
	RET
SDSK:	MVI	A,79
	STA	MAXTRK
	MVI	A,1
	STA	SKEW
	RET

STEPIN:	LDA	TWOSID
	ORA	A
	JZ	STI		; single sided
	LDA	HEAD
	ORA	A
	MVI	A,1
	JZ	STI0
	MVI	A,0
STI0:	STA	HEAD
	JZ	STI1

STI:	LDA	SELBYT
	ANI	SID0		; mask side bit off
	OUT	CONT		; select side 0
	STA	SELBYT
	CALL 	WAIT
	MVI	A,STEPI
	CALL	OUTCMD
STILP:	IN	STAT
	RAR
	JC	STILP
	RET 

STI1:	LDA	SELBYT
	ORI	SID1
	OUT	CONT		; select side 1
	STA	SELBYT
	LXI	H,TRACK
	DCR	M
	JMP	WAIT

WRITRK:	IN	STAT
	RAR
	JC	WRITRK
	LXI	H,TKBUF
	MVI	A,WRTRK		; write track command
	CALL	OUTCMD
	CALL	WR
	MOV	A,B
	STA	STATUS
	ANI	ERRIII
	RET

WR:	IN	STAT		; get fdc status
	MOV	B,A		; save status
	RAR			; check busy
	RNC			; end of command, return to caller
	RAR			; check for drq
	JNC	WR		; if not
	MOV	A,M		; get a byte from buffer
	OUT	WDAT		; to fdc
	INX	H		; bump hl
	JMP	WR		; again

MAKETRK: LXI	H,TKBUF		; create track image in memory
	MVI	B,60
	MVI	A,4EH
MTLP:	MOV	M,A		; write gap4a
	INX	H
	DCR	B
	JNZ	MTLP
	SHLD	DMAADR		; sector 1 starts here
	LDA	HEAD
	STA	SIDE
	LDA	TPI96
	ORA	A
	LXI	H,128*256+2
	JZ	MTL1
	LXI	H,3
MTL1:	CALL	CHGFMT
	CALL	GETSKW		; point hl to skew table

LOOP:	MOV	C,M
	INX	H
	INR	C
	JZ	ENDTRK		; if c was 0ffh
	DCR	C
	LDA	SBIAS
	ADD	C
	STA	SECTOR
	PUSH	H		; save skew pointer on stack
	CALL	MAKTRK
	POP	H
	JMP	LOOP

ENDTRK:	LHLD	DMAADR
	LXI	B,1000
ETLP:	MVI	M,4EH
	INX	H
	DCX	B
	MOV	A,B
	ORA	C
	JNZ	ETLP
	SHLD	DMAADR
	RET

CHGFMT:	MOV	A,L
	STA	SIZE
	MOV	A,H
	MVI	B,4
	LXI	H,DAT0
	LXI	D,2
CHG0:	MOV	M,A
	DAD	D
	DCR	B
	JNZ	CHG0
	RET

MAKTRK:	LHLD	DMAADR		; track image
	PUSH	H		; save it
	LXI	H,TABLE		; track format table
MAKT1:	MOV	D,M		; get count
	INX	H		; bump pointer
	MOV	E,M		; get data
	INX	H		; bump pointer
	MOV	A,E		; check for end of table (0ffh)
	INR	A		; 0ffh + 1 = 0
	JZ	MAKT2		; if end of table
	XTHL			; point to track buffer
	CALL	LOAD		; fill track buffer
	XTHL			; point to format table
	JMP	MAKT1		; again
MAKT2:	POP	H		; get track buffer address from stack
	SHLD	DMAADR		; save pointer to track buffer
	RET			; to caller

WRITETRK: ; write current track from read buffer
	LXI	H,RDBUF
	SHLD	DMAADR
	CALL	GETSKW		; point hl to skew table
WTLOOP:	MOV	C,M
	INX	H		; bump pointer
	INR	C
	RZ			; finished, end of table is 0ffh
	DCR	C
	LDA	SBIAS
	ADD	C
	OUT	WSEC		; to fdc
	PUSH	H		; save skew pointer
	CALL	WRITESEC
	POP	H
	JMP	WTLOOP

WRITESEC: ; write a sector from dma buffer
	IN	STAT		; clear status
	IN	RDAT		; clear any trash
	LHLD	DMAADR
	MVI	A,WRSEC		; write sector command
	CALL	OUTCMD		; to fdc
	CALL	WR		; write the data
	SHLD	DMAADR		; save pointer
	MOV	A,B		; get status
	STA	STATUS
	ANI	ERRII
	RZ			; return to this caller
	POP	H		; adjust stack from writetrk
	POP	H
	RET			; return to previous caller

READTRK: ; read current track into read buffer
	LXI	H,RDBUF
	SHLD	DMAADR
	CALL	GETSKW		; piont hl to skew table
RTLOOP:	MOV	C,M		; get sector number from table
	INX	H		; bump pointer
	INR	C
	RZ			; finished, end of table is 0ffh
	DCR	C
	LDA	SBIAS
	ADD	C
	STA	SECTOR
	OUT	WSEC		; to fdc
	PUSH	H		; save skew pointer
	CALL	READSEC		; read the sector
RT0:	POP	H
	JMP	RTLOOP

READSEC:
	IN	STAT		; clear status
	IN	RDAT		; clear any trash
	LHLD	DMAADR		; pointer to read buffer
	MVI	A,RDSEC		; read sector command
	CALL	OUTCMD		; to fdc
	CALL	RD		; read the data
	SHLD	DMAADR		; save pointer
	MOV	A,B
	STA	STATUS
	ANI	ERRII
	RZ
	POP	H		; adjust stack from readtrk
	POP	H
	RET			; to previous caller

RD:	IN	STAT		; get fdc status byte
	MOV	B,A		; save status
	RAR			; rotate busy bit to carry
	RNC			; end of command, return to caller
	RAR			; rotate data request to carry
	JNC	RD		; wait for data request
RD0:	IN	RDAT		; get data from fdc
	MOV	M,A		; put it into buffer
	INX	H		; bump hl
	JMP	RD		; again

READID:	IN	STAT
	IN	RDAT
	LXI	H,IDSV
	MVI	A,RDID
	CALL	OUTCMD
	CALL	RD
	MOV	A,B		; get status
	STA	STATUS
	ANI	ERRIII
	RET

WAIT:	LXI	B,50 * MSEC
WT0:	DCX	B
	MOV	A,B
	ORA	C
	JNZ	WT0
	RET

WAIT10:	LXI	B,10 * MSEC	; wait 10 ms
	JMP	WT0		; wt0 returns to caller

LOAD:	MOV	M,E		; (e) to track buffer
	INX	H		; increment pointer
	DCR	D		; decrement count
	JNZ	LOAD		; again
	RET

CLRSC:	MVI	D,24		; print 24 line feed chars to clear screen
CLS:	MVI	A,LF
	CALL	CONOUT		; out to console
	CALL	WAIT10		; wait 10 ms after line feed
	DCR	D		; decrement count
	JNZ	CLS		; again
	RET

PRINT:	MOV	A,M		; hl points to text string
	INX	H		; bump hl
	CPI	'$'		; check for terminator
	RZ			; return to caller
	PUSH	PSW		; save for test
	CALL	CONOUT		; send byte to console
	POP	PSW		; get the character
	CPI	LF		; if line feed..
	CZ	WAIT10
	JMP	PRINT		; again

ILPRT:	XTHL			; return address to hl, point to text
	CALL	PRINT
	XTHL			; return address follows text
	RET

CONOUT:	PUSH	B		; save
	PUSH	D		;  the
	PUSH	H		;   registers
	MOV	E,A		; text to be printed
	mvi	c,6
	call	bdos
	POP	H		; restore
	POP	D		;  the
	POP	B		;   registers
	RET

BIOS:	LHLD	1
	MOV	L,A
	PCHL

CONIN:
CONINB:	PUSH	B
	PUSH	D		; save
	PUSH	H		;  registers
	mvi	e,0ffh
	MVI	C,6		; console input function
	CALL	5		; bdos
	POP	H		; restore
	POP	D		;  registers
	POP	B
	RET

; The current setup routine does a restore with the 1772
; motor on flag enabled.  It exits as soon as the BUSY
; status bit goes away, or after a 4 second timeout.

SETUP:	LDA	SELBYT		; get selected drive
	OUT	CONT		; select the drive
  	MVI	A,REST		; restore command
	CALL	OUTCMD		; output the command
	LXI	H,4000		; 4000 ms timeout setup
DEL0:	MVI	C,0
DEL1:	DCR	C
	JNZ	DEL1
	IN	STAT
	RAR
	JNC	READY		; go on if 1772 done
	DCX	H
	MOV	A,H
	ORA	L
	JNZ     DEL1
	MVI	A,0D0H		; timed out -- force interrupt,
	CALL	OUTCMD		;   then report failure
	JMP	FAILED

; The current ready function checks to see if a diskette
; is inserted in a drive.  A read address command with 
; the motor on flag enabled is used.  If the command
; hangs up, the 1772 is not receiving index pulses,
; indicating the lack of a spinning diskette.

READY:	
	MVI	A,0C0H		; read address command
	CALL	OUTCMD		
	LXI	H,4000		; 4000 mS timeout setup
	MVI	C,0
RDY1:	DCR	C
	JNZ	RDY1
	IN	STAT
	RAR
	RNC			; finished, if 1772 done
	DCX	H
	MOV 	A,H
	ORA	L
	JNZ	RDY1		; loop till timeout
	MVI	A,0D0H		; timeout -- force interrupt,
	CALL	OUTCMD		;   then report failure
	
FAILED:
	LDA	SELBYT
	ANI	0FH
	MVI	C,0
FLD1:	INR	C
	RAR
	JNC	FLD1
	MOV	A,C
	adi	30H		; add ascii bias
	STA	DRI
	CALL	ILPRT		; print following string
	DB	CR,LF,'Drive '
DRI:	DB	'A Not Ready, Insert disk, close door, and try again.',CR,LF,'$'
	POP	B		; adjust stack
        RET

GETSKW:	; point hl to skew table
	MVI	H,0
	LDA	SKEW		; skew factor -1
	MOV	L,A		; hl holds skew factor -1
	DAD	H		; *2
	XCHG
	LXI	H,SK1		; first skew table address
	DAD	D		; hl points to correct table pointer
	MOV	E,M		; get lo order
	INX	H
	MOV	D,M		; get hi order
	XCHG			; hl points to skew table
	RET

OUTCMD:	OUT	CMND
	MVI	A,10
OC0:	DCR	A
	JNZ	OC0		; wait 35 us. for fdc to set-up
	RET

KEY:
	push	b
	push	d
	MVI	C,6
	mvi	e,0ffh
	CALL	5
	pop	d
	pop	b
	ORA	A
	RZ
	CPI	CTLC
	JZ	EXIT
	PUSH	PSW
	CALL	ILPRT
	DB	CR,'                              ',CR,'$' ; clear the line
	POP	PSW
	CPI	ESC
	RNZ
	POP	H
	RET

EXIT:	
	JMP	0		; warm boot

TMRPRT:	LDA	TRACK
	LXI	D,TKMSG0
	CALL	BINASC
	LDA	HEAD
	LXI	D,SDMSG0
	CALL	BINASC
	LXI	H,TKMSG
	JMP	PRINT		; print returns to caller

REPORT:	LXI	D,DR		; point to error message
	LDA	SELBYT
	ANI	0FH		; mask upper nybble
	MVI	C,0		; clear counter
R1:	INR	C		; bump it
	RAR			; rotate selbit into carry
	JNC	R1		; again?
	MOV	A,C
	ADI	30H		; add ascii bias
	STAX	D		; store it in message
	STA	WPRMSG0
	LDA	STATUS
	ANI	WPR		; is disk protected?
	JZ	R2
	LXI	H,WPRMSG	; write protected message
	CALL	PRINT
	POP	H		; adjust the stack
R1A:	CALL	CONIN		; get response
	CPI	CTLC
	JZ	EXIT
	CPI	CR		; yes?
	JZ	EXEC		; if so
	CPI	ESC
	JNZ	R1A
	RET			; get out of here
R2:	LXI	D,TK		; in error message
	LDA	TRACK
	CALL	BINASC
	LXI	D,HD
	LDA	SIDE
	ORA	A		; set flags
	JZ	RPRT1
	MVI	A,1
RPRT1:	CALL	BINASC
	LXI	D,SC
	LDA	SECTOR
	CALL	BINASC
	LXI	D,ST
	LDA	STATUS
	ANI	RNF
	JNZ	SRNF
	LXI	H,CRCER
	CALL	MOV3
	JMP	RPRT2
SDNR:	LXI	H,DNRER
	CALL	MOV3
	JMP	RPRT2
SRNF:	LXI	H,RNFER
	CALL	MOV3
RPRT2:	LXI	H,ERRMSG
RPRT3:	JMP	PRINT		; print returns to caller

MOV3:	MVI	B,3
MOVL:	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCR	B
	JNZ	MOVL
	RET

BINASC:	PUSH	PSW		; save value
	PUSH	D		; save string ptr
	MVI	A,'0'		; ascii zero
	STAX	D
	LXI	B,2		; b=0, c=2
	XRA	A		; a=0
B0:	INX	D		; bump ptr
	STAX	D		; zero to str
	DCR	C
	JNZ	B0		; again please
	POP	D		; restore de
	LXI	H,TBL		; conversion table
B1:	MVI	C,0		; clear count
	MOV	A,M		; table value
	INR	A
	JZ	BEND		; end of table, conv done
	POP	PSW		; restore value
B3:	SUB	M		; begin division
	JC	B2
	INR	C		; keep count
	JMP	B3
B2:	ADD	M		; restore acc
	PUSH	PSW		; save it
	MOV	A,C		; count
	CPI	0
	JNZ	STSTR		; set ascii value in string
	MOV	A,B
	CPI	0		; 1st time?
	JNZ	STSTR
B4:	INX	H		; bump tbl ptr
	JMP	B1		; next

STSTR:	MVI	B,1		; flag 1st time
	MOV	A,C		; count to c
	ADI	30H		; ascii bias
	STAX	D		; write it into string
	INX	D		; bump str ptr
	JMP	B4

BEND:	POP	PSW		; adjust stack
	RET			; return to caller

TBL:	DB	100
	DB	10
	DB	1
	DB	255		; end of table

SETSEL:	inr	a
	MOV	C,A
	XRA	A
	STC
STSL:	RAL
	DCR	C
	JNZ	STSL
	ORI	ROMOFF
	STA	SELBYT
	RET

ERRMSG:
	DB	CR,LF,' Error:  Drive '
DR:	DB	0,':  Track '
TK:	DB	0,0,0,'  Head '
HD:	DB	0,0,0,'  Sector '
SC:	DB	0,0,0,'  Status  '
ST:	DB	0,0,0,CR,LF,'$'
TKMSG:	DB	'Track '
TKMSG0:	DB	0,0,0,'  Side '
SDMSG0:	DB	0,0,0,CR,'$'
WRMSG:	DB	'Writing $'
VFMSG:	DB	'Verify  $'
RDMSG:	DB	'Reading $'
FRMSG:	DB	'Format  $'
CPMSG:	DB	'Copy    $'
WPRMSG:	DB	CR,'                              '
	DB	CR,LF,'Remove write protect tab from destination diskette.'
	DB	CR,LF,'Place diskette in drive '
WPRMSG0:DB	'A, then press <RETURN> to write or <ESC> to quit.  $'
DNRER:	DB	'DNR'
RNFER:	DB	'RNF'
CRCER:	DB	'CRC'

TABLE:	DB	12,0		; defines sector format (#bytes,data)
	DB	3,0F5H		; write 0a1h
	DB	1,0FEH		; id address mark
	DB	1
TRACK:	DB	0
	DB	1
SIDE:	DB	0
	DB	1
SECTOR:	DB	1
	DB	1
SIZE:	DB	2		; always 512 bytes
	DB	1,0F7H		; write crc (2 bytes)
GAP2:	DB	22,4EH		; gap 2 length, data
	DB	12,0		; preamble
	DB	3,0F5H		; write 0a1h
	DB	1,0FBH		; data address mark
DAT0:	DB	128,0E5H	; 128 bytes of data fill
	DB	128,0E5H	; 128 bytes of data fill
	DB	128,0E5H	; 128 bytes of data fill
	DB	128,0E5H	; 128 bytes of data fill
	DB	1,0F7H		; write crc (2 bytes)
GAP3:	DB	24,4EH		; gap 3 length, data
	DW	-1		;  end of table (0ffffh)

SK1:	DW	SKW1		; point to skw1
SK2:	DW	SKW2		; 2

SKW1:	DB	1,6,2,7,3,8,4,9,5,10,255	; skew factor 2
SKW2:	DB	1,4,2,5,3,255			; skew 2 (Osborne)

FUNC:	DS	1		; function 1=format, 2=verify, 3=copy
MAXTRK:	DS	1		; maximum track
TWOSID:	DS	1
HEAD:	DB	0
SDRIV:	DS	1		; source drive
DDRIV:	DS	1		; destination drive
SELBYT:	DS	1
SBIAS:	DB	0		; sector bias  0=ss, 10h=ds, 30h=ds96
TPI96:
SKEW:	DB	0		; default skew factor 2
DMAADR:	DS	2
STATUS:	DS	1
TRIES:	DS	1
	DS	64		; space for 32 level stack
STACK:	DS	2		; space for ccp stack pointer
IDSV:	DS	6		; id save area
RDBUF:	DS	10 * 512	; read buffer
TKBUF:	END	START		; end of program

