title 	'CP/M 3 - HEXCOM - Oct 1982'
;

;  Copyright (C) 1982
;  Digital Research
;  P.O. Box 579
;  Pacific Grove, CA 93950

;  Revised:
;    22 Oct  82  by Paul Lancaster
;    25 Oct  82  by Doug Huskey
;
;
;	**********  HEXCOM  **********
;

;PROGRAM TO CREATE A CP/M "COM" FILE FROM A "HEX" FILE.

;THIS PROGRAM IS VERY SIMILAR IN FUNCTION TO THE CP/M
;UTILITY CALLED "LOAD". IT IS OPTIMIZED WITH RESPECT TO
;EXECUTION SPEED AND MEMORY SPACE. IT RUNS ABOUT TWICE
;AS FAST AS THE CP/M COUNTERPART ON A LONG "HEX" FILE.
;IT IS ALSO ABOUT 700 BYTES SHORTER.

;ONE MINOR DIFFERENCE BETWEEN "HEXCOM" AND "LOAD" THAT MAY
;BE VISIBLE TO THE USER IS THAT VERY LARGE LOAD ADDRESS
;INVERSIONS ARE TOLERATED BY "HEXCOM", WHEREAS THE MAXIMUM
;ALLOWED INVERSION IN "LOAD" IS 80H. THE MAXIMUM IN "HEXCOM"
;IS A FUNCTION OF THE TPA SIZE.
;CAUTION SHOULD BE EXERCIZED WHEN USING AN INVERSION GREATER
;THAN 80H IN "HEXCOM" SINCE PART OF THE COMFILE MAY NOT
;GET CREATED IF THE FINAL LOAD ADDRESS IS INVERTED WITH
;RESPECT TO THE "LAST ADDRESS" IN THE "HEX" FILE.

;*******************************************************

;VERSION 1.00			6 MARCH 1979
;ORIGINAL VERSION.
;*******************************************************

;22 October 1982 - Changed assumed CCP length for CP/M-PLUS
;25 October 1982 - Changed version to 3.0
;
;
			EQUATES

VERS		EQU	300		;VERSION TIMES 100
CR		EQU	0DH
LF		EQU	0AH
BDOS		EQU	5
DEFAULT$FCB	EQU	5CH


		ORG	100H

	;	include file for use with ASM programs
	;
	;*********************************************
	;* STANDARD DIGITAL RESEARCH COM FILE HEADER *
	;*********************************************
	;
	JMP	BEGIN		;LABEL CAN BE CHANGED
	;
	;*********************************************
	;* Patch Area, Date, Version & Serial Number *
	;*********************************************
	;
	dw	0,0,0,0,0,0
	dw	0,0,0,0,0,0,0,0
	dw	0,0,0,0,0,0,0,0
	dw	0,0,0,0,0,0,0,0
	db	0

	db	'CP/M Version 3.0'
	maclib	makedate	;[JCE] Build date
	@LCOPY
	@BDATE			; version date day-month-year
	db	0,0,0,0		; patch bit map
	db	'654321'	; Serial no.

;
BEGIN:
;	code starts here
	LXI	H,0
	DAD	SP		;GET CURRENT CCP STACK
	SHLD	STACK$SAVE	;SAVE IT
	LXI	SP,STACK	;INIT LOCAL STACK
	LXI	D,SIGNON$MSG	;POINT SIGN-ON MESSAGE
	CALL	PRINT$BUFFER	;SEND IT TO CONSOLE
	LXI	D,DEFAULT$FCB	;FILE NAME TO HEX FCB
	LXI	H,HEX$FCB
	PUSH	D		;SAVE COM FCB ADDR
	PUSH	H		;-AND HEX FCB ADDR
	MVI	C,33		;MOVE ENTIRE FCB
