	TITLE	BASE UTILITY DRIVER
;********************************************************
;
;   Base Utility Driver Main Module
;
;   Copyright (c) 1983   GRH Enterprisess, Cupertino, CA
;   by Girvin Herr
;
;********************************************************
;
;   1-	Implements the loading of object files 
;      in the Intel HEX format into the memory buffer. 
;   3- Extensive error checking against memory buffer bounds, will not allow
;	operating system or code to be compromised. 
;
;   Uses the following commands:
;
;*HElp
;	Displays the command summary list.
;*LOAD addr filename
;	Reads file 'filename' with optional 'HEX' format starting at buffer
;	relative location 'addr'. Buffer bounds are checked.
;*SAve first, last filename
;	Saves the buffer image starting at buffer relative location 'first'
;	through 'last' in Hex file format as file 'filename'
;*FIll first, last, value
;	Fills buffer relative locations from 'first' to 'last' (inclusive)
;	with 'value'. Buffer bounds are checked.
;*MOve first, last, dest
;	Moves code from buffer relative position 'first' through 'last'
;	into new buffer relative location starting at 'dest'. Buffer
;	bounds for 'dest' are checked.
;*DIsplay first <,last>
;	Display buffer data from buffer relative location 'first' through
;	buffer relative location 'last'. If 'last' is not specified then
;	256 bytes are displayed. If 'first' is not specified, then the
;	previous 'last' is used for 'first'.
;*Substitute addr
;	Allows changing byte values starting at buffer relative location
;	'addr' one at a time. Upon execution, this command outputs:
;	'addr' & the current data value then waits for the user response.
;	If the user enters hex data, the 2 Hex digits are loaded into
;	the 'addr' location then 'addr' is incremented by 1 & the process
;	is repeated. If a RETURN is entered (nul line) the data value is
;	left as is & 'addr' is incremented by 1 & the process is repeated.
;	If a period '.' is entered as the 1st character, the command is
;	terminated. Note, the Hex values entered must have 0 in the high
;	byte. I.E. 00xx or just plain xx or even yy00xx will be ok.
;
;*********************************************************
;
;   REVISION STATUS:
;   2.0 - 6 Jul 83   GRH
;	  Test release
;   2.1 - 26 Feb 84   GRH
;	Changed Sign-on, Command table & help messages to user module
;
REVSN:	EQU	'21'
;
;*********************************************************
	SUBTTL	DECLARATIONS
FALSE	EQU	0
TRUE	EQU	NOT FALSE
LISTINC	EQU	FALSE

;
;   SYSTEM PARAMS
;
SYSTEM	EQU	0000H
DFLTDK:	EQU	0004H		;DEFAULT DISK STORAGE LOC
BDOS:	EQU	0005H		;CP/M ENTRY POINT
DEFBFR:	EQU	0080H		;DEFAULT BUFFER LOC
TPA	EQU	0100H		;START OF PROGRAM
;
CPMEOF:	EQU	1AH		;CP/M END OF FILE CHAR (CTRL-Z)
RDCF:	EQU	10
OPENF:	EQU	15
RDF:	EQU	20

;
;   ASCII CHARACTERS
;
LF	EQU	0AH
CR	EQU	0DH

;
;   CONSTANTS
;
STKSIZ:	EQU	128		;STACK SIZE
SECSIZ:	EQU	128		;DISK SECTOR SIZE
TYPE:	EQU	8		;OFFSET LOCATION OF TYPE EXTENSION OF FILENMAE

	SUBTTL	PROGRAM AREA
	REL
;
;	PUBLIC PROCEDURES
;
	ENTRY	SYNERR, ASC2HX, SPACE, CONOUT, CONIN, CSTS, SEARCH, SKIP
	ENTRY	CRLF, EDITOR, RDCON, GET3PAR, GET2PAR, GET1PAR, CKBNDS
	ENTRY	CPBNDS, GETNUM, PRT2H, PRTHEX, HX2ASC
;
;	GLOBAL VARIABLES
;
	ENTRY	CMDPTR, PARAM1, PARAM2, PARAM3, MEMTOP, $MEMRY


;
;	EXTERNAL PROCEDURES
;
	EXTRN	INIT
	EXTRN	UCMDT, UCMDTX, SIGNON

;
;	PTR TO END OF PROGRAM
;
	DATA			;FIRST BYTE OF DATA AREA SHOULD BE 103H
$MEMRY	DS	2		;SET BY LINK-80
	REL			;BACK TO CSEG


;
;	SIGN ON MESSAGE HERE TO GIVE COPYRIGHT VISIBILITY
;
;SIGNON:
	DB	CR,LF,'Hard Disk Monitor Utility     Ver '
	DB	HIGH REVSN
	DB	'.'
	DB	LOW REVSN
	DB	CR,LF,'Copyright (c) 1983'
	DB	'  GRH Enterprises  Cupertino, CA'
	DB	CR,LF,'Type ''HELP'' for commands'
	DB	CR,LF,'$'


;
;	MAIN PROGRAM
;
START:	LD	SP,STACK	;INITIALIZE

	LD	HL,(BDOS+1)	;GET LAST MEMORY LOC.
	DEC	HL
	LD	(MEMTOP),HL

	LD	HL,($MEMRY)	;SET UP FOR EVEN NIBBLE
	LD	A,L
	AND	0FH		;IF NIBBLE == 0 THEN OK
	JR	Z,MEMOK

	LD	A,L		;ELSE BUMP
	AND	0F0H
	ADD	A,10H
	LD	L,A
	LD	A,0
	ADC	A,H
	LD	H,A
	LD	($MEMRY),HL
MEMOK:
	LD	(DISTRT),HL	;INIT DISPLAY VARS
	LD	(DISEND),HL

	LD	DE,SIGNON	;OUTPUT SIGN-ON MSG
	CALL	EDITOR

	CALL	INIT		;GO DO INITIALIZATION

;
;	COMMAND INPUT ENTRY. ALL COMMANDS RETURN HERE IF OK.
;
CONT1:	CALL	CRLF		;NEW LINE

	LD	C,'*'		;OUTPUT PROMPT
	CALL	CONOUT

	CALL	RDCON		;GET USER INPUT

	LD	HL,(CMDPTR)
	CALL	SKIP		;IF NO INPUT THEN ERR
	JR	Z,CONT1

	LD	(CMDPTR),HL	;UPDATE PTR

	EX	DE,HL		;DE := COMMAND PTR

;
;	DO USER MODULE'S COMMANDS FIRST
;
	LD	HL,UCMDT
NXTUCMD:
	LD	A,(HL)		;IF CHAR COUNT = 0 THEN DONE WITH USER
	OR	A
	JR	Z,DOINTC

	LD	B,A		;USE AS COUNT
UCMDCK:	INC	HL		;IF CHARS NOT MATCH THEN EXIT
	LD	A,(DE)
	CP	(HL)

	CALL	NZ,NOT1ST
	JR	NZ,NXTUCMD

	INC	DE		;NEXT CHAR

	DJNZ	UCMDCK		;IF COUNT EXHAUSTED THEN MUST BE COMMAND

	JR	CMDFND

;
;	NOW DO INTERNAL COMMANDS
;
DOINTC:	LD	HL,CMDTBL
	LD	DE,(CMDPTR)	;RESTORE PTR
NXTCMD:	
	LD	B,(HL)		;B := CMD CHAR CNT

	LD	A,B		;IF COUNT == 0 THEN DONE
	OR	A
	JR	Z,CMDERR

