; RUNENT - Written by Bill Sudbrink in June 2006
;
; Updated 5/25/14 by Mike Douglas
;	- Echo only the address to the screen while loading the .ENT file
;	    to speed up the load process.
;	- Assume a .ENT extension if an extension not supplied
;	- Assume start address is the lowest address in the file if
;	    a starting address not specified on the command line.
;
; USAGE:
;  RUNENT <ENT FILE NAME> [<2ND ENT FILE NAME>] [:<STARTING OFFSET IN HEX>]
;
; This program allows one or two ENT files to be loaded and run from CP/M.
;
; An ENT file is file containing the memory image of a Processor Technology
; program that was originally intended to be loaded from tape.  The file
; is ASCII text and the main data has the format:
;
; AAAA: BB CC DD...
;
; where "AAAA" is a four digit HEX address and "BB CC DD" are byte
; values (also in HEX) to store in memory starting at that address.
; The ENT files normally have an address for every 16 values with
; the address starting a new line.  This program does not require
; embedded addresses, just a starting address but it will honor
; addresses whereever they are found in the file.  Also, this program
; ignores whitespace (defined here as any value below 21H with the
; exception of 1AH which is recognized as end of file).  ENT files
; are usually prefixed with "EN <address>" or "ENTER <address>"
; on the first line.  This program ignores the file content up to
; the first valid address.  ENT files are normally terminated with a
; slash character "/".  This program will continue to process a file
; until:
;   1) The normal ENT file end marker (/) is encountered
;   2) A CP/M file end marker (HEX 1A, control-Z) is encounterd
;   3) An error is encountered.  Only a small amount of error checking
;      is performed.  Possible errors are:
;       A) An invalid character is encountered after the first
;          address.  Valid characters are whitespace, control-z, /,
;          0-9, :, A-F, a-f
;       B) No address is found in the file
;       C) An odd number of HEX digits are found between two
;          colons
;       D) Fewer than four HEX digits are found between two
;          colons
;       E) An odd number of HEX digits are found between the
;          last colon in the file and the end of the file
;
; NOTE: THIS IS NOT IMPLEMENTED:
;       F) An address is reached that would overwrite this
;          program's work code at the top of the TPA
; NOTE: RIGHT NOW, THIS PROGRAM WILL STOMP ON ITSELF IF GIVEN A FILE
;       WITH THE RIGHT (WRONG?) ADDRESSES.  I've tried to implement
;       this several times.  Each time it just got too big and ugly.
;       Maybe some other smart person will come up with a neat solution.
;
; If an error is encountered, a short message is printed to the console
; and control is returned to CP/M.
;
; The program carries out the following steps:
;
; 1) Process the command line parameters.
; 2) check that the file(s) exist and contain a valid address (XXXX:) specifier
; 3) Relocate the work code to the top of the TPA (discovered by examining
;    address 6 and 7).
; 4) Jump to the work code which will then:
; 5) Copy 0000H-00FFH to 0100H-01FFH.  This allows CP/M compatable ENT files to
;    be run.
; 5) Load the specified file(s).  Target addresses below BF00H are offset by
;    0100H to ensure that CP/M runs normally until the file load(s) is(are)
;    completed.  Above BF00H?  You're on your own.  This allows the PT 8080
;    development system to be loaded.
; 6) Shift memory down, that is, move the memory from 0100H-<top of TPA>
;    to 0000H-<top of TPA minus 0100H>.
; 7) Set up the stack pointer and the HL register pair to look like Solos/Cutter
;    did the work.  NOTE: You might need to change this if your Solos/Cutter is
;    not at address 0C000H
; 8) Jump to the specified start address or 0000H default.
;

CPM	EQU	5
FCB1	EQU	5CH
FCB2	EQU	6CH
PARAMS	EQU	81H

	ORG	100H
	JMP	START


RLADR:	DW	0	; relocation address... where the code is going
FUINC:	DW	0	; fixup address increment... how much will
			;      addresses be adjusted
