;
; TITLE		PACK UP THE BUFFER AND MOVE ROUTINES FOR FAST.COM
; FILENAME	PACKUP.ASM
; AUTHOR	Robert A. Van Valzah   12/25/78
; LAST REVISED	R.A.V.   5/20/79
; REASON	MOVED DEFAULT OPTION STRING TO 130H
;
;
BOOT	EQU	0
CURDSK	EQU	4
BDOS	EQU	5
;
FCB1	EQU	5CH
FCB2	EQU	6CH
DBUF	EQU	80H
DIRTRK	EQU	2	;DIRECTORY TRACK
MTYTRK	EQU	0FFH	;TRACK NUMBER SHOWING A DDB IS EMPTY
SECLEN	EQU	80H	;LENGTH OF A SECTOR IN BYTES
;
;
	ORG	100H
ENTRY:
	JMP	SKIPMES
	DB	'Copyright (C) 1979, Robert A. Van Valzah'
	DB	0,0,0,0,0 ;SO DFLTOPT IS AT NICE EASY BOUNDRY
;
DFLTOPT:		;OPTION STRING TO USE IF NONE SUPPLIED
	DB	'[RS]     '
;
; SECTOR ORDER TABLES
;
TRKSEC:
	DB	26,25,24,23,22,21,20,19,18,17,16,15,14
	DB	13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1
	DB	0	;EOT MARKER
	; RESERVE SPACE FOR DOUBLE DENSITY SECTOR TABLE
	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
;
DIRSEC:
	DB	25, 23, 21, 19, 17, 15, 14, 13
	DB	11,  9,  8,  7,  5,  3,  2,  1
	DB	0	;EOT MARKER
	; RESERVE SPACE FOR DOBLE DENSITY
	DB	0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
	PAGE
;
;	< < < < < <   FILE NAME PARSING SUBROUTINES > > > > > >
;
; GETFN GETS A FILE NAME FROM TEXT POINTED TO BY REG HL INTO
; AN FCB POINTED TO BY REG DE.  LEADING DELIMETERS ARE 
; IGNORED.
; ENTRY	HL	FIRST CHARACTER TO BE SCANED
;	DE	FIRST BYTE OF FCB
; EXIT	HL	CHARACTER FOLLOWING FILE NAME
;
GETFN:
	CALL	INITFCB	;FILL FCB WITH DEFAULTS
	CALL	GETSTART ;SCAN TO FIRST CHARACTER OF NAME
	RZ		;END OF LINE WAS FOUND - LEAVE FCB BLANK
	CALL	GETDRV	;GET DRIVE SPEC. IF PRESENT
	CALL	GETPS	;GET PRIMARY AND SECONDARY NAME
	RET
;
; INITFCB FILLS AN FCB WITH THE DEFAULT INFORMATION.  THE
; DRIVE SPEC IS DEFAULTED TO THE CURRENT DRIVE, AND THE
; PRIMARY AND SECONDARY NAME BYTES ARE FILLED WITH BLANKS.
; ENTRY	DE	FIRST BYTE OF FCB
; EXIT	DE	PRESERVED
;	A,C	CLOBBERED
;
INITFCB:
	PUSH	D	;SAVE FCB START
	XRA	A	;INIT DRIVE SPEC
	STAX	D
	INX	D	;POINT TO PRIMARY NAME FIELD
	MVI	A,' '	;CHAR TO FILL NAMES WITH
	MVI	C,11	;LENGTH OF PRI AND SEC NAMES
BLANKL:
	STAX	D
	INX	D
	DCR	C
	JNZ	BLANKL
	POP	D	;RESTORE FCB START POINTER
	RET
	PAGE