MOVEFCB	LDAX	D		;GET BYTE FROM DFLT FCB
	MOV	M,A		;MOVE TO HEX FCB
	INX	D		;BUMP POINTERS
	INX	H
	DCR	C		;HIT COUNTER
	JNZ	MOVEFCB		;LOOP TILL DONE
	LXI	H,HEX$FCB+9	;"HEX" TYPE NAME TO FCB
	MVI	M,'H'
	INX	H
	MVI	M,'E'
	INX	H
	MVI	M,'X'
	LXI	H,DEFAULT$FCB+9	;"COM" TYPE NAME TO FCB
	MVI	M,'C'
	INX	H
	MVI	M,'O'
	INX	H
	MVI	M,'M'
	POP	D		;HEX$FCB TO <DE>
	MVI	C,15		;OPEN FILE
	CALL	BDOS
	INR	A		;SEE IF -1 FOR ERROR
	LXI	D,COSMSG
	JZ	ERROR$ABORT	;CANNOT OPEN SOURCE
	POP	D		;COM FCB ADDR
	PUSH	D		;KEEP COPY ON STACK
	MVI	C,19		;DELETE FILE
	CALL	BDOS		;DELETE OLD "COM" FILE
	POP	D		;GET COM FCB ADDR AGAIN
	PUSH	D		;SAVE IT STILL
	MVI	C,22		;MAKE FILE
	CALL	BDOS		;CREATE "COM" FILE
	INR	A		;SEE IF -1 FOR ERROR
	LXI	D,NMDSMSG
	JZ	ERROR$ABORT	;NO MORE DIR SPACE

;DEFINE AND CLEAR THE COMFILE BUFFER

	LDA	7		;GET BDOS PAGE ADDRESS
	SUI	16		;ALLOW FOR UP TO 4K CCP
	MOV	H,A		;HI BYTE OF COM BUFFER TOP
	MVI	L,0		;END ON PAGE BOUNDARY
	SHLD	CURR$COM$BUF$END
	SUI	(HIGH COMFILE$BUFFER)+1
	MVI	L,80H		;START IN MIDDLE OF PAGE
	MOV	H,A		;BUFFER LENGTH IN PAGES
	SHLD	CURR$COM$BUF$LEN
	CALL	CLEAR$COMBUFFER	;ZERO-OUT COM BUFFER

;	HEX RECORD LOOP

SCAN$FOR$COLON:
	CALL	GET$HEXFILE$CHAR
	CPI	':'		;DO WE HAVE COLON YET?
	JNZ	SCAN$FOR$COLON
	CALL	GET$BINARY$BYTE	;GOT COLON. GET LOAD COUNT
	STA	LOAD$COUNT	;STORE COUNT FOR THIS RECORD
	JZ	FINISH$UP	;ZERO MEANS ALL DONE

;INCREMENT BYTES-READ COUNTER BY NUMBER OF BYTES TO BE
;LOADED IN THIS RECORD.

	LXI	H,BYTES$READ$COUNT
	ADD	M	;ADD LO BYTE OF SUM
	MOV	M,A	;SAVE NEW LO BYTE
	JNC	FORM$LOAD$ADDRESS
	INX	H	;POINT HI BYTE OF SUM
	INR	M	;BUMP HI BYTE

;NOW SET NEW LOAD ADDRESS FROM THE
;HEX FILE RECORD.

FORM$LOAD$ADDRESS:
	CALL	GET$BINARY$BYTE
	PUSH	PSW	
	CALL	GET$BINARY$BYTE
	POP	H		;HI BYTE TO <H>
	MOV	L,A		;AND LO BYTE TO <L>
	SHLD	LOAD$ADDRESS	;SAVE NEW LOAD ADDRESS
	XCHG			;PUT IN <DE>
	LHLD	CURRENT$COM$BASE

