;
;FORMAT.ASM VERSION 1.4
;
;FORMATS A DISKETTE AND CORRECTS FIF DATA ON DIRECTORY TRACK
;TO IBM COMPATIBLE SO CP/M WILL THINK THE DIRECTORY IS EMPTY
;
CR	EQU	0DH
LF	EQU	0AH
BDOS	EQU	5		;SYSTEM CALL BDOS
DCOM	EQU	0E8H		;DISK COMMAND PORT
DSTAT	EQU	DCOM		;DISK STATUS PORT
TRACK	EQU	DCOM+1		;DISK TRACK COMMAND
SECTP	EQU	DCOM+2		;DISK SECTOR PORT
DDATA	EQU	DCOM+3		;DISK DATA PORT
WAIT	EQU	DCOM+4		;DISK WAIT CONTROL PORT
;
;MAIN PROGRAM FORMATS & REBOOTS TO MOUNT FORMATTED DISK
;
	ORG	0100H
FORMAT:	LXI	SP,STACK
	LXI	D,SIGNON
	CALL	TMSG		;PRINT SIGN-ON MSG
	LXI	D,M1
	CALL	TMSG		;TELL USER THIS DESTROYS DISK
	CALL	INCH		;SEE IF THAT'S WHAT USER WANTS
	CPI	'Y'
	JZ	F1		;YES
	LXI	D,ABORTMG	;ELSE PRINT OPER. ABORTED MSG
	CALL	TMSG
	JMP	REBOOT		;AND RE-BOOT CP/M
F1:	LXI	D,M2		;TELL USER TO REMOVE SYS. DISK
	CALL	TMSG
	CALL	INCH		;AND WAIT FOR USER'S C/R
	LXI	D,M3		;ASK USER WHICH DRIVE TO FORMAT
DN1:	CALL	TMSG
	CALL	INCH		;AND GET ANSWER
	STA	INMSG		;MODIFY MSG TO REFLECT DRIVE
	CPI	'A'
	LXI	H,DISK
	MVI	M,0
	JZ	HAVDRV
	CPI	'B'
	MVI	M,1
	JZ	HAVDRV
	LXI	D,M3A		;USER'S ANS. DIDN'T MAKE SENSE
	JMP	DN1		;ASK HIM FOR DRIVE AGAIN
;
;ONE MORE CONFIRMATION
;
HAVDRV:	LXI	D,M4		;ASK USER IF READY TO START
	CALL	TMSG
	CALL	INCH
	CPI	'Y'
	JNZ	EREX2		;AND REBOOT IF NOT
	CALL	CRLF
	LXI	D,1		;IN CASE OF NOT READY ERROR
;
;DO IT
;
;
;RESTORE DRIVE TO TRACK 00
;
START:	CALL	HOME		;SELECT & HOME DRIVE
	MVI	C,0		;SET TRACK NUMBER TO 0
	MVI	H,77		;SET TOTAL TRACKS TO 77
NXTTRK:	MVI	D,1		;SECTOR CNT TO 1
	MVI	E,26		;SET MAX # SECTORS 
	MVI	B,46		;TWO PREINDEX GAPS
	MVI	A,0F4H		;LOAD TRACK WRITE COMMAND
	OUT	DCOM		;ISSUE TRACK WRITE
	;
	;WRITE PREINDEX FILL
	;
PREIND:	IN	WAIT		;WAIT FOR DRQ
	ORA	A		;SET FLAGS
	JP	ERRMSG		;JMP OUT IF ERROR
	XRA	A		;LOAD PREINDEX FILL
	OUT	DDATA		;WRITE IT ON DISK
	DCR	B		;COUNT=COUNT-1
	JNZ	PREIND		;GO BACK TILL B=0
;
;WRITE ADDRESS MARK ON TRACK
;
	IN	WAIT		;WAIT FOR DRQ
	ORA	A		;SET FLAGS
	JP	ERRMSG		;JMP OUT IF ERROR
	MVI	A,0FCH		;LOAD ADDRESS MARK
	OUT	DDATA		;WRITE IT ON DISK
;
;POST INDEX GAP
;
	MVI	B,26		;SET # OF BYTES