;
; GETSTART ADVANCES THE TEXT POINTER (REG HL) TO THE FIRST
; NON DELIMITER CHARACTER (I.E. IGNORES BLANKS).  RETURNS A
; FLAG IF END OF LINE (00H OR ';') IS FOUND WHILE SCANING.
; EXIT	HL	POINTING TO FIRST NON DELIMITER
;	A	CLOBBERED
;	ZERO	SET IF END OF LINE WAS FOUND
;
GETSTART:
	CALL	GETCH	;SEE IF POINTING TO DELIM?
	RNZ		;NOPE - RETURN
	CPI	';'	;END OF LINE?
	RZ		;YUP - RETURN W/FLAG
	ORA	A
	RZ		;YUP - RETURN W/FLAG
	INX	H	;NOPE - MOVE OVER IT
	JMP	GETSTART ;AND TRY NEXT CHAR
;
; GETDRV CHECKS FOR THE PRESENCE OF A DRIVE SPEC AT THE TEXT
; POINTER, AND IF PRESENT FORMATS IT INTO THE FCB AND
; ADVANCES THE TEXT POINTER OVER IT.
; ENTRY	HL	TEXT POINTER
;	DE	POINTER TO FIRST BYTE OF FCB
; EXIT	HL	POSSIBLY UPDATED TEXT POINTER
;	DE	POINTER TO SECOND (PRIMARY NAME) BYTE OF FCB
;
GETDRV:
	INX	D	;POINT TO NAME IF SPEC NOT FOUND
	INX	H	;LOOK AHEAD TO SEE IF ':' PRESENT
	MOV	A,M
	DCX	H	;PUT BACK IN CASE NOT PRESENT
	CPI	':'	;IS A DRIVE SPEC PRESENT?
	RNZ		;NOPE - RETURN
	MOV	A,M	;YUP - GET THE ASCII DRIVE NAME
	SUI	'A'-1	;CONVERT TO FCB DRIVE SPEC
	DCX	D	;POINT BACK TO DRIVE SPEC BYTE
	STAX	D	;STORE SPEC INTO FCB
	INX	D	;POINT BACK TO NAME
	INX	H	;SKIP OVER DRIVE NAME
	INX	H	;AND OVER ':'
	RET
	PAGE
;
; GETPS GETS THE PRIMARY AND SECONDARY NAMES INTO THE FCB.
; ENTRY	HL	TEXT POINTER
; EXIT	HL	CHARACTER FOLLOWING SECONDARY NAME (IF PRESENT)
;
GETPS:
	MVI	C,8	;MAX LENGTH OF PRIMARY NAME
	CALL	GETNAM	;PACK PRIMARY NAME INTO FCB
	MOV	A,M	;SEE IF TERMINATED BY A PERIOD
	CPI	'.'
	RNZ		;NOPE - SECONDARY NAME NOT GIVEN
			;RETURN DEFAULT (BLANKS)
	INX	H	;YUP - MOVE TEXT POINTER OVER PERIOD
FTPOINT:		;YUP - UPDATE FCB POINTER TO SECONDARY
	MOV	A,C
	ORA	A
	JZ	GETFT
	INX	D
	DCR	C
	JMP	FTPOINT
GETFT:
	MVI	C,3	;MAX LENGTH OF SECONDARY NAME
	CALL	GETNAM	;PACK SECONDARY NAME INTO FCB
	RET
	PAGE
;
; GETNAM COPIES A NAME FROM THE TEXT POINTER INTO THE FCB FOR
; A GIVEN MAXIMUM LENGTH OR UNTIL A DELIMITER IS FOUND, WHICH
; EVER OCCURS FIRST.  IF MORE THAN THE MAXIMUM NUMBER OF
; CHARACTERS IS PRESENT, CHARACTER ARE IGNORED UNTIL A
; A DELIMITER IS FOUND.
; ENTRY	HL	FIRST CHARACTER OF NAME TO BE SCANED
;	DE	POINTER INTO FCB NAME FIELD
;	C	MAXIMUM LENGTH
; EXIT	HL	POINTING TO TERMINATING DELIMITER
;	DE	NEXT EMPTY BYTE IN FCB NAME FIELD
;	C	MAX LENGTH - NUMBER OF CHARACTERS TRANSFERED
;
GETNAM:
	CALL	GETCH	;ARE WE POINTING TO A DELIMITER YET?
	RZ		;IF SO, NAME IS TRANSFERED
	INX	H	;IF NOT, MOVE OVER CHARACTER
	CPI	'*'	;AMBIGIOUS FILE REFERENCE?
	JZ	AMBIG	;IF SO, FILL THE REST OF FIELD WITH '?'
	STAX	D	;IF NOT, JUST COPY INTO NAME FIELD
	INX	D	;INCREMENT NAME FIELD POINTER
	DCR	C	;IF NAME FIELD FULL?
	JNZ	GETNAM	;NOPE - KEEP FILLING
	JMP	GETDEL	;YUP - IGNORE UNTIL DELIMITER