;NEW LOAD ADDRESS MINUS THE CURRENT COMFILE BASE GIVES
;THE NEW COM BUFFER OFFSET.

	MOV	A,E
	SUB	L
	MOV	L,A
	MOV	A,D
	SBB	H
	MOV	H,A
	SHLD	COM$BUF$OFFSET	;STORE NEW OFFSET
	LXI	D,ILAMSG	;POINT ERR MSG
	JC	ERROR$ABORT	;FATAL INVERSION IF CY SET

;FIRST ADDRESS HAS ALREADY BEEN ESTABLISHED IF "FIRST$ADDRESS"
;IS NON-ZERO.

	LDA	FIRST$ADDRESS+1	;--ONLY PAGE NO. NEED BE
	ORA	A		;--CHECKED SINCE 1ST ADDR
	JNZ	GET$ZERO$BYTE	;--CAN'T BE IN PAGE ZERO
	LXI	D,FAMSG		;POINT "1ST ADDR" MSG
	CALL	MSG$ON$NEW$LINE	;ANNOUNCE FIRST ADDRESS
	LHLD	LOAD$ADDRESS	;THIS IS FIRST ADDR
	SHLD	FIRST$ADDRESS	;SET FIRST ADDRESS
	CALL	WORD$OUT	;SEND IT TO CONSOLE

;SKIP OVER THE ZERO BYTE OF THE HEX RECORD. IT HAS NO
;SIGNIFICANCE TO THIS PROGRAM.

GET$ZERO$BYTE:
	CALL	GET$BINARY$BYTE

;THIS LOOP LOADS THE COM FILE WITH THE BYTE VALUES IN THE
;CURRENT HEX RECORD.

BYTE$LOAD$LOOP:
	CALL	GET$BINARY$BYTE	;GET BYTE TO LOAD
	CALL	PUT$TO$COMFILE	;LOAD IT TO COM FILE
	LXI	H,LOAD$COUNT
	DCR	M		;HIT LOAD COUNT
	JNZ	BYTE$LOAD$LOOP	;MORE LOADING IF NOT-ZERO

;UPDATE THE LAST ADDRESS IF CURRENT ABSOLUTE LOAD ADDRESS
;IS HIGHER THAN THE CURRENT VALUE OF "LAST$ADDRESS"

	LHLD	LAST$ADDRESS	;GET THE CURR VALUE
	XCHG			;TO <DE>
	CALL	ABSOLUTE	;ABSOLUTE ADDR TO <HL>
	MOV	A,E		;--SUBTRACT ABSOLUTE
	SUB	L		;--ADDRESS FROM CURRENT
	MOV	A,D		;--LAST ADDRESS
	SBB	H
	JNC	CHECK$CHECKSUM	;LAST ADDR LARGER IF NC
	DCX	H		;DOWN 1 FOR LAST ACTUAL LOAD
	SHLD	LAST$ADDRESS	;UPDATE IT

;VERIFY THE CHECKSUM FOR THIS RECORD.

CHECK$CHECKSUM:
	CALL	GET$BINARY$BYTE	;GET CHECKSUM BYTE
	JZ	SCAN$FOR$COLON	;ZERO ON FOR CHECKSUM OK
	LXI	D,CSEMSG	;CHECKSUM ERROR
	JMP	HEXFILE$ERROR

;SEND PROCESSING SUMMARY TO THE CONSOLE AND FLUSH THE
;COM BUFFER OF ANY UNWRITTEN DATA.

FINISH$UP:
	LXI	D,LSTADDRMSG	;POINT "LAST ADDR" MSG
	CALL	MSG$ON$NEW$LINE	;SEND IT OUT
	LHLD	LAST$ADDRESS	;GET THE LAST ADDRESS
	CALL	WORD$OUT	;SEND IT TO CONSOLE
	LXI	D,BRMESSAGE	;POINT "BYTES READ" MSG
	CALL	MSG$ON$NEW$LINE	;SEND IT OUT
	LHLD	BYTES$READ$COUNT	;GET THE COUNT
	CALL	WORD$OUT	;SEND IT OUT

