	.title	'Harddisk to Floppy Backup Utility'
	.sbttl	'HARDBACK'
version ==	1
revision==	8	;last changed 05/06/83 DRB
patch	==	' '	
	.pabs
	.phex
	.loc	100h
;----------
; Harddisk Backup Utility
;
; Written by Peter Kavaler, February 1981
;
; 1.1  05/01/81 Initial Release
; 1.2  06/15/81 Minor Improvements
; 1.3  06/18/81 Handle Shugart Fixed heads
; 1.4  08/17/81 Use correct pre-comp when formatting
;		Retry floppy errors
; 1.5  06/10/82 use new cpmmap returned info about
;		multi hard disks and mini floppies
; 1.6  7/14/82	allow backing up of any of many
;		volumes (Les Wilson)
; 1.6A 04/28/83 fix multi-volume select for 46mb
;		backup (Mike Pompa)
; 1.6B 04/28/83 clean up user interface (MEP)
; 1.7  05/06/83 Bump up version number for release,
;		Add patch byte to login msg.  DRB
; 1.8  12/14/83 Provided integral HDSTAT so the BIOS
;		need not have HD drivers. (DSB)
;
;
true	== 0ffh
false	== 0
cr	==	0Dh	; carriage return
lf	==	0Ah	; linefeed
ctrlC	==	03h	; abort
ctrlS	==	13h	; skip to next partition
bell	==	07h	; bell
endcom	==	0FEh	; end of floppy command
DMA	==	38h	; DMA data port
FLOP	==	18h	; NEC765 DMA port
FLOPSR	==	10h	; NEC765 status port
FLOPDR	==	11h	; NEC765 data port
PIOAD	==	08h	; PIO channel A, data
PIOBD	==	09h	; PIO channel B, data
STOPFLOP==	03h	; NEC765 terminal count
HARDDATAPORT	==	01h	; hard disk data port
RESETHARD==	00h	; reset hard disk
statHARD==	18h	; hd status req command
onprecmp=	22	; turn on pre-compensation
maxprec =	59	; turn off S/L bit
stepset =	900h	; head step settle time = 15ms
RxRDY	=	0	; receiver ready bit
TxRDY	=	2	; transmitter ready bit
WBOOT	=	1	; for bios calls
SIO1AC	=	2Ah	; SIO-1 channel A, control
SIO1AD	=	28h	; SIO-1 channel A, data -port 0
SETMAP	=	03h	; Set memory map register
;
;	device types as returned by cpmmap
;
sd	==	0<5	;single density 8 inch floppy
dd	==	1<5	;double dens 8 inch flop
hard	==	2<5	;hard disk map
net	==	3<5	;network map
mini1	==	4<5	;1 sided mini(5inch) floppy
mini2	==	5<5	;2 sided mini floppy

.insert volinfo

	.page
;----------
; Greet the user
	lxi	SP,stack
	lxi	H,hello$
	call	PRTMSG
;
; Setup DMA vector at base of BIOS
	lhld	1
	mvi	L,0
	mvi	M,DMAdone&0FFh
	inx	H
	mvi	M,(DMAdone>8)&0FFh
;	
; Ask user that he/she wants to do
menu:
	lxi	H,select$
	call	PRTMSG
..ask:	call	CONIN
	mov	C,A
	call	CONOUT
	cpi	cr
	jz	term	;terminate if RETURN
	cpi	ctrlC
	jz	term	; warm boot if ctrl-C
	sui	'0'
	cpi	4
	jz	BFORMAT ; check if format option
	jc	..ok
	lxi	h,error$
	call	prtmsg
	jmpr	..ask
..ok:
	push	PSW	; save user's selection
;----------		
;	get disk size from hdstat
;
	lxi	B,statbuf
	call	hdstat
..tellpresent:
	lxi	H,mbf1		;tell them which volumes are present
	call	prtmsg
	xra	A		;start at zero
..volinfoloop:
	sta	volnum
	call	setvolptr
	mov	A,vlipresent(x)
	cpi	true
	jrnz	..nextvol
	lxi	H,crlf$
	call	prtmsg
	lda	volnum
	call	prtbyt		;print volume number
	call	prtvol		;print volume label
..nextvol:
	lda	volnum
	inr	a
	cpi	4
	jrnz	..volinforloop
;
;	ask which vol they want to work with
;
..whichvol:
	lxi	h,mbf2
	call	prtmsg
	call	conin
	mov	c,a
	call	conout	;echo answer
	cpi	ctrlc
	jz	0	;warm boot 
	sui	'0'
	jrc	..badans
	cpi	4
	jrc	..goodans
..badans:
	lxi	h,mbf3
	call	prtmsg
	jmpr	..whichvol
..goodans:
	sta	volnum
	call	setvolptr
	mov	a,vlipresent(x)
	cpi	true
	jrz	..ispresent
	lxi	h,mbf4
	call	prtmsg
	jmp	..tellpresent
..ispresent:


; Get disk size from hard stat
	mov	a,vlitracks(x)
	sui	3	;the magic number to ignore
			;bad sector tracks
	sta	maxtrk
	mov	a,vlitype(x)	;sectors/track
	sta	maxsec
	mov	a,vlisize(x)	;head mask
	ani	7		;allow 8 heads per 
				;track max
	sta	maxhead
;
;	do a CP/M drive select so all further
;	talking goes to that volume
;
	mvi	a,0		;partition # is unimportant
	sta	hardcom + 1	;but give valid one anyway
	lda	volnum		;store vol in command
	sta	hardcom + 2	
	mvi	a,'M'		;allow multi volumes
	sta	hardcom+6	;(which vol is in byte 2)
	mvi	a,13h		;CP/M drive select cmd
	call	cmdhard
	
;			
; Compute THS of bad sector table
..ths:
	lxi	H,17
	call	CVTblk
	lxi	H,HARDtrk
	lxi	D,BADtrk
	lxi	B,3
	ldir		; save THS of bad sector table
;----------	
	pop	PSW	; restore user's selection
	mov	C,A
	ani	1	; bit 0 = 1 if double sided
	sta	MAXside
	mov	A,C
	srlr	A
	jz	GObackup; backup from disk to floppy
	jmp	GOload	; load from floppy to disk
;----------
term:
	lda	bkdisk	;backup disk loaded?
	ora	A
	jz	0	;no, warm boot
	lxi	h,unload$ ;tell them unload backup
	call	prtmsg
	call	conin	;Wait for any character
	jmp	0	;warm boot
	.page
;----------
; Backup from disk to floppy
GObackup:
	lxi	H,date$
	call	PRTMSG
	call	INSTRING; get date
	lxi	H,conbuf
	lxi	D,date%
	lxi	B,8
	ldir		; save date for later
	lxi	H,time$
	call	PRTMSG
	call	INSTRING; get time
	lxi	H,conbuf
	lxi	D,time%
	lxi	B,5
	ldir		; save time for later
	jmp	FIRSTbackup ;do first backup msg