POSTID:	IN	WAIT		;WAIT FOR DRQ
	ORA	A		;SET FLAGS
	JP	ERRMSG		;JMP OUT IF ERROR
	MVI	A,0FFH		;LOAD FILL DATA
	OUT	DDATA		;WRITE IT ON DISK
	DCR	B		;COUNT = COUNT - 1
	JNZ	POSTID		;IF NOT 0 GO BACK
;
;PRE ID SECTION
;
ASECT:	MVI	B,6		;GET # OF BYTES
SECTOR:	IN	WAIT		;WAIT FOR DRQ
	ORA	A		;SET FLAGS
	JP	ERRMSG		;JMP OUT IF ERROR
	XRA	A		;MAKE A = 0
	OUT	DDATA		;WRITE IT ON TRACK
	DCR	B		;COUNT=COUNT+1
	JNZ	SECTOR		;JMP BACK IF NOT DONE
;
;WRITE ID ADDRESS MARK
;
	IN	WAIT		;WAIT FOR DRQ
	ORA	A		;SET FLAGS
	JP	ERRMSG		;IF ERROR JMP OUT
	MVI	A,0FEH		;GET ADDRESS MARK
	OUT	DDATA		;WRITE IT ON DISK
;
;WRITE TRACK NUMBER ON DISK
;
	IN	WAIT		;WAIT FOR DRQ
	ORA	A		;SET FLAGS
	JP	ERRMSG		;JMP OUT IF ERROR
	MOV	A,C		;GET TRACK NUMBER
	OUT	DDATA		;WRITE IT ON DISK
;
;WRITE ONE BYTE OF 00
;
	IN	WAIT		;WAIT FOR DRQ
	ORA	A		;SET FLAGS
	JP	ERRMSG		;JMP OUT IF ERROR
	XRA	A		;SET A TO 0
	OUT	DDATA		;WRITE IT ON DISK
;
;WRITE SECTOR # ON DISK
;
	IN	WAIT		;WAIT FOR DRQ
	ORA	A		;SET FLAGS
	JP	ERRMSG		;JMP OUT IF ERROR
	MOV	A,D		;GET SECTOR #
	OUT	DDATA		;WRITE IT ON DISK
;
;ONE MORE BYTE 0
;
	IN	WAIT		;WAIT FOR DRQ
	ORA	A		;SET FLAGS
	JP	ERRMSG		;JMP OUT IF ERROR
	XRA	A		;SET A TO _
	OUT	DDATA		;WRITE IT TO DISK
	INR	D		;BUMP SECT. #
;
;WRITE 2 CRC'S ON THIS SECTOR
;
	IN	WAIT		;WAIT FOR DRQ
	ORA	A		;SET FLAGS
	JP	ERRMSG		;JMP OUT IF ERROR
	MVI	A,0F7H		;GET CRC PATTERN
	OUT	DDATA		;WRITE IT ON DISK
;
;PRE DATA 17 BYTES 00
;
	MVI	B,17		;SET COUNT
PREDAT:	IN	WAIT		;WAIT FOR DRQ
	ORA	A		;SET FLAGS
	JP	ERRMSG		;JMP OUT IF ERROR
	XRA	A		;SET A TO 00
	OUT	DDATA		;WRITE IT ON DISK
	DCR	B		;REDUCE COUNT BY 1
	JNZ	PREDAT		;GO BACK IF NOT DONE
;
;DATA ADDRESS MARK
;
	IN	WAIT		;WAIT FOR DRQ
	ORA	A		;SET FLAGS
	JP	ERRMSG		;JMP OUT IF ERROR
	MVI	A,0FBH		;GET DATA ADDRESS MARK
	OUT	DDATA		;WRITE IT ON DISK
;
;FILL DATA FIELD WITH E5
;
	MVI	B,128		;SET FIELD LENGTH
DFILL:	IN	WAIT		;WAIT FOR DRQ
	ORA	A		;YOU KNOW WHAT
	JP	ERRMSG		;HAPPENS HERE BY NOW
	MVI	A,0E5H		;GET FILL BYTE
	OUT	DDATA		;WRITE IT ON DISK
	DCR	B		;DROP 1 FROM COUNT
	JNZ	DFILL		;DO TIL 00
;
;WRITE CRC'S
;
	IN	WAIT		;WAIT TILL DRQ
	ORA	A		;SET FLAGS
	JP	ERRMSG		;JMP OUT IF ERROR
	MVI	A,0F7H		;GET CRC BYTE
	OUT	DDATA		;WRITE IT ON DISK