AMBIG:
	MVI	A,'?'	;FILL CHARACTER FOR WILD CARD MATCH
FILL?:
	STAX	D	;FILL UNTIL FIELD IS FULL
	INX	D
	DCR	C
	JNZ	FILL?
			;FALL THRU TO INGORE REST OF NAME
GETDEL:
	CALL	GETCH	;POINTING TO A DELIMITER?
	RZ		;YUP - ALL DONE
	INX	H	;NOPE - IGNORE ANTOHER ONE
	JMP	GETDEL
	PAGE
;
; GETCH GETS THE CHARACTER POINTED TO BY THE TEXT POINTER
; AND SETS THE ZERO FLAG IF IT IS A DELIMITER.
; ENTRY	HL	TEXT POINTER
; EXIT	HL	PRESERVED
;	A	CHARACTER AT TEXT POINTER
;	Z	SET IF A DELIMITER
;
GETCH:
	MOV	A,M	;GET THE CHARACTER
	IRPC	CHAR,<.,; :=<>>
	  CPI	'&CHAR'
	  RZ
	ENDM
	ORA	A	;SET ZERO FLAG ON END OF TEXT
	RET
	PAGE
;
;
;	<<<<<<	OPTION STRING PARSING SUBROUTINES   >>>>>>
;
;
; GETOPT GETS AN OPTION STRING FROM TEXT POINTED TO BY REG HL
; IF NO OPTION STRING IS PRESENT, THE DEFAULT STRING (DFLTOPT)
; IS PARSED INSTEAD.  AN OPTION STRING STARTS WITH '['.
;
GETOPT:
	CALL	GETSTART ;GET FIRST CHARACTER OF ARGUMENT
	CPI	'['	;IS THIS THE START OF AN OPTION STRING?
	JZ	SCANOPT	;IF SO - GO PARSE ARGUMENT STRING
	PUSH	H	;IF NOT - SAVE ARGUMENT TXA AND . . .
	LXI	H,DFLTOPT ;PARSE DEFAULT STRING INSTEAD
	CALL	SCANOPT
	POP	H	;GET ARG TXA BACK
	RET
;
; SCAN AN OPTION STRING, CALLING DDB CREATION ROUTINES TO GIVE
; REQUESTED OPTIONS
;
SCANOPT:
	XCHG	;SAVE OPTION TXA WHILE . . .
	LHLD	BDOS+1	;INITIALIZING DDB ALLOCATIN POINTER
	MVI	L,0	;MOVE DOWN TO PAGE BOUNDRY
	SHLD	BUFSTRT
	XCHG		;GET OPTION TXA BACK
	INX	H	;MOVE OVER '['
SCANDRV:
	CALL	GETODRV	;GET DRIVE SPEC IF PRESENT
	MOV	A,C	;SAVE DRIVE SPEC FOR DDB CREATION
	STA	BUFDRV
SCANBUF:
	CALL	GETOBUF	;GET BUFFER SPEC
	MOV	A,C	;WAS A BUFFER SPEC PRESENT?
	CPI	4
	JNZ	OPTOK	;YES - THAT'S AN OK OPTION
	MOV	A,B	;NO - IT'S OK ONLY IF . . .
	ORA	A	;A DRIVE SPEC WAS PRESENT
	JZ	OPTOK