;THE FOLLOWING CODE PREPARES FOR AND MAKES THE FINAL CALL
;TO THE "PUT" ROUTINE IN ORDER TO FLUSH THE "COM" BUFFER.
;IT HAS BEEN "KLUGED" IN ORDER TO WORK AROUND THE BOUNDARY
;CONDITION OF HAVING AN OFFSET OF <100H AT FLUSH TIME.
;WE FORCE THE OFFSET AND LENGTH TO BE NON-ZERO SO THE
;INITIAL COMPARE IN THE "PUT" ROUTINE WON'T GET SCREWED
;UP. THE BUFFER END ADDRESS IS NOT PLAYED WITH, HOWEVER.
;THIS IS TO INSURE THAT THE CORRECT NUMBER OF RECORDS GET
;WRITTEN.

	LHLD	COM$BUF$OFFSET	;GET THE CURRENT OFFSET
	PUSH	H		;SAVE OFFSET FOR LATER
	LXI	D,COMFILE$BUFFER ;GET BUFFER ADDRESS
	DAD	D		;ADD TO OFFSET TO GET LEN
	SHLD	CURR$COM$BUF$END ;STORE NEW END ADDR
	LXI	H,CLEAR$FLAG	;POINT TO CLEAR FLAG
	INR	M		;DISABLE CLEAR WITH NON-ZERO
	POP	H		;GET OFFSET BACK
	MVI	H,1		;FORCE HI BYTE NON-ZERO
	SHLD	COM$BUF$OFFSET	;FAKE OFFSET
	SHLD	CURR$COM$BUF$LEN ;AND FAKE LENGTH
	CALL	PUT$TO$COMFILE	;FLUSH THE BUFFER
	LXI	D,RWMSG		;POINT "REC WRIT" MSG
	CALL	MSG$ON$NEW$LINE	;SEND IT OUT
	LDA	RECORDS$WRITTEN	;GET THE COUNT
	CALL	BYTE$OUT	;SEND IT OUT
	CALL	CRLF		;SEND OUT CRLF
	POP	D		;COM FILE FCB ADDR
	MVI	C,16		;CLOSE FILE
	CALL	BDOS		;COM FILE CLOSE
	INR	A		;SEE IF -1 FOR ERROR
	LXI	D,CCFMSG	;CANNOT CLOSE FILE
	JZ	ERROR$ABORT
CRLF$AND$EXIT:
	CALL	CRLF
EXIT:
	LXI	D,80H
	MVI	C,26		;RE-SET DMA TO 80H
	CALL	BDOS
	LHLD	STACK$SAVE	;RECOVER CCP STACK POINTER
	SPHL			;TO <SP>
	RET			;RET TO CCP




;		SUBROUTINES



;THIS ROUTINE GETS TWO CHARACTERS FROM THE HEX FILE
;AND CONVERTS TO AN 8-BIT BINARY VALUE, RETURNED IN <A>.

GET$BINARY$BYTE:
	CALL	GET$HEX$DIGIT	;GET HI NYBBLE FIRST
	ADD	A		;SHIFT UP 4 SLOTS
	ADD	A
	ADD	A
	ADD	A
	PUSH	PSW		;SAVE HI NYBBLE
	CALL	GET$HEX$DIGIT	;NOW GET LO NYBBLE
	POP	B		;HI NYBBLE TO <B>
	ORA	B		;COMBINE NYBBLES TO FORM BYTE
	MOV	B,A		;SAVE THE BYTE
	LXI	H,CHECKSUM
	ADD	M		;UPDATE THE CHECKSUM
	MOV	M,A		;AND STORE IT
	MOV	A,B		;GET BYTE BACK
	RET			;ZERO SET MEANS CHECKSUM=0


