	.Z80
	TITLE	COLEX floppy format program for TurboDOS
VERS	EQU	10
	SUBTTL	Copyright (C) 1984 Colex Electronic Co. Ltd.
	NAME	('FORMAT')
	INCLUDE	COPYRIGH
;****************************************************************
;*								*
;*		Colex TurboDOS Floppy Formatter.		*
;*								*
;*	This formatter can format 8" diskettes, 5.25" 80 and 40	*
;*	track diskettes, in either single or double density	*
;*	The standard colex format can be produced by entering	*
;*	";2DC8" for the command line switches. IBM compatible	*
;*	CP/M-86 diskettes can be produced with ";2DC4".		*
;*								*
;*	IMPORTANT!!! In order to operate, this program must be	*
;*	linked with Digital Research's LINK, no other linker	*
;*	can be used with out modifying the program. If L80 is	*
;*	used change ALL occurances of ?MEMRY to $MEMRY.		*
;*								*
;****************************************************************
;  Date		   Author		Modification
;17-Sep-84	R.J. Wellsted
	PAGE
	PUBLIC	?MEMRY		;highest used address filled in by link

BDOS	EQU	0005H		;C-function entry point
TDOS	EQU	0050H		;T-function entry point
DFCB	EQU	005CH		;default fcb (holds drive id)
DBUF	EQU	0080H		;default dma buffer (hold command line)

CR	EQU	0DH
LF	EQU	0AH
BEL	EQU	07H
CTRLC	EQU	03H

	CSEG
	JP	START
	DB	'Copyright (C) 1984, '
	DB	'Colex Electronic Co. Ltd.'

START:	LD	SP,STACK	;set up the stack
	CALL	DMS		;display signon message
	DB	CR,LF
	DB	'Colex TurboDOS Floppy Formatter Version '
	DB	VERS/10+'0','.',VERS MOD 10 +'0'+80H
	LD	C,12		;T-func 12
	CALL	TDOS		;get serial no
	BIT	7,B		;test for privileged logon
	JP	Z,NPUERR	;error if not 
	LD	E,-1		;interrogate current bank
	LD	C,43		;T-func 43
	CALL	TDOS		;select memory bank
	OR	A		;test for bank 0
	JP	NZ,BNKERR	;error if not
	LD	A,(DFCB)	;get specifed drive
	OR	A		;test it
	JP	Z,DRVERR	;error if none specified
	DEC	A		;adjust to 0-15
	LD	(DRIVE),A	;save drive id
	CALL	PARSE		;parse command line
	LD	A,(FLAGS)	;get flags
	BIT	1,A		;test for verify only
	JP	NZ,VERIFY	;skip if so
FORMAT:	CALL	DMS		;display message
	DB	CR,LF
	DC	'Insert disk to be formatted in drive '
	LD	A,(DRIVE)	;get drive id
	ADD	A,'A'		;convert to ascii
	CALL	CONOUT		;display it
	CALL	DMS		;display message
	DB	':',CR,LF
	DC	'Press <CR> to start formatting.'
FMT05:	CALL	PURGE		;purge console input
	CALL	CONIN		;get reply
	CP	CR		;is it cr?
	JR	Z,FMT10		;skip if so
	CP	CTRLC		;is it ctrl-c
	JP	Z,QUIT		;quit if so
	CALL	DMS		;sound the bell
	DB	BEL OR 80H
	JR	FMT05		;get next key
FMT10:	CALL	LOCK		;try to lock the drive
	JP	NZ,LCKERR	;error if unable to lock it
	CALL	DMS		;display message
	DB	CR,LF
	DB	'Starting format pass.'
	DB	CR,LF OR 80H
	LD	A,4		;load format track command
	LD	(OPCODE),A	;save in packet
	LD	HL,(?MEMRY)	;get buffer address
	LD	(DMAADR),HL	;save buffer pointer
	LD	HL,0		;start at track 0
FMT15:	LD	(TRACK),HL	;save track number
	LD	DE,(MAXTRK)	;get highest track no.
	OR	A		;clear carry
	SBC	HL,DE		;test if at end of disk
	JR	Z,FMT45		;skip if so
	CALL	DMS		;display dot
	DC	'.'
	XOR	A		;start on side 0
FMT20:	LD	(SIDE),A	;save side number
	CALL	SETMEM		;set up memory
	OR	A		;test result
	JP	NZ,IMFERR	;skip if error
	LD	HL,SECTOR	;point to sector no.
	LD	A,(FLAGS)	;get flag byte
	AND	11110000B	;mask for density etc.
	LD	(HL),A		;save for format command
	LD	A,(SIDE)	;get side flag
	OR	A		;test for side 1
	JR	NZ,FMT25	;skip if so
	RES	4,(HL)		;force side 0 otherwise
FMT25:	CALL	ACCESS		;format track
	OR	A		;test for error
	JR	Z,FMT30		;skip if ok
	CALL	DMS		;display start of error message
	DB	CR,LF
	DC	'Format'
	CALL	ERROR		;rest of error processing
	JP	C,EXIT		;abort
	JR	NZ,FMT25	;retry