CMDCHK:	INC	HL		;IF CHARS NOT MATCH THEN EXIT
	LD	A,(DE)
	CP	(HL)
	CALL	NZ,NOT1ST
	JR	NZ,NXTCMD

	INC	DE		;NEXT CHAR

	DJNZ	CMDCHK		;IF COUNT EXHAUSTED THEN MUST BE COMMAND

CMDFND:	INC	HL		;FETCH SERVICE ADDR
	LD	E,(HL)
	INC	HL
	LD	D,(HL)

	PUSH	DE		;SET UP FOR COMMAND SCANNING

	LD	HL,(CMDPTR)	;SET UP OPERANDS FOR SOME COMMANDS
	LD	C,' '
	CALL	SEARCH
	CALL	SKIP		;ALSO PASS IN ZF IF NO FIELDS
	LD	(CMDPTR),HL

	LD	HL,CONT1	;SET UP RETURN ADDR
	EX	(SP),HL		;EXECUTE COMMAND
	JP	(HL)


;	COMMAND NOT FOUND IN TABLE

CMDERR:	LD	DE,CMDERM	;ELSE OUTPUT ERROR MESSAGE
	CALL	EDITOR
	JR	CONT1		;TRY NEXT COMMAND

SYNERR:	POP	DE		;WASTE RETURN ADDR FOR SUBRS

	LD	DE,SYNERM
	CALL	EDITOR
	JR	CONT1


	SUBTTL	COMMANDS
;**********************************
;
;   READ FILE INTO BUFFER COMMAND
;  READ ADDR <HEX> FILENAME
;
;**********************************
READC:	JP	Z,SYNERR	;IF NO OPERAND THEN ERR

	LD	HL,(CMDPTR)	;GET ADDR
	CALL	GETNUM
	JP	C,SYNERR	;IF ILLEGAL NUMBER THEN ERR

	CALL	CPBNDS		;IF READ AREA INTO RESERVED AREA THEN
	JP	C,BNDSM		;ABORT

	LD	(PARAM1),HL

	LD	HL,(CMDPTR)	;SEEK FILENAME
	LD	C,' '
	CALL	SEARCH
	LD	(CMDPTR),HL
;
;	WE SHOULD NOW BE AT THE FILENAME
;
RC1:	LD	HL,FCB		;FORMAT FCB
	CALL	FORMAT
	JP	C,SYNERR
	JP	NZ,SYNERR	;IF WILDCARDS USED THEN ERR
;
;	FORMAT OK, SO OPEN FILE
;
	LD	DE,FCB
	CALL	OPEN
	INC	A		;IF NOT THERE THEN
	JR	NZ,RDC1

	LD	DE,FNFM		;  OUTPUT 'FILE NOT FOUND'
	JP	EDITOR

;
;	OUTPUT BOUNDS ERROR MESSAGE
;
BNDSM:	LD	DE,BOUNDM	;THEN OUTPUT ERROR MESSAGE
	JP	EDITOR

MEMER1:	LD	DE,MEMERM
	CALL	EDITOR
	JP	CONT1

;
;   HEX FILE READ ROUTINE
;
RDC1:
RDC2:	XOR	A		;SET UP BUFFER FOR 1ST READ
	LD	(BFRCNT),A

	LD	HL,(PARAM1)	;SET PTR TO LOAD ADDR
	LD	(BFRPTR),HL

HEXRD1:	CALL	GETCH		;FETCH CHARACTER OF FILE
	JP	C,HXEMPT	;IF FILE EMPTY THEN ERR

	CP	':'		;IF CHAR <> COLON THEN IGNORE
	JR	NZ,HEXRD1

	LD	D,0		;CHECKSUM = 0

	CALL	BYTE		;IF FILE EMPTY THEN ERR
	JP	C,HXERR

	AND	A		;IF RECORD LENTH == 0 THEN DONE
	JP	Z,CONT1

	LD	E,A		;RECORD COUNT = BYTE

	CALL	BYTE		;GET LOAD ADDRESS OF 1ST RECORD & USE AS BASE
	JP	C,HXERR

	LD	H,A		;HIGH BYTE OF ADDR
	CALL	BYTE
	JP	C,HXERR

	LD	L,A		;LOW BYTE OF ADDR
	LD	(HEXBASE),HL	;SAVE BASE ADDRESS

	LD	HL,(BFRPTR)	;SET LOAD PTR TO START OF BUFFER
	LD	(HEXPTR),HL

	LD	C,E		;IF LOAD_PTR + COUNT <= LAST THEN GET DATA
	LD	B,0
	ADD	HL,BC
	LD	BC,(MEMTOP)
	INC	BC
	OR	A
	SBC	HL,BC
	JR	C,GETREC

	JR	MEMER1		;ELSE OUTPUT MEMORY ERROR
;
HEXRD:	CALL	GETCH		;GET CHAR OF FILE
	JR	C,HXERR

	CP	':'		;IF CHAR <> COLON THEN IGNORE
	JR	NZ,HEXRD

	LD	D,0		;CHECKSUM = 0

	CALL	BYTE
	JR	C,HXERR		;FILE EMPTY

	AND	A		;IF RECORD LENGTH = 0 THEN DONE
	JP	Z,CONT1

	LD	E,A		;RECORD COUNT = BYTE
	CALL	BYTE		;GET LOAD ADDRESS
	JR	C,HXERR

	LD	H,A
	CALL	BYTE
	JR	C,HXERR

	LD	L,A

	LD	BC,(HEXBASE)	;IF NEW ADDR < BASE ADDR THEN ERR
	SBC	HL,BC
	JR	C,HXADERR

	LD	BC,(BFRPTR)	;ELSE COMPUTE NEW PTR
	ADD	HL,BC
	LD	(HEXPTR),HL

	LD	C,E		;IF PTR + COUNT > BFR_TOP THEN ERR
	LD	B,0
	ADD	HL,BC
	LD	BC,(MEMTOP)
	INC	BC
	OR	A
	SBC	HL,BC
	JP	NC,MEMER1
;
GETREC:	CALL	BYTE		;GET RECORD TYPE
	JR	C,HXERR		;FILE EMPTY

	OR	A		;IF RECORD TYPE <> 0 THEN ERROR
	JR	NZ,HXTYPER

	LD	HL,(HEXPTR)	;RESTORE PTR
LOOP:	CALL	BYTE		;INPUT DATA
	JR	C,HXERR

	LD	(HL),A
	INC	HL
	DEC	E		;IF (COUNT = COUNT -1) =<> 0 THEN LOOP
	JR	NZ,LOOP

	CALL	BYTE		;CHECK CHECKSUM
	JR	C,HXERR

	XOR	A
	ADD	D
	JP	Z,HEXRD

HXERR:	LD	DE,HXERM	;IF ERROR THEN TELL SO
	JP	EDITOR

HXADERR:
	LD	DE,HXADRM	;OUTPUT ILLEGAL LOAD ADDRESS REGRESSION
	JP	EDITOR

HXTYPER:
	LD	DE,HXTYPM	;OUTPUT ILLEGAL HEX RECORD TYPE
	JP	EDITOR

HXEMPT:	LD	DE,HXEMTM	;OUTPUT FILE EMPTY
	JP	EDITOR