NEXTbackup:
	lxi	H,npart$ ;prompt next partition
	call	PRTMSG
	call	INSTRING ;get partition name
	lda	conbuf-1 ;check for RETURN
	ora	A
	jz	menu	;go to main menu if so
	jmpr	pback	;else do partition
FIRSTbackup:
	lxi	H,part$
	call	PRTMSG
	call	INSTRING; get partition name
	lda	conbuf-1
	ora	A
	jrz	fullback
pback:
	lxi	H,conbuf
	lxi	D,part%
	lxi	B,8
	ldir		; save partition name for later
;----------
; Backup a single partition
	call	ALLOC	; read allocation table
	call	FINDPART; find partition name
	ora	A
	jrnz	..foundpart
	lxi	H,nopart$
	call	PRTMSG	; tell the user he goofed
	jmpr	NEXTbackup
..foundpart:
	call	PARTBACK
	jmpr	NEXTbackup
;----------
; Backup entire disk (start each partition on new disk)
fullback:
	call	ALLOC	; read the ALLOC table
	lxi	H,ALLOCtab+32
..next: mov	A,M
	cpi	0FFh	; check for end of table
	jz	menu	; return to main menu
	push	H
	ora	A
	lxi	B,16
	dsbc	B
	push	H
	inx	H
	lxi	D,part%
	lxi	B,8
	ldir
	lxi	H,part%
	lxi	D,backpart
	lxi	B,8
	ldir
	lxi	H,backing$
	call	PRTMSG
	pop	H
	call	PARTBACK
	pop	H
	lxi	B,16
	dad	B
	jmpr	..next
;----------
; Partition backup routine
PARTBACK:
	mov	A,M
	push	PSW	; save starting block #
	lxi	B,16
	dad	B
;
; Compute size
	mov	B,A	; starting block
	mov	A,M	; ending block
	sub	B	; size = multiples of 256
	sta	realsize
	push	H	; save pointer to ending block
	lxi	H,SIZEtab
	mvi	B,6
..look: cmp	M
	jrz	..ok
	lxi	D,6
	dad	D
	djnz	..look
	rst	6	; internal error
..ok:	inx	H
	lxi	D,size%
	lxi	B,5
	ldir
	pop	H	; restore pointer to ending blk
	mov	H,M	; ending block #
	mvi	L,0
	call	CVTBLK	; compute ending THS
	lxi	H,HARDtrk
	lxi	D,endtrk
	lxi	B,3
	ldir
	pop	PSW	; restore starting block #
	mov	H,A
	mvi	L,1
	call	CVTblk	; compute starting THS
	call	BACKUP
	call	offMOTOR; unload floppy head
	ret
	.page
;----------
; Backup from current THS to ending THS
BACKUP: 
	lda	realsize
	mov	H,A
	mvi	L,0	; HL = 1K block count
	lxi	B,608	; 608K on single-sided diskette
	lda	MAXside
	ora	A
	jrz	..size
	lxi	B,1216	; 1216K on double-sided disk
..size: call	DIVIDE	; compute number of diskettes
	mov	A,E
	inr	A
	call	CVTDEC	; convert to decimal ASCII
	shld	last%
	shld	last
	lxi	H,disks$
	call	PRTMSG	; tell how many disks needed
	sub	A
	sta	count
BACKgo: 
	lda	count
	inr	A
	sta	count
	sta	realcount
	call	CVTDEC
	shld	count%
	lxi	H,fresh$
	call	WAITCR	; mount fresh diskette
	cpi	ctrlS
	rz
	mvi	1,A	;backup disk loaded,
	sta	bkdisk	;set flag
	call	HOME	; home to track 0
	call	WRITE0	; write info to track 0
	mvi	A,1
	sta	FLOPtrk
;
; Read 8K from the hard disk
..copy: call	SEEK	; position head over floppy
	lxi	H,iobuff
	mvi	D,8	; read 8 times
..read: push	H
	push	D
	call	READh	; read 1K sector from harddisk
	call	NEXTh
	pop	D
	pop	H
	ora	A
	jrnz	..end
	lxi	B,1024
	dad	B	; increment buffer address
	dcr	D	; loop until 8 sectors read
	jrnz	..read
;
; Write 8K to the floppy disk
..end:	sta	endflg	; non-zero if end of disk
	call	SENSE	; get seek status
	cnz	IOERR	; error if diskette removed
..write:call	WRITEf	; write track to floppy
	jrnz	..retry
	call	SCANf	; read track from floppy
	jrz	..ok
..retry:
	lxi	H,retry$
	call	PRTMSG	; tell user we will retry
	jmpr	..write ; retry if error
;
; Increment to next floppy track until done
..ok:	mvi	C,'.'
	call	CONOUT	; indicate track was OK
	lda	endflg
	ora	A
	jrnz	..done
	call	NEXTf
	ora	A
	jrnz	..copy
	call	offMOTOR; unload the head
	jmpr	BACKgo
;
; Finished
..done: lxi	H,done$
	call	PRTMSG
	ret
	.page
;----------
; Load from floppy to disk
GOload:
	call	LOAD
	call	offMOTOR; unload floppy head
	jmpr	GOload	; do another partition
;----------
; Process first diskette
LOAD:	
	mvi	A,1
	sta	count
	lxi	H,newload$
	call	WAITCR	; mount first diskette
	cpi	ctrlS	; if control-s
	jz	menu	; return to main menu
	cpi	cr
	jrnz	LOAD	; wait for RETURN
	mvi	a,1	;backup disk loaded,
	sta	bkdisk	;so set flag
	call	HOME	; home to track 0
	call	READ0	; read info from track 0
	lxi	H,info%
	call	PRTMSG	; print info from track 0
	lxi	H,part%
	lxi	D,curpart
	lxi	B,8
	ldir
;
; Make sure the partition is in the ALLOC table
	call	ALLOC
	call	FINDPART
	ora	A
	jrnz	..intable
	lxi	H,nopart$
	call	PRTMSG	; partition not in ALLOC table
	ret
;
; Compute beginning and ending THS
..intable:
	mov	A,M
	push	PSW	; save starting block #
	lxi	B,16
	dad	B
	mov	B,A	; starting block #
	mov	A,M	; ending block #
	sub	B	; compute size
	sta	loadsize
	mov	H,M	; ending block #
	mvi	L,0
	call	CVTblk	; compute ending THS
	lxi	H,HARDtrk
	lxi	D,endtrk
	lxi	B,3
	ldir
	pop	PSW	; restore starting block #
	mov	H,A
	mvi	L,1
	call	CVTblk	; compute starting THS
	jmpr	check
;----------
; Process next diskette
LOADgo:
	lxi	H,count
	inr	M
LOADnext:
	call	offMOTOR; unload head
	lxi	H,fresh$
	call	WAITCR	; mount fresh diskette
	cpi	ctrlS
	rz		; abort if ctrl-S
	call	HOME	; home to track 0
	call	READ0	; read info from track 0
	lxi	H,info%
	call	PRTMSG	; print info from track 0
;
; Check whether partition name matches
	lxi	H,part%
	lxi	D,curpart
	mvi	B,8