FMT30:	LD	HL,FLAGS	;point to flag byte
	BIT	4,(HL)		;test if double sided
	JR	Z,FMT40		;skip if not
	LD	A,(SIDE)	;get side number
	INC	A		;adjust
	CP	2		;test if done both sides
	JR	C,FMT20		;skip if not
FMT40:	LD	HL,(TRACK)	;get track number
	INC	HL		;adjust
	JR	FMT15		;format next track
FMT45:	CALL	DMS		;display message
	DB	CR,LF
	DC	'Successful format.'
VERIFY:	LD	A,(FLAGS)	;get flags
	BIT	1,A		;test for verify only
	JR	Z,VRFY10	;skip if not
	CALL	DMS		;display message
	DB	CR,LF
	DC	'Insert disk to be verified in drive '
	LD	A,(DRIVE)	;get drive id
	ADD	A,'A'		;convert to ascii
	CALL	CONOUT		;display it
	CALL	DMS		;display message
	DB	':',CR,LF
	DC	'Press <CR> to start verifying.'
VRFY05:	CALL	PURGE		;purge console input
	CALL	CONIN		;get reply
	CP	CR		;is it cr?
	JR	Z,VRFY10	;skip if so
	CP	CTRLC		;is it ctrl-c
	JP	Z,QUIT		;quit if so
	CALL	DMS		;sound the bell
	DB	BEL OR 80H
	JR	VRFY05		;get next key
VRFY10:	CALL	LOCK		;try to lock the drive
	JP	NZ,LCKERR	;error if unable to lock it
	CALL	DMS		;display message
	DB	CR,LF
	DB	'Starting verify pass.'
	DB	CR,LF OR 80H
	LD	HL,DSTCPY	;point to local dst copy
	LD	(DMAADR),HL	;save pointer
	LD	A,2		;load get dst code
	LD	(OPCODE),A	;save it
	CALL	ACCESS		;get dst
	OR	A		;test for error
	JP	NZ,BADTYP	;skip if so
	LD	HL,1		;size for 1 sector
	LD	A,(SECSIZ)	;get sector size code
	CALL	SIZSEC		;calc sector size
	EX	DE,HL		;save result
	LD	HL,(BDOS+1)	;get top of memory
	DEC	HL		;adjust
	LD	BC,(?MEMRY)	;get bottom of memory
	OR	A		;clear carry
	SBC	HL,BC		;calc memory size
	SBC	HL,DE		;check sector will fit
	JP	C,IMVERR	;give an error if not
	LD	BC,1		;set count =1
VRFY15:	SBC	HL,DE		;test if another sector will fit in memory
	JR	C,VRFY20	;skip if not
	INC	BC		;adjust count
	JR	VRFY15		;loop
VRFY20:	LD	HL,(SECTRK)	;get sectors/track
	LD	(NSECTS),HL	;save in sector count
	OR	A		;clear carry
	SBC	HL,BC		;test if whole track will fit in memory
	JR	C,VRFY25	;skip if so
	LD	(NSECTS),BC	;save no. of sectors that will fit
VRFY25:	XOR	A		;load read command
	LD	(OPCODE),A	;save command
	LD	HL,(?MEMRY)	;get start of buffer
	LD	(DMAADR),HL	;save in dma address
	LD	HL,0		;start at track 0
	LD	(TRACK),HL	;save track number
VRFY30:	LD	DE,(MAXTRK)	;get maximum track no.
	OR	A		;clear carry
	SBC	HL,DE		;test if at end of disk
	JP	Z,VRFY60	;done if so
	CALL	DMS		;display dot
	DC	'.'
	LD	HL,0		;always start with sector 0
	LD	DE,(NSECTS)	;get no. of sectors
	LD	(SECTOR),HL	;save sector no.
VRFY35:	LD	(SECCNT),DE	;save sector count
	LD	A,(SECSIZ)	;get sector size
	EX	DE,HL		;swap sector count
	CALL	SIZSEC		;calc no. of bytes
	LD	(BYTCNT),HL	;save byte count
VRFY40:	CALL	ACCESS		;read sector(s)
	OR	A		;test for error
	JR	Z,VRFY45	;skip if none
	CALL	DMS		;display start of error message
	DB	BEL,CR,LF
	DC	'Verify'
	CALL	ERROR		;get response
	JP	C,EXIT		;abort
	JR	NZ,VRFY40	;retry
VRFY45:	LD	HL,(SECTOR)	;get sector no.
	LD	DE,(SECCNT)	;get sector count
	ADD	HL,DE		;adjust for no. of sectors read
	EX	DE,HL		;save result
	LD	HL,(SECTRK)	;get no. of sectors/track
	OR	A		;clear carry
	SBC	HL,DE		;test if at end of track
	JR	Z,VRFY55	;skip if so
	LD	(SECTOR),DE	;save start sector
	EX	DE,HL		;save no. of sector on track
	LD	HL,(NSECTS)	;get max no. of sectors to read
	OR	A		;clear carry
	SBC	HL,DE		;test which is less
	JR	NC,VRFY35	;skip if no. of sector left on track
	LD	DE,(NSECTS)	;load max no. of sectors
	JR	VRFY35		;read next block of sectors
VRFY55:	LD	HL,(TRACK)	;get track number
	INC	HL		;adjust
	LD	(TRACK),HL	;save it
	JP	VRFY30		;loop back