OPTERR:			;GIVE OPTION ERROR AND REBOOT
	LXI	D,OPTMES
	MVI	C,9
	CALL	BDOS
	JMP	0
;
OPTMES:
	DB	'INVALID OPTION', 13, 10, '$'
;
OPTOK:
	PUSH	H	;SAVE TXA DURING CREATION
	CALL	CRTBUF	;CREATE THE REQUESTED BUFFERS
	POP	H	;GET TXA BACK
	CALL	GETOBUF	;SEE IF ANY MORE BUFF SPEC PRESENT
	MOV	A,C
	CPI	4
	JNZ	OPTOK	;YES - GO CREATE THEM
	CALL	GETOCH	;NO - SEE IF OUT OF OPTION SPEC
	JNZ	SCANDRV	;NO - EXPECT ANOTHER DRIVE SPEC
	RET		;YES - OUR JOB HERE IS DONE
;
; GET AN OPTION DRIVE SPEC FROM TEXT, RETURNED IN REG C.
; IF NOT PRESENT, RETURN CURRENTLY LOGGED DISK AND SET FLAG.
;
GETODRV:
	LDA	CURDSK	;GET CURRENT DISK IN CASE OF FAILURE
	MOV	C,A
	MVI	B,0FFH	;SET DEFAULT FLAG ALSO
	MOV	A,M	;GET POSSIBLE DRIVE SPEC CHR
	SUI	'A'	;LESS THAN 'A'
	RC		;YES - RETURN TAKING DEFAULT
	CPI	'D'-'A'+1 ;GREATER THAN 'D'?
	RNC		;YES - RETURN TAKING DEFAULT
	MOV	C,A	;NO - VALID SPEC WAS PRESENT, RETURN
	MVI	B,0	;IT IN REG C, AND RESET DEFAULT FLAG
	INX	H	;MOVE OVER VALID DRIVE SPEC CHARACTER
	RET
;
; GET OPTION BUFFER SPECIFICATION, RETURNING CORRESPONDING
; TOKEN IN REG C.
; BUFFER	 TOKEN
;  SPEC		RETURNED
; ======	========
;   R		   0		READ
;   W		   1		WRITE TRACK
;   S		   2		SEEK (DIRECTORY)
;   Y		   3		YES (ALL OF THE ABOVE)
; <NULL>	   4		NONE OF THE ABOVE
;
GETOBUF:
	MVI	C,4	;PREPARE TO RETURN NULL IF
	CALL	GETOCH
	RZ		;END OF OPTION IS FOUND
	INX	H	;ASSUME WE WILL FIND A SPEC, MOVE OVER
	DCR	C	;GET YES TOKEN
	CPI	'Y'	;RETURN IF YES SPEC
	RZ
	DCR	C	;GET SEEK TOKEN
	CPI	'S'	;RETURN IF SEEK SPEC
	RZ
	DCR	C	;GET WRITE TOKEN
	CPI	'W'	;RETURN IF WRITE TOKEN
	RZ
	DCR	C	;GET READ TOKEN
	CPI	'R'	;RETURN IF READ TOKEN
	RZ
	DCX	H	;SPEC NOT FOUND - BACKUP TO UNKNOWN CHR
	MVI	C,4	;AND RETURN DEFAULT TOKEN
	RET
;
; GET AN OPTION CHARACTER FROM THE TEXT POINTER.  SET FLAGS
; IF END OF OPTION STRING FOUND
;
GETOCH:
	MOV	A,M	;GET A CHARCTER
	CPI	' '	;SPACE TERMINATES AN OPTION STRING
	RZ
	CPI	']'	;SO DOES RIGHT BRACKET, BUT
	INX	H	;MOVE TEXT POINTER OVER IT
	RZ
	DCX	H	;NOT ']', GET TXA BACK
	ORA	A	;RETURN FLAG IF END OF ARGUMENT TO FAST
	RET
	PAGE