;ROUTINE TO GET A HEX-ASCII CHARACTER FROM THE HEX FILE
;AND RETURN IT IN THE <A> REGISTER CONVERTED TO BINARY.
;A CHECK FOR LEGAL HEX VALUE IS MADE. PROGRAM ABORTS
;WITH APPROPRIATE MESSAGE IF ILLEGAL DIGIT ENCOUNTERED.

GET$HEX$DIGIT:
	CALL	GET$HEXFILE$CHAR
	SUI	'0'		;REMOVE ASCII BIAS
	CPI	10		;DECIMAL DIGIT?
	RC
	SUI	7		;STRIP ADDITIONAL BIAS
	CPI	10		;MUST BE AT LEAST 10
	JC	ILLHEX
	CPI	16		;MUST BE 15 OR LESS
	RC
ILLHEX	LXI	D,IHDMSG	;ILLEGAL HEX DIGIT

;ROUTINE TO INDICATE THAT AN ERROR HAS BEEN FOUND IN THE
;HEX FILE (EITHER CHECKSUM OR ILLEGAL HEX DIGIT).
;APPROPRIATE MESSAGES ARE PRINTED AND THE PROGRAM ABORTS.

HEXFILE$ERROR:
	CALL	MSG$ON$NEW$LINE	;PRINT ERROR TYPE
	LXI	D,LAMESSAGE	;POINT "LOAD ADDR" MSG
	CALL	MSG$ON$NEW$LINE	;SEND IT OUT
	LHLD	LOAD$ADDRESS	;GET LOAD ADDR
	CALL	WORD$OUT	;SEND IT OUT
	LXI	D,EAMSG		;POINT "ERR ADDR" MSG
	CALL	MSG$ON$NEW$LINE	;SEND IT OUT
	CALL	ABSOLUTE	;GET ABSOLUTE ADDR
	CALL	WORD$OUT	;THIS IS ERR ADDR
	LXI	D,BRMESSAGE	;POINT "BYTES READ" MSG
	CALL	MSG$ON$NEW$LINE	;SEND IT OUT
	CALL	PRINT$LOAD$ADDR	;SEND OUT CURR LOAD ADDR

;PRINT OUT ALL BYTES THAT WERE LOADED FROM THE CURRENT
;HEX RECORD UP TO THE POINT WHERE THE ERROR WAS DETECTED.

ERR$OUT$LOOP:
	LHLD	LOAD$ADDRESS	;POINT TO BYTE TO BE OUTPUT
	XCHG			;TO <DE>
	CALL	ABSOLUTE	;GET ABSOLUTE ADDR
	MOV	A,E		;--SEE IF "LOAD ADDR"
	SUB	L		;--HAS REACHED ABSO ADDR
	MOV	A,D
	SBB	H
	JNC	CRLF$AND$EXIT	;DONE IF THEY'RE EQUAL
	MOV	A,E		;SEE IF MULTIPLE OF 16
	ANI	0FH
	CZ	PRINT$LOAD$ADDR	;IF MULTIPLE OF 16
	LHLD	LOAD$ADDRESS	;GET LOAD ADDR AGAIN
	XCHG			;TO <DE>
	LHLD	CURRENT$COM$BASE
	MOV	A,E		;--CALC OFFSET OF CURR
	SUB	L		;--BYTE TO GO OUT
	MOV	L,A		;LO BYTE OF OFFSET
	MOV	A,D		;HI BYTE OF LOAD ADDR
	SBB	H
	MOV	H,A		;HI BYTE OF OFFSET
	LXI	B,COMFILE$BUFFER
	DAD	B	;<HL> NOW POINTS TO BYTE TO GO
	MOV	A,M		;GET THE BYTE FROM BUFFER
	CALL	BYTE$OUT	;SEND IT OUT
	LHLD	LOAD$ADDRESS	;BUMP LOAD ADDRESS
	INX	H
	SHLD	LOAD$ADDRESS
	MVI	A,' '		;SEND A SPACE BETWEEN BYTES
	CALL	CHAR$TO$CONSOLE
	JMP	ERR$OUT$LOOP	;BACK FOR MORE