VRFY60:	CALL	UNLOCK		;remove drive lock
	CALL	DMS		;display message
	DB	CR,LF
	DC	'Successful verify.'
	JP	EXIT

NPUERR:	CALL	DMS		;display error message
	DB	BEL,CR,LF
	DC	'Non-privileged user.'
	JP	QUIT		;abort program

BNKERR:	CALL	DMS		;display error message
	DB	BEL,CR,LF
	DC	'Unable to execute from bank 1.'
	JP	QUIT		;abort program

DRVERR:	CALL	DMS		;display error message
	DB	BEL,CR,LF
	DC	'No drive specified.'
	JP	QUIT		;abort program

IMFERR:	CALL	DMS		;display error message
	DB	BEL,CR,LF
	DC	'Insufficient memory to format.'
	JP	QUIT		;abort program

IMVERR:	CALL	DMS		;display error message
	DB	BEL,CR,LF
	DC	'Insufficient memory to verify.'
	JP	QUIT		;abort program

LCKERR:	CALL	DMS		;display error message
	DB	BEL,CR,LF
	DC	'Unable to lockout drive '
	LD	A,(DRIVE)	;get drive code
	ADD	A,'A'		;convert to ascii
	CALL	CONOUT		;send to console
	CALL	DMS		;display colon
	DC	':'
	JP	QUIT		;abort program

BADTYP:	CALL	DMS		;display error message
	DB	BEL,CR,LF
	DC	'Unknown diskette format, unable to verify.'
	JR	EXIT		;done

QUIT:	XOR	A		;make a 0
	LD	(FLAGS),A	;clear all flags
EXIT:	CALL	UNLOCK		;unlock the drive
	LD	HL,FLAGS	;get flags
	BIT	0,(HL)		;check for repeat
	LD	C,0		;assume not
	JP	Z,BDOS		;done if assumption true
	BIT	1,(HL)		;get verify flag
	JP	NZ,VERIFY	;go to it if true
	JP	FORMAT		;else do format
	PAGE
;****************************************************************
;*								*
;*			Subroutines				*
;*								*
;****************************************************************
ACCESS:	LD	DE,OPCODE	;point to pdr packet
	LD	C,22		;T-func 22
	CALL	TDOS		;do physical disk access
	LD	HL,OPCODE	;point to opcode
	BIT	1,(HL)		;test for inverted status
	RET	Z		;done if not
	CPL			;toggle status
	RET			;done

ERROR:	CALL	DMS		;display start of error message
	DC	' error, Drive '
	LD	A,(DRIVE)	;get drive id
	ADD	A,'A'		;convert to ascii
	CALL	CONOUT		;display it
	CALL	DMS		;display next part of error message
	DC	':, Track '
	LD	HL,(TRACK)	;get track number
	CALL	PRNUMB		;display it
	CALL	DMS		;display end of error message
	DC	' (Retry, Ignore, Abort) '
	CALL	PURGE		;purge console buffer
ERRLP:	CALL	DMS		;sound the bell
	DB	BEL OR 80H
	CALL	CONIN		;get reply
	CALL	UPRCAS		;convert to upper case
	CP	'I'		;test if ignore
	RET	Z		;done if so
	CP	'A'		;test if abort
	SCF			;set abort flag
	RET	Z		;done if so
	CP	'R'		;test if retry
	JR	NZ,ERRLP	;get new input if not
	LD	A,1		;load retry flag
	OR	A		;set flags
	RET			;done

PRNUMB:	LD	C,0		;enable zero suppression
	LD	DE,10000	;load 10e4
	CALL	PRNUM1		;print 1st digit
	LD	DE,1000		;load 10e3
	CALL	PRNUM1		;print 2nd digit
	LD	DE,100		;load 10e2
	CALL	PRNUM1		;print 3rd digit
	LD	DE,10		;load 10e1
	CALL	PRNUM1		;print 4th digit
	LD	DE,1		;load 10e0
	LD	C,1		;disable 0 supression
PRNUM1:	XOR	A		;clear result
PRNUM2:	SBC	HL,DE		;subtract power of 10
	JR	C,PRNUM3	;skip if below
	INC	A		;adjust result
	JR	PRNUM2		;loop
PRNUM3:	ADD	HL,DE		;compensate for subtract
	OR	A		;test result
	JR	NZ,PRNUM4	;skip if not zero
	BIT	0,C		;test if zero suppresion enabled
	RET	Z		;done if so
PRNUM4:	ADD	A,'0'		;adjust for ascii
	CALL	CONOUT		;display digit
	SET	0,C		;disable zero suppression
	RET			;done

SIZSEC:	ADD	HL,HL		;*2
	ADD	HL,HL		;*4
	ADD	HL,HL		;*8
	ADD	HL,HL		;*16
	ADD	HL,HL		;*32
	ADD	HL,HL		;*64
	ADD	HL,HL		;*128
SZSEC1:	DEC	A		;adjust code
	RET	M		;done if was 0
	ADD	HL,HL		;shift left
	JR	SZSEC1		;loop

DMS:	EX	(SP),HL		;get string pointer
	CALL	DMSHL		;go print string
	EX	(SP),HL		;save return address
	RET			;done