;
;
;	<<<<<<	DDB CREATION SUBROUTINES  >>>>>>
;
;
; CREATE ONE OR MORE DDB'S FROM A BUFFERING SPEC TOKEN AND
; A DRIVE SPEC
; 
CRTBUF:
	MOV	A,C	;GET BUFFER TOKEN
	ORA	A	;READ TRACK?
	JZ	CRT$R	;YES - CREATE A READ DDB
	DCR	C	;WRITE TRACK?
	JZ	CRT$W	;YES - CREATE A WRITE DDB
	DCR	C	;SEEK
	JZ	CRT$S	;YES - CREATE A SEEK DDB
	CALL	CRT$R	;NONE OF THE ABOVE, MUST BE NULL OR Y
	CALL	CRT$S	;AND BOTH NEED READ AND SEEK
	DCR	C	;NULL?
	RNZ		;YES - READ AND SEEK ARE DONE - RETURN
	CALL	CRT$W	;NO - I.E. YES - CREATE WRITE DDB ALSO
	RET
;
; CREATE A READ TRACK DDB
;
CRT$R:
	LXI	H,RDBUF	;POINT TO READ DDB ADDRESS TABLE IN FAST
	JMP	CRT$TDDB ;CONTINE TO CREATE A FULL TRACK DDB
;
; CREATE A WRITE TRACK DDB
;
CRT$W:
	LXI	H,WRBUF	;POINT TO WRITE DDB ADDRESS TABLE
CRT$TDDB:
	LXI	D,TRKSEC ;POINT TO FULL TRACK SECTOR TABLE
	CALL	CRT$DDB	;CREATE A GENERALIZED DDB
	MVI	A,MTYTRK ;SET DDB TO EMPTY TRACK
	STAX	D
	RET
;
; CREATE A SEEK DDB
;
CRT$S:
	LXI	H,DIRBUF ;POINT TO DIRECTORY DDB ADDRESS TABLE
	LXI	D,DIRSEC ;PARTIAL TRACK (DIRECTORY) SECTOR TABLE
	CALL	CRT$DDB
	MVI	A,DIRTRK ;INITIALIZE TRACK TO DIRECTORY TRACK
	STAX	D
	RET
;
; GENERALIZED CREATE DDB ROUTINE.  A DDB FOR THE DRIVE IN
; BUFDRV IS CREATED USING THE SECTOR TABLE PASSED IN REG DE.
; THE ADDRESS OF THE DDB IS FILLED INTO THE DDB ADDRESS
; TABLE WITHIN FAST.  MEMORY IS DOWNSIZED BY THE LENGTH OF
; THE DDB.
;
CRT$DDB:
	PUSH	B	;SAVE CALLERS REG BC
	PUSH	H	;SAVE DDB ADDRESS TABLE POINTER
	LHLD	BUFSTRT	;GET HIGHEST BYTE NOW IN USE
	DCX	H	;POINT TO NEXT FREE BYTE
	MVI	M,0	;PUT IN END OF DDB MARKER
	LDAX	D	;GET LAST SECTOR NUMBER TO REG A
	LXI	B,-(SECLEN+2) ;NEGATIVE LENGHT BETWEEN SECTORS