FUTP:	DW	0	; fixup table pointer
; fixup table:
FUTBL:	DW	FU000 + 1	; These first two entries should always be here
	DW	FU001 + 1

	DW	FU002 + 1
	DW	FU003 + 1
	DW	FU004 + 1
	DW	FU005 + 1
	DW	FU006 + 1
	DW	FU007 + 1
	DW	FU008 + 1
	DW	FU009 + 1
	DW	FU00A + 1
	DW	FU00B + 1
	DW	FU00C + 1
	DW	FU00D + 1
	DW	FU00E + 1
	DW	FU00F + 1

	DW	FU010 + 1
	DW	FU011 + 1
	DW	FU012 + 1
	DW	FU013 + 1
	DW	FU014 + 1
	DW	FU015 + 1
	DW	FU016 + 1
	DW	FU017 + 1
	DW	FU018 + 1

	DW	FU050 + 1
	DW	FU051 + 1
	DW	FU052 + 1
	DW	FU053 + 1
	DW	FU054 + 1
	DW	FU055 + 1
	DW	FU056 + 1
	DW	FU057 + 1
	DW	FU058 + 1
	DW	FU059 + 1

	DW	FU05X + 1
	DW	FU05Y + 1

	DW	FU060 + 1
	DW	FU061 + 1
	DW	FU062 + 1
	DW	FU063 + 1
	DW	FU064 + 1
	DW	FU065 + 1
	DW	FU066 + 1
	DW	FU067 + 1
	DW	FU068 + 1
	DW	FU069 + 1
	DW	FU06A + 1

	DW	FU06X + 1
	DW	FU06Y + 1
	DW	FU06Z + 1
	DW	FU06P + 1
	DW	FU06Q + 1

	DW	FU100 + 1
	DW	FU101 + 1
	DW	FU102 + 1
	DW	FU103 + 1
	DW	FU104 + 1
	DW	FU105 + 1
	DW	FU106 + 1
	DW	FU107 + 1
	DW	FU108 + 1
	DW	FU109 + 1
	DW	FU10A + 1
	DW	FU10B + 1
	DW	FU10C + 1

	DW	FU200 + 1
	DW	FU201 + 1
	DW	FU202 + 1
	DW	FU203 + 1
	DW	FU204 + 1
	DW	FU205 + 1
	DW	FU206 + 1
	DW	FU207 + 1
	DW	FU208 + 1
	DW	FU209 + 1
	DW	FU20A + 1

	DW	FU500 + 1
	DW	FU501 + 1
	DW	FU502 + 1
	DW	FU503 + 1

	DW	0H	; leave this zero here, it marks the table end

WELCOME:DB	'RUNENT - Written in 2006 by Bill Sudbrink',0DH,0AH,'$'
USEMSG:	DB	'USAGE:',0DH,0AH,0DH,0AH
	DB	'   RUNENT'
	DB	' <ENT FILE NAME>'
	DB	' [<2ND ENT FILE NAME>]'
	DB	' [:<STARTING OFFSET IN HEX>]$'
FBDMSG:	DB	' open failed.',0DH,0AH,'$'
FOPMSG:	DB	' opened.$'
FNOAD:	DB	'  No address found in file.',0DH,0AH,'$'
FADOK:	DB	'  Address found.',0DH,0AH,'$'
BSADMSG:DB	'Bad start address. Please supply four hexadecimal digits.',0DH,0AH,'$'
NSADMSG:DB	'No start address specified, using first address in file.'
NEWLN:	DB	0DH,0AH,'$'
PTSADR:	DW	0	; pointer to the starting address parameter in the
			; command line (if present)
DIGCNT:	DB	0
WRKFCB:	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0

; REMOVE AFTER DEBUGGING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;	DB	'1000: 0e 02 1e 3a cd 05 00 C3 00 00 /'


; 16 bit subtract DE from HL
SUBX:	MOV	A,L
	SUB	E
	MOV	L,A
	MOV	A,H
	SBB	D
	MOV	H,A
	RET