;ROUTINE TO GET A CHARACTER FROM THE HEX FILE BUFFER.
;CHAR IS RETURNED IN <A>.


GET$HEXFILE$CHAR:
	LDA	HEX$BUFFER$OFFSET
	INR	A		;BUMP HEX OFFSET
	JP	GETCHAR		;PLUS IF NOT 80H YET
	LXI	D,HEX$BUFFER
	MVI	C,26		;SET-DMA CODE
	CALL	BDOS		;SET DMA ADDR TO HEX BUFFER
	LXI	D,HEX$FCB	;POINT HEX FCB
	MVI	C,20		;READ-NEXT-RECORD CODE
	CALL	BDOS		;GET NEXT HEXFILE RECORD
	ORA	A		;TEST FOR ERROR
	LXI	D,DRMSG		;ASSUME ERROR FOR NOW
	JNZ	ERROR$ABORT	;FATAL ERR IF NOT ZERO
GETCHAR:
	STA	HEX$BUFFER$OFFSET
	MVI	H,HIGH HEX$BUFFER
	MOV	L,A		;POINT TO NEXT CHAR
	MOV	A,M		;GET THE CHARACTER
	RET


;
;THIS ROUTINE PUTS A DATA BYTE TO THE "COM" FILE.
;THE BYTE IS PASSED IN <A>.
;THE FIRST COMPARE IS DONE ON JUST THE HI BYTES FOR THE
;SAKE OF SPEED, SINCE WE ARE PROCESSING THE "HEX" FILE
;"ON THE FLY".

PUT$TO$COMFILE:
	PUSH	PSW		;SAVE BYTE TO LOAD
	LHLD	COM$BUF$OFFSET	;GET CURRENT OFFSET
	XCHG			;TO <DE>
PTC	LDA	CURR$COM$BUF$LEN+1 ;PAGE NO. OF BUFF TOP
	DCR	A		;ONE LESS FOR COMPARE
	CMP	D		;TOP < OFFSET?
	JNC	STORE$BYTE	;STORE BYTE IF NOT
	LHLD	CURR$COM$BUF$LEN
	MOV	A,E		;SUBTRACT LEN FROM OFFSET--
	SUB	L		;--TO GET NEW OFFSET
	MOV	C,A		;<C> HAS LO BYTE OF DIFF
	MOV	A,D		;HI BYTE OF OFFSET
	SBB	H		;MINUS HI BYTE OF BUFF LENGTH
	MOV	B,A		;<BC> HAS NEW OFFSET
	PUSH	B		;SAVE NEW OFFSET
	XCHG			;BUFFER LENGTH TO <DE>
	LHLD	CURRENT$COM$BASE ;COM BASE TO <HL>
	DAD	D		;INCREASE IT BY BUFFER LENGTH
	SHLD	CURRENT$COM$BASE ;STORE NEW BASE
	LHLD	CURR$COM$BUF$END
	LXI	D,COMFILE$BUFFER	;BUFFER ADDR TO <DE>
COMLOOP:
	MOV	A,E		;SUBTRACT BUFF END FROM POINTER
	SUB	L
	MOV	A,D
	SBB	H		;WRITTEN TO END OF BUFFER YET?
	JNC	STORE		;CY OFF MEANS WE'RE DONE
	PUSH	H		;SAVE BUFFER END ADDRESS
	PUSH	D		;SAVE WRITE POINTER
	MVI	C,26		;SET DMA FUNCTION CODE
	CALL	BDOS		;SET NEW DMA ADDRESS
	MVI	C,21		;WRITE-NEXT-RECORD CODE
	LXI	D,DEFAULT$FCB	;POINT COM FILE FCB
	CALL	BDOS		;WRITE NEXT COM RECORD
	ORA	A		;TEST FOR ERROR ON WRITE
	LXI	D,DWMSG		;POINT WRITE ERROR MSG
	JNZ	ERROR$ABORT	;BOMB IF WRITE ERROR
	POP	D		;RESTORE WRITE POINTER
	LXI	H,128		;SECTOR SIZE
	DAD	D		;BUMP POINTER BY 128
	XCHG			;NEW POINTER TO <DE>
	LXI	H,RECORDS$WRITTEN
	INR	M
	POP	H		;RESTORE BUFFER END ADDR
	JMP	COMLOOP		;SEE IF END OF BUFFER YET