DMSHL:	LD	A,(HL)		;get char
	INC	HL		;adjust pointer
	CALL	CONOUT		;display char
	BIT	7,A		;test for end of string
	RET	NZ		;done if so
	JR	DMSHL		;loop back for next char

CONOUT:	PUSH	AF
	PUSH	HL		;save regs
	PUSH	DE
	PUSH	BC
	AND	7FH		;clear msb
	LD	E,A		;copy char
	LD	C,4		;C-function 4
	CALL	BDOS		;raw console output
	POP	BC
	POP	DE
	POP	HL		;restore regs
	POP	AF
	RET			;done

CONIN:	PUSH	HL
	PUSH	DE		;save regs
	PUSH	BC
CILOOP:	LD	E,-1		;console input
	LD	C,6		;C-func 6
	CALL	BDOS		;direct console i/o
	OR	A		;test for input
	JR	Z,CILOOP	;loop if none
	POP	BC
	POP	DE		;restore regs
	POP	HL
	RET

CONST:	PUSH	HL
	PUSH	DE		;save regs
	PUSH	BC
	LD	C,11		;C-func 11
	CALL	BDOS		;get console status
	POP	BC
	POP	DE		;restore regs
	POP	HL
	OR	A		;test if key pressed
	RET			;done

PURGE:	CALL	CONST		;get console status
	RET	Z		;done if no input
	CALL	CONIN		;get char
	JR	PURGE		;loop back

UPRCAS:	CP	'a'		;test if above start of lower case
	RET	C		;done if below
	CP	'z'+1		;test if below end of lower case
	RET	NC		;done if above
	SUB	32		;remove lower case offset
	RET			;done

PARSE:	LD	HL,DBUF		;point to command line buffer
	LD	IX,FLAGS	;point to flags byte
	LD	A,(HL)		;get char count
	OR	A		;test for input
	JP	Z,GETPRM	;skip if none
	LD	B,A		;save count
	INC	HL		;point to first char
PRS05:	LD	A,(HL)		;get char
	CP	';'		;is it ; ?
	JR	Z,PRS10		;skip if so
	INC	HL		;adjust pointer
	DJNZ	PRS05		;adjust count & loop
	JP	GETPRM		;skip to request params
PRS10:	INC	HL		;adjust pointer
	DEC	B		;adjust counter
	JR	Z,GETPRM	;skip if done
	LD	A,(HL)		;get next char
	CALL	UPRCAS		;ensure upper case
	CP	'R'		;test for repeat switch
	JR	NZ,PRS15	;skip if not
	SET	0,(IX+0)	;set relevant flag
	JR	PRS10		;get next char
PRS15:	CP	'V'		;test for verify switch
	JR	NZ,PRS20	;skip if not
	SET	1,(IX+0)	;set relevant flag
	JR	PRS10		;get next char
PRS20:	CP	'1'		;test for single sided
	JR	NZ,PRS25	;skip if not
	LD	(SFLAG),A	;save sides flag
	JR	PRS10		;get next char
PRS25:	CP	'2'		;test for double sided
	JR	NZ,PRS30	;skip if not
	LD	(SFLAG),A	;save sides flag
	JR	PRS10		;get next char
PRS30:	CP	'S'		;test for single density
	JR	NZ,PRS35	;skip if not
	LD	(DFLAG),A	;save density flag
	JR	PRS10		;get next char
PRS35:	CP	'D'		;test for double density
	JR	NZ,PRS40	;skip if not
	LD	(DFLAG),A	;save density flag
	JR	PRS10		;get next char
PRS40:	CP	'C'		;test for CP/M format
	JR	NZ,PRS45	;skip if not
	LD	(CFLAG),A	;save format flag
	JR	PRS10		;get next char
PRS45:	CP	'T'		;test for TurboDOS format
	JR	NZ,PRS50	;skip if not
	LD	(CFLAG),A	;save format flag
	JR	PRS10		;get next char
PRS50:	CP	'4'		;test for 40 track
	JR	NZ,PRS55	;skip if not
	LD	(TFLAG),A	;save track flag
	JR	PRS10		;get next char
PRS55:	CP	'7'		;test for 77 track
	JR	NZ,PRS60	;skip if not
	LD	(TFLAG),A	;save track flag
	JR	PRS10		;get next char
PRS60:	CP	'8'		;test for 80 track
	JR	NZ,PRS10	;skip if not
	LD	(TFLAG),A	;save track flag
	JR	PRS10		;get next char
GETPRM:	LD	HL,FLAGS	;point to flags
	BIT	1,(HL)		;test for verify only ?
	RET	NZ		;done if so
	LD	A,(SFLAG)	;get sides flag
	OR	A		;is it set?
	JR	NZ,GPRM10	;skip if so
GPRM05:	CALL	DMS		;display message
	DB	CR,LF
	DC	'1 or 2 sides ? '
	CALL	LINEIN		;get reply
	CP	'1'		;is it single sided ?
	JR	Z,GPRM10	;skip if so
	CP	'2'		;is it double sided ?
	JR	Z,GPRM10	;skip if so
	CALL	DMS		;sound the bell
	DB	BEL OR 80H
	JR	GPRM05		;get new input