..comp: ldax	D
	cmp	M
	jrz	..same
	lxi	H,diff$ ; different partition names
	call	PRTMSG
	jmpr	LOADnext
..same: inx	H
	inx	D
	djnz	..comp
;
; Check if partition size matches
check:
	lda	realsize
	lxi	H,loadsize
	cmp	M	; compare partition sizes
	jrz	..ok2
	lxi	H,badsize$
	call	PRTMSG	; size doesn't compare
	jmp	LOADnext
;
; Check if diskette number matches
..ok2:
	lda	realcount
	lxi	H,count
	cmp	M	; compare diskette count
	jrz	..ok3
	lxi	H,badcount$
	call	PRTMSG
	jmp	LOADnext
;
; Ask user to verify as well
..ok3:
	lxi	H,verify$
	call	WAITCR	; ask user if OK to continue
	cpi	ctrlS
	jrnz	..ok4
	lda	count
	cpi	1
	rz		; if first diskette, abort
	jmp	LOADnext; if not first, let user retry
..ok4:	mvi	A,1
	sta	FLOPtrk
;
; Read 8K from floppy
..copy: call	SEEK
	call	SENSE
	cnz	IOERR	; floppy removed during copy
	call	READf
	jrz	..ok5
	lxi	H,retry$
	call	PRTMSG	; tell user we will retry
	jmpr	..copy	; retry if error
;
; Write 8K to harddisk
..ok5:	lxi	H,iobuff
	mvi	D,8	; make 8 passes, 1K per pass
..write:push	H
	push	D
	call	WRITEh
	call	NEXTh
	pop	D
	pop	H
	ora	A
	jrnz	..done
	lxi	B,1024
	dad	B	; increment buffer address
	dcr	D
	jrnz	..write
;
; Increment to next floppy track
	mvi	C,'.'
	call	CONOUT
	call	NEXTf
	ora	A
	jrnz	..copy
	jmp	LOADgo
;
; Finished
..done: 
	lxi	H,done$
	call	PRTMSG
	ret
	.page
;----------
; Read the allocation table and compute beginning
; block numbers
ALLOC:
	lxi	H,16
	call	CVTBLK	; compute track, head, sector
	lxi	H,ALLOCtab
	call	READh	; read allocation table
	lxi	H,ALLOCtab
	lxi	D,16
	mvi	B,0	; B = cumulative block count
..alloc:
	mov	A,M	; get partition size
	mov	M,B	; replace with starting block #
	ora	A
	jrz	..end	; jump if at end of table
	stc
	mvi	C,0	; compute C = partition blocks
..rot:	ralr	C
	dcr	A
	jrnz	..rot
	mov	A,B
	add	C	; increment total block count
	mov	B,A
	dad	D	; move to next partition
	jmpr	..alloc
..end:	dad	D
	mvi	M,0FFh	; mark end-of-table
	ret
;----------
; Search ALLOC table for partition name match
;  Regs out:  A  = 0 if partition not found
;	      A  = 0FFh if partition found
;	      HL = address of ALLOC entry
FINDPART:
	lxi	H,ALLOCtab
..next: mov	A,M	; check for end-of-table
	inr	A
	rz
	inx	H
	lxi	D,part%
	lxi	B,8<8+0FFh; compare 8 bytes
..cmp:	ldax	D
	cmp	M
	jrz	..same
	mvi	C,0	; set "not equal" flag
..same: inx	D
	inx	H
	djnz	..cmp
	mov	A,C
	ora	A	; check "not equal" flag
	jrnz	..equal
;
; Match not found, so continue searching
	lxi	B,7	; move to next entry
	dad	B
	jmpr	..next
;
; Match found, so return entry address
..equal:
	lxi	B,9
	dsbc	B
	ret
;----------
; Convert block number to track, head, sector
;  Regs in:    HL = block number
;  Destroyed:  All
CVTblk:
	lda	MAXsect
	mvi	B,0
	mov	C,A
	call	DIVIDE	; compute blockno/sects
	mov	A,L	; sector = remainder
	ora	A
	jrnz	..ok
	lda	MAXsect
	dcx	D
..ok:	sta	HARDsec
	lda	MAXhead
	mov	C,A
	ana	E	; head = low bits of result
	sta	HARDhead
	xchg
	mvi	B,0
	inr	C
	call	DIVIDE	; compute track
	mov	A,E
	sta	HARDtrk
	ret
	.page
;----------
; Write a track on the floppy
WRITEf:
	sub	A
	out	PIOAD	; multiplex DMA to NEC765
	lxi	H,iobuff
	shld	wrfadr
	lxi	H,lenbuff-1
	shld	wrflen
	lxi	H,DMAwrf
	lxi	B,lenwrf<8+DMA
	outir		; program the DMA chip
	mvi	A,0FFh
	sta	FLOPcrap; set DTL in write command
	lxi	H,FLOPcom
	mvi	M,wrDDFLOP
	jmp	FLOPop
;----------
; Read a track on the floppy
READf:
	sub	A
	out	PIOAD	; multiplex DMA to NEC765
	lxi	H,iobuff
	shld	rdfadr
	lxi	H,lenbuff-1
	shld	rdflen
	lxi	H,DMArdf
	lxi	B,lenrdf<8+DMA
	outir		; program the DMA chip
	mvi	A,0FFh
	sta	FLOPcrap; set DTL in read command
	lxi	H,FLOPcom
	mvi	M,rdDDFLOP
	jmp	FLOPop
;----------
; Scan a track on the floppy
SCANf:
	sub	A
	out	PIOAD	; multiplex DMA to NEC765
	lxi	H,iobuff
	shld	wrfadr
	lxi	H,lenbuff; give NEC765 one more byte
	shld	wrflen	 ; than it really needs
	lxi	H,DMAwrf ; (stupid, but necessary)
	lxi	B,lenwrf<8+DMA
	outir
	mvi	A,1
	sta	FLOPcrap; set STP in scan command
	lxi	H,FLOPcom
	mvi	M,scanFLOP
	call	FLOPop
	rnz
	lda	FLOPstat+2
	bit	2,A
COMPerr:cnz	IOERR	; scan failed
	ret
;----------
; Compute next floppy track and side
;  Regs out:   A = 0 if beyond last track
NEXTf:
	lxi	H,FLOPsid
	lda	MAXside
	cmp	M
	jrnz	..inr
	mvi	M,0
	dcx	H
	mvi	A,76
	sub	M
	rz
..inr:	inr	M
	ret
;----------
; Write to track 0, sector 3
WRITE0:
	sub	A
	out	PIOAD	; multiplex DMA to NEC765
	lxi	H,info%
	shld	wrfadr
	lxi	H,127
	shld	wrflen
	lxi	H,DMAwrf
	lxi	B,lenwrf<8+DMA
	outir
	lxi	H,FLOP0
	mvi	M,wrSDFLOP
	jmpr	FLOPop