FILLSEC:
	DAD	B	;POINT TO UPDATE FLAG
	MVI	M,0	;RESET UPDATE FLAG
	DCX	H	;POINT TO SECTOR NUMBER FIELD
	MOV	M,A	;FILL IN ANOTHER SECTOR NUMBER
	LDA	LEN+1	;HIGH ORDER LENGTH INTO REG A
	ADI	(HIGH CODE1)+1 ;ADD FAST START ADDRESS TO GIVE
			;HIGH ORDER MINIMUM BUFFER START
	CMP	H	;IS NEW BUFFER START LESS THAN MIN?
	JNC	OMERR	;YES - GIVE OUT OF MEMORY ERROR
	INX	D	;POINT TO NEXT SECTOR NUMBER FROM TABLE
	LDAX	D	;GET NEXT SECTOR
	ORA	A	;END OF TABLE?
	JNZ	FILLSEC	;NO - KEEP ALLOCATING SECTORS
	DCX	H	;NOW POINTING TO DRIVE FIELD OF DDB
	LDA	BUFDRV	;GET DRIVE FOR THIS DDB
	MOV	M,A	;AND FILL IT IN
	DCX	H	;AND LEAVE ROOM FOR TRACK NUMBER
	SHLD	BUFSTRT	;DOWNSIZE MEMORY
	XCHG		;DDB ADDRESS TO REG DE
	POP	H	;POINTER TO DDB ADDRESS TABLE TO REG HL
	ADD	A	;DOUBLE DRIVE NUMBER TO INDEX INTO TABLE
	MOV	C,A	;FORM INDEX IN REG BC
	MVI	B,0
	DAD	B	;ADD INDEX TO BASE
	MOV	A,M	;MAKE SURE NO DDB EXISTS FOR THIS SPEC
	INX	H
	ORA	M
	JNZ	OPTERR	;ONE EXISTS - SPECIFIED TWICE ERROR
	MOV	M,D	;EMPTY SO FAR, SO FILL IN DDB ADDRESS
	DCX	H
	MOV	M,E
	POP	B	;RESTORE CALLERS REG BC
	RET
;
OMERR:
	MVI	C,9	;PRINT ERROR MESSAGE AND BOOT
	LXI	D,OMMES
	CALL	BDOS
	JMP	BOOT
;
OMMES:	DB	'OUT OF MEMORY$'
	RET
	PAGE
;
;
;	<<<<<<<  MAIN LINE CODE STARTS HERE  >>>>>>>>
;
SKIPMES:
	LXI	SP,STACK ;SETUP LOCAL STACK
	LDA	DBUF	;GET LENGHT OF ARGUMENT TO FAST COMMAND
	ADI	DBUF+1	;COMPUTE ADDRESS OF LAST CHAR + 1
	MOV	L,A
	MVI	H,HIGH DBUF
	MVI	M,0	;FOLLOW ARGUMENT WITH A 0 TO EASE PARSING
;
; REPACK ARGUMENT BUFFER TO ELIMINATE ARGUMENTS TO FAST.
;
	LXI	H,DBUF+1 ;POINT TO FIRST CHAR OF ARG
	CALL	GETOPT	;GET OPTIONS AS NECESSARY
	LXI	D,COMFCB ;PACK TRANSIENT FCB INTO FAST
	CALL	GETFN	;MOVE TEXT POINTER PAST COM FILE NAME
	LXI	D,DBUF+1 ;DESTINATION FOR REPACKED ARG
	MOV	A,L	;COMPUTE LENGTH OF FAST ARGUMENT
	SUB	E
	MOV	C,A	;SAVE IN REG C
	LDA	DBUF	;GET TOTAL ARG LEGTH
	SUB	C	;SUBTRACT FAST ARG LENGTH
	STA	DBUF	;LEAVING LENGTH OF TRANSIENT ARG
	MOV	C,A	;THIS IS ALSO LENGTH TO REPACK
	INR	C	;ADD ONE FOR END OF TEXT BYTE
	CALL	MOVESUB	;ACTUALLY DO THE REPACKING
;
	LXI	H,DBUF+1 ;NOW PACK FCB'S FOR TRANSIENT
	LXI	D,FCB1
	CALL	GETFN	;PACK FCB1
	LXI	D,FCB2
	CALL	GETFN	;PACK FCB2
	LXI	H,COMFCB+9 ;FILL IN TRANSIENT FILE TYPE 'COM'
	MVI	M,'C'
	INX	H
	MVI	M,'O'
	INX	H
	MVI	M,'M'
	PAGE