GPRM10:	RES	4,(HL)		;assume single sided
	CP	'1'		;test assumption
	JR	Z,GPRM15	;skip if assumption correct
	SET	4,(HL)		;mark as double sided
GPRM15:	LD	A,(DFLAG)	;get density flag
	OR	A		;test if set
	JR	NZ,GPRM25	;skip if so
GPRM20:	CALL	DMS		;display message
	DB	CR,LF
	DC	'Single or Double density ? '
	CALL	LINEIN		;get input
	CP	'S'		;test for single density
	JR	Z,GPRM25	;skip if so
	CP	'D'		;test for double den
	JR	Z,GPRM25	;skip if so
	CALL	DMS		;sound the bell
	DB	BEL OR 80H
	JR	GPRM20		;try again
GPRM25:	RES	7,(HL)		;assume double density
	CP	'D'		;test assumption
	JR	Z,GPRM30	;skip if correct
	SET	7,(HL)		;mark as single density
GPRM30:	LD	A,(CFLAG)	;get cp/m-turbodos flag
	OR	A		;test if set
	JR	NZ,GPRM40	;skip if so
GPRM35:	CALL	DMS		;display message
	DB	CR,LF
	DC	'Cp/m or Turbodos format ? '
	CALL	LINEIN		;get reply
	CP	'C'		;test for cp/m
	JR	Z,GPRM40	;skip if so
	CP	'T'		;test for turbodos
	JR	Z,GPRM40	;skip if so
	CALL	DMS		;sound the bell
	DB	BEL OR 80H
	JR	GPRM35		;try again
GPRM40:	RES	2,(HL)		;assume cp/m format
	CP	'C'		;test assumption
	JR	Z,GPRM45	;skip if true
	SET	2,(HL)		;mark as turbodos
GPRM45:	LD	A,(TFLAG)	;get track flag
	OR	A		;test if set
	JR	NZ,GPRM55	;skip if so
GPRM50:	CALL	DMS		;display message
	DB	CR,LF
	DC	'4=40 track, 8=80 track minifloppy, 7=77 track 8"floppy ? '
	CALL	LINEIN		;get reply
	CP	'4'		;test for 40 track
	JR	Z,GPRM55	;skip if so
	CP	'7'		;test if 77 track
	JR	Z,GPRM55	;skip if so
	CP	'8'		;test if 80 track
	JR	Z,GPRM55	;skip if so
	CALL	DMS		;sound the bell
	DB	BEL OR 80H
	JR	GPRM50		;try again
GPRM55:	LD	DE,77		;assume 77 track
	RES	5,(HL)		;assume 8"
	RES	6,(HL)		;cant double step 8"
	CP	'7'		;test assumption
	JR	Z,GPRM60	;skip if true
	SET	5,(HL)		;must be 5.25" then
	LD	DE,80		;assume 80 track
	CP	'8'		;test assumption
	JR	Z,GPRM60	;skip if true
	SET	6,(HL)		;mark as 40 track
	LD	DE,40
GPRM60:	LD	(MAXTRK),DE	;save no. of tracks
	CALL	DMS		;newline
	DB	CR,LF OR 80H
	RET			;done

LINEIN:	PUSH	HL		;save pointer
	LD	DE,DBUF		;point to buffer
	LD	A,1		;load length to input
	LD	(DE),A		;save it
	LD	C,10		;C-function 10
	CALL	BDOS		;console line input
	POP	HL		;restore pointer
	LD	A,(DBUF+1)	;get length input
	OR	A		;test it
	RET	Z		;done if no input
	LD	A,(DBUF+2)	;get input char
	JP	UPRCAS		;go convert to upper case

SETMEM:	LD	HL,(BDOS+1)	;get top of memory
	LD	DE,(?MEMRY)	;get bottom of memory
	OR	A		;clear carry
	SBC	HL,DE		;calc memory size
	LD	B,H		;copy to bc
	LD	C,L
	DEC	BC		;adjust
	EX	DE,HL		;get bottom of memory
	LD	D,H		;copy to de
	LD	E,L
	INC	DE		;adjust
	LD	A,(FLAGS)	;get format flags
	LD	(HL),4EH	;assume double density
	BIT	7,A		;test assumption
	JR	Z,SMEM05	;skip if true
	LD	(HL),0FFH	;save single density fill byte
SMEM05:	LDIR			;copy through memory
	LD	A,(FLAGS)	;get format mode flags
	AND	10100100B	;mask to density,size,format
	SRL	A		;shift
	LD	B,A		;save
	SRL	A		;shift
	SRL	A
	LD	C,A		;save
	SRL	A		;shift
	AND	00001000B	;remove unwanted bits
	OR	B		;merge saved bits
	OR	C
	AND	00001110B	;remove anything not needed
	LD	E,A		;make 16 bit value
	LD	D,0	
	LD	HL,FMTTBL	;point to table
	ADD	HL,DE		;add offset
	LD	E,(HL)		;get pointer lo
	INC	HL		;adjust table pointer
	LD	D,(HL)		;get pointer hi
	LD	A,D		;test for zero entry
	OR	E
	JR	NZ,SMEM10	;skip if not
	CALL	DMS		;display error message
	DB	BEL,CR,LF
	DC	'Unknown diskette format, unable to format.'
	JP	QUIT		;abort program