;----------
; Read from track 0, sector 3
READ0:
	sub	A
	out	PIOAD	; multiplex DMA to NEC765
	lxi	H,info%
	shld	rdfadr
	lxi	H,127
	shld	rdflen
	lxi	H,DMArdf
	lxi	B,lenrdf<8+DMA
	outir
	lxi	H,FLOP0
	mvi	M,rdSDFLOP
	jmpr	FLOPop
;----------
; Process a floppy operation
FLOPop:
	lda	FLOPsid
	slar	A
	slar	A
	sta	FLOPdsk
	call	COMMAND ; program the NEC765 chip
	call	RESULT	; get result status from floppy
	lda	FLOPstat
	ani	0C0h	; Z flag = 0 if no error
	rz		; return if no errors
;
; Check for any errors
	lxi	B,FLOPstat+3; point to THS
	lda	FLOPstat+2
	bit	5,A
DATAerr:cnz	IOERR	; data CRC error
	bit	4,A	
TRACerr:cnz	IOERR	; on wrong track
	bit	0,A
MADRerr:cnz	IOERR	; missing address mark
	lda	FLOPstat+1
	bit	7,A
ENDTerr:cnz	IOERR	; read beyond end-of-track
	bit	5,A
IDerr:	cnz	IOERR	; ID CRC error
	bit	4,A
ORUNerr:cnz	IOERR	; overrun (DMA failure)
	bit	2,A
SECTerr:cnz	IOERR	; cannot find sector
	bit	1,A
PROTerr:cnz	IOERR	; write-protected
	bit	0,A
DENSerr:cnz	IOERR	; missing ID address mark
	call	IOERR	; unknown error
	.page
;----------
; Home the floppy to track 0
HOME:
	sub	A	; select floppy drive 0
	call	onMOTOR
	lxi	B,1500h ; wait 35ms for head settle
	call	DELAY
..home: lxi	H,homeFLOP
	call	SEEKER
	call	SENSE
	jrnz	..home	; rehome if drive not ready
	ret
;----------
; Seek a track
SEEK:
	lda	FLOPtrk
	sta	seekFLOP+2
	lxi	H,seekFLOP
SEEKER:
	sub	A	; select floppy drive 0
	call	onMOTOR ; load the head
	jmp	COMMAND ; issue the seek command
;----------
; Wait for seek complete
SENSE:
	mvi	B,0	; clear seek flag
..sense:lxi	H,senseFLOP
	call	COMMAND
	call	RESULT
	lda	FLOPstat
	bit	3,A	; retry if not ready
	rnz
	bit	5,A	; check for seek complete
	jrnz	..done
	mvi	B,1	; set seek flag
	jmpr	..sense
..done: mov	A,B
	ora	A	; no need to delay ...
	rz		; if already over track
	lxi	B,stepset; delay 15ms for step settle
;----------
; Delay loop
;  Regs in:   BC = delay time
;  Regs out:  none
;  Destroyed: A, BC
DELAY:	dcx	B
	mov	A,B
	ora	C
	jrnz	DELAY
	ret
;----------
; Load floppy head
;  Regs in:   A = floppy drive number
;  Regs out:  none
;  Destroyed: A, B
onMOTOR:
	mov	B,A
	set	2,B	; turn on head load bit
	lda	FLOPtrk
	cpi	onprec	; small precomp tracks 22-58
	jrc	..load
	set	5,B
	cpi	maxprec ; large precomp tracks 59-76
	jrnc	..load
	set	4,B
..load: mov	A,B
	out	PIOBD
	ret
;----------
; Unload floppy head
offMOTOR:
	sub	A
	out	PIOBD
	ret
;----------
; Send a command to the floppy controller
;  Regs in:   HL = address of command string
;  Regs out:  none
;  Destroyed: A, HL
COMMAND:
	mov	A,M
	cpi	endcom
	rz
	call	WAITDR
SYNCerr:cc	IOERR
	mov	A,M
	out	FLOPDR
	inx	H
	jmpr	COMMAND
;----------
; Get result status from the floppy controller
;  Regs in:   none
;  Regs out:  none
;  Destroyed: A, HL
RESULT:
	lxi	H,FLOPstat
..loop: call	WAITDR
	rnc
	mvi	A,0C3h
	out	DMA
	in	FLOPDR
	mov	M,A
	inx	H
	jmpr	..loop
;----------
; Wait for floppy data register ready
;  Regs in:   none
;  Regs out:  A = status register, shifted left by 1
WAITDR:
	in	FLOPsr
	rlc
	jrnc	WAITDR
	rlc
	ret
	.page
;----------
; Read 1K from the harddisk
;  Regs in:   HL = Input buffer address
;  Regs out:  A  = nonzero if disk error
READh:
	push	H
	mvi	A,rdHARD
	call	CMDHARD
	call	RESHARD
	pop	H
	lxi	B,1024	; read 1K
	call	RECHARD
	sub	A	; return OK status
	ret	
;----------
; Write 1K to the harddisk
;  Regs in:   HL = Input buffer address
;  Regs out:  A  = nonzero if disk error
WRITEh:
	push	H
	mvi	A,wrHARD
	call	CMDHARD
	pop	H
	lxi	B,1024	; write 1K
	call	SENDHARD
	call	RESHARD
	sub	A	; return OK status
	ret
;----------
; Compute next track, head, sector
;  Regs out:  A = nonzero if beyond last sector
NEXTh:
	lxi	H,HARDsec
	lda	MAXsect
	cmp	M
	jrnz	..inr	; jump to increment sector
	mvi	M,1	; reset to sector 1
	dcx	H
	lda	MAXhead
	cmp	M
	jrnz	..inr	; jump to increment head
	mvi	M,0	; reset to head 0
	dcx	H
..inr:	inr	M	; increment track, head, sector
;
; Check whether beyond last sector
	lxi	H,HARDtrk
	lda	ENDtrk
	sub	M
	rc		; return if track too large
	jrnz	..ok
	inx	H
	lda	ENDhead
	sub	M
	rc		; return if head too large
	jrnz	..ok
	inx	H
	lda	ENDsect
	sub	M
	rc		; return if sector too large
;
; Skip bad sector table
..ok:	lxi	H,HARDtrk
	lda	BADtrk
	sub	M
	jrnz	..ret
	inx	H
	lda	BADhead
	sub	M
	jrnz	..ret
	inx	H
	lda	BADsect
	sub	M
	jrz	NEXTh
..ret:	sub	A	; return OK status
	ret
	.page
;----------
; Send a block to the harddisk controller
;  Regs in:   HL = block address
;	      BC = byte count
;  Regs out:  none
;  Destroyed: all
SENDHARD:
	mov	A,B
	ora	A
	jrnz	..SENDmany
;
; Send fewer than 256 bytes, so use status loop
	mov	B,C
	mvi	C,HARDDATAPORT
..wait: in	PIOAD
	bit	3,A
	jrnz	..wait
	outi
	jrnz	..wait
	ret
;
; Send more than 256 bytes, so use DMA chip
..SENDmany:
	shld	wrhadr	; DMA address
	dcx	B
	sbcd	wrhlen	; DMA length - 1
	mvi	A,4	
	out	PIOAD	; multiplex DMA to harddisk
	lxi	H,DMAwrh
	lxi	B,lenwrh<8+DMA
	outir		; program DMA chip
	jmp	WAITDMA ; wait for DMA completion