; move the block of bytes starting at DE and ending at BC-1 to HL
; we need to have this routine twice, once not to be relocated (MOVBK1)
; and the other to be relocated (MOVBK2)
MOVBK1:	LDAX	D	; get the data
	INX	D	; increment the source pointer
	MOV	M,A	; store the data
	INX	H	; increment the destination pointer
	MOV	A,D	; check the high byte of the source pointer against...
	CMP	B	; the high byte of the source end pointer
	JNZ	MOVBK1	; no match? keep going
	MOV	A,E	; check the low byte of the source pointer against...
	CMP	C	; the low byte of the source end pointer
	JNZ	MOVBK1	; no match? keep going
	RET		; otherwise, done

PRTSTR:	MVI	C,9	; print string command
	JMP	CPM	; cute trick, let CP/M do the return for us

PRTCHR:	MVI	C,2	; print character command
	JMP	CPM	; cute trick, let CP/M do the return for us

; print the string pointed to by DE and exit
PRTEXIT:
	CALL	PRTSTR
	JMP	0	; back to CP/M

; print the file name in the work FCB, including the drive letter if
; not default
PRTWKFN:
	LDA	WRKFCB
	ORA	A
	JZ	NODRV
	ADI	64
	MOV	E,A
	CALL	PRTCHR
	MVI	E,':'
	CALL	PRTCHR
NODRV:	MVI	A,'$'
	STA	WRKFCB + 12
	LXI	D,WRKFCB + 1
	CALL	PRTSTR
	XRA	A
	STA	WRKFCB + 12
	RET