STORE:
	LDA	CLEAR$FLAG	;GET CLEAR-BUFFER FLAG
	ORA	A		;SHALL WE CLEAR?
	CZ	CLEAR$COMBUFFER	;ZERO THE BUFFER
	POP	D		;GET BACK NEW OFFSET
	JMP	PTC		;SEE IF WE MUST FLUSH AGAIN
STORE$BYTE:
	LXI	H,COMFILE$BUFFER	;BUFFER ADDR TO <HL>
	DAD	D			;ADD TO CURRENT OFFSET
	POP	PSW		;RETRIEVE BYTE TO WRITE
	MOV	M,A		;STUFF IT
	INX	D		;BUMP OFFSET
	XCHG			;TO <HL> FOR STORE
	SHLD	COM$BUF$OFFSET	;UPDATE OFFSET
	RET			;ALL DONE


;
;ROUTINE TO CONVERT THE 2-BYTE VALUE IN <HL> TO
;TWO ASCII CHARACTERS AND SEND THEM TO THE CONSOLE.
;
WORD$OUT:
	PUSH	H	;SAVE WORD
	MOV	A,H	;HI WORD GOES OUT 1ST
	CALL	BYTE$OUT
	POP	H	;RESTORE WORD
	MOV	A,L	;LO BYTE GOES NEXT
BYTE$OUT:
	PUSH	PSW	;SAVE BYTE
	RRC! RRC! RRC! RRC	;HI NYBBLE COMES DOWN
	CALL	NYBBLE$OUT
	POP	PSW	;RESTORE VALUE
NYBBLE$OUT:
	ANI	0FH
	ADI	90H
	DAA
	ACI	40H
	DAA
CHAR$TO$CONSOLE:
	MOV	E,A
	MVI	C,2	;WRITE CONSOLE CHAR FUNC CODE
	JMP	BDOS
;
;ROUTINE TO OUTPUT A "CRLF".
;
CRLF:
	MVI	A,CR
	CALL	CHAR$TO$CONSOLE
	MVI	A,LF
	JMP	CHAR$TO$CONSOLE
;
;ROUTINE TO PRINT A BUFFER TO THE CONSOLE.
;<DE> POINTS TO THE MESSAGE ON ENTRY.
;EARLIEST ENTRY POINT STARTS MESSAGE ON A NEW LINE
;
MSG$ON$NEW$LINE:
	PUSH	D	;SAVE MESSAGE POINTER
	CALL	CRLF	;START NEW LINE
	POP	D	;RESTORE MESSAGE POINTER
PRINT$BUFFER:
	MVI	C,9	;OUTPUT BUFFER TO CONSOLE
	JMP	BDOS
;
;
;ERROR ABORT ROUTINE
;

ERROR$ABORT:
	PUSH	D		;SAVE MESSAGE POINTER
	LXI	D,ERRMSG	;POINT "ERROR" MSG
	CALL	MSG$ON$NEW$LINE	;SEND IT OUT
	POP	D		;RESTORE MESSAGE POINTER
	CALL	PRINT$BUFFER	;SEND OUT ERR TYPE
	LXI	D,LAMESSAGE	;POINT "LOAD ADDR" MSG
	CALL	MSG$ON$NEW$LINE	;SEND IT OUT
	CALL	ABSOLUTE	;GET ABSOLUTE ADDR
	CALL	WORD$OUT	;SEND IT OUT
	JMP	EXIT		;BAIL OUT