;----------
; Receive a block from the harddisk controller
;  Regs in:   HL = block address
;	      BC = byte count
;  Regs out:  none
RECHARD:
	mov	A,B
	ora	A
	jrnz	..RECmany
;
; Receive fewer than 256 bytes, so use status loop
	mov	B,C
	mvi	C,HARDDATAPORT
..wait: in	PIOAD
	bit	4,A
	jrz	..wait
	ini
	jrnz	..wait
	ret
;
; Receive more than 256 bytes, so use DMA chip
..RECmany:
	shld	rdhadr	; DMA address
	dcx	B
	sbcd	rdhlen	; DMA length - 1
	mvi	A,5	
	out	PIOAD	; multiplex DMA to harddisk
	lxi	H,DMArdh
	lxi	B,lenrdh<8+DMA
	outir		; program DMA chip
	jmp	WAITDMA ; wait for DMA completion
;----------
; Send a command to the harddisk controller
;  Regs in:   A = command byte
;  Regs out:  none
;  Destroyed: all
CMDHARD:
	sta	HARDcom
..flush:in	HARDDATAPORT
	mvi	A,51h	; "request to send"
	out	HARDDATAPORT
..wait: in	PIOAD	; wait for response
	bit	4,A
	jrz	..wait
	in	HARDDATAPORT
	cpi	52h
	jrnz	..flush ; retry if not "clear to send"
	lxi	H,HARDcom
	lxi	B,8
	jmpr	SENDHARD; send command
;----------
; Receive status from the harddisk controller
;  Regs in:   none
;  Regs out:  none
;  Destroyed: all
RESHARD:
	lxi	H,HARDstat
	lxi	B,8
	call	RECHARD
	lda	HARDstat+7
	ora	A
	rz
	lxi	B,HARDstat+1; point to THS
	bit	7,A
HARCerr:cnz	IOERR	; command error
	bit	6,A
HARDerr:cnz	IOERR	; data error
	bit	5,A
HARSerr:cnz	IOERR	; sector error
	bit	4,A
HARFerr:cnz	IOERR	; fault error
	call	IOERR	; unknown error
	ret
	.page
;----------
; Wait for an interrupt from the DMA chip
WAITDMA:
	sub	A
	sta	DMAflag
	ei		; make sure interrupts enabled
..wait: lda	DMAflag
	ora	A
	jrz	..wait
	ret
;----------
; Process an interrupt from the DMA chip
DMAflag:.byte	0	; non-zero signals DMA complete
DMAdone:
	push	PSW
	in	STOPFLOP; turn off floppy controller
	mvi	A,0C3h
	out	DMA	; reset the DMA chip
	sta	DMAflag ; signal DMA completion
	pop	PSW
	ei
	ret
;----------
; Convert binary to ASCII
;  Reg in:   A	= number
;  Reg out:  HL = ASCII representation (00-99)
CVTDEC:
	ora	A
	jrz	..ok
	mov	B,A
	sub	A
..adj:	inr	A
	daa
	djnz	..adj
..ok:	push	PSW
	rlc
	rlc
	rlc
	rlc
	call	..nib
	mov	L,A
	pop	PSW
	call	..nib
	mov	H,A
	ret
..nib:	ani	0Fh
	adi	'0'
	cpi	'9'+1
	rc
	adi	'A'-('9'+1)
	ret
;----------
; Divide HL by BC, put result in DE and remainder in HL
;  (Use a very simple algorithm - speed isnt important)
DIVIDE:
	lxi	D,0
	ora	A
..div:	dsbc	B	; subtract BC from HL
	jrc	..ret
	inx	D	; increment result each pass
	rz
	jmpr	..div
..ret:	dad	B	; compute correct remainder
	ret
;----------
; Process an I/O error
;
ermsg1: .asciz	[bell][cr][lf]'*** '
ermsg2: .asciz	' error T'
hmsg:	.asciz	' H'
smsg:	.asciz	' S'
IOERR:
	pop	D	; save address of caller
	push	B	; save pointer to THS
	dcx	D
	dcx	D
	dcx	D
;
; Search for error in table
	lxi	H,errtab
	mvi	B,numerr
..look: mov	A,M
	cmp	E
	inx	H
	mov	A,M
	inx	H
	jrnz	..again
	cmp	D
	jrz	..found
..again:inx	H
	inx	H
	inx	H
	inx	H
	djnz	..look
;
; Print an error message
..found:push	H	; save address of error message
	lxi	H,ermsg1; print heading message
	call	PRTMSG
	pop	H
	mvi	B,4	
..prt:	mov	C,M
	push	H
	call	CONOUT	; print 4 letter error code
	pop	H
	inx	H
	djnz	..prt
	lxi	H,ermsg2
	call	PRTMSG	; print trailing message
;
; Print track, head, sector of error
	pop	B
	ldax	B
	push	B
	call	PRTBYT	; print track
	lxi	H,hmsg
	call	PRTMSG
	pop	B
	inx	B
	ldax	B
	push	B
	call	PRTBYT	; print head
	lxi	H,smsg
	call	PRTMSG
	pop	B
	inx	B
	ldax	B
	call	PRTBYT	; print sector
	sub	A
	inr	A
	ret
;----------
; Error table
errtab:
	.word	SYNCerr ; FDC out of sync with CPU
	.ascii	'SYNC'
	.word	DATAerr ; DATA CRC error
	.ascii	'DATA'
	.word	TRACerr ; head positioned on wrong trk
	.ascii	'TRAC'
	.word	ENDTerr ; access beyond end-of-track
	.ascii	'ENDT'
	.word	IDerr	; ID CRC error
	.ascii	' ID '
	.word	ORUNerr ; FDC not service in time
	.ascii	'ORUN'
	.word	SECTerr ; sector not found
	.ascii	'SECT'
	.word	PROTerr ; disk write-protected
	.ascii	'PROT'
	.word	DENSerr ; missing ID address mark
	.ascii	'DENS'
	.word	MADRerr ; missing DATA address mark
	.ascii	'MADR'
	.word	COMPerr ; backup compare error
	.ascii	'COMP'
	.word	HARDerr ; hard disk data error
	.ascii	'HARD'
	.word	HARCerr ; hard disk command error
	.ascii	'HARC'
	.word	HARSerr ; hard disk sector error
	.ascii	'HARS'
	.word	HARFerr ; hard disk fault
	.ascii	'HARF'
numerr	=	(.-errtab)/6
	.ascii	'I/O '
;----------
; Wait for carriage return
;  Regs in:   HL = message to print before waiting
;  Regs out:  A  = cr or cntl-C
;  Destroyed: all
WAITCR:
	call	PRTMSG
..wait: call	CONIN
	cpi	ctrlS	; wait for control-S
	rz
	cpi	ctrlC	; or control-C
	jz	0	;  (warm-boot if ctrl-C)
	cpi	cr	; or carriage-return
	jrnz	..wait
	lxi	H,crlf$
	call	PRTMSG
	mvi	A,cr
	ret