; check the file name in the work FCB, if OK, print it and try to open it
; if the open fails, we print an error message and go back to CP/M (don't return)
; if we return with the zero flag set, there was no file name
DOWKFCB:
	LDA	WRKFCB + 1
	CPI	' '
	RZ			;NO FILE SPECIFIED
	LDA	WRKFCB + 9
	CPI	' '
	JNZ	FNOK		;EXTENSION SPECIFIED
	LXI	H,'EN'		;FORCE .ENT EXTENSION
	SHLD	WRKFCB + 9
	MVI	A,'T'
	STA	WRKFCB + 11

FNOK:	CALL	PRTWKFN
	LXI	D,WRKFCB
	MVI	C,15	; open
	CALL	CPM
	INR	A
	JNZ	OPENOK
	LXI	D,FBDMSG
	JMP	PRTEXIT

OPENOK:	LXI	D,FOPMSG
	CALL	PRTSTR
	XRA	A
	INR	A
	RET

; store the work FCB to HL
STWKFCB:
	LXI	D,WRKFCB; block move source in DE
	LXI	B,WRKFCB+36; block move source end in BC (get the whole thing to record the opened state)
	CALL	MOVBK1
	RET

; check to be sure that the file (whose open FCB is pointed to by CURFCB)
; has an address specification (XXXX:) in it.  if we find the address,
; back up the current file offset to the start of it
; print error message and go back to CP/M on failure (don't return)
; print address good message if we find the address
CHKADDR:
	XRA	A
	STA	DIGCNT
CHKLOOP:
	CALL	RDCHR
	JZ	GOTCHR
	LXI	D,FNOAD; no address in the file
	JMP	PRTEXIT
GOTCHR:
	CALL	TSTDIG
	JC	CHKBAD
	LDA	DIGCNT
	INR	A
	STA	DIGCNT
	JMP	CHKLOOP
CHKBAD:	CPI	':'
	JNZ	CHKADDR
	LDA	DIGCNT
	CPI	4
	JC	CHKADDR

; if a startng address was not entered on the command line, then use
;    the first address in the file as the starting address.

	LDA	JMPSADR+2	;check MSB of jump address
	INR	A		;if 0FFh, not set yet
	JNZ	HAVEST		;already have start
	DCX	H		;move back to start of ascii address
	DCX	H
	DCX	H
	DCX	H
	CALL	GDGQAD		;convert ascii address to binary in DE
	XCHG			;move it to HL
	SHLD	JMPSADR+1 	;store it in our jump instruction	

HAVEST:	LHLD	CUROFF
	DCX	H
	DCX	H
	DCX	H
	DCX	H
	DCX	H
	SHLD	CUROFF
	LXI	D,FADOK
	CALL	PRTSTR
	RET

; reset the current file offset and buffer pointer
RESET:
	XRA	A
	STA	CUROFF
	STA	CUROFF + 1
	DCR	A
	STA	BUFPTR	; force a block read (0FFH in BUFPTR)
	RET

START:	LXI	SP,STACK; set up our stack...
			; note that we relocate it later

; print welcome
	LXI	D,WELCOME
	CALL	PRTSTR

; save FCB2
	LXI	H,SFCB2	; block move destination in HL
	LXI	D,FCB2	; block move source in DE
	LXI	B,FCB2+13; block move source end in BC (just get the drive and name)
	CALL	MOVBK1

; find and (if found) process the starting address parameter
DOSADR:	LXI	H,PARAMS; point LH to the command line
SADRLP:	MOV	A,M
	INX	H
	ORA	A
	JZ	NOSADR	; hit zero? then no address specified
	CPI	' '	; see if we're on a space...
	JNZ	SADRLP
	MOV	A,M	; followed by a...
	CPI	':'	; colon
	JNZ	SADRLP	; nope? loop
	INX	H	; skip the colon
	SHLD	PTSADR	; store the pointer to what should be the four ASCII char HEX start address
	MVI	C,4	; check the four digits of the specified address
	CALL	TSTDGS
	JNC	GOODSADR
	LXI	D,BSADMSG
	JMP	PRTEXIT
NOSADR:	LXI	D,NSADMSG
	CALL	PRTSTR
	JMP	SADRDONE
GOODSADR:
	LHLD	PTSADR
	CALL	GDGQAD	; convert the ASCII hex value into a value in DE
	XCHG		; move it to HL
	SHLD	JMPSADR + 1; store it in our jump instruction
SADRDONE:

; prep to check and print first file name (move FCB1 to the work FCB)
	LXI	H,WRKFCB; block move destination in HL
	LXI	D,FCB1	; block move source in DE
	LXI	B,FCB1+13; block move source end in BC (just get the drive and name)
	CALL	MOVBK1

; check for a file name in WRKFCB and print and open it
	CALL	DOWKFCB
	JNZ	F1OK
	LXI	D,USEMSG; no filename, print use message
	JMP	PRTEXIT
F1OK:
; move the opened FCB back to FCB1
	LXI	H,FCB1
	CALL	STWKFCB
	CALL	CHKADDR	; now see if it has a valid address in it
; store the offset where the first address was found in the first file
	LHLD	CUROFF
	SHLD	STOFF
	CALL	RESET

; REMOVE AFTER DEBUGGING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;	MVI	A,0FFH	; second file is optional
;	STA	F2DONE
;	JMP	PDONE

; prep to check and print second file name (move SFCB2 to the work FCB)
	LXI	H,WRKFCB; block move destination in HL
	LXI	D,SFCB2	; block move source in DE
	LXI	B,SFCB2+36; block move source end in BC (get the whole thing to zero out leftovers from first file)
	CALL	MOVBK1

; check for a file name in WRKFCB and print and open it
	CALL	DOWKFCB
	JNZ	F2OK
	MVI	A,0FFH	; second file is optional
	STA	F2DONE
	JMP	PDONE	; go do the starting offset parameter processing

F2OK:
; move the opened FCB back to SFCB2
	LXI	H,SFCB2	; block move destination in HL
	CALL	STWKFCB
	CALL	CHKADDR
; store the offset where the first address was found in the second file
	LHLD	CUROFF
	SHLD	STOFF2
	CALL	RESET

; done with command line parameters
PDONE:
; set the current FCB to FCB1
	LXI	H,FCB1
	SHLD	CURFCB


; REMOVE AFTER DEBUGGING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;	JMP	RLBST

; Code to perform relocation starts here:

; Calc size of the relocate block (RLBND - RLBST) + 3
; note that the '+ 3' assumes that the smallest possible
; relocatable block must contain one 3 byte instruction
	LXI	H,RLBST	; relocate block start
	XCHG
	LXI	H,RLBND	; relocate block end
	CALL	SUBX
	INX	H
	INX	H
	INX	H	; HL has size of the relocate block

; Calc new offset of relocate block (<top of TPA> - <size of relocate block>)
	XCHG		; size of the relocate block in DE
	LHLD	CPM + 1	; top of TPA in HL
	CALL	SUBX
	SHLD	RLADR	; HL has relocation target address

; Calc address increment (RLADR - RLBST)
	XCHG		; relocation target address in DE
	LXI	H,RLBST	; relocation block start
	XCHG
	CALL	SUBX
	SHLD	FUINC	; HL has fixup address increment

; Walk through the fixup table, increment each address by FUINC
	LXI	H,FUTBL	; start at top of table
FULOOP:	MOV	E,M	; get fixup pointer from table into DE
	INX	H
	MOV	D,M
	INX	H
	SHLD	FUTP	; store fixup table pointer
	XCHG		; get fixup pointer into HL
	MOV	A,H	; see if we're zero, end of table
	ANA	A
	JNZ	FUCONT	; not zero, continue
	MOV	A,L
	ANA	A
	JZ	FUDONE	; end of table, exit fixup loop
FUCONT:	MOV	C,M	; get the address from the block into BC
	INX	H
	MOV	B,M
	DCX	H
	XCHG		; put the fixup pointer back into DE
	LHLD	FUINC	; get the increment value
	DAD	B	; add the code value
	XCHG		; fixup pointer back in HL, new address in DE
	MOV	M,E
	INX	H
	MOV	M,D
	LHLD	FUTP	; get the fixup table pointer back into HL
	JMP	FULOOP	; process the next table entry

; fixup is done, now move the block
FUDONE:	LHLD	RLADR	; block move destination in HL
	LXI	D,RLBST	; block move source in DE
	LXI	B,RLBND+1; block move source end in BC
	CALL	MOVBK1

; Now jump to the relocated code.
FU000:	JMP	RLBST	; This jump should have been fixed
			; up by the first table entry

; Code to relocate starts here::::::::::::::::::::::::::::::::::::::::::::::::::::::::

RLBST:
FU001:	LXI	SP,STACK; reset the stack...
			; fixed up by the second table entry

; copy the "zero page" up so we can copy it back down later if it isn't overwritten
	LXI	D,0
	LXI	B,0100H
	LXI	H,0100H
FU500:	CALL	MOVBK2

STSEEK:			; seek CURFCB to STOFF
FU100:	LHLD	STOFF
	MOV	A,H
	ORA	A
FU101:	JNZ	NXTCHR
	MOV	A,L
	ORA	A
FU102:	JZ	SEEKOK
NXTCHR:	DCX	H
FU103:	SHLD	STOFF
FU104:	CALL	RDCHR
FU105:	JMP	STSEEK
SEEKOK:			; finished the seek, read the first address
FU106:	LXI	H,RDBUF
	MVI	C,5
FU107:	CALL	MULTIRD

MAINLP:
FU108:	CALL	STEP1	; process address if we're on one
FU109:	JZ	DOFL2	; zero flag if we hit EOF
	MOV	A,B
	CPI	1
FU10A:	JZ	MAINLP	; B is 1 if STEP1 processed an address
FU10B:	CALL	STEP2	; process a byte
FU10C:	JMP	MAINLP

; switch to the second file FCB if we haven't done it already
DOFL2:
FU200:	LDA	F2DONE	; test if we've done file 2
	ORA	A
FU201:	JNZ	FPDONE
FU202:	STA	CUROFF	; reset current offset
FU203:	STA	CUROFF + 1
	DCR	A
FU204:	STA	F2DONE	; set the file 2 done flag
FU205:	STA	BUFPTR	; force a block read
FU206:	LXI	H,SFCB2
FU207:	SHLD	CURFCB
FU208:	LHLD	STOFF2	; set the starting offset to the second file's value
FU209:	SHLD	STOFF
FU20A:	JMP	STSEEK

; file processing done
FPDONE:
; shift the whole TPA down on top of the "zero page".  if the ENT file had any
; "zero page" addresses in it, here's where we trash CP/M
	LXI	H,0
	LXI	D,0100H
FU501	LXI	B,RLBST
FU502:	CALL	MOVBK2
; Attempt to set up a couple of registers like Solos/Cutter would:
; NOTE: You will need to modify these if you have Solos/Cutter at
;       some other address.
	LXI	SP,0CC00H ; Point the stack into Solos/Cutter RAM
	LXI	H,0C000H ; Point HL at Solos/Cutter base address
JMPSADR:
	JMP	0FFFFH		;0FFFF indicates not set yet

; STEP1: look for an address spec, EOF or invalid data
; EOF               - zero flag
; not address       - B <- 0
; processed address - B <- 1
; error             - No return, bomb out here
STEP1:
	MVI	B,0
FU050:	LDA	RDBUF
	CPI	1AH		; EOF?
	RZ
	CPI	'/'		; EOF?
	RZ
FU051:	LDA	RDBUF + 4
	CPI	':'		; address?
	RNZ
	PUSH	B
	MVI	C,4
FU052:	LXI	H,RDBUF
FU053:	CALL	TSTDGS
FU054:	JC	FLERR

FU05X:	LXI	D,CRLF
	MVI	C,9
	CALL	CPM
FU05Y:	LXI	D,RDBUF
	MVI	C,9
	CALL	CPM

FU055:	LXI	H,RDBUF
FU056:	CALL	GDGQAD
	MOV	A,D
	CPI	0BFH
FU057:	JNC	NOTTPA
	INR	D		; keep us in the TPA
NOTTPA:	PUSH	D
FU058:	LXI	H,RDBUF
	MVI	C,5
FU059:	CALL	MULTIRD
	POP	D
	POP	B
	INR	B
	RET

; STEP2: process the first 2 characters in RDBUF as a byte value,
; store the value at DE, increment DE, shift RDBUF down 2 bytes
; and read 2 more bytes into the end.  File error if the first 2
; characters in RDBUF are not HEX digits... prints message and
; quits (does not return)
STEP2:
	MVI	C,2
FU060:	LXI	H,RDBUF
FU061:	CALL	TSTDGS
FU062:	JC	FLERR
FU063:	LXI	H,RDBUF
FU064:	CALL	GDGPAR
	STAX	D
	INX	D
	PUSH	D

FU06X:	LXI	D,BLANK
	MVI	C,9
;	CALL	CPM	;commented to not display all .ent bytes
FU06Z:	LDA	RDBUF+2
	MOV	B,A
	PUSH	B
	MVI	A,'$'
FU06P:	STA	RDBUF+2
FU06Y:	LXI	D,RDBUF
	MVI	C,9
;	c
	POP	B
	MOV	A,B
FU06Q:	STA	RDBUF+2

FU065:	LXI	D,RDBUF + 2
FU066:	LXI	B,RDBUF + 5
FU067:	LXI	H,RDBUF
FU068:	CALL	MOVBK2
	MVI	C,2
FU069:	LXI	H,RDBUF + 3
FU06A:	CALL	MULTIRD
	POP	D
	RET

FLERR:
FU503:	LXI	D,BADFL
	MVI	C,9
	CALL	CPM
	JMP	0

; read C bytes into HL.  Fill with 1AH if we hit EOF (due to RDCHR)
MULTIRD:
	PUSH	H
	PUSH	B
FU002:	CALL	RCHRNW
	POP	B
	POP	H
	MOV	M,A
	INX	H
	DCR	C
FU003:	JNZ	MULTIRD
	RET

; read a byte into A but skip values lower than 21H (except for 1A)
RCHRNW:
FU004:	CALL	RDCHR
	RNZ
	CPI	1AH
	RZ
NOT1A:	CPI	21H
FU005:	JC	RCHRNW	; less than 21H is white space for us
	RET

RDCHR:
FU006:	LHLD	CUROFF
	INX	H
FU007:	SHLD	CUROFF
FU008:	LDA	BUFPTR
	INR	A
FU009:	JNZ	BUFOK
FU00A:	CALL	RDBLK
FU00B:	JZ	RDBOK
	MVI	A,1AH
	RET
RDBOK:	MVI	A,80H
BUFOK:	MOV	L,A
FU00C:	STA	BUFPTR
	XRA	A	; zero flag - OK
	MOV	H,A
	MOV	A,M
	RET

RDBLK:
FU00D:	LHLD	CURFCB
	XCHG
	MVI	C,20
	CALL	CPM
	ORA	A
	RET		; zero flag - OK, no zero flag - EOF

; move the block of bytes starting at DE and ending at BC-1 to HL
; we need to have this routine twice, once not to be relocated (MOVBK1)
; and the other to be relocated (MOVBK2)
MOVBK2:	LDAX	D	; get the data
	INX	D	; increment the source pointer
	MOV	M,A	; store the data
	INX	H	; increment the destination pointer
	MOV	A,D	; check the high byte of the source pointer against...
	CMP	B	; the high byte of the source end pointer
FU00E:	JNZ	MOVBK2	; no match? keep going
	MOV	A,E	; check the low byte of the source pointer against...
	CMP	C	; the low byte of the source end pointer
FU00F:	JNZ	MOVBK2	; no match? keep going
	RET		; otherwise, done

; test C characters at HL to see if they are valid HEX digits
; they will be converted to upper case
; no carry - good hex digits, carry - at least one non-hex digit
TSTDGS:
	MOV	A,M
FU010:	CALL	TSTDIG
	RC
	MOV	M,A
	INX	H
	DCR	C
FU011:	JNZ	TSTDGS
	RET

; test the character in A to see whether it is a valid HEX digit
; convert it to upper case if necessary
; no carry - good hex digit, carry - not a hex digit
TSTDIG:	CPI	'0'
	RC		; less than zero
	CPI	':'
FU012:	JC	TSTGD	; it's between zero and nine, return good
	CPI	60H	; make sure it's upper case
FU013:	JC	UCOK
	ANI	0DFH
UCOK:	CPI	'A'	; check A to F
	RC		; less than A
	CPI	'G'
FU014:	JNC	TSTBAD
TSTGD:	CMC
	RET
TSTBAD:	STC
	RET

; NOTE: the routines below assume checked data, converted to upper case!

; translate the four HEX digits at HL into a 16-bit value in DE
GDGQAD:
FU015:	CALL	GDGPAR
	MOV	D,A
FU016:	CALL	GDGPAR
	MOV	E,A
	RET

; translate the pair of HEX digits at HL into a byte value in A
GDGPAR:
FU017:	CALL	GETDIG
	RLC
	RLC
	RLC
	RLC
	MOV	B,A
FU018:	CALL	GETDIG
	ADD	B
	RET

; translate a single HEX digit at HL into a nibble in A
GETDIG:	MOV	A,M
	INX	H
	SBI	'0'
	CPI	10
	RC
	SBI	7
	RET

	DS	32
STACK:	DB	0

BADFL:	DB	'Invalid file data'
CRLF:	DB	0DH,0AH,'$'
BLANK	DB	' $'

CURFCB:	DW	WRKFCB
BUFPTR:	DB	0FFH
CUROFF:	DW	0
STOFF:	DW	0
STOFF2:	DW	0
RDBUF:	DB	0,0,0,0,0,'$'

F2DONE:	DB	0

SFCB2:	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0

RLBND:	JMP	0	; end of relocated block
	END	START