;
; NOW THAT DBUF AND FCB'S HAVE BEEN REPACKED, BEGIN THE UPWARD
; MOVEMENT AND RELOCATION OF FAST.
;
	LHLD	LEN	;GET LENGTH OF FAST CODE
	MOV	B,H	;INTO BC TO
	MOV	C,L
	LDA	BUFSTRT+1 ;GET PAGE OF LOWEST BUFFER
	SUB	B	;DOWNSIZE MEMORY BY LENGHT OF FAST
	MOV	H,A
	PUSH	H	;SAVE DEST FOR ENTRY WHEN RELOC IS DONE
	LXI	D,CODE1	;POINTER TO CODE ORGED FOR 0

MOVEREL:
	PUSH	B	;SAVE LENGTH
	PUSH	H	;SAVE DEST
MOVE:
	LDAX	D	;GET A BYTE FROM CODE 1 IMAGE
	MOV	M,A	;MOVE TO DEST
	INX	D	;BUMP CODE 1 POINTER
	INX	H	;BUMP DEST POINTER
	DCX	B	;MOVED WHOLE THING YET?
	MOV	A,B
	ORA	C
	JNZ	MOVE

	POP	H	;GET DEST BACK
	POP	B	;GET LENGTH BACK
	PUSH	D	;PUSH BASE OF RELTBL
	MOV	D,H	;BIAS IN REG D
NEWBYT:
	XTHL		;GET RELOC TBL ADR
	MOV	E,M	;KEEP A REL BYTE IN REG E
	INX	H	;BUMP RELOC TBL POINTER
	XTHL		;PUT TBL PTR BACK
RELBYT:
	MOV	A,E	;GET RELOC BYTE
	RLC		;MOVE A BIT INTO CARY
	MOV	E,A	;SAVE THE REST OF THE RELOC BITS
	JNC	NOREL	;BIT WAS 0, DON'T RELOCATE THIS BYTE
	MOV	A,D	;GET BIAS TO ADD
	ADD	M	;ADD TO BYTE FROM DEST
	MOV	M,A
NOREL:
	INX	H	;BUMP DEST POINTER
	DCX	B	;DONE WITH ALL BYTES?
	MOV	A,B
	ORA	C
	JZ	MOVEDONE ;YUP - VECTOR TO REL BASE
	MOV	A,L	;NOPE - TEST IF AT 8 BYTE BOUNDRY
	ANI  0000$0111B	;IF SO, TIME FOR A NEW BYTE FROM TABLE
	JNZ	RELBYT	;NOT AT BOUNDRY
	JMP	NEWBYT	;AT A BOUNDRY

MOVEDONE:
	POP	B	;REMOVE RELOC TBL ADR FROM STACK
	RET		;VECTOR TO FAST ENTRY
;
MOVESUB:
	MOV	A,M
	STAX	D
	INX	D
	INX	H
	DCR	C
	JNZ	MOVESUB
	RET
;
; RAM AREAS
;
	DS	20	;STACK SPACE
STACK:
;
BUFDRV	DB	0	;TEMP FOR OPTION DRIVE SPEC SCAN
BUFSTRT	DW	0	;LOWEST ADDRESS USED FOR BUFFERS
;
	ORG	(($-1) OR 255) + 1 ;ORG TO NEXT PAGE BOUNDRY
BIAS:	;BIAS USED TO LOAD FAST ORGED FOR 0
CODE1:	;BASE ADDRESS OF CODE ORGED FOR 0
	DS	3	;MOVE OVER ENTRY JMP
LEN:	;WORD HOLDING LENGTH OF FAST CODE
	DS	2
COMFCB:			;FCB FOR COM FILE TO BE LOADED
	DS	33
	ORG	CODE1+100H ;FIRST ADDRESS NOT OVERLAID
	DS	6	;SPACE FOR BDOS SERIAL NUMBER
	DS	3	;SPACE FOR JMP TO REAL BDOS
RDBUF	DS	8	;READ TRACK DDB ADDRESS TABLE
WRBUF	DS	8	;WRITE TRACK DDB ADDRESS TABLE
DIRBUF	DS	8	;DIRECTORY DDB ADDRESS TABLE
;
	END	ENTRY