;
;END OF SECTOR FILL
;
	DCR	E		;REDUCE SECTOR COUNT
	JZ	ENDTRK		;IF 0 DO END OF TRACK RT
DATGAP:	IN	WAIT		;WAIT FOR DRQ
	ORA	A		;SET FLAGS 
	JP	ERRMSG		;JMP OUT IF ERROR
	MVI	A,0FFH		;GET FILL CHAR
	OUT	DDATA		;WRITE IT ON DISK
	JMP	POSTID-2	;DO NEXT SECTOR
;
;DO TRACK & SECTOR HOUSE KEEPING
;
ENDTRK:	IN	WAIT		;WAIT FOR DRQ OR INTRQ
	ORA	A		;SET FLAGS
	JP	DONE		;JMP OUT IF ERROR
	MVI	A,0FFH		;LOAD A WITH FFH
	OUT	DDATA		;WRITE IT ON DISK
	JMP	ENDTRK		;DO UNTIL INTRQ
;
;ERROR SORT ROUTINE
;
DONE:	IN	DSTAT		;READ STATUS
	ANI	0FFH		;TEST FOR FLAG
	JNZ	ERRMSG+2	;IF ERR GO TO ERR PRINT RTN
	INR	C		;BUMP TRACK #
	DCR	H		;TRK COUNT = TRK COUNT - 1
	JZ	ANOTHR		;DONE WITH DISK
;
BMPTRK:	MVI	A,50H		;LOAD STEP IN CMD.
	OUT	DCOM		;DO IT.
	IN	WAIT		;WAIT TIL DONE.
	IN	DSTAT		;READ STATUS.
	ANI	80H
	JNZ	ERRMSG+2	;
	MVI	A,1		;GET PERSCI STEP CMD.
	OUT	WAIT		;STEP IT
	LDA	DISK
	RLC
	RLC
	RLC
	RLC
	ANI	10H
	CMA
	PUSH	PSW
	ANI	72H		;CHANGE WAIT TO HOLD FOR
	OUT	WAIT		;SEEK COMPLETE
	IN	WAIT		;
	POP	PSW
	ANI	0F2H		;CHANGE BACK TO HOLD FOR
	OUT	WAIT		;DRQ/IRQ
	JMP	NXTTRK
;
;DONE, ASK USER IF HE WANT'S TO DO ANOTHER
;
ANOTHR:	LXI	D,M8		;DO ANOTHER ?
	CALL	TMSG		;"Y FOR ANOTHER, CR TO EXIT"
	CALL	INCH
	CALL	CRLF
	CPI	'Y'
	JZ	START
REBOOT:	LDA	4		;GET CPM'S LOGGED DISK
	STA	DISK		;
	LXI	D,1		;IN CASE DRIVE NOT READY
	LXI	H,NRMSG2
	SHLD	MSGPTR+1
	CALL	HOME		;TO RE-SELECT CURRENT DRIVE
	JMP	0		;ALL DONE
;
;ROUTINE TO HOME THE DISK -- ALSO SELECTS PROPER DRIVE
;NOTE THAT WITH THE PERSCI'S, HOMING EITHER DRIVE HOME'S
;BOTH OF THEM -- PERMITS A SLIGHT REDUCTION IN REQD CODE
;
HOME:	MVI	A,0B2H		;GET RESTORE COMND DRIVE 0
	OUT	WAIT		;THEN ISSUE IT
	MVI	A,3		;NOW DO HOME FOR 1771
	OUT	DCOM
	IN	WAIT		;WAIT FOR HOME
	LDA	DISK		;CLR RESTORE & SELECT DRIVE
	RLC
	RLC
	RLC
	RLC
	ANI	10H		;MOVE DISK BIT TO 10H
	CMA			;COMPLEMENT
	ANI	0F2H		;RESET RESTORE LINE.
	OUT	WAIT
	IN	DSTAT		;GET DISK STATUS
	ANI	80H		;MASK & SET FLAGS
	RZ
;
;GET HERE IF DRIVE NOT READY
;
NOTRDY:	DCX	D
	MOV	A,D
	ORA	E
	JNZ	HOME
MSGPTR:	LXI	D,NRMSG
	CALL	TMSG		;ASK USER TO PLEASE INSERT DISK
	LXI	D,4000
	JMP	NOTRDY