;THIS ROUTINE PRINTS THE LOAD ADDRESS OF THE CURRENT
;HEX RECORD ON A NEW LINE FOLLOWED BY A ':' AND SPACE.

PRINT$LOAD$ADDR:
	CALL	CRLF
	LHLD	LOAD$ADDRESS
	CALL	WORD$OUT
	MVI	A,':'
	CALL	CHAR$TO$CONSOLE
	MVI	A,' '
	JMP	CHAR$TO$CONSOLE


;ROUTINE TO CLEAR THE COMFILE BUFFER.


CLEAR$COMBUFFER:
	LXI	H,COMFILE$BUFFER
	LDA	CURR$COM$BUF$END+1	;PAGE NO. OF BUF END
	MVI	C,0			;GET ZERO
CLOOP	MOV	M,C			;ZERO TO BUFFER
	INX	H			;BUMP POINTER
	CMP	H			;END OF BUFFER YET?
	JNZ	CLOOP			;LOOP TILL DONE
	RET


;ROUTINE TO COMPUTE CURRENT ABSOLUTE LOAD ADDRESS
;AND RETURN IT IN <HL>


ABSOLUTE:
	LHLD	CURRENT$COM$BASE ;GET BASE OF COM BUFFER
	MOV	B,H		;MOVE IT TO <BC>
	MOV	C,L
	LHLD	COM$BUF$OFFSET	;GET THE CURRENT OFFSET
	DAD	B		;SUM IS THE ABSO ADDR
	RET


;			MESSAGES


ERRMSG:
	DB	'ERROR: $'
DRMSG:
	DB	'DISK READ$'
ILAMSG:
	DB	'LOAD ADDRESS LESS THAN 100$'
DWMSG:
	DB	'DISK WRITE$'
LAMESSAGE:
	DB	'LOAD  ADDRESS $'
EAMSG:
	DB	'ERROR ADDRESS $'
IHDMSG:
	DB	'INVALID HEX DIGIT$'
CSEMSG:
	DB	'CHECKSUM ERROR $'
FAMSG:
	DB	'FIRST ADDRESS $'
LSTADDRMSG:
	DB	'LAST  ADDRESS $'
BRMESSAGE:
	DB	'BYTES READ    $'
RWMSG:
	DB	'RECORDS WRITTEN $'
COSMSG:
	DB	'CANNOT OPEN SOURCE FILE$'
NMDSMSG:
	DB	'DIRECTORY FULL$'
CCFMSG:
	DB	'CANNOT CLOSE FILE$'
SIGNON$MSG:
	DB	'HEXCOM	VERS: ',VERS/100+'0'
	DB	'.',VERS/10 MOD 10 +'0'
	DB	VERS MOD 10 + '0',CR,LF,'$'


;		DATA AREA



HEX$BUFFER$OFFSET	DB	127
FIRST$ADDRESS		DW	0
LAST$ADDRESS		DW	0
BYTES$READ$COUNT	DW	0
RECORDS$WRITTEN		DB	0
LOAD$ADDRESS		DW	100H
CURRENT$COM$BASE	DW	100H
CHECKSUM		DB	0
COM$BUF$OFFSET		DW	0
CLEAR$FLAG		DB	0	;CLEAR-COM-BUF FLAG



;		STORAGE AREA



STACK$SAVE		DS	2
HEX$FCB			DS	33
LOAD$COUNT		DS	1
CURR$COM$BUF$END	DS	2	;COM BUFFER TOP
CURR$COM$BUF$LEN	DS	2	;COM BUFFER LENGTH
			DS	32	;STACK AREA
STACK			EQU	$
		ORG	((HIGH $)+1)*256
HEX$BUFFER		DS	128
COMFILE$BUFFER		EQU	$
		END