;----------
; Print a message on the console
;  Regs in:   HL = address of message
;  Regs out:  none
;  Destroyed: all
PRTMSG:
	mov	A,M
	ora	A
	rz
	mov	C,A
	push	H
	call	CONOUT
	pop	H
	inx	H
	jmpr	PRTMSG
;----------
; Print a byte in hex format
;  Regs in:   A = byte to be printed
PRTBYT:
	push	PSW
	rrc
	rrc
	rrc
	rrc
	call	..nib	; print high nibble
	pop	PSW
..nib:	ani	0Fh
	adi	'0'
	cpi	'9'+1
	jrc	..out
	adi	'A'-('9'+1)
..out:	mov	C,A
	jmp	CONOUT
;----------
; Input a string from the console
;
	.byte	lencon,0
conbuf: .blkb	10	; console buffer
lencon	==	.-conbuf; length of console buffer
INSTRING:
	lxi	H,conbuf
	mvi	M," "
	lxi	D,conbuf+1
	lxi	B,lencon-1
	ldir		; blank-fill console buffer
	lxi	D,conbuf-2
	mvi	C,10
	call	5	; ask the BDOS to do the work
	lda	conbuf-1
	ora	A
	rz
;
; Convert lower to upper case
	mov	B,A	; B = number of characters
	lxi	H,conbuf
..cvt:	mov	A,M
	cpi	'a'
	jrc	..next
	cpi	'z'+1
	jrnc	..next
	sui	'a'-'A'
	mov	M,A
..next: inx	H
	djnz	..cvt
	ret
;----------
; Output a character to the console
;  Regs in:   C = character
;  Regs out:  none
;  Destroyed: all
CONOUT:
	lhld	1
	lxi	D,09h
	dad	D
	pchl		; jump directly to the BIOS
;----------
; Input a character from the console
;  Regs in:   none
;  Regs out:  A = character
;  Destroyed: all
CONIN:
	lhld	1
	lxi	D,06h
	dad	D
	pchl		; jump directly to the BIOS
	.page
;----------
; DMA commands
;
; Read 8K from floppy to memory
DMArdf:
	.byte	0C3h
	.byte	0C7h
	.byte	0CBh
	.byte	7Dh
rdfadr: .word	0	; DMA address
rdflen: .word	0	; DMA length - 1
	.byte	14h
	.byte	28h
	.byte	95h
	.byte	FLOP
	.byte	12h
	.byte	0	; DMA vector
	.byte	9Ah
	.byte	0CFh
	.byte	1
	.byte	0CFh
	.byte	0ABh
	.byte	87h
lenrdf	==	.-DMArdf
;----------
; Write 8K from memory to floppy
DMAwrf:
	.byte	0C3h
	.byte	0C7h
	.byte	0CBh
	.byte	79h
wrfadr: .word	0	; DMA address
wrflen: .word	0	; DMA length - 1
	.byte	14h
	.byte	28h
	.byte	95h
	.byte	FLOP
	.byte	12h
	.byte	0	; DMA vector
	.byte	9Ah
	.byte	0CFh
	.byte	5
	.byte	0CFh
	.byte	0ABh
	.byte	87h
lenwrf	==	.-DMAwrf
	.page
;----------
; Read 1K from harddisk to memory
DMArdh:
	.byte	0C3h
	.byte	0C7h
	.byte	0CBh
	.byte	7Dh
rdhadr: .word	0	; DMA address
rdhlen: .word	0	; DMA length - 1
	.byte	14h
	.byte	28h
	.byte	95h
	.byte	HARDDATAPORT
	.byte	12h
	.byte	0	; DMA vector
	.byte	9Ah
	.byte	0CFh
	.byte	1
	.byte	0CFh
	.byte	0ABh
	.byte	87h
lenrdh	==	.-DMArdh
;----------
; Write 1K from memory to harddisk
DMAwrh:
	.byte	0C3h
	.byte	0C7h
	.byte	0CBh
	.byte	79h
wrhadr: .word	0	; DMA address
wrhlen: .word	0	; DMA length - 1
	.byte	14h
	.byte	28h
	.byte	95h
	.byte	HARDDATAPORT
	.byte	12h
	.byte	0	; DMA vector
	.byte	92h
	.byte	0CFh
	.byte	5
	.byte	0CFh
	.byte	0ABh
	.byte	87h
lenwrh	==	.-DMAwrh
	.page
;----------
; Messages

mbf1:	.asciz	[cr][lf]'Volumes present are:'

mbf2:	.asciz	[cr][lf]'Which volume do you wish to work with(0-3):'

mbf3:	.asciz	[cr][lf]'Answer must be 0,1,2 or 3.'

mbf4:	.asciz	[cr][lf]'Volume is not present.'

hello$: .ascii	[cr][lf]'Harddisk to Floppy '
	.ascii	'Backup Utility '
	.byte	version+'0','.',revision+'0'
	.byte	patch
	.ascii	[cr][lf][lf]
	.ascii	'(To abort, enter CTRL-C)'
	.byte	0
badctl$:.ascii	[cr][lf][lf]
	.asciz	'Bad controller program'

select$:.ascii	[cr][lf][lf]
	.ascii	'Select one of the following options:'
	.ascii	[cr][lf]
	.ascii	[cr][lf]
	.ascii	' 0 = backup on single-sided diskettes'
	.ascii	[cr][lf]
	.ascii	' 1 = backup on double-sided diskettes'
	.ascii	[cr][lf]
	.ascii	' 2 = load from single-sided diskettes'
	.ascii	[cr][lf]
	.ascii	' 3 = load from double-sided diskettes'
	.ascii	[cr][lf]
	.ascii	' 4 = format backup diskette'
	.ascii	[cr][lf]
	.asciz	[cr][lf]'Enter choice: '

date$:	.asciz	[cr][lf]"Enter date (MM/DD/YY): "

time$:	.asciz	[cr][lf]"Enter time (HH:MM): "

part$:	.asciz	[cr][lf]"Enter partition name or RETURN to scan all: "

npart$: .asciz	[cr][lf]"Enter partition name or RETURN for main menu: "

nopart$:.ascii	[cr][lf]"*** Partition not found"
	.asciz	[cr][lf]

error$: .ascii	[cr][lf]"*** Input error - "
	.asciz	"Please re-enter: "

diff$:	.ascii	[cr][lf]"*** Error - partition name "
	.ascii	"does not match name on first diskette."
	.asciz	[cr][lf]

badsiz$:.ascii	[cr][lf]"*** Error - partition size "
	.ascii	"does not match "
	.asciz	"size in ALLOC table."[cr][lf]

badcou$:.ascii	[cr][lf]"*** Error - diskette number "
	.asciz	"does not match expected number."[cr][lf]

backing$:.ascii [cr][lf][lf]"Partition name is "
backpar:.asciz	"XXXXXXXX"

disks$: .ascii	[cr][lf]
last:	.asciz	'XX diskettes required'