;
NRMSG:	DB	'PLEASE INSERT DISK IN DRIVE '
INMSG:	DB	0,CR,LF,'$'	;DRIVE LETTER STORED HERE
;
NRMSG2:	DB	'PLEASE INSERT SYSTEM DISK FOR REBOOTING IN'
	DB	' DRIVE A:',CR,LF,'$'
;
;ERROR ROUTINE FOR DISK ERRORS
;
ERRMSG:	IN	DSTAT		;GET DISK STAUS
	LXI	D,ME1		;DISK ERROR MESSAGE
	CALL	TMSG
	CALL	HOUT		;TYPE HEX ERROR CODE
	LXI	D,ME2		;" AT TRACK "
	CALL	TMSG
	MOV	A,C
	CALL	HOUT		;TYPE TRACK # IN HEX
;
;ERROR AFTER FORMAT OPERATION BEGUN
;
EREX2:	LXI	D,ABORTMG	;PRINT ERROR MESSAGE
	CALL	TMSG
	CALL	CRLF
	LXI	D,M9		;HAVE USER RESTORE SYS. DISK
	CALL	TMSG
	CALL	INCH		;INPUT AND IGNORE CHARACTER
	JMP	REBOOT		;GO RETURN TO SYSTEM
;
;OUTPUT CRLF TO CONSOLE
;
CRLF:	LXI	D,CRLFM
;
;OUTPUT	MESSAGE (DE) TO CONSOLE
;
TMSG:	PUSH	PSW
	PUSH	B
	MVI	C,9
	CALL	BDOS
	POP	B
	POP	PSW
	RET
;
;INPUT	CHAR TO A, WITH ECHO
;
INCH:	MVI	C,1
	CALL	BDOS
	CPI	03H		;CONTROL-C
	JZ	CCEXIT		;REBOOT ON ^C
	CPI	CR
	RNZ			;ALL DONE EXCEPT CR
	MVI	A,0AH		;LF AFTER CR
;
;OUTPUT CHAR FROM A
;
OUCH:	PUSH	PSW
	MOV	E,A
	MVI	C,2
	CALL	BDOS
	POP	PSW
	RET
;
;CONTROL-C EXIT (GET HERE FROM "INCH" ROUTINE)
;
CCEXIT:	MVI	A,'^'
	CALL	OUCH		;PRINT "^C"
	MVI	A,'C'
	CALL	OUCH
	JMP	EREX2		;ASSUME SYSTEM DISK REMOVED
;
;HEX OUTPUT (A) TO CONSOLE
;
HOUT:	PUSH	PSW
	RAR
	RAR
	RAR
	RAR
	CALL	NIBBLE
	POP	PSW
NIBBLE:	ANI	0FH
	CPI	0AH
	JC	NBL1
	ADI	'A'-'9'-1
NBL1:	ADI	'0'
	JMP	OUCH
;
SIGNON:	DB	'FORMAT VERS 1.4.0 ',CR,LF,LF,'$'
M1:	DB	'THIS COMMAND INITIALIZES A DISKETTE,',CR,LF
	DB	'WIPING OUT ALL INFORMATION ON IT.',CR,LF
	DB	'IS THAT WHAT YOU WANT? (Y-N) $'
M2:	DB	CR,LF,'REMOVE SYSTEM DISKETTE, TYPE CR $'
M3A:	DB	' ? ',CR,LF
M3:	DB	'ENTER DRIVE OF DISK TO FORMAT (A-B) $'
M4:	DB	CR,LF,'OK TO GO? (Y-N) $'
M8:	DB	CR,LF,'DONE',CR,LF
	DB	'TO FORMAT ANOTHER DISKETTE ON SAME DRIVE,'
	DB	' TYPE ''Y'' ',CR,LF,'OTHERWISE '
M9:	DB	'INSERT SYSTEM DISKETTE BACK IN DRIVE A, TYPE CR $'
ABORTMG:DB	CR,LF,'OPERATION ABORTED ',CR,LF,'$'
ME1:	DB	'DISC ERROR CODE $'
ME2:	DB	' TRACK $'
CRLFM:	DB	CR,LF,'$'
;
DISK:	DB	0
;
	DS	11H
STACK:	EQU	$
	END	FORMAT