;*******************************
;
;	SAVE FILE COMMAND
; SAVE FIRST, LAST HEX FILENAME
;
;*******************************
SAVEC:	JP	Z,SYNERR	;IF NO OPERANDS THEN ERR

	LD	DE,PARAM1	;GET 1ST & LAST
	LD	HL,(CMDPTR)
	CALL	GET2PAR
	JP	C,SYNERR

	LD	HL,(PARAM2)	;COUNT = LAST - FIRST + 1
	LD	DE,(PARAM1)
	OR	A
	SBC	HL,DE
	JP	C,SYNERR	;IF FIRST > LAST THEN ERR

	INC	HL
	LD	(PARAM3),HL	;SAVE COUNT IN P3
	EX	DE,HL		;OFFSET FIRST FOR PTR
	CALL	CPBNDS
	LD	(PARAM1),HL

	LD	HL,(CMDPTR)
	LD	C,' '		;POINT TO FILENAME
	CALL	SEARCH
	CALL	SKIP
	LD	(CMDPTR),HL
	JP	Z,SYNERR

	LD	HL,FCB		;FORMAT FCB
	CALL	FORMAT
	JP	C,SYNERR
	JP	NZ,SYNERR

	LD	DE,FCB		;OPEN FILE, IF EXISTING THEN PROMPT FOR DELETE
	CALL	OPEN
	INC	A
	JR	Z,WRC1

	LD	DE,DELMSG
	CALL	EDITOR
	CALL	CONIN		;IF 'Y' OR 'y' THEN DELETE
	AND	5FH		;CONVERT TO LOWER CASE

	CP	'Y'
	RET	NZ		;ELSE RETURN TO COMMAND MODE

WRC2:	LD	DE,FCB		;DELETE FILE 1ST
	LD	C,19
	CALL	BDOS

WRC1:	LD	DE,FCB		;MAKE NEW FILE
	LD	C,22
	CALL	BDOS
;
;	MAIN LOOP OF HEX OUTPUT ROUTINE
;
	LD	A,0		;CLEAR WRITE BUFFER
	LD	(BFRCNT),A

	LD	HL,DEFBFR
	LD	(DBPTR),HL

	LD	HL,(PARAM1)	;INIT START ADDR
WR0:	LD	C,':'		;OUTPUT START OF RECORD SYMBOL
	CALL	PCHAR
	JP	C,WERR

	LD	BC,16
	PUSH	HL		;SAVE START ADDR
WR1:	LD	HL,(PARAM3)	;IF COUNT - 16 >= 0 THEN
	OR	A
	LD	A,L		;SAVE COUNT FOR RESTORING
	SBC	HL,BC
	LD	B,C		;   REC_COUNT = 16
	LD	(PARAM3),HL	;   COUNT -= 16
	JR	NC,WR2

;				ELSE REC_COUNT = COUNT
	LD	B,A
	LD	HL,0		;   COUNT = 0 FOR LAST RECORD
	LD	(PARAM3),HL

WR2:	POP	HL		;RESTORE START PTR
	LD	D,0		;CHECKSUM = 0
	LD	A,B		;OUTPUT RECORD LENGTH
	OR	A		;IF COUNT = 0 THEN SKIP TO EOF
	JR	Z,WR5

	CALL	PBYTE
	JR	C,WERR

	PUSH	HL		;SAVE PTR
	PUSH	DE		;SAVE CS
	LD	DE,($MEMRY)	;REMOVE OFFSET FOR OUTPUT
	OR	A
	SBC	HL,DE
	POP	DE		;CS
	LD	A,H		;OUTPUT LOAD ADDR
	CALL	PBYTE
	JR	C,WERRP

	LD	A,L
	CALL	PBYTE
	POP	HL		;PTR
	JR	C,WERR

	XOR	A		;OUTPUT 0 RECORD TYPE
	CALL	PBYTE
	JR	C,WERR

WR3:	LD	A,(HL)		;OUTPUT BYTE FROM MEMORY
	CALL	PBYTE
	JR	C,WERR

	INC	HL		;PTR += 1
	DJNZ	WR3		;NEXT BYTE

	XOR	A		;OUTPUT -CHECKSUM
	SUB	A,D
	CALL	PBYTE
	JR	C,WERR

	LD	C,CR		;OUTPUT CR-LF
	CALL	PCHAR
	JR	C,WERR

	LD	C,LF
	CALL	PCHAR
	JR	C,WERR

	PUSH	HL
	LD	HL,(PARAM3)	;IF COUNT = 0 THEN DONE
	LD	A,L
	OR	H
	POP	HL
	JP	NZ,WR0		;ELSE DO ANOTHER LINE

	LD	C,':'		;OUTPUT END OF FILE RECORD
	CALL	PCHAR
	JR	C,WERR

WR5:	LD	B,5		;OUTPUT 5 00 BYTES
WR4:	XOR	A
	CALL	PBYTE
	JR	C,WERR

	DJNZ	WR4

	CALL	CLOSE
	JR	C,WERR
	RET			;NORMAL EXIT

WERRP:	POP	HL		;BALANCE STACK
WERR:	LD	DE,WRERRM	;OUTPUT WRITE ERROR
	JP	EDITOR


;*************************
;
;   FILL COMMAND
; FILL FIRST, LAST, VALUE
;
;*************************
FILLC:	JP	Z,SYNERR	;IF NO OPERANDS THEN ERR

	CALL	GET3PAR
	JP	C,SYNERR

	CALL	CKBNDS
	JP	C,BNDSM

	LD	DE,(PARAM1)	;FIRST
	LD	HL,(PARAM2)	;LAST
	SBC	HL,DE		;IF FIRST > LAST THEN ERR
	JP	C,SYNERR

	PUSH	HL		;ELSE COUNT = LAST - FIRST
	POP	BC
	LD	A,C		;IF CNT = 0 THEN ERR
	OR	B
	JP	Z,SYNERR

	LD	A,(PARAM3)	;GET VALUE
	LD	HL,(PARAM1)
	LD	(HL),A		;SET FIRST LOCATION
	PUSH	HL		;DESTINATION = SOURCE + 1
	POP	DE
	INC	DE
	LDIR			;FILL
	RET


;************************
;
;   MOVE COMMAND
; MOVE FIRST, LAST, DEST
;
;************************
MOVEC:	CALL	GET3PAR
	JP	C,SYNERR

	LD	HL,(PARAM3)	;IF DESTINATION OUT OF BOUNDS THEN ERR
	CALL	CPBNDS
	JP	C,BNDSM

	LD	(PARAM3),HL
	LD	HL,(PARAM1)	;ADD BUFFER BIAS TO VALUES
	CALL	CPBNDS
	LD	(PARAM1),HL
	LD	HL,(PARAM2)
	CALL	CPBNDS
	LD	(PARAM2),HL
	LD	DE,(PARAM1)	;IF FIRST > LAST THEN ERR
	OR	A
	SBC	HL,DE
	JP	C,SYNERR

	PUSH	HL		;ELSE CNT = # OF BYTES
	POP	BC
	EX	DE,HL		;IF DEST > FIRST THEN
	LD	DE,(PARAM3)
	SBC	HL,DE
	JR	C,SPLIT		;TOP DOWN

	INC	BC		;ELSE BOTTOM UP
	LD	HL,(PARAM1)
	LD	A,C		;IF COUNT = 0 THEN ERR
	OR	B
	JP	Z,SYNERR

	LDIR
	RET