newload$:.ascii [cr][lf][lf]
	.ascii	'Insert first diskette and type RETURN, '
	.asciz	'or CTRL-S for main menu.'

fresh$: .ascii	[cr][lf][lf]'Insert next diskette '
	.ascii	'and type RETURN to continue, '
	.asciz	'CTRL-S to skip.'

verify$:.ascii	[cr][lf]'Type RETURN to continue, '
	.asciz	'CTRL-S to skip.'

crlf$:	.asciz	[cr][lf]

done$:	.asciz	[cr][lf]'All done.'[cr][lf]

retry$: .asciz	' - will retry'

unload$:.ascii	[cr][lf]'Remove backup diskette, reinsert '
	.asciz	'CP/M diskette, press RETURN'
	.page
;----------
; Floppy commands
;
homeFLOP:.byte	7,0,endcom
seekFLOP:.byte	15,0,0,endcom
senseFLOP:.byte 8,endcom

wrDDFLOP==	45h
rdDDFLOP==	46h
scanFLOP==	51h

FLOPcom:.byte	0	; command
FLOPdsk:.byte	0	; disk
FLOPtrk:.byte	0	; track
FLOPsid:.byte	0	; side
	.byte	1	; sector
	.byte	6	; 8K sector size
	.byte	1	; last sector
	.byte	35h	; gap length
FLOPcrap:.byte	0	; DTL or STP
	.byte	endcom

wrSDFLOP==	05h
rdSDFLOP==	06h

FLOP0:	.byte	0	; command
	.byte	0	; disk
	.byte	0	; track
	.byte	0	; side
	.byte	3	; sector
	.byte	0	; 128 byte sector size
	.byte	26	; last sector
	.byte	7	; gap length
	.byte	128	; sector size
	.byte	endcom

FLOPstat:.blkb	7	; floppy status
;----------
; Harddisk commands
;
wrHARD	==	2
rdHARD	==	1

HARDcom:.byte	0	; command
HARDtrk:.byte	0	; track
HARDhed:.byte	0	; head
HARDsec:.byte	0	; sector
	.byte	0	; tag 0
	.byte	0	; tag 1
	.byte	8Ah	; 10 retries, with ECC
	.byte	0

HARDstat:.blkb	8	; harddisk status
;----------
; Disk information
;
endflg: .byte	0	; non-zero if beyond last sect

endtrk: .byte	0	; last track
endhead:.byte	0	; last head
endsect:.byte	0	; last sect

maxtrk: .byte	0	; max track
maxhead:.byte	0	; max head
maxsect:.byte	0	; max sector

badtrk: .byte	0	; bad track
badhead:.byte	0	; bad head
badsect:.byte	0	; bad sector

maxsid: .byte	0	; max side (on floppy)
loadsiz:.byte	0	; number of 256K blocks to load
count:	.byte	0	; diskette count
curpart:.blkb	8	; current load partition name
bkdisk: .byte	0	; backup disk loaded flag

SIZEtab:
	.ascii	[1]' 256K'
	.ascii	[2]' 512K'
	.ascii	[4]'1024K'
	.ascii	[8]'2048K'
	.ascii	[16]'4096K'
	.ascii	[32]'8192K'
;----------
; Information on track 0, sector 3 of BACKUP diskette
;
info%:
	.blkb	128	; reserve one sector
	.loc	info%
	.ascii	[cr][lf]"Date: "
date%:	.ascii	"XX/XX/XX"
	.ascii	[cr][lf]"Time: "
time%:	.ascii	"XX:XX"
	.ascii	[cr][lf]"Partition: "
part%:	.ascii	"XXXXXXXX"
	.ascii	[cr][lf]"Size: "
size%:	.ascii	"XXXXX"
	.ascii	[cr][lf]"Diskette: "
count%: .ascii	"XX"
	.ascii	" of "
last%:	.ascii	"XX"
	.asciz	[cr][lf]
realsize:.byte	0	; multiples of 256K
realcoun:.byte	0	; diskette number
	.reloc
;----------
; Buffer and stack area
stack	==	1C00h
ALLOCtab==	1C00h
lenbuff ==	8192
iobuff	==	2000h
	.page
;----------
; Home selected disk drive to track 0
BHOME:
	mvi	A,0	; current track now zero
	sta	curTRK
	sta	FLOPtrk
	lxi	H,homeFLOP+1 ; point to drive number
	jmpr	bseek	; share code with SETTRK
;----------
; Select disk drive
SELDSK:
	lxi	H,DEVqst ; ask which disk
	call	PRTMSG
	call	CONIN	; get the answer
	cpi	03h	; check for cntrl-C
	rz
	cpi	1Bh	; check for ESC
	jz	100h	; restart program
	mov	C,A
	call	CONOUT	; echo it
	sui	'0'
	jm	SELDSK
	cpi	8
	jp	SELDSK
	sta	curDSK
	ret
DEVqst: .ascii	[cr][lf][lf]'Enter floppy drive number '
	.asciz	'(0-7) or ESC for main menu: '
;----------
; Set track
SETTRK:
	lda	curTRK	; get current track
	lxi	H,seekFLOP+2 ; point to track number
	mov	M,A	; store into command
	dcx	H
;
; Seek a track (used by SETTRK and HOME)
bseek:
	lda	curDSK	; get current disk
	mov	M,A	; store into command
	dcx	H
	call	onMOTOR ; turn on the drive motor
reseek: mov	D,H	; save ptr to seek command
	mov	E,L
	call	COMMAND ; seek
bsense: lxi	H,IsenseFLOP
	call	COMMAND ; sense interrupt status
	call	RESULT
	mov	H,D	; restore ptr to seek command
	mov	L,E
	lda	FLOPstat; get first result byte
	bit	3,A	; check ready bit
	jrnz	reseek	; jump if not ready
	bit	5,A	; mask out top 2 bits
	jrz	bsense	; loop until seek completed
	ani	0D0h
	cnz	IOERR
	lxi	B,stepset; delay 10 ms for step settle
	jmp	DELAY
	.page
;----------
; Format the entire disk single or double density
;
BFORMAT:
	lxi	H,FORMstr ; print start-up message
	call	PRTMSG
..form: call	SELDSK	; ask which disk
	jz	term	; return if cntrl-C
	call	BWAITCR ; wait for return
	mvi	a,1	; backup disk loaded,
	sta	bkdisk	; set flag
	call	BHOME	; recalibrate the disk drive
	mvi	D,0	; track 0 is single density
	call	TABLIN	; track 0 is linear
	call	FORM1TK ; format the first track
	
..1:	call	SETTRK	; seek to next track
	mvi	D,6
	call	TAB1TK	; tracks 1-76 are special
	call	FORM1TK ; format next track
	cpi	78	; all tracks formatted
	jrnz	..1	; if not, loop
	lxi	H,FORMdon ; say 'done'
	call	PRTMSG
	call	offMOTOR
	jmpr	..form

FORMstr:.ascii	[cr][lf][lf]
	.ascii	'Note:  The diskette will be formatted with '
	.ascii	'1 sector per track.'[cr][lf]
	.asciz	'       It can be read or written only by HARDBACK.'