SMEM10:	PUSH	DE		;copy to ix
	POP	IX
	LD	L,(IX-2)	;get byte count lo
	LD	H,(IX-1)	;get byte count hi
	LD	(BYTCNT),HL	;save byte count
	EX	DE,HL		;swap
	LD	HL,(BDOS+1)	;get top of memory
	LD	BC,(?MEMRY)	;get bottom of memory
	OR	A		;clear carry
	SBC	HL,BC		;calc memory size
	SBC	HL,DE		;check if track buffer will fit
	JR	NC,SMEM15	;skip if so
	LD	A,-1		;mark error
	RET			;done
SMEM15:	LD	B,(IX-4)	;get number of sectors (assume 80 track or 8")
	LD	A,(FLAGS)	;get flag byte
	BIT	6,A		;test assumption
	JR	Z,SMEM17	;skip if correct
	LD	B,(IX-3)	;get 40 track sector count
SMEM17:	LD	A,0		;assume offset of 0
	BIT	7,B		;test assumption
	RES	7,B		;always clear flag
	JR	Z,SMEM20	;skip if so
	LD	A,(SIDE)	;get side byte
	OR	A		;test which side
	JR	Z,SMEM20	;skip if on side 0
	LD	A,B		;get offset for side 1
SMEM20:	LD	HL,SECTBL	;point to sector table
SMEM25:	INC	A		;adjust sector no.
	LD	(HL),A		;save sector no.
	INC	HL		;adjust table pointer
	DJNZ	SMEM25		;adjust counter & loop
	LD	(HL),0		;mark end of table
	LD	DE,(?MEMRY)	;point to start of memory buffer
	LD	HL,SECTBL	;point to sector table
INTPRT:	LD	BC,INTPRT	;load return address
	PUSH	BC		;save on stack
	LD	C,(IX+0)	;get execution address lo
	INC	IX		;adjust pointer
	LD	B,(IX+0)	;get execution address hi
	INC	IX		;adjust pointer
	PUSH	BC
	RET			;go to it

FILL:	LD	B,(IX+0)	;get fill count
	INC	IX		;adjust pointer
	LD	A,(IX+0)	;get fill value
	INC	IX		;adjust pointer
FILL1:	LD	(DE),A		;save fill value
	INC	DE		;adjust destination pointer
	DJNZ	FILL1		;adjust count & loop
	RET			;done

SIDNMB:	LD	A,(SIDE)	;get side no.
	LD	(DE),A		;save in buffer
	INC	DE		;adjust pointer
	RET			;done

TRKNMB:	LD	A,(TRACK)	;get track no.
	LD	(DE),A		;save in buffer
	INC	DE		;adjust pointer
	RET			;done

SECNMB:	LDI			;get & save next sector no.
	RET			;done

SAVSEC:	LD	A,(HL)		;get next sector no.
	OR	A		;test for 0
	JR	NZ,SSEC05	;skip if not
	POP	HL		;discard tos
	XOR	A		;clear flag
	RET			;done fill
SSEC05:	LD	C,(IX+0)	;get sector fill pointer lo
	LD	B,(IX+1)	;get sector fill pointer hi
	PUSH	BC		;save on stack
	POP	IX		;copy to ix
	RET			;done

LOCK:	LD	A,(LCKFLG)	;get lock flag
	SUB	-1		;test if set
	RET	Z		;done if so
	LD	A,(DRIVE)	;get drive code
	LD	E,A		;copy to e
	LD	D,-1		;lock drive
	LD	C,25		;T-func 25
	CALL	TDOS		;attempt to lock drive
	OR	A		;test if successful
	PUSH	AF		;save result
	CPL			;invert
	LD	(LCKFLG),A	;save flag
	POP	AF		;restore result
	RET			;done

UNLOCK:	LD	A,(LCKFLG)	;get lock flag
	OR	A		;test if set
	RET	Z		;done if not
	LD	A,(DRIVE)	;get drive code
	LD	E,A		;copy to e
	LD	D,0		;unlock drive
	LD	C,25		;T-func 25
	CALL	TDOS		;attempt to unlock drive
	OR	A		;test if successful
	LD	(LCKFLG),A	;save flag
	RET			;done

	PAGE
;****************************************************************
;*								*
;*			Data Area				*
;*								*
;****************************************************************
	DSEG
	DS	64		;stack area
STACK	EQU	$

LCKFLG:	DB	0		;drive lock flag
SIDE:	DB	0		;side byte
SECTBL:	DS	32		;space for sector table

OPCODE:	DS	1
DRIVE:	DS	1
TRACK:	DS	2
SECTOR:	DS	2
SECCNT:	DS	2
BYTCNT:	DS	2
DMAADR:	DS	2
DSTPNT:	DS	2
DSTCPY:	DS	1		;block size
	DS	2		;no. of blocks
	DS	1		;no. of dir blocks
SECSIZ:	DS	1		;sector size
SECTRK:	DS	2		;no. of sectors/track
MAXTRK:	DS	2		;no. of tracks
	DS	2		;no. of reserved tracks

NSECTS:	DS	2		;no. of sectors to verify at a time
;****************************************************************
;*								*
;*	Flag Byte format:					*
;*	bit 7:	density, 0=double, 1=single			*
;*	bit 6:	step mode 0=80 track, 1=35/40 track (5.25" only)*
;*	bit 5:	size. 0=8", 1=5.25".				*
;*	bit 4:	sides. 0=single sided, 1=double sided.		*
;*	bit 3:	reserved.					*
;*	bit 2:	0=cp/m format, 1= turbodos format		*
;*	bit 1:	0=format & verify, 1=verify only.		*
;*	bit 0:	0=single pass, 1=repeat.			*
;*								*
;****************************************************************
FLAGS:	DB	00000000B
SFLAG:	DB	0		;sides flag
DFLAG:	DB	0		;density flag
CFLAG:	DB	0		;TurboDOS-CP/M flag
TFLAG:	DB	0		;track flag

?MEMRY:	DW	$-$		;filled in by link

FMTTBL:	DW	TRKD8C		;double density 8" cp/m
	DW	TRKD8T		;double density 8" tdos
	DW	TRKD5C		;double density 5.25" cp/m
	DW	TRKD5T		;double density 5.25" tdos
	DW	TRKS8C		;single density 8" cp/m
	DW	TRKS8T		;single density 8" tdos
	DW	TRKS5C		;single density 5.25" cp/m
	DW	TRKS5T		;single density 5.25" tdos

;single density, 8" CP/M format
	DB	26,0		;sectors/track
	DW	5300		;track length
TRKS8C:	DW	FILL		;pre-index gap
	DB	40,0FFH
	DW	FILL		;sync field
	DB	6,0
	DW	FILL		;index address mark
	DB	1,0FCH
	DW	FILL		;post index gap
	DB	26,0FFH
	DW	SAVSEC		;save sectors
	DW	SECS8C

SECS8C:	DW	FILL		;sync field
	DB	6,0
	DW	FILL		;id address mark
	DB	1,0FEH
	DW	TRKNMB		;track number
	DW	SIDNMB		;side number
	DW	SECNMB		;sector number
	DW	FILL		;sector size code
	DB	1,0		;(128 bytes)
	DW	FILL		;id crc
	DB	1,0F7H
	DW	FILL		;post id gap
	DB	11,0FFH
	DW	FILL		;sync field
	DB	6,0
	DW	FILL		;data address mark
	DB	1,0FBH
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;data crc
	DB	1,0F7H
	DW	FILL		;post data gap
	DB	27,0FFH
	DW	SAVSEC		;repeat sector until all done
	DW	SECS8C

;single density, 8" TurboDOS format
	DB	8,0		;sectors/track
	DW	5300		;track length
TRKS8T:	DW	FILL		;pre-index gap
	DB	40,0FFH
	DW	FILL		;sync
	DB	6,0
	DW	FILL		;index mark
	DB	1,0FCH
	DW	FILL		;post index gap
	DB	26,0FFH
	DW	SAVSEC		;save sectors
	DW	SECS8T

SECS8T:	DW	FILL		;sync field
	DB	6,0
	DW	FILL		;id address mark
	DB	1,0FEH
	DW	TRKNMB		;track number
	DW	SIDNMB		;side number
	DW	SECNMB		;sector number
	DW	FILL		;sector size code
	DB	1,2		;(512 bytes)
	DW	FILL		;id crc
	DB	1,0F7H
	DW	FILL		;post id gap
	DB	11,0FFH
	DW	FILL		;sync field
	DB	6,0
	DW	FILL		;data address mark
	DB	1,0FBH
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;data crc
	DB	1,0F7H
	DW	FILL		;post data gap
	DB	27,0FFH
	DW	SAVSEC		;repeat sectors until all done
	DW	SECS8T

;double density, 8" CP/M format
	DB	26,0		;sectors/track
	DW	10500		;track length
TRKD8C:	DW	FILL		;pre-index gap
	DB	80,4EH
	DW	FILL		;sync field
	DB	12,0
	DW	FILL
	DB	3,0F6H
	DW	FILL		;index address mark
	DB	1,0FCH
	DW	FILL		;post index gap
	DB	50,4EH
	DW	SAVSEC		;save sectors
	DW	SECD8C

SECD8C:	DW	FILL		;sync field
	DB	12,0
	DW	FILL
	DB	3,0F5H
	DW	FILL		;id address mark
	DB	1,0FEH
	DW	TRKNMB		;track number
	DW	SIDNMB		;side number
	DW	SECNMB		;sector number
	DW	FILL		;sector size
	DB	1,1		;(256 bytes)
	DW	FILL		;id crc
	DB	1,0F7H
	DW	FILL		;post id gap
	DB	22,4EH
	DW	FILL		;sync field
	DB	12,0
	DW	FILL
	DB	3,0F5H
	DW	FILL		;data address mark
	DB	1,0FBH
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;data crc
	DB	1,0F7H
	DW	FILL		;post sector gap
	DB	54,4EH
	DW	SAVSEC		;repeat sectors until all done
	DW	SECD8C

;double density, 8" TurboDOS format
	DW	8,0		;sectors/track
	DW	10500		;track length
TRKD8T:	DW	FILL		;pre-index gap
	DB	80,4EH
	DW	FILL		;sync field
	DB	12,0
	DW	FILL
	DB	3,0F6H
	DW	FILL		;index address mark
	DB	1,0FCH
	DW	FILL		;post index gap
	DB	50,4EH
	DW	SAVSEC		;save sectors
	DW	SECD8T

SECD8T:	DW	FILL		;sync field
	DB	12,0
	DW	FILL
	DB	3,0F5H
	DW	FILL		;id address mark
	DB	1,0FEH
	DW	TRKNMB		;track number
	DW	SIDNMB		;side number
	DW	SECNMB		;sector number
	DW	FILL		;sector size
	DB	1,3		;(1024 bytes)
	DW	FILL		;id crc
	DB	1,0F7H
	DW	FILL		;post id gap
	DB	22,4EH
	DW	FILL		;sync field
	DB	12,0
	DW	FILL
	DB	3,0F5H
	DW	FILL		;data address mark
	DB	1,0FBH
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;data crc
	DB	1,0F7H
	DW	FILL		;post data gap
	DB	54,4EH
	DW	SAVSEC		;repeat sectors until all done
	DW	SECD8T

;single density, 5.25" CP/M format
	DB	8,8		;sectors/track (80/40 track)
	DW	3200		;track length
TRKS5C:	DW	FILL		;post index gap
	DB	40,0FFH
	DW	SAVSEC		;save sector data
	DW	SECS5C

SECS5C:	DW	FILL		;sync field
	DB	6,0
	DW	FILL		;id address mark
	DB	1,0FEH
	DW	TRKNMB		;track number
	DW	SIDNMB		;side number
	DW	SECNMB		;sector number
	DW	FILL		;sector size code
	DB	1,1		;(256 bytes)
	DW	FILL		;id crc
	DB	1,0F7H
	DW	FILL		;post id gap
	DB	11,0FFH
	DW	FILL		;sync field
	DB	6,0
	DW	FILL		;data address mark
	DB	1,0FBH
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;data crc
	DB	1,0F7H
	DW	FILL		;post data gap
	DB	10,0FFH
	DW	SAVSEC		;repeat sectors until all done
	DW	SECS5C

;single density, 5.25" TurboDOS format
	DB	5,5		;sector/track
	DW	3200		;track length
TRKS5T:	DW	FILL		;post index gap
	DB	40,0FFH
	DW	SAVSEC		;save sectors
	DW	SECS5T

SECS5T:	DW	FILL		;sync field
	DB	6,0
	DW	FILL		;id address mark
	DB	1,0FEH
	DW	TRKNMB		;track number
	DW	SIDNMB		;side number
	DW	SECNMB		;sector number
	DW	FILL		;sector size code
	DB	1,2		;(512 bytes)
	DW	FILL		;id crc
	DB	1,0F7H
	DW	FILL		;post id gap
	DB	11,0FFH
	DW	FILL		;sync field
	DB	6,0
	DW	FILL		;data address mark
	DB	1,0FBH
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;data crc
	DB	1,0F7H
	DW	FILL		;post data gap
	DB	10,0FFH
	DW	SAVSEC		;repeat sectors until all done
	DW	SECS5T

;double density, 5.25" CP/M format
	DB	10+80H,8	;sectors/track
	DW	6300		;track length
TRKD5C:	DW	FILL		;post index gap
	DB	60,4EH
	DW	SAVSEC		;save sectors
	DW	SECD5C

SECD5C:	DW	FILL		;sync field
	DB	12,0
	DW	FILL
	DB	3,0F5H
	DW	FILL		;id address mark
	DB	1,0FEH
	DW	TRKNMB		;track number
	DW	SIDNMB		;side number
	DW	SECNMB		;sector number
	DW	FILL		;sector size code
	DB	1,2		;(512 bytes)
	DW	FILL		;id crc
	DB	1,0F7H
	DW	FILL		;post id gap
	DB	22,4EH
	DW	FILL
	DB	12,0		;sync field
	DW	FILL
	DB	3,0F5H
	DW	FILL		;data address mark
	DB	1,0FBH
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;data crc
	DB	1,0F7H
	DW	FILL		;post data gap
	DB	24,4EH
	DW	SAVSEC		;repeat sectors until all done
	DW	SECD5C

;double density, 5.25" TurboDOS format
	DB	5,5		;sectors/track
	DW	6300		;track length
TRKD5T:	DW	FILL		;post index gap
	DB	60,4EH
	DW	SAVSEC		;save sectors
	DW	SECD5T

SECD5T:	DW	FILL		;sync field
	DB	12,0
	DW	FILL
	DB	3,0F5H
	DW	FILL		;id address mark
	DB	1,0FEH
	DW	TRKNMB		;track number
	DW	SIDNMB		;side number
	DW	SECNMB		;sector number
	DW	FILL		;sector size code
	DB	1,3		;(1024 bytes)
	DW	FILL		;id crc
	DB	1,0F7H
	DW	FILL		;post id gap
	DB	22,4EH
	DW	FILL		;sync field
	DB	12,0
	DW	FILL
	DB	3,0F5H
	DW	FILL		;data address mark
	DB	1,0FBH
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;sector data
	DB	128,0E5H
	DW	FILL		;data crc
	DB	1,0F7H
	DW	FILL		;post data gap
	DB	24,4EH
	DW	SAVSEC		;repeat sectors until all done
	DW	SECD5T

	END