;
SPLIT:	LD	HL,(PARAM3)	;DEST = DEST + CNT
	ADD	HL,BC
	EX	DE,HL
	LD	HL,(PARAM2)
	INC	BC		;INCLUSIVE
	LD	A,C
	OR	B
	JP	Z,SYNERR

	LDDR
	RET


;************************
;
;   HELP COMMAND
; HELP
;
;************************
HELPC:	LD	DE,HELPM	;DO INTERNAL COMMANDS
	CALL	EDITOR

	LD	DE,UCMDTX	;NOW DO USER MODULE
	JP	EDITOR


;*****************************
;
;   DISPLAY MEMORY COMMAND
; DISPLAY <FIRST><,LAST>
;
;*****************************
DISPC:	LD	HL,(CMDPTR)
	JR	Z,DISP1		;IF NO OPERANDS THEN USE DEFAULTS

	LD	A,(HL)		;IF 1ST CHAR = ',' THEN USE DEFAULT
	CP	','
	JR	Z,DISP1

	CALL	GETNUM		;ELSE GET PARAMETER 1
	JP	C,SYNERR

	CALL	CPBNDS		;ADD OFFSET
	LD	(DISTRT),HL

DISP1:	LD	HL,(CMDPTR)	;IF NO ,NNNN THEN END= START + 12 LINES
	LD	A,','
	CP	(HL)
	JR	Z,DISP2

	LD	C,A
	CALL	SEARCH
	JR	NZ,DISP2

	LD	HL,(DISTRT)	;ELSE END = START MOD 16 + 255
	LD	A,L
	AND	0F0H
	LD	L,A
	LD	DE,255
	ADD	HL,DE
	JR	DISP3
;
DISP2:	INC	HL		;GET VALUE OF END
	CALL	SKIP
	JP	Z,SYNERR

	LD	(CMDPTR),HL
	CALL	GETNUM
	JP	C,SYNERR

	CALL	CPBNDS		;ADD OFFSET TOWARD BUFFER

DISP3:	LD	(DISEND),HL

DISP4:	CALL	CRLF		;NEW LINE
	CALL	CSTS		;IF KEY PRESSED THEN ABORT
	RET	NZ

	LD	HL,(DISTRT)	;TEMP = START
	LD	(DISTMP),HL
	LD	DE,($MEMRY)	;OUTPUT OFFSET ADDR
	SBC	HL,DE
	CALL	PRT2H
	LD	HL,(DISTRT)	;RESTORE PTR

DISP5:	PUSH	HL
	CALL	SPACE
	POP	HL
	LD	A,(HL)		;PRINT DATA
	INC	HL
	PUSH	HL
	CALL	PRTHEX
	POP	HL
	CALL	CKDONE		;IF PTR = END THEN DONE
	JR	C,DISP6

	LD	A,L
	AND	0FH
	JR	NZ,DISP5

DISP6:	LD	(DISTRT),HL	;UPDATE TO NEW START
	CALL	SPACE
	LD	HL,(DISTMP)	;START OVER

DISP7:	LD	A,(HL)		;IF CHAR = PRINTING THEN OK AS IS
	CP	7FH
	JR	NC,OUTDEC

	CP	' '
	JR	NC,DISPOK

OUTDEC:	LD	A,'.'		;ELSE OUTPUT '.'

DISPOK:	PUSH	HL
	LD	C,A
	CALL	CONOUT
	POP	HL
	INC	HL		;PTR = PTR + 1
	LD	DE,(DISTRT)	;IF PTR = NEXT START THEN DONE
	LD	A,E
	SUB	L
	JR	NZ,DISP7

	LD	A,D
	SUB	H
	JR	NZ,DISP7

	EX	DE,HL
	CALL	CKDONE		;IF NOT DONE THEN DO NEXT LINE

	RET	C
	JR	DISP4


;******************************
;
;   SUBSTITUTE MEMORY COMMAND
;SUBSTITUTE ADDR
;
;******************************
SUBSC:	JP	Z,SYNERR	;IF NO OPERANDS THEN ERR

	LD	HL,(CMDPTR)
	CALL	GETNUM
	JP	C,SYNERR

	CALL	CPBNDS		;IF ADDR OUT OF BOUNDS THEN ERR
	JP	C,BNDSM

SUBS2:	LD	(DISTMP),HL	;SAVE ADDR
	PUSH	HL		;OUTPUT ADDR
	CALL	CRLF
	POP	HL
	PUSH	HL

	OR	A		;ADD OFFSET
	LD	DE,($MEMRY)
	SBC	HL,DE
	CALL	PRT2H

	CALL	SPACE		;OUTPUT SPACE

	POP	DE
	LD	A,(DE)		;OUTPUT CURRENT DATA
	CALL	PRTHEX

	CALL	SPACE		;OUTPUT ANOTHER SPACE

	CALL	RDCON		;GET USER INPUT
	LD	HL,(CMDPTR)	;IF NULL LINE THEN SKIP LOCATION
	CALL	SKIP

	LD	DE,(DISTMP)
	JR	Z,SUBS1

	LD	A,(HL)		;IF CHAR = '.' THEN ABORT
	CP	'.'
	RET	Z

	CALL	GETNUM		;ELSE CONVERT TO NUMBER
	LD	A,H		;IF NUMBER > 255 THEN ERR
	OR	A
	JP	NZ,SYNERR

	LD	A,L		;LOAD DATA INTO LOCATION
	LD	DE,(DISTMP)
	LD	(DE),A

SUBS1:	INC	DE		;NEXT LOCATION
	EX	DE,HL
	JR	SUBS2


;**************************************
;
;	NON DOCUMENTED QUERY COMMAND
; OUTPUTS BUFFER OFFSET ADDR
;
;**************************************
QUERYC:
	CALL	CRLF
	LD	HL,($MEMRY)
	CALL	PRT2H
	JP	CRLF

	SUBTTL	SUBROUTINES
;***************************************************************
;
;	NOT 1ST SUBR SKIPS COMMAND TABLE ENTRY TO NEXT ENTRY
;	ENTRY-	HL= TABLE PTR
;		B= COMMAND CHAR COUNT
;	EXIT -	HL= NEXT COMMAND PTR
;		FLAGS NOT AFFECTED
;
;***************************************************************
NOT1ST:	INC	HL		;WASTE ENTRY
	DJNZ	NOT1ST

	INC	HL		;SKIP ADDRESS
	INC	HL

	LD	DE,(CMDPTR)	;RESTORE CMD PTR TO START OF CMD
	RET


;*******************************************************
;
;	OUTPUT MEMORY ERROR & RETURN TO SYSTEM
;	EXIT -	FLAGS SAVED FOR CALLER'S CONDITIONAL
;
;*******************************************************
MEMER:	PUSH	AF
	LD	DE,MEMERM
	CALL	EDITOR
	POP	AF
	RET


;******************************************************
;
;   BYTE SUBR CONVERTS 2 ASCII-HEX CHARS TO BINARY
;	ENTRY-	D= CHECKSUM
;	EXIT -	A= BYTE FROM ASCII-HEX
;		D= NEW CHECKSUM
;		CF= ERROR
;
;******************************************************
BYTE:	CALL	GETCH		;GET 1ST DIGIT
	RET	C

	CALL	ASC2HX		;TRY TO CONVERT BUT IF DIGIT NOT HEX THEN ERR
	JR	C,CVERR

	SLA	A		;MOVE OVER 1 NIBBLE
	SLA	A
	SLA	A
	SLA	A
	LD	B,A
	CALL	GETCH		;GET 2ND DIGIT
	RET	C

	CALL	ASC2HX		;CONVERT 2ND NIBBLE
	JR	C,CVERR

	ADD	B		;ADD 2ND TO 1ST
	LD	B,A		;COMPUTE CHECKSUM
	ADD	D
	LD	D,A
	LD	A,B
	OR	A		;CLEAR FLAG
	RET