FORMdon:.asciz	[cr][lf]'FORMAT COMPLETE'

;----------
; Wait for CR
BWAITCR:
	lxi	H,WCRqst ; print a message
	call	PRTMSG
..1:	call	CONIN	; get an answer
	cpi	cr	; is it cr
	jrnz	..1	; if not loop
	mov	C,A
	call	CONOUT	; echo itV
	ret
WCRqst: .asciz	[cr][lf]'Type return to start.'
;----------
; Format curTRK and increment it.
;
; Build Sector Table (linear)
TABLIN:
	lxi	H,bufFLOP ; build sector table in buf
	lda	curTRK	; C should have curTRK
	mov	C,A
	mvi	A,1	; A has sector number

..1:	mov	M,C	; fill in TRACK
	inx	H
	push	PSW
	lda	curDSK
	rrc
	rrc
	ani	1	; compute head (ie, side)
	mov	M,A	; fill in HEAD (0 or 1)
	pop	PSW
	inx	H
	mov	M,A	; fill in SECTOR (counting)
	inx	H
	mov	M,D	; code for BYTES/SECTOR
	inx	H
	inr	A	; set to next sector
	cpi	27	; end of list
	jrnz	..1	; no, then loop
	ret
;
; Format track 1-76 with one 8K sector
TAB1TK:
	lxi	H,bufFLOP
	lda	curTRK
	mov	M,A	; fill in TRACK
	inx	H
	lda	curDSK
	rrc
	rrc
	ani	1
	mov	M,A	; fill in HEAD
	inx	H
	mvi	M,1	; fill in SECTOR
	inx	H
	mvi	M,6	; code for BYTES/SECTOR
	ret
;----------
; Format an entire track
FORM1TK:
;
; Set up the DMA chip
	mvi	A,0	; multiplex Floppy to DMA
	out	PIOAD
	lxi	H,DMAfmt
	lxi	B,DMAfm$<8+DMA
	outir		; program the chip
;
; Set up the floppy format command
	lda	curDSK	; set up current disk
	sta	FORMdsk
	mov	A,D	; set up floppy density
	sta	FORMmod 
	cpi	0	; check for density
	jrnz	..2	; and fill in special fields
	mvi	A,0Dh	; set up for single density
	sta	FORMcom ; format command
	mvi	A,1Bh	; set gap size
	sta	FORMgap
	mvi	A,26	; sectors per track
	sta	FORMsec
	jmpr	..3
..2:	mvi	A,4Dh	; set up for double density
	sta	FORMcom ; format command
	mvi	A,074h	; set gap size
	sta	FORMgap
	mvi	A,1	; sectors per track
	sta	FORMsec
..3:
;
; Execute the floppy command
	lxi	H,FORMcom
	call	EXflop
;
; Increment to next track
	lda	curTRK
	inr	A
	sta	curTRK
	sta	FLOPtrk
	ret		; return
;
; Execute the floppy command
EXflop: ei		; turn on interupts
	call	COMMAND
	call	RESULT
	lda	FLOPstat; get first result byte
	ani	0C0h	; mask out top 2 bits
	cnz	IOERR	; jump if abnormal termination
	lda	FLOPstat+1 ; get second result byte
	ani	33h	; check for CRC error
	cnz	IOERR
	ret
	.page
;----------
; Current disk position information
curDSK: .byte	0	; current physical drive
curTRK: .byte	0	; current physical track

;----------
; Floppy controller commands
IsenseFLOP:
	.byte	8	; sense interrupt status
	.byte	endcom
;----------
; FORMAT command for Floppy Controller
FORMcom:
	.byte	0FFh	; format command (SD/DD)
FORMdsk:.byte	0FFh	; curDSK
FORMmod:.byte	0FFh	; 0=SD, 1=DD
FORMsec:.byte	0FFh	; 26 sectors/track
FORMgap:.byte	0FFh	; gap length
	.byte	0E5h	; fill byte
	.byte	endcom
;----------
; DMA commands for FORMAT command
DMAfmt:
	.byte	0C3h	; master reset
	.byte	0C7h	; reset port A
	.byte	0CBh	; reset port B
	.byte	079h	; read from memory
	.word	bufFLOP ; address
	.word	103	; size - 1
	.byte	14h	; port A inc, memory
	.byte	28h	; port B fixed, I/O
	.byte	95h	; byte mode
	.byte	FLOP	; port B
	.byte	12h	; interrupt at end of block
	.byte	00	; interrupt vector
	.byte	9Ah	; stop at end of block
	.byte	0CFh	; load starting address
	.byte	05	; write to I/O
	.byte	0CFh	; load starting address
	.byte	0ABh	; enable interrupts
	.byte	87h	; enable DMA
DMAfm$	=	.-DMAfmt
bufFLOP =	3000h	; location of I/O buffer
	.page
	.sbttl	'volume info tables and routines'
;------------------------------------------------
; Get the hard disk status block
; Regs in:  B = address of 136-byte buffer
; Regs out: none
; Trashed:  all
hdstat:
	push	B	; save block address
	mvi	A,statHARD
	call	cmdHARD ; send the stat request cmd
	pop	H	; get set to receive block
	push	H
	lxi	B,8	; block length
	call	RECHARD ; get first 8 bytes
	pop	H
	lxi	D,7
	dad	D	; check status byte
	sub	A
	ora	M
	rnz		; no more if non-zero
	inx	H	; move past 8-byte block
	lxi	B,128	; get 128 more
	call	RECHARD
	ret

;---------------------------------
;	set the x register to point to volume
;	info for volume in volnum
;
;	regs in: none
;	regs out: x points to volume info
;	regs destroyed: a,de,hl
setvolptr:
	lda	volnum
	slar	a	;*2
	mov	e,a
	mvi	d,0
	lxi	h,vliindex
	dad	d
	mov	e,m
	inx	h
	mov	d,m
	push	d
	pop	x
	ret

;
;	routine to print volume label
prtvol:
	mvi	c,' '
	call	conout
	mov	c,vlivollabel(x)
	call	conout
	mov	c,vlivollabel+1(x)
	call	conout
	mov	c,vlivollabel+2(x)
	call	conout
	mov	c,vlivollabel+3(x)
	call	conout
	mov	c,vlivollabel+4(x)
	call	conout
	mov	c,vlivollabel+5(x)
	call	conout
	mov	c,vlivollabel+6(x)
	call	conout
	mov	c,vlivollabel+7(x)
	call	conout
	mov	c,vlivollabel+8(x)
	call	conout
	mov	c,vlivollabel+9(x)
	call	conout
	mvi	c,' '
	call	conout
	ret

volnum: .blkb	1	;current volume
vliindex:
	.word	vli0
	.word	vli1
	.word	vli2
	.word	vli3
statbuf:
	.blkb	8	;command status
vli0:	.blkb	vlibufsiz
vli1:	.blkb	vlibufsiz
vli2:	.blkb	vlibufsiz
vli3:	.blkb	vlibufsiz

.end

	.blkb	8	;command status
v