;	HEX CONVERSION ERROR MESSAGE OUTPUT

CVERR:	LD	DE,CVERM
	CALL	EDITOR
	SCF			;ALSO RETURN ERROR FLAG
	RET


;************************************************
;
;	CONVERT ASCII-HEX CHAR TO BINARY SUBR
;	ENTRY-	A= CHAR
;	EXIT -	A= BINARY VALUE
;		CF= ERROR
;
;************************************************
ASC2HX:	SUB	'0'		;REMOVE ASCII BIAS
	RET	C

	CP	10		;IF 0..9 THEN OK
	CCF
	RET	NC

	SUB	7		;CONVERT 'A'..'F' TO 10..15
	CP	10
	RET	C

	CP	16
	CCF
	RET


;***************************************************************************
;
;   GET CHARACTER SUBR RETURNS NEXT CHAR OF INPUT FILE
;   ENTRY- FCB FORMATTED & FILE OPENED, (BFRCNT INIT'D FOR NEW FILE)
;   EXIT - CHAR FROM FILE IN A
;          CF = EOF
;
;***************************************************************************
GETCH:	PUSH	HL
	LD	A,(BFRCNT)	;IF BUFFER EMPTY THEN READ
	OR	A
	JP	NZ,GC1

	PUSH	DE		;READ SECTOR INTO DEFAULT BUFFER
	PUSH	BC
	LD	DE,DEFBFR
	CALL	SETDMA
	LD	DE,FCB
	CALL	RDSEC
	OR	A		;IF FILE EMPTY THEN RETURN CF
	SCF
	JR	NZ,FEMPTY

	LD	HL,DEFBFR
	LD	(DBPTR),HL	;SET VARS
	LD	A,SECSIZ
	LD	(BFRCNT),A
	POP	BC
	POP	DE

GC1:	LD	HL,(DBPTR)	;GET CHAR FROM BFR
	LD	A,(HL)
	INC	HL
	LD	(DBPTR),HL
	LD	HL,BFRCNT	;COUNT = COUNT - 1
	DEC	(HL)
	POP	HL
	OR	A		;INSURE NC
	RET
;
FEMPTY:	POP	BC		;RESTORE STACK
	POP	DE
	POP	HL
	RET


;***************************************************************************
;
;	PUT CHAR ROUTINE OUTPUTS CHAR TO OUTPUT BUFFER & WRITES TO OUTPUT
;  FILE WHEN FULL. BUFFER IS DEFAULT BUFFER AT 80H
;	ENTRY-	C= CHAR TO OUTPUT
;		FCB= OPENED FILE
;	EXIT -	CF= ERROR
;
;***************************************************************************
PCHAR:	PUSH	HL		;SAVE REGS
	PUSH	DE
	PUSH	BC
	LD	HL,(DBPTR)	;GET BUFFER PTR
	LD	(HL),C		;ADD CHAR TO BUFFER
	INC	HL		;PTR += 1
	LD	(DBPTR),HL
	LD	A,(BFRCNT)	;IF BUFFER FULL THEN WRITE IT
	INC	A
	LD	(BFRCNT),A
	CP	SECSIZ
	CCF			;SWAP FLAG SENSE TO RETURN NC
	JP	NC,NOWRT

CLOS1:	LD	DE,DEFBFR	;SET WRITE PTR
	LD	(DBPTR),DE
	CALL	SETDMA
	LD	DE,FCB		;WRITE IT
	LD	C,21
	CALL	BDOS
	OR	A		;IF WRITE ERR THEN RETURN CF
	JR	NZ,WRERR

	LD	(BFRCNT),A	;ELSE BUFFER COUNT = 0

NOWRT:	POP	BC
	POP	DE
	POP	HL
	RET

WRERR:	SCF
	JR	NOWRT


;************************************
;
;	CLOSE OUTPUT BUFFER SUBR
;	EXIT -	CF= ERROR
;
;************************************
CLOSE:	LD	C,CPMEOF	;OUTPUT EOF CHAR
	CALL	PCHAR
	RET	C

	LD	A,(BFRCNT)	;IF BUFFER EMPTY THEN NO NEED TO WRITE
	OR	A
	JR	Z,CLOSRET

	PUSH	HL		;PUT NEW RETURN ADDRESS ON STACK
	LD	HL,CLOSRET
	EX	(SP),HL
	PUSH	HL		;THEN SAVE REGS FOR STACK BALANCE
	PUSH	DE
	PUSH	BC
	JR	CLOS1		;WRITE REMAINDER OF BUFFER

CLOSRET:
	RET	C		;IF WRITE ERROR THEN RETURN CF

	LD	DE,FCB		;THEN CLOSE FILE
	LD	C,16
	CALL	BDOS
	OR	A		;CLEAR POSSIBLE CARRY
	INC	A		;IF NO ERROR THEN RETURN NC
	RET	NZ

	SCF			;ELSE RETURN CF
	RET


;***************************************************************************
;
;	PUT BYTE SUBR CONVERTS BYTE TO ASCII-HEX & OUTPUTS IT TO FILE BUFFER
;	ENTRY-	A= BYTE
;	EXIT -	CF= WRITE ERROR
;
;***************************************************************************
PBYTE:	PUSH	BC
	LD	B,A		;SAVE BYTE IN B
	ADD	D		;ADD TO CHECKSUM
	LD	D,A
	LD	A,B		;RESTORE BYTE
	SRL	A		;DO HIGH NIBBLE 1ST
	SRL	A
	SRL	A
	SRL	A
	CALL	HX2ASC		;CONVERT IT TO ASCII
	LD	C,A		;THEN OUTPUT IT
	CALL	PCHAR
	JR	C,PBYT1

	LD	A,B		;THEN DO LO NIBBLE
	CALL	HX2ASC
	LD	C,A		;& OUTPUT IT
	CALL	PCHAR

PBYT1:	POP	BC
	RET


;********************************************
;
;	OUTPUT A SPACE TO CONSOLE SUBR
;	EXIT -	ALL REGS ?
;
;********************************************
SPACE:	LD	C,' '


;********************************************
;
;	CONSOLE OUTPUT SUBR
;	ENTRY-	C= CHAR TO OUTPUT
;	EXIT -	ALL REGS ?
;
;********************************************
CONOUT:	LD	E,C		;SETUP
	LD	C,2
	JP	BDOS


;*************************************
;
;	CONSOLE INPUT SUBR
;	EXIT -	A= CHAR
;		ALL OTHER REGS ?
;
;************************************
CONIN:	LD	C,1
	JP	BDOS


;************************************************************
;
;	CONSOLE STATUS SUBR
;	EXIT -	A= 0 : NONE READY, A= NOT 0 : CHAR READY
;		ZF = REFLECTS CONDITION OF A
;
;************************************************************
CSTS:	LD	C,11
	CALL	BDOS
	OR	A
	RET


;*********************************************************************
;
;   SEARCH SUBR SEARCHES A NUL TERMINATED STRING FOR A CHAR THE SAME
; AS CONTENTS OF C.
;   ENTRY- HL= STRING PTR
;          C= CHAR TO SEARCH FOR
;   EXIT - HL= CHAR PTR
;          Z= NOT FOUND (END OF TEXT)
;
;*********************************************************************
SEARCH:	LD	A,(HL)		;IF CHAR = NUL THEN RETURN Z
	OR	A
	RET	Z

	CP	C		;ELSE IF CHAR=C THEN RETURN NZ
	JR	Z,FOUND

	INC	HL		;ELSE TRY NEXT CHAR
	JR	SEARCH

FOUND:	OR	A
	RET


;***********************************************************************
;
;	SKIP BYPASSES TRAILING SPACES UNTIL NON-SPACE CHAR IS FOUND
;	ENTRY-	HL= STRING PTR
;	EXIT -	HL= 1ST NON-SPACE CHAR PTR
;		A= NON-SPACE CHAR
;		ZF= NOT FOUND
;
;***********************************************************************
SKIP:	LD	A,(HL)
	CP	' '		;IF CHAR <> ' ' THEN RETURN
	JR	NZ,FOUND

	INC	HL
	JR	SKIP


;*************************************
;
;   CARRIAGE RETURN/LINE FEED SUBR
;	EXIT -	ALL REGS ?
;
;*************************************
CRLF:	LD	C,CR
	CALL	CONOUT
	LD	C,LF
	JP	CONOUT


;*******************************************************
;
;	MESSAGE EDITOR SUBR OUTPUTS TEXT STRING
;
;	ENTRY-	DE= STRING PTR
;	EXIT -	ALL REGS ?
;
;******************************************************
EDITOR:	LD	C,9
	JP	BDOS


;***************************************************
;
;   CP/M FILE CONTROL BLOCK FORMATTER
;   ENTRY- (CMDPTR) = ASCII STRING PTR OF FILE
;          HL = FCB TO FORMAT
;   EXIT - ZF = NO WILDCARDS USED
;          NZ = WILDCARD(S) USED, A REG. = NUMBER OF 
;               WILDCARDS
;
;*********************************************************
;
;   CONSTANTS
;
ZROSIZ:	EQU	22
NAMCNT:	EQU	8		;FILENAME CHAR CNT
TYPCNT:	EQU	3		;FILE EXTENSION CHAR CNT
;
FORMAT:	PUSH	HL		;SAVE FPB PTR IN IY
	POP	IY
	LD	HL,(CMDPTR)
	CALL	SKIP		;SKIP LEADING SPACES
	LD	(CMDPTR),HL	;SAVE NEW PTR
	EX	DE,HL
	PUSH	IY
	POP	HL		;FPB PTR
	LD	A,(DE)		;IF CHAR = 0 THEN DONE
	OR	A
	JR	Z,L89

	SBC	'A' - 1		;MAKE 'A'-'P' = 1-15 FOR DRIVE #
	LD	B,A
	INC	DE		;IF NEXT CHAR = ':' THEN MUST BE DRIVE
	LD	A,(DE)
	CP	':'
	JR	Z,L90

	DEC	DE		;ELSE BACK UP & USE DEFAULT DRIVE

L89:	LD	A,(DFLTDK)
	LD	(HL),A		;FCB(DISK) := DRIVE #
	JR	L96

L90:	LD	A,B		;USE SPECIFIED DISK
	LD	(HL),B
	INC	DE		;SKIP OVER ':'

L96:	LD	B,NAMCNT	;B := MAX CHARS

L98:	CALL	RSVP
	JR	Z,LB9

	INC	HL		;PTR := PTR +1
	CP	'*'		;IF WILDCARD THEN ENTER '?'
	JR	NZ,LA9

	LD	(HL),'?'
	JR	LAB

LA9:	LD	(HL),A		;ADD CHAR TO FCB FILENAME
	INC	DE

LAB:	DJNZ	L98		;IF NOT DONE THEN LOOP

LAF:	CALL	RSVP		;IF RESERVED CHAR THEN EXIT
	JR	Z,LC0

	INC	DE		;SKIP OVER EXTRA CHARS TO '.'
	JR	LAF

LB9:	INC	HL		;FILL REMAINDER OF FCB WITH SPACES
	LD	(HL),' '
	DJNZ	LB9

LC0:	LD	B,TYPCNT	;COUNT := TYPE CHAR COUNT
	CP	'.'		;IF RESERVED CHAR NOT '.' THEN FILL 
;				  WITH SPACES
	JR	NZ,LE9

	INC	DE		;GET NEXT CHAR AFTER '.'

LC8:	CALL	RSVP		;IF RESERVED CHAR THEN EXIT
	JR	Z,LE9

	INC	HL		;IF CHAR <> '*' THEN EXIT
	CP	'*'
	JR	NZ,LD9

	LD	(HL),'?'
	JR	LDB

LD9:	LD	(HL),A		;ADD CHAR TO FCB
	INC	DE

LDB:	DJNZ	LC8

LDF:	CALL	RSVP		;IF RESERVED CHAR THEN EXIT
	JR	Z,LF0

	INC	DE		;WASTE CHARS
	JR	LDF

LE9:	INC	HL		;FILL REMAINING TYPE WITH SPACES
	LD	(HL),' '
	DJNZ	LE9

LF0:	LD	B,ZROSIZ	;ZERO EXTENT, S1, S2, RECORD COUNT

LF2:	INC	HL
	LD	(HL),0
	DJNZ	LF2

	LD	(CMDPTR),DE
	PUSH	IY
	POP	HL		;COUNT WILDCARDS
	LD	BC,NAMCNT + TYPCNT

L01:	INC	HL
	LD	A,(HL)
	CP	'?'
	JR	NZ,L09

	INC	B

L09:	DEC	C
	JR	NZ,L01

	LD	A,B
	OR	A
	RET


;************************************
;
;   RESERVED CHAR TEST SUBR
;   ENTRY- (DE) = CHAR TO TEST
;   EXIT - ZF = FOUND
;
;************************************
RSVP:	LD	A,(DE)		;IF CHAR <= ' ' THEN RETURN Z
	CP	' ' + 1
	JR	NC,RSVP1

	XOR	A
	RET

RSVP1:	PUSH	BC
	PUSH	HL
	LD	B,TABCNT	;B := ENTRY COUNT
	LD	HL,RSVPT

RSVPL:	CP	(HL)
	JR	Z,RSVPX		;IF FOUND THEN EXIT

	INC	HL
	DJNZ	RSVPL

	INC	B		;RETURN NZ

RSVPX:	POP	HL
	POP	BC
	RET

RSVPT:	DB	'=_.:;,<>'
TABCNT:	EQU	$ - RSVPT


;**************************************
;
;	OPEN FILE SUBR
;	ENTRY-	DE= FCB PTR
;	EXIT -	A= -1 IF NOT ON DISK
;
;**************************************
OPEN:	LD	C,OPENF
	JP	BDOS


;**************************************
;
;	READ SECTOR SUBR
;	ENTRY-	DE= FCB PTR
;	EXIT -	A= 0 IF OK
;
;**************************************
RDSEC:	LD	C,RDF
	JP	BDOS


;**************************************
;
;	SET DISK READ ADDRESS SUBR
;	ENTRY-	DE= ADDRESS
;
;**************************************
SETDMA:	LD	C,26D
	JP	BDOS


;**************************************
;
;	READ CONSOLE BUFFER SUBR
;	EXIT -	CMDBFR = CONSOLE INPUT
;
;**************************************
RDCON:	LD	DE,CMDBFR	;INIT BUFFER
	LD	A,CBFRSZ	;CMDBFR[0] = CBFRSZ
	LD	(DE),A
	PUSH	DE		;BUFFER PTR
	LD	C,RDCF		;GET INPUT FROM USER
	CALL	BDOS
	POP	HL		;BUFFER PTR
	INC	HL		;COUNT = CMDBFR[1] AND 7FH
	LD	C,(HL)
	LD	B,0
	RES	7,C		;MAKE SURE < 128
	INC	HL		;CMDPTR = .CMDBFR[2]
	LD	(CMDPTR),HL
	ADD	HL,BC		;CMDBFR[COUNT + 2] = 0 /* END OF LINE = NUL */
	LD	(HL),0
	RET


;**********************************************************
;
;   READ FILE SKIPS ANY HEADER BLOCK & ONLY LOADS CODE
;   ENTRY- BC = LOAD BUFFER PTR
;   EXIT - Z = SUCCESSFUL READ
;          NZ = READ ERROR
;
;**********************************************************
RDFILE:	LD	(RDPTR),BC	;SAVE ACTUAL READ PTR
	LD	DE,DEFBFR	;READ 1ST SECTOR INTO DEFAULT BUFFER FOR NOW
	CALL	SETDMA
	LD	DE,FCB
	CALL	RDSEC
	OR	A		;IF BAD READ THEN RETURN ERR
	RET	NZ

;	IF FILE STARTS WITH 	JP	0000H THEN ASSUME IT IS A HEADER BLOCK

	LD	A,(DEFBFR)
	CP	0C3H
	JR	NZ,RESCAN	;ELSE ASSUME THE 1ST SECTOR IS GOOD & LOAD IT
	LD	HL,(DEFBFR+1)
	LD	A,H
	OR	L
	JR	Z,READ1

;	NOT HEADER IF HERE, MOVE INTO LOAD AREA & CONTINUE WITH NEXT SECTOR

RESCAN:	LD	HL,DEFBFR
	LD	DE,(RDPTR)
	LD	BC,SECSIZ
	LDIR
	LD	(RDPTR),DE	;UPDATE READ PTR

;	IF HERE, EITHER WE ARE DISCARDING THE 1ST SECTOR OR READING THE 2ND

READ1:	LD	DE,(RDPTR)	;READ INTO LOAD AREA DIRECTLY
	CALL	SETDMA
	LD	DE,FCB
	CALL	RDSEC
	OR	A		;IF END OF FILE THEN DONE
	JR	NZ,RDDONE
	LD	DE,SECSIZ	;PTR = PTR + SECTOR$SIZE
	LD	HL,(RDPTR)
	ADD	HL,DE
	LD	(RDPTR),HL
	LD	A,(MEMTOP+1)	;IF NEW PAGE > RESERVED_PAGE - 1  THEN
	DEC	A		;   GOING INTO RSVP SO ABORT BY RETURNING CF
	CP	H
	RET	C
	JP	READ1
;
RDDONE:	XOR	A		;RETURN Z
	RET


;**********************************************************
;
;   FETCH PARAMETERS SUBR
;	ENTRY-	CMDPTR= PTR TO 1ST NUMERICAL PARAMETER
;	EXIT -	CF= ERR
;
;**********************************************************
GET3PAR:
	LD	HL,(CMDPTR)		;IF NO PARAMETERS THEN RETURN CF
	CALL	SKIP
	SCF
	RET	Z

	LD	(CMDPTR),HL		;SAVE NEW PTR
	CALL	GETNUM			;GET 1ST PARAMETER
	RET	C

	LD	DE,PARAM1		;INIT PTR TO PARAM STORAGE
	CALL	LOAD16
	LD	HL,(CMDPTR)		;SEARCH FOR 2ND PARAMETER (AFTER ',')
	LD	C,','
	CALL	SEARCH
	SCF				;IF NO 2ND PARAMETER THEN RETURN CF
	RET	Z

	INC	HL			;PASS OVER ','


;**********************************************************
;
;	GET 2 PARAMETERS SUBR. SAME AS ABOVE EXCEPT:
;	ENTRY-	DE= PARAMETER STORAGE PTR
;		HL= CMDPTR
;	EXIT -	CF= ERR
;
;**********************************************************
GET2PAR:
	CALL	SKIP			;SKIP ANY TRAILING SPACES
	SCF				;IF END OF LINE THEN RETURN CF
	RET	Z

	LD	(CMDPTR),HL		;SAVE NEW PTR
	CALL	GETNUM			;GET 2ND PARAM.
	RET	C

	CALL	LOAD16


;**************************************
;
;	THIS ENTRY IS SAME AS ABOVE
; EXCEPT DOES SEARCH FOR COMMA.
;
;**************************************
GET1PC:	LD	HL,(CMDPTR)
	LD	C,','
	CALL	SEARCH
	SCF
	RET	Z

	INC	HL


;************************************************
;
;	GET 1 PARAMETER SUBR SAME AS ABOVE
;
;************************************************
GET1PAR:
	CALL	SKIP		;SKIP ANY SPACES
	SCF
	RET	Z

	LD	(CMDPTR),HL	;SAVE NEW PTR
	CALL	GETNUM		;GET PARAMETER
	RET	C

	CALL	LOAD16
	OR	A		;RETURN NO ERR
	RET


;**********************************************************
;
;	LOAD WORD SUBR
;	ENTRY-	DE= WORD PTR
;		HL= VALUE
;	EXIT -	DE= WORD PTR + 2 (NEXT WORD IN SEQUENCE)
;
;**********************************************************
LOAD16:	EX	DE,HL		;STORE IT
	LD	(HL),E
	INC	HL
	LD	(HL),D
	INC	HL
	EX	DE,HL
	RET


;***************************************************************************
;
;   CHECK BOUNDS SUBR TESTS PARAM 1 & 2 FOR OUT OF BOUNDS VALUES & IF OK,
;  STORES THE OFFSETTED VALUES BACK INTO THE RESPECTIVE LOCATIONS.
;	EXIT -	CF= OUT OF BOUNDS
;
;***************************************************************************
CKBNDS:	LD	HL,(PARAM1)		;START WITH PARAM 1
	CALL	CPBNDS			;IF OUT THEN RETURN CF
	RET	C

	LD	(PARAM1),HL		;ELSE STORE NEW VALUE
	LD	HL,(PARAM2)		;THEN CHECK PARAM 2
	CALL	CPBNDS
	LD	(PARAM2),HL		;STORE IT & RETURN RESULT
	RET


;***************************************************************************
;
;   COMPARE BOUNDS SUBR ADDS VALUE TO THE BASE OF THE BUFFER & CHECKS FOR
;  OVERFLOW. THEN CHECKS NEW VALUE'S PAGE AGAINST THE TOP OF AVAILABLE
;  MEMORY'S PAGE & RETURNS CARRY IF EITHER IS OUT.
;	ENTRY-	HL = VALUE TO COMPARE
;	EXIT -	HL = CORRECTED VALUE
;		CF = BOUNDS ERROR
;
;***************************************************************************
CPBNDS:	LD	A,(MEMTOP+1)	;SET UP MEMTOP PAGE -1
	DEC	A
	LD	DE,($MEMRY)	;ADD VALUE TO BUFFER START (FROM OVERLAY)
	ADD	HL,DE
	RET	C		;IF OVERFLOW THEN RETURN CF

	CP	H		;ELSE COMPARE PAGE VALUE TO MEMTOP PAGE -1
	RET			;RETURN CF IF PAGE > MEMTOP PAGE -1


;****************************************************************************
;
;   GET NUMBER SUBR CONVERTS ASCII-HEX CHARS IN STRING TO BINARY WORDS UNTIL
;  A NON- HEX CHAR IS ENCOUNTERED. IF NOT SPACE OR COMMA THEN RETURNS CARRY.
;	ENTRY-	HL = TEXT POINTER
;	EXIT -	HL = NUMBER
;		CF= NON HEX CHAR (BUT NOT SPACE OR COMMA)
;		BC= PTR TO NON-HEX CHAR
;		A=  NON-HEX CHAR
;
;***************************************************************************
GETNUM:	PUSH	HL		;HL -> BC
	POP	BC
	LD	A,0		;FLAG = 0
	LD	(GETFLG),A
	LD	HL,0		;ACCUM = 0

GETLP:	LD	A,(BC)		;CHAR = (PTR)
	CALL	ASC2HX		;ATTEMPT TO CONVERT CHAR
	JR	C,GETCHK	;IF ILLEGAL THEN CHECK FOR DELIMITER

	ADD	HL,HL		;ACCUM = ACCUM * 16
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	L		;ACCUM = ACCUM + DATA
	LD	L,A
	INC	BC		;PTR = PTR + 1
	LD	A,(GETFLG)	;BUMP FLAG TO NOT 0
	INC	A
	LD	(GETFLG),A
	JR	GETLP

GETCHK:	LD	A,(GETFLG)	;IF FLAG = 0 THEN RETURN CF
	SUB	1
	RET	C

	LD	A,(BC)		;GE-GET CHAR
	CP	' '		;IF SPACE THEN RETURN NC
	RET	Z

	CP	','		;IF COMMA THEN RETURN NC
	RET	Z

	OR	A		;IF END OF LINE THEN RETURN NC
	RET	Z

	SCF			;ELSE RETURN CF
	RET


;*********************************
;
;	PRINT HEX VALUE SUBR
;	ENTRY-	HL = VALUE
;
;********************************
PRT2H:	LD	A,H
	CALL	PRTHEX
	LD	A,L


;************************************************
;
;	PRINT BYTE HEX VALUE TO CONSOLE SUBR
;	ENTRY-	A= VALUE
;
;************************************************
PRTHEX:	OR	A
	RLA
	CALL	PRHXDG

PRHXDG:	RLA
	RLA
	RLA
	RLA
	PUSH	AF
	CALL	HX2ASC		;CONVERT NIBBLE TO ASCII-HEX
	PUSH	HL
	LD	C,A
	CALL	CONOUT
	POP	HL
	POP	AF
	RET


;***************************************************************************
;
;	HEX TO ASCII-HEX CONVERTER CONVERTS BINARY NIBBLE TO ASCII-HEX
;	ENTRY-	A= BINARY NIBBLE
;	EXIT -	A= ASCII REPRESENTATION OF THE NIBBLE
;
;***************************************************************************
HX2ASC:	AND	0FH		;ONLY NIBBLE PLEASE
	CP	10		;IF 0..9 THEN OK
	JR	C,NTALPH

	ADD	7		;ELSE OFFSET 10..15 TO 'A'..'F'

NTALPH:	ADD	'0'		;CONVERT TO ASCII
	RET


;****************************
;
;   CHECK FOR DONE SUBR
;
;****************************
CKDONE:	EX	DE,HL
	LD	HL,(DISEND)
	OR	A
	SBC	HL,DE
	EX	DE,HL
	RET

	SUBTTL	MESSAGES

RDERRM:	DB	CR,LF,'Disk Read Error$'
WRERRM:	DB	CR,LF,'Disk Write Error, Probably Full!$'
DELMSG:	DB	CR,LF,'File Exists, Delete?..(Y/N) - $'
CMDERM:
SYNERM:	DB	CR,LF,'*** Syntax Error ***...Type ''HElp'' For List$'
FNFM:	DB	CR,LF,'File Not Found$'
BOUNDM:	DB	CR,LF,'Value Out of Bounds$'
MEMERM:	DB	CR,LF,'Memory Error$'
HXERM:	DB	CR,LF,'Hex File Error$'
CVERM:	DB	CR,LF,'Hex Conversion Error$'
HXADRM:	DB	CR,LF,'Hex Load Address Lower Than 1st Record Load Address!$'
HXTYPM:	DB	CR,LF,'Illegal Hex Record Type!$'
HXEMTM:	DB	CR,LF,'Hex File Empty!$'

HELPM:	DB	CR,LF,'HElp'
	DB	CR,LF,'LOad addr d:file (MUST BE ''HEX''FORMAT)'
	DB	CR,LF,'SAve first, last d:file (WILL BE ''HEX'' FORMAT)'
	DB	CR,LF,'FIll first, last, value'
	DB	CR,LF,'MOve first, last, dest'
	DB	CR,LF,'DIsplay <first><, last>'
	DB	CR,LF,'SUbstitute addr$'

	SUBTTL	COMMAND TABLE
;------------------
;
;   COMMAND                                                                                                                                OVEC

	DB	2,'DI'	;DISPLAY
	DW	DISPC

	DB	2,'SU'	;SUBSTITUTE
	DW	SUBSC

	DB	2,'SA'	;SAVE
	DW	SAVEC

	DB	2,'HE'	;HELP
	DW	HELPC

	DB	2,'?',0	; "
	DW	HELPC

	DB	2,'H',0	; "
	DW	HELPC

	DB	2,'QU'	;QUERY BUFFER START
	DW	QUERYC

	DB	0		;TABLE TERMINATOR

	SUBTTL   VARIABLES
	DATA

CMDPTR:	DW	CMDBFR
CMDBFR:	EQU	$		;COMMAND INPUT TEXT BUFFER
	REPT	130
	LIST	OFF
	DB	0
	LIST	ON
	ENDM
CBFRSZ:	EQU	$-CMDBFR-2

;
;	INPUT PARAMETER STORAGE
;
PARAM1:	DW	0
PARAM2:	DW	0
PARAM3:	DW	0

;
MEMTOP:	DW	0		;TOP OF AVAILABLE MEMORY PTR
RDPTR:	DW	0		;FILE READ PTR

DISTRT:	DW	0		;DISPLAY COMMAND LAST START PTR
DISEND:	DW	0		;DISPLAY COMMAND LAST END PTR
DISTMP:	DW	0		;DISPLAY COMMAND TEMP STORAGE

TEMP:	DW	0		;TEMPORARY STORAGE
GETFLG:	DB	0		;FLAG FOR GETNUM

BFRCNT:	DB	0		;FILE READ COMMAND BUFFER COUNT
DBPTR:	DW	DEFBFR
BFRPTR:	DW	0		;HEX FILE READ PTR

HEXBASE:
	DW	0		;HEX FILE BASE LOAD ADDR
HEXPTR:	DW	0		;HEX FILE READ PTR

FCB:	EQU	$		;FILE CONTROL BLOCK STORAGE
	REPT	33
	LIST	OFF
	DB	0
	LIST	ON
	ENDM

	DS	STKSIZ
STACK:	EQU	$
	DB	0

	REL
	END	START
