	TITLE	'ZCPR Version 1.0'
;
;  CP/M Z80 Command Processor Replacement (CPR) Version 1.0
;	CCPZ CREATED AND CUSTOMIZED FOR ARIES-II BY RLC
;	FURTHER MODIFIED BY RGF AS V2.0
;	FURTHER MODIFIED BY RLC AS V2.1
;	FURTHER MODIFIED BY KBP AS V2.2
;	FURTHER MODIFIED BY RLC AS V2.4 (V2.3 skipped)
;	FURTHER MODIFIED BY RLC AS V2.5
;	FURTHER MODIFIED BY RLC AS V2.6
;	FURTHUR MODIFIED BY SBB AS V2.7
;	FURTHER MODIFIED BY RLC AS V2.8
;	FURTHER MODIFIED BY RLC AS V2.9
;	FURTHER MODIFIED BY RLC AS V3.0
;	FURTHER MODIFIED BY RLC AS V3.1
;	FURTHER MODIFIED BY RLC AS V4.0
;	ZCPR VERSION 1.0 CREATED FROM CCPZ VERSION 4.0 BY RLC IN
;		A COORDINATED EFFORT WITH CCP-GROUP
;
;	ZCPR is a group effort by CCP-GROUP, whose active membership involved
; in this project consists of the following:
;		RLC - Richard Conn
;		RGF - Ron Fowler
;		KBP - Keith Peterson
;		FJW - Frank Wancho
;	The following individual also provided a contribution:
;		SBB - Steve Bogolub
;
;
;******** Structure Notes ********
;
;	This CPR is divided into a number of major sections.  The following
; is an outline of these sections and the names of the major routines
; located therein.
;
; Section	Function/Routines
; -------	-----------------
;
;   --		Opening Comments, Equates, and Macro Definitions
;
;    0		JMP Table into CPR
;
;    1		Buffers
;
;    2		CPR Starting Modules
;			CPR1	CPR	RESTRT	RSTCPR	RCPRNL
;			PRNNF
;
;    3		Utilities
;			CRLF	CONOUT	CONIN	LCOUT	LSTOUT
;			READF	READ	BDOSB	PRINTC	PRINT
;			GETDRV	DEFDMA	DMASET	RESET	BDOSJP
;			LOGIN	OPENF	OPEN	GRBDOS	CLOSE
;			SEARF	SEAR1	SEARN	SUBKIL	DELETE
;			RESETUSR GETUSR	SETUSR
;
;     4		CPR Utilities
;			SETUD	SETU0D	UCASE	REDBUF	CNVBUF
;			BREAK	USRNUM	ERROR	SDELM	ADVAN
;			SBLANK	ADDAH	NUMBER	NUMERR	HEXNUM
;			DIRPTR	SLOGIN	DLOGIN	COMLOG	SCANER
;			CMDSER
;
;     5		CPR-Resident Commands and Functions
;     5A		DIR	DIRPR	FILLQ
;     5B		ERA
;     5C		LIST
;     5D		TYPE	PAGER
;     5E		SAVE
;     5F		REN
;     5G		USER
;     5H		DFU
;     5I		JUMP
;     5J		GO
;     5K		COM	CALLPROG	ERRLOG	ERRJMP
;     5L		GET	MEMLOAD	PRNLE
;
;
FALSE	EQU	0
TRUE	EQU	NOT FALSE
;
;  CUSTOMIZATION EQUATES
;
;  The following equates may be used to customize this CPR for the user's
;    system and integration technique.  The following constants are provided:
;
;    REL - TRUE if integration is to be done via MOVCPM
;        - FALSE if integration is to be done via DDT and SYSGEN
;
;    BASE - Base Address of user's CP/M system (normally 0 for DR version)
;           This equate allows easy modification by non-standard CP/M (eg,H89)
;
;    CPRLOC - Base Page Address of CPR; this value can be obtained by running
;	      the BDOSLOC program on your system, or by setting the
;	      MSIZE and BIOSEX equates to the system memory size in
;	      K-bytes and the "extra" memory required by your BIOS
;	      in K-bytes. BIOSEX is zero if your BIOS is normal size,
;	      and can be negative if your BIOS is in PROM or in
;	      non-contiguous memory.
;
;    RAS - Remote-Access System; setting this equate to TRUE disables
;	   certain CPR commands that are considered harmful in a Remote-
;	   Access environment; use under Remote-Access Systems (RBBS) for
;	   security purposes
;
REL	EQU	FALSE	;SET TO TRUE FOR MOVCPM INTEGRATION
;
BASE	EQU	0	;BASE OF CP/M SYSTEM (SET FOR STANDARD CP/M)
;
	IF	REL
CPRLOC	EQU	0	;MOVCPM IMAGE
	ELSE
;
; If REL is FALSE, the value of CPRLOC may be set in one
; of two ways.  The first way is to set MSIZE and BIOSEX
; as described above using the following three lines:
;
;MSIZE	EQU	56	;SIZE OF MEM IN K-BYTES
;BIOSEX	EQU	0	;EXTRA # K-BYTES IN BIOS
;CPRLOC	EQU	3400H+(MSIZE-20-BIOSEX)*1024	;CPR ORIGIN
;
; The second way is to obtain the origin of your current
; CPR using BDSLOC or its equivalent, then merely set CPRLOC
; to that value as as in the following line:
;
;CPRLOC	EQU	0BD00H	;FILL IN WITH BDOSLOC SUPPLIED VALUE
CPRLOC	EQU	0D5b3H	;WW - CUSTOMIZED FOR ROMWBW
;
; Note that you should only use one method or the other.
; Do NOT define CPRLOC twice!
;
; The following gives the required offset to load the CPR into the
; CP/M SYSGEN Image through DDT (the Roffset command); Note that this
; value conforms with the standard value presented in the CP/M reference
; manuals, but it may not necessarily conform with the location of the
; CPR in YOUR CP/M system; several systems (Morrow Designs, P&T, Heath
; Org-0 to name a few) have the CPR located at a non-standard address in
; the SYSGEN Image
;
;CPRR	EQU	0980H-CPRLOC	;DDT LOAD OFFSET
CPRR	EQU	1100H-CPRLOC	;DDT LOAD OFFSET FOR MORROW DESIGNS
	ENDIF
;
RAS	EQU	FALSE	;SET TO TRUE IF CPR IS FOR A REMOTE-ACCESS SYSTEM
;
; The following is presented as an option, but is not generally user-customiz-
; able.  A basic design choice had to be made in the design of ZCPR concerning
; the execution of SUBMIT files.  The original CCP had a problem in this sense
; in that it ALWAYS looked for the SUBMIT file from drive A: and the SUBMIT
; program itself (SUBMIT.COM) would place the $$$.SUB file on the currently
; logged-in drive, so when the user was logged into B: and he issued a SUBMIT
; command, the $$$.SUB was placed on B: and did not execute because the CCP
; looked for it on A: and never found it.
;	After much debate it was decided to have ZCPR perform the same type of
; function as CCP (look for the $$$.SUB file on A:), but the problem with
; SUBMIT.COM still exists.  Hence, RGF designed SuperSUB and RLC took his
; SuperSUB and designed SUB from it; both programs are set up to allow the
; selection at assembly time of creating the $$$.SUB on the logged-in drive
; or on drive A:.
;	A final definition of the Indirect Command File ($$$.SUB or SUBMIT
; File) is presented as follows:
;		"An Indirect Command File is one which contains
;		 a series of commands exactly as they would be
;		 entered from a CP/M Console.  The SUBMIT Command
;		 (or SUB Command) reads this files and transforms
;		 it for processing by the ZCPR (the $$$.SUB File).
;		 ZCPR will then execute the commands indicated
;		 EXACTLY as if they were typed at the Console."
;	Hence, to permit this to happen, the $$$.SUB file must always
; be present on a specific drive, and A: is the choice for said drive.
; With this facility engaged as such, Indirect Command Files like:
;		DIR
;		A:
;		DIR
; can be executed, even though the currently logged-in drive is changed
; during execution.  If the $$$.SUB file was present on the currently
; logged-in drive, the above series of commands would not work since the
; ZCPR would be looking for $$$.SUB on the logged-in drive, and switching
; logged-in drives without moving the $$$.SUB file as well would cause
; processing to abort.
;
SUBA	equ	FALSE 	; Set to TRUE to have $$$.SUB always on A:
			; Set to FALSE to have $$$.SUB on the logged-in drive
;
;   The following flag enables extended processing for user-program supplied
; command lines.  This is for Command Level 3 of ZCPR.  Under the CCPZ Version
; 4.0 philosophy, three command levels exist:
;	(1) that command issued by the user from his console at the '>' prompt
;	(2) that command issued by a $$$.SUB file at the '$' prompt
;	(3) that command issued by a user program by placing the command into
; CIBUFF and setting the character count in CBUFF
;   Setting CLEVEL3 to TRUE enables extended processing of the third level of
; ZCPR command.  All the user program need do is to store the command line and
; set the character count; ZCPR will initialize the pointers properly, store
; the ending zero properly, and capitalize the command line for processing.
; Once the command line is properly stored, the user executes the command line
; by reentering the ZCPR through CPRLOC [NOTE:  The C register MUST contain
; a valid User/Disk Flag (see location 4) at this time.]
;
CLEVEL3	equ	TRUE		;ENABLE COMMAND LEVEL 3 PROCESSING
;
;
;*** TERMINAL AND 'TYPE' CUSTOMIZATION EQUATES
;
NLINES	EQU	24		;NUMBER OF LINES ON CRT SCREEN
WIDE	EQU	TRUE		;TRUE IF WIDE DIR DISPLAY
FENCE	EQU	'|'		;SEP CHAR BETWEEN DIR FILES
;
PGDFLT	EQU	TRUE  		;SET TO FALSE TO DISABLE PAGING BY DEFAULT
PGDFLG	EQU	'P'		;FOR TYPE COMMAND: PAGE OR NOT (DEP ON PGDFLT)
				;  THIS FLAG REVERSES THE DEFAULT EFFECT
;
MAXUSR	EQU	15 		;MAXIMUM USER NUMBER ACCESSABLE
;
SYSFLG	EQU	'A' 		;FOR DIR COMMAND: LIST $SYS AND $DIR
;
SOFLG	EQU	'S'		;FOR DIR COMMAND: LIST $SYS FILES ONLY
;
SUPRES	EQU	FALSE 		;SUPRESSES USER # REPORT FOR USER 0
;
DEFUSR	EQU	0 		;DEFAULT USER NUMBER FOR COM FILES
;
SPRMPT	EQU	'$'		;CPR PROMPT INDICATING SUBMIT COMMAND
CPRMPT	EQU	'>'		;CPR PROMPT INDICATING USER COMMAND
;
NUMBASE	EQU	'H'		;CHARACTER USED TO SWITCH FROM DEFAULT
				; NUMBER BASE
;
SECTFLG	EQU	'S'		;OPTION CHAR FOR SAVE COMMAND TO SAVE SECTORS
;
; END OF CUSTOMIZATION SECTION
;
CR	EQU	0DH
LF	EQU	0AH
TAB	EQU	09H
;
WBOOT	EQU	BASE+0000H		;CP/M WARM BOOT ADDRESS
UDFLAG	EQU	BASE+0004H		;USER NUM IN HIGH NYBBLE, DISK IN LOW
BDOS	EQU	BASE+0005H		;BDOS FUNCTION CALL ENTRY PT
TFCB	EQU	BASE+005CH		;DEFAULT FCB BUFFER
TBUFF	EQU	BASE+0080H		;DEFAULT DISK I/O BUFFER
TPA	EQU	BASE+0100H		;BASE OF TPA
;
;
; MACROS TO PROVIDE Z80 EXTENSIONS
;   MACROS INCLUDE:
;
$-MACRO 		;FIRST TURN OFF THE EXPANSIONS
;
;	JR	- JUMP RELATIVE
;	JRC	- JUMP RELATIVE IF CARRY
;	JRNC	- JUMP RELATIVE IF NO CARRY
;	JRZ	- JUMP RELATIVE IF ZERO
;	JRNZ	- JUMP RELATIVE IF NO ZERO
;	DJNZ	- DECREMENT B AND JUMP RELATIVE IF NO ZERO
;	LDIR	- MOV @HL TO @DE FOR COUNT IN BC
;	LXXD	- LOAD DOUBLE REG DIRECT
;	SXXD	- STORE DOUBLE REG DIRECT
;
;
;
;	@GENDD MACRO USED FOR CHECKING AND GENERATING
;	8-BIT JUMP RELATIVE DISPLACEMENTS
;
@GENDD	MACRO	?DD	;;USED FOR CHECKING RANGE OF 8-BIT DISPLACEMENTS
	IF (?DD GT 7FH) AND (?DD LT 0FF80H)
	DB	100H	;Displacement Range Error on Jump Relative
	ELSE
	DB	?DD
	ENDIF
	ENDM
;
;
; Z80 MACRO EXTENSIONS
;
JR	MACRO	?N	;;JUMP RELATIVE
	DB	18H
	@GENDD	?N-$-1
	ENDM
;
JRC	MACRO	?N	;;JUMP RELATIVE ON CARRY
	DB	38H
	@GENDD	?N-$-1
	ENDM
;
JRNC	MACRO	?N	;;JUMP RELATIVE ON NO CARRY
	DB	30H
	@GENDD	?N-$-1
	ENDM
;
JRZ	MACRO	?N	;;JUMP RELATIVE ON ZERO
	DB	28H
	@GENDD	?N-$-1
	ENDM
;
JRNZ	MACRO	?N	;;JUMP RELATIVE ON NO ZERO
	DB	20H
	@GENDD	?N-$-1
	ENDM
;
DJNZ	MACRO	?N	;;DECREMENT B AND JUMP RELATIVE ON NO ZERO
	DB	10H
	@GENDD	?N-$-1
	ENDM
;
LDIR	MACRO		;;LDIR
	DB	0EDH,0B0H
	ENDM
;
LDED	MACRO	?N	;;LOAD DE DIRECT
	DB	0EDH,05BH
	DW	?N
	ENDM
;
LBCD	MACRO	?N	;;LOAD BC DIRECT
	DB	0EDH,4BH
	DW	?N
	ENDM
;
SDED	MACRO	?N	;;STORE DE DIRECT
	DB	0EDH,53H
	DW	?N
	ENDM
;
SBCD	MACRO	?N	;;STORE BC DIRECT
	DB	0EDH,43H
	DW	?N
	ENDM
;
; END OF Z80 MACRO EXTENSIONS
;
;
;**** Section 0 ****
;
	ORG	CPRLOC
;
;  ENTRY POINTS INTO ZCPR
;    If the ZCPR is entered at location CPRLOC (at the JMP to CPR), then
; the default command in CIBUFF will be processed.  If the ZCPR is entered
; at location CPRLOC+3 (at the JMP to CPR1), then the default command in
; CIBUFF will NOT be processed.
;    NOTE:  Entry into ZCPR in this way is permitted under ZCPR Version 4.0,
; but in order for this to work, CIBUFF and CBUFF MUST be initialized properly
; AND the C register MUST contain a valid User/Disk Flag (see Location 4: the
; most significant nybble contains the User Number and the least significant
; nybble contains the Disk Number).
;    Some user programs (such as SYNONYM3) attempt to use the default
; command facility.  Under the original CPR, it was necessary to initialize
; the pointer after the reserved space for the command buffer to point to
; the first byte of the command buffer.  Under Version 4.x of ZCPR, this is
; no longer the case.  The CIBPTR (Command Input Buffer PoinTeR) is located
; to be compatable with such programs (provided they determine the buffer
; length from the byte at MBUFF [CPRLOC + 6]), but under Version 4.x of ZCPR
; this is no longer necessary.  ZCPR Version 4.x automatically initializes
; this buffer pointer in all cases.
;
ENTRY:
	JMP	CPR	; Process potential default command
	JMP	CPR1	; Do NOT process potential default command
;
;**** Section 1 ****
; BUFFERS ET AL
;
; INPUT COMMAND LINE AND DEFAULT COMMAND
;   The command line to be executed is stored here.  This command line
; is generated in one of three ways:
;	(1) by the user entering it through the BDOS READLN function at
; the du> prompt [user input from keyboard]
;	(2) by the SUBMIT File Facility placing it there from a $$$.SUB
; file
;	(3) by an external program or user placing the required command
; into this buffer
;   In all cases, the command line is placed into the buffer starting at
; CIBUFF.  This command line is terminated by the last character (NOT Carriage
; Return), and a character count of all characters in the command line
; up to and including the last character is placed into location CBUFF
; (immediately before the command line at CIBUFF).  The placed command line
; is then parsed, interpreted, and the indicated command is executed.
; If CLEVEL3 is permitted, a terminating zero is placed after the command
; (otherwise the user program has to place this zero) and the CIBPTR is
; properly initialized (otherwise the user program has to init this ptr).
; If the command is placed by a user program, entering at CPRLOC is enough
; to have the command processed.  Again, under CCPZ Version 4.0, it is not
; necessary to store the pointer to CIBUFF in CIBPTR; ZCPR will do this for
; the calling program if CLEVEL3 is made TRUE.
;   WARNING:  The command line must NOT exceed BUFLEN characters in length.
; For user programs which load this command, the value of BUFLEN can be
; obtained by examining the byte at MBUFF (CPRLOC + 6).
;
BUFLEN	EQU	80		;MAXIMUM BUFFER LENGTH
MBUFF:
	DB	BUFLEN		;MAXIMUM BUFFER LENGTH
CBUFF:
	DB	0		;NUMBER OF VALID CHARS IN COMMAND LINE
CIBUFF:
	DB	'               '	;DEFAULT (COLD BOOT) COMMAND
CIBUF:
	DB	0			;COMMAND STRING TERMINATOR
	DS	BUFLEN-($-CIBUFF)+1	;TOTAL IS 'BUFLEN' BYTES
;
CIBPTR:
	DW	CIBUFF		;POINTER TO COMMAND INPUT BUFFER
CIPTR:
	DW	CIBUF		;CURRENT POINTER
;
	DS	26		;STACK AREA
STACK	EQU	$		;TOP OF STACK
;
; FILE TYPE FOR COMMAND
;
COMMSG:
	DB	'COM'
;
; SUBMIT FILE CONTROL BLOCK
;
SUBFCB:
	IF	SUBA		;IF $$$.SUB ON A:
	DB	1		;DISK NAME SET TO DEFAULT TO DRIVE A:
	ENDIF
;
	IF	NOT SUBA	;IF $$$.SUB ON CURRENT DRIVE
	DB	0		;DISK NAME SET TO DEFAULT TO CURRENT DRIVE
	ENDIF
;
	DB	'$$$'		;FILE NAME
	DB	'     '
	DB	'SUB'		;FILE TYPE
	DB	0		;EXTENT NUMBER
	DB	0		;S1
SUBFS2:
	DS	1		;S2
SUBFRC:
	DS	1		;RECORD COUNT
	DS	16		;DISK GROUP MAP
SUBFCR:
	DS	1		;CURRENT RECORD NUMBER
;
; COMMAND FILE CONTROL BLOCK
;
FCBDN:
	DS	1		;DISK NAME
FCBFN:
	DS	8		;FILE NAME
FCBFT:
	DS	3		;FILE TYPE
	DS	1		;EXTENT NUMBER
	DS	2		;S1 AND S2
	DS	1		;RECORD COUNT
FCBDM:
	DS	16		;DISK GROUP MAP
FCBCR:
	DS	1		;CURRENT RECORD NUMBER
;
; OTHER BUFFERS
;
PAGCNT:
	DB	NLINES-2	;LINES LEFT ON PAGE
CHRCNT:
	DB	0		;CHAR COUNT FOR TYPE
QMCNT:
	DB	0		;QUESTION MARK COUNT FOR FCB TOKEN SCANNER
;
; CPR BUILT-IN COMMAND TABLE
;
NCHARS	EQU	4		;NUMBER OF CHARS/COMMAND
;
; CPR COMMAND NAME TABLE
;   EACH TABLE ENTRY IS COMPOSED OF THE 4-BYTE COMMAND AND 2-BYTE ADDRESS
;
CMDTBL:
	DB	'DIR '
	DW	DIR
	DB	'LIST'
	DW	LIST
	DB	'TYPE'
	DW	TYPE
	DB	'USER'
	DW	USER
	DB	'DFU '
	DW	DFU
;
	IF	NOT RAS		;FOR NON-RAS
	DB	'GO  '
	DW	GO
	DB	'ERA '
	DW	ERA
	DB	'SAVE'
	DW	SAVE
	DB	'REN '
	DW	REN
	DB	'GET '
	DW	GET
	DB	'JUMP'
	DW	JUMP
	ENDIF
;
NCMNDS	EQU	($-CMDTBL)/(NCHARS+2)
;
;
;**** Section 2 ****
; CPR STARTING POINTS
;
; START CPR AND DON'T PROCESS DEFAULT COMMAND STORED
;
CPR1:
	XRA	A		;SET NO DEFAULT COMMAND
	STA	CBUFF
;
; START CPR AND POSSIBLY PROCESS DEFAULT COMMAND
;
; NOTE ON MODIFICATION BY RGF: BDOS RETURNS 0FFH IN
; ACCUMULATOR WHENEVER IT LOGS IN A DIRECTORY, IF ANY
; FILE NAME CONTAINS A '$' IN IT.  THIS IS NOW USED AS
; A CLUE TO DETERMINE WHETHER OR NOT TO DO A SEARCH
; FOR SUBMIT FILE, IN ORDER TO ELIMINATE WASTEFUL SEARCHES.
;
CPR:
	LXI	SP,STACK	;RESET STACK
	PUSH	B
	MOV	A,C		;C=USER/DISK NUMBER (SEE LOC 4)
	RAR			;EXTRACT USER NUMBER
	RAR
	RAR
	RAR
	ANI	0FH
	MOV	E,A		;SET USER NUMBER
	CALL	SETUSR
	CALL	RESET		;RESET DISK SYSTEM
	STA	RNGSUB		;SAVE SUBMIT CLUE FROM DRIVE A:
	POP	B
	MOV	A,C		;C=USER/DISK NUMBER (SEE LOC 4)
	ANI	0FH		;EXTRACT DEFAULT DISK DRIVE
	STA	TDRIVE		;SET IT
	JRZ	NOLOG		;SKIP IF 0...ALREADY LOGGED
	CALL	LOGIN		;LOG IN DEFAULT DISK
;
	IF	NOT SUBA	;IF $$$.SUB IS ON CURRENT DRIVE
	STA	RNGSUB		;BDOS '$' CLUE
	ENDIF
;
NOLOG:
	LXI	D,SUBFCB	;CHECK FOR $$$.SUB ON CURRENT DISK
RNGSUB	EQU	$+1		;POINTER FOR IN-THE-CODE MODIFICATION
	MVI	A,0		;2ND BYTE (IMMEDIATE ARG) IS THE RNGSUB FLAG
	ORA	A		;SET FLAGS ON CLUE
	CMA			;PREPARE FOR COMING 'CMA'
	CNZ	SEAR1
	CMA			;0FFH IS RETURNED IF NO $$$.SUB, SO COMPLEMENT
	STA	RNGSUB		;SET FLAG (0=NO $$$.SUB)
	LDA	CBUFF		;EXECUTE DEFAULT COMMAND?
	ORA	A		;0=NO
	JRNZ	RS1
;
; PROMPT USER AND INPUT COMMAND LINE FROM HIM
;
RESTRT:
	LXI	SP,STACK	;RESET STACK
;
; PRINT PROMPT (DU>)
;
	CALL	CRLF		;PRINT PROMPT
	CALL	GETDRV		;CURRENT DRIVE IS PART OF PROMPT
	ADI	'A'		;CONVERT TO ASCII A-P
	CALL	CONOUT
	CALL	GETUSR		;GET USER NUMBER
;
	IF	SUPRES		;IF SUPPRESSING USR # REPORT FOR USR 0
	ORA	A
	JRZ	RS000
	ENDIF
;
	CPI	10		;USER < 10?
	JRC	RS00
	SUI	10		;SUBTRACT 10 FROM IT
	PUSH	PSW		;SAVE IT
	MVI	A,'1'		;OUTPUT 10'S DIGIT
	CALL	CONOUT
	POP	PSW
RS00:
	ADI	'0'		;OUTPUT 1'S DIGIT (CONVERT TO ASCII)
	CALL	CONOUT
;
; READ INPUT LINE FROM USER OR $$$.SUB
;
RS000:
	CALL	REDBUF		;INPUT COMMAND LINE FROM USER (OR $$$.SUB)
;
; PROCESS INPUT LINE
;
RS1:
;
	IF	CLEVEL3		;IF THIRD COMMAND LEVEL IS PERMITTED
	CALL	CNVBUF		;CAPITALIZE COMMAND LINE, PLACE ENDING 0,
				; AND SET CIBPTR VALUE
	ENDIF
;
	CALL	DEFDMA		;SET TBUFF TO DMA ADDRESS
	CALL	GETDRV		;GET DEFAULT DRIVE NUMBER
	STA	TDRIVE		;SET IT
	CALL	SCANER		;PARSE COMMAND NAME FROM COMMAND LINE
	CNZ	ERROR		;ERROR IF COMMAND NAME CONTAINS A '?'
	LXI	D,RSTCPR	;PUT RETURN ADDRESS OF COMMAND
	PUSH	D		;ON THE STACK
	LDA	TEMPDR		;IS COMMAND OF FORM 'D:COMMAND'?
	ORA	A		;NZ=YES
	JNZ	COM		; IMMEDIATELY
	CALL	CMDSER		;SCAN FOR CPR-RESIDENT COMMAND
	JNZ	COM		;NOT CPR-RESIDENT
	MOV	A,M		;FOUND IT:  GET LOW-ORDER PART
	INX	H		;GET HIGH-ORDER PART
	MOV	H,M		;STORE HIGH
	MOV	L,A		;STORE LOW
	PCHL			;EXECUTE CPR ROUTINE
;
; ENTRY POINT FOR RESTARTING CPR AND LOGGING IN DEFAULT DRIVE
;
RSTCPR:
	CALL	DLOGIN		;LOG IN DEFAULT DRIVE
;
; ENTRY POINT FOR RESTARTING CPR WITHOUT LOGGING IN DEFAULT DRIVE
;
RCPRNL:
	CALL	SCANER		;EXTRACT NEXT TOKEN FROM COMMAND LINE
	LDA	FCBFN		;GET FIRST CHAR OF TOKEN
	SUI	' '		;ANY CHAR?
	LXI	H,TEMPDR
	ORA	M
	JNZ	ERROR
	JR	RESTRT
;
; No File Error Message
;
PRNNF:
	CALL	PRINTC		;NO FILE MESSAGE
	DB	'No Fil','e'+80H
	RET
;
;**** Section 3 ****
; I/O UTILITIES
;
; OUTPUT CHAR IN REG A TO CONSOLE AND DON'T CHANGE BC
;
;
; OUTPUT <CRLF>
;
CRLF:
	MVI	A,CR
	CALL	CONOUT
	MVI	A,LF	;FALL THRU TO CONOUT
;
CONOUT:
	PUSH	B
	MVI	C,02H
OUTPUT:
	MOV	E,A
	PUSH	H
	CALL	BDOS
	POP	H
	POP	B
	RET
;
CONIN:
	MVI	C,01H	;GET CHAR FROM CON: WITH ECHO
	CALL	BDOSB
	JMP	UCASE	;CAPITALIZE
;
LCOUT:
	PUSH	PSW	;OUTPUT CHAR TO CON: OR LST: DEP ON PRFLG
PRFLG	EQU	$+1	;POINTER FOR IN-THE-CODE MODIFICATION
	MVI	A,0	;2ND BYTE (IMMEDIATE ARG) IS THE PRINT FLAG
	ORA	A	;0=TYPE
	JRZ	LC1
	POP	PSW	;GET CHAR
;
; OUTPUT CHAR IN REG A TO LIST DEVICE
;
LSTOUT:
	PUSH	B
	MVI	C,05H
	JR	OUTPUT
LC1:
	POP	PSW	;GET CHAR
	PUSH	PSW
	CALL	CONOUT	;OUTPUT TO CON:
	POP	PSW
	CPI	LF	;CHECK FOR PAGING
	JZ	PAGER
	RET
;
READF:
	LXI	D,FCBDN ;FALL THRU TO READ
READ:
	MVI	C,14H	;FALL THRU TO BDOSB
;
; CALL BDOS AND SAVE BC
;
BDOSB:
	PUSH	B
	CALL	BDOS
	POP	B
	ORA	A
	RET
;
; PRINT STRING (ENDING IN 0) PTED TO BY RET ADR;START WITH <CRLF>
;
PRINTC:
	PUSH	PSW		;SAVE FLAGS
	CALL	CRLF		;NEW LINE
	POP	PSW
;
PRINT:
	XTHL			;GET PTR TO STRING
	PUSH	PSW		;SAVE FLAGS
	CALL	PRIN1		;PRINT STRING
	POP	PSW		;GET FLAGS
	XTHL			;RESTORE HL AND RET ADR
	RET
;
; PRINT STRING (ENDING IN 0) PTED TO BY HL
;
PRIN1:
	MOV	A,M		;GET NEXT BYTE
	ORA	A		;WW - SET FLAGS
	RZ			;WW - DONE IF ZERO
	ANI	7FH		;WW - CLEAR HIGH BIT
	CALL	CONOUT		;PRINT CHAR
	MOV	A,M		;GET NEXT BYTE AGAIN FOR TEST
	INX	H		;PT TO NEXT BYTE
	ORA	A		;SET FLAGS
	RM			;DONE IF MSB SET
	JR	PRIN1
;
; BDOS FUNCTION ROUTINES
;
;
; RETURN NUMBER OF CURRENT DISK IN A
;
GETDRV:
	MVI	C,19H
	JR	BDOSJP
;
; SET 80H AS DMA ADDRESS
;
DEFDMA:
	LXI	D,TBUFF 	;80H=TBUFF
DMASET:
	MVI	C,1AH
	JR	BDOSJP
;
RESET:
	MVI	C,0DH
BDOSJP:
	JMP	BDOS
;
LOGIN:
	MOV	E,A
	MVI	C,0EH
	JR	BDOSJP	;SAVE SOME CODE SPACE
;
OPENF:
	XRA	A
	STA	FCBCR
	LXI	D,FCBDN ;FALL THRU TO OPEN
;
OPEN:
	MVI	C,0FH	;FALL THRU TO GRBDOS
;
GRBDOS:
	CALL	BDOS
	INR	A	;SET ZERO FLAG FOR ERROR RETURN
	RET
;
CLOSE:
	MVI	C,10H
	JR	GRBDOS
;
SEARF:
	LXI	D,FCBDN ;SPECIFY FCB
SEAR1:
	MVI	C,11H
	JR	GRBDOS
;
SEARN:
	MVI	C,12H
	JR	GRBDOS
;
; CHECK FOR SUBMIT FILE IN EXECUTION AND ABORT IT IF SO
;
SUBKIL:
	LXI	H,RNGSUB	;CHECK FOR SUBMIT FILE IN EXECUTION
	MOV	A,M
	ORA	A		;0=NO
	RZ
	MVI	M,0		;ABORT SUBMIT FILE
	LXI	D,SUBFCB	;DELETE $$$.SUB
;
DELETE:
	MVI	C,13H
	JR	BDOSJP	;SAVE MORE SPACE
;
; RESET USER NUMBER IF CHANGED
;
RESETUSR:
TMPUSR	EQU	$+1		;POINTER FOR IN-THE-CODE MODIFICATION
	MVI	A,0		;2ND BYTE (IMMEDIATE ARG) IS TMPUSR
	MOV	E,A		;PLACE IN E
	JR	SETUSR		;THEN GO SET USER
GETUSR:
	MVI	E,0FFH		;GET CURRENT USER NUMBER
SETUSR:
	MVI	C,20H		;SET USER NUMBER TO VALUE IN E (GET IF E=FFH)
	JR	BDOSJP		;MORE SPACE SAVING
;
; END OF BDOS FUNCTIONS
;
;
;**** Section 4 ****
; CPR UTILITIES
;
; SET USER/DISK FLAG TO CURRENT USER AND DEFAULT DISK
;
SETUD:
	CALL	GETUSR		;GET NUMBER OF CURRENT USER
	ADD	A		;PLACE IT IN HIGH NYBBLE
	ADD	A
	ADD	A
	ADD	A
	LXI	H,TDRIVE	;MASK IN DEFAULT DRIVE NUMBER (LOW NYBBLE)
	ORA	M		;MASK IN
	STA	UDFLAG		;SET USER/DISK NUMBER
	RET
;
; SET USER/DISK FLAG TO USER 0 AND DEFAULT DISK
;
SETU0D:
TDRIVE	EQU	$+1		;POINTER FOR IN-THE-CODE MODIFICATION
	MVI	A,0		;2ND BYTE (IMMEDIATE ARG) IS TDRIVE
	STA	UDFLAG		;SET USER/DISK NUMBER
	RET
;
; CONVERT CHAR IN A TO UPPER CASE
;
UCASE:
	CPI	61H		;LOWER-CASE A
	RC
	CPI	7BH		;GREATER THAN LOWER-CASE Z?
	RNC
	ANI	5FH		;CAPITALIZE
	RET
;
; INPUT NEXT COMMAND TO CPR
;	This routine determines if a SUBMIT file is being processed
; and extracts the command line from it if so or from the user's console
;
REDBUF:
	LDA	RNGSUB		;SUBMIT FILE CURRENTLY IN EXECUTION?
	ORA	A		;0=NO
	JRZ	RB1		;GET LINE FROM CONSOLE IF NOT
	LXI	D,SUBFCB	;OPEN $$$.SUB
	PUSH	D		;SAVE DE
	CALL	OPEN
	POP	D		;RESTORE DE
	JRZ	RB1		;ERASE $$$.SUB IF END OF FILE AND GET CMND
	LDA	SUBFRC		;GET VALUE OF LAST RECORD IN FILE
	DCR	A		;PT TO NEXT TO LAST RECORD
	STA	SUBFCR		;SAVE NEW VALUE OF LAST RECORD IN $$$.SUB
	CALL	READ		;DE=SUBFCB
	JRNZ	RB1		;ABORT $$$.SUB IF ERROR IN READING LAST REC
	LXI	D,CBUFF 	;COPY LAST RECORD (NEXT SUBMIT CMND) TO CBUFF
	LXI	H,TBUFF 	;  FROM TBUFF
	LXI	B,BUFLEN	;NUMBER OF BYTES
	LDIR
	LXI	H,SUBFS2	;PT TO S2 OF $$$.SUB FCB
	MVI	M,0		;SET S2 TO ZERO
	INX	H		;PT TO RECORD COUNT
	DCR	M		;DECREMENT RECORD COUNT OF $$$.SUB
	LXI	D,SUBFCB	;CLOSE $$$.SUB
	CALL	CLOSE
	JRZ	RB1		;ABORT $$$.SUB IF ERROR
	MVI	A,SPRMPT	;PRINT SUBMIT PROMPT
	CALL	CONOUT
	LXI	H,CIBUFF	;PRINT COMMAND LINE FROM $$$.SUB
	CALL	PRIN1
	CALL	BREAK		;CHECK FOR ABORT (ANY CHAR)
;
	IF	CLEVEL3		;IF THIRD COMMAND LEVEL IS PERMITTED
	RZ			;IF <NULL> (NO ABORT), RETURN TO CALLER AND RUN
	ENDIF
;
	IF	NOT CLEVEL3	;IF THIRD COMMAND LEVEL IS NOT PERMITTED
	JRZ	CNVBUF		;IF <NULL> (NO ABORT), CAPITALIZE COMMAND
	ENDIF
;
	CALL	SUBKIL		;KILL $$$.SUB IF ABORT
	JMP	RESTRT		;RESTART CPR
;
; INPUT COMMAND LINE FROM USER CONSOLE
;
RB1:
	CALL	SUBKIL		;ERASE $$$.SUB IF PRESENT
	CALL	SETUD		;SET USER AND DISK
	MVI	A,CPRMPT	;PRINT PROMPT
	CALL	CONOUT
	MVI	C,0AH		;READ COMMAND LINE FROM USER
	LXI	D,MBUFF
	CALL	BDOS
;
	IF	CLEVEL3		;IF THIRD COMMAND LEVEL IS PERMITTED
	JMP	SETU0D		;SET CURRENT DISK NUMBER IN LOWER PARAMS
	ENDIF
;
	IF	NOT CLEVEL3	;IF THIRD COMMAND LEVEL IS NOT PERMITTED
	CALL	SETU0D		;SET CURRENT DISK NUMBER IF LOWER PARAMS
				; AND FALL THRU TO CNVBUF
	ENDIF
;
; CAPITALIZE STRING (ENDING IN 0) IN CBUFF AND SET PTR FOR PARSING
;
CNVBUF:
	LXI	H,CBUFF 	;PT TO USER'S COMMAND
	MOV	B,M		;CHAR COUNT IN B
	INR	B		;ADD 1 IN CASE OF ZERO
CB1:
	INX	H		;PT TO 1ST VALID CHAR
	MOV	A,M		;CAPITALIZE COMMAND CHAR
	CALL	UCASE
	MOV	M,A
	DJNZ	CB1		;CONTINUE TO END OF COMMAND LINE
CB2:
	MVI	M,0		;STORE ENDING <NULL>
	LXI	H,CIBUFF	;SET COMMAND LINE PTR TO 1ST CHAR
	SHLD	CIBPTR
	RET
;
; CHECK FOR ANY CHAR FROM USER CONSOLE;RET W/ZERO SET IF NONE
;
BREAK:
	PUSH	D		;SAVE DE
	MVI	C,11		;CSTS CHECK
	CALL	BDOSB
	CNZ	CONIN		;GET INPUT CHAR
BRKBK:
	POP	D
	RET
;
; GET THE REQUESTED USER NUMBER FROM THE COMMAND LINE AND VALIDATE IT.
;
USRNUM:
	CALL	NUMBER
	CPI	MAXUSR+1
	RC
;
; INVALID COMMAND -- PRINT IT
;
ERROR:
	CALL	CRLF		;NEW LINE
	LHLD	CIPTR		;PT TO BEGINNING OF COMMAND LINE
ERR2:
	MOV	A,M		;GET CHAR
	CPI	' '+1		;SIMPLE '?' IF <SP> OR LESS
	JRC	ERR1
	PUSH	H		;SAVE PTR TO ERROR COMMAND CHAR
	CALL	CONOUT		;PRINT COMMAND CHAR
	POP	H		;GET PTR
	INX	H		;PT TO NEXT
	JR	ERR2		;CONTINUE
ERR1:
	CALL	PRINT		;PRINT '?'
	DB	'?'+80H
	CALL	SUBKIL		;TERMINATE ACTIVE $$$.SUB IF ANY
	JMP	RESTRT		;RESTART CPR
;
; CHECK TO SEE IF DE PTS TO DELIMITER; IF SO, RET W/ZERO FLAG SET
;
SDELM:
	LDAX	D
	ORA	A		;0=DELIMITER
	RZ
	CPI	' '		;ERROR IF < <SP>
	JRC	ERROR
	RZ			;<SP>=DELIMITER
	CPI	'='		;'='=DELIMITER
	RZ
	CPI	5FH		;UNDERSCORE=DELIMITER
	RZ
	CPI	'.'		;'.'=DELIMITER
	RZ
	CPI	':'		;':'=DELIMITER
	RZ
	CPI	';'		;';'=DELIMITER
	RZ
	CPI	'<'		;'<'=DELIMITER
	RZ
	CPI	'>'		;'>'=DELIMITER
	RET
;
; ADVANCE INPUT PTR TO FIRST NON-BLANK AND FALL THROUGH TO SBLANK
;
ADVAN:
	LDED	CIBPTR
;
; SKIP STRING PTED TO BY DE (STRING ENDS IN 0) UNTIL END OF STRING
;   OR NON-BLANK ENCOUNTERED (BEGINNING OF TOKEN)
;
SBLANK:
	LDAX	D
	ORA	A
	RZ
	CPI	' '
	RNZ
	INX	D
	JR	SBLANK
;
; ADD A TO HL (HL=HL+A)
;
ADDAH:
	ADD	L
	MOV	L,A
	RNC
	INR	H
	RET
;
; EXTRACT DECIMAL NUMBER FROM COMMAND LINE
;   RETURN WITH VALUE IN REG A;ALL REGISTERS MAY BE AFFECTED
;
NUMBER:
	CALL	SCANER		;PARSE NUMBER AND PLACE IN FCBFN
	LXI	H,FCBFN+10 	;PT TO END OF TOKEN FOR CONVERSION
	MVI	B,11		;11 CHARS MAX
;
; CHECK FOR SUFFIX FOR HEXADECIMAL NUMBER
;
NUMS:
	MOV	A,M		;GET CHARS FROM END, SEARCHING FOR SUFFIX
	DCX	H		;BACK UP
	CPI	' '		;SPACE?
	JRNZ	NUMS1		;CHECK FOR SUFFIX
	DJNZ	NUMS		;COUNT DOWN
	JR	NUM0		;BY DEFAULT, PROCESS
NUMS1:
	CPI	NUMBASE		;CHECK AGAINST BASE SWITCH FLAG
	JRZ	HNUM0
;
; PROCESS DECIMAL NUMBER
;
NUM0:
	LXI	H,FCBFN		;PT TO BEGINNING OF TOKEN
	LXI	B,1100H		;C=ACCUMULATED VALUE, B=CHAR COUNT
				; (C=0, B=11)
NUM1:
	MOV	A,M		;GET CHAR
	CPI	' '		;DONE IF <SP>
	JRZ	NUM2
	INX	H		;PT TO NEXT CHAR
	SUI	'0'		;CONVERT TO BINARY (ASCII 0-9 TO BINARY)
	CPI	10		;ERROR IF >= 10
	JRNC	NUMERR
	MOV	D,A		;DIGIT IN D
	MOV	A,C		;NEW VALUE = OLD VALUE * 10
	RLC
	RLC
	RLC
	ADD	C		;CHECK FOR RANGE ERROR
	JRC	NUMERR
	ADD	C		;CHECK FOR RANGE ERROR
	JRC	NUMERR
	ADD	D		;NEW VALUE = OLD VALUE * 10 + DIGIT
	JRC	NUMERR		;CHECK FOR RANGE ERROR
	MOV	C,A		;SET NEW VALUE
	DJNZ	NUM1		;COUNT DOWN
;
; RETURN FROM NUMBER
;
NUM2:
	MOV	A,C		;GET ACCUMULATED VALUE
	RET
;
; NUMBER ERROR ROUTINE FOR SPACE CONSERVATION
;
NUMERR:
	JMP	ERROR		;USE ERROR ROUTINE - THIS IS RELATIVE PT
;
; EXTRACT HEXADECIMAL NUMBER FROM COMMAND LINE
;   RETURN WITH VALUE IN REG A; ALL REGISTERS MAY BE AFFECTED
;
HEXNUM:
	CALL	SCANER		;PARSE NUMBER AND PLACE IN FCBFN
HNUM0:
	LXI	H,FCBFN		;PT TO TOKEN FOR CONVERSION
	LXI	D,0		;DE=ACCUMULATED VALUE
	MVI	B,11		;B=CHAR COUNT
HNUM1:
	MOV	A,M		;GET CHAR
	CPI	' '		;DONE?
	JRZ	HNUM3		;RETURN IF SO
	CPI	NUMBASE		;DONE IF NUMBASE SUFFIX
	JRZ	HNUM3
	SUI	'0'		;CONVERT TO BINARY
	JRC	NUMERR		;RETURN AND DONE IF ERROR
	CPI	10		;0-9?
	JRC	HNUM2
	SUI	7		;A-F?
	CPI	10H		;ERROR?
	JRNC	NUMERR
HNUM2:
	INX	H		;PT TO NEXT CHAR
	MOV	C,A		;DIGIT IN C
	MOV	A,D		;GET ACCUMULATED VALUE
	RLC			;EXCHANGE NYBBLES
	RLC
	RLC
	RLC
	ANI	0F0H		;MASK OUT LOW NYBBLE
	MOV	D,A
	MOV	A,E		;SWITCH LOW-ORDER NYBBLES
	RLC
	RLC
	RLC
	RLC
	MOV	E,A		;HIGH NYBBLE OF E=NEW HIGH OF E,
				;  LOW NYBBLE OF E=NEW LOW OF D
	ANI	0FH		;GET NEW LOW OF D
	ORA	D		;MASK IN HIGH OF D
	MOV	D,A		;NEW HIGH BYTE IN D
	MOV	A,E
	ANI	0F0H		;MASK OUT LOW OF E
	ORA	C		;MASK IN NEW LOW
	MOV	E,A		;NEW LOW BYTE IN E
	DJNZ	HNUM1		;COUNT DOWN
;
; RETURN FROM HEXNUM
;
HNUM3:
	XCHG			;RETURNED VALUE IN HL
	MOV	A,L		;LOW-ORDER BYTE IN A
	RET
;
; PT TO DIRECTORY ENTRY IN TBUFF WHOSE OFFSET IS SPECIFIED BY A AND C
;
DIRPTR:
	LXI	H,TBUFF 	;PT TO TEMP BUFFER
	ADD	C		;PT TO 1ST BYTE OF DIR ENTRY
	CALL	ADDAH		;PT TO DESIRED BYTE IN DIR ENTRY
	MOV	A,M		;GET DESIRED BYTE
	RET
;
; CHECK FOR SPECIFIED DRIVE AND LOG IT IN IF NOT DEFAULT
;
SLOGIN:
	XRA	A		;SET FCBDN FOR DEFAULT DRIVE
	STA	FCBDN
	CALL	COMLOG		;CHECK DRIVE
	RZ
	JR	DLOG5		;DO LOGIN OTHERWISE
;
; CHECK FOR SPECIFIED DRIVE AND LOG IN DEFAULT DRIVE IF SPECIFIED<>DEFAULT
;
DLOGIN:
	CALL	COMLOG		;CHECK DRIVE
	RZ			;ABORT IF SAME
	LDA	TDRIVE		;LOG IN DEFAULT DRIVE
;
DLOG5:	JMP	LOGIN
;
; ROUTINE COMMON TO BOTH LOGIN ROUTINES; ON EXIT, Z SET MEANS ABORT
;
COMLOG:
TEMPDR	EQU	$+1		;POINTER FOR IN-THE-CODE MODIFICATION
	MVI	A,0		;2ND BYTE (IMMEDIATE ARG) IS TEMPDR
	ORA	A		;0=NO
	RZ
	DCR	A		;COMPARE IT AGAINST DEFAULT
	LXI	H,TDRIVE
	CMP	M
	RET			;ABORT IF SAME
;
; EXTRACT TOKEN FROM COMMAND LINE AND PLACE IT INTO FCBDN;
;   FORMAT FCBDN FCB IF TOKEN RESEMBLES FILE NAME AND TYPE (FILENAME.TYP);
;   ON INPUT, CIBPTR PTS TO CHAR AT WHICH TO START SCAN;
;   ON OUTPUT, CIBPTR PTS TO CHAR AT WHICH TO CONTINUE AND ZERO FLAG IS RESET
;     IF '?' IS IN TOKEN
;
; ENTRY POINTS:
;	SCANER - LOAD TOKEN INTO FIRST FCB
;	SCANX - LOAD TOKEN INTO FCB PTED TO BY HL
;
SCANER:
	LXI	H,FCBDN 	;POINT TO FCBDN
SCANX:
	XRA	A		;SET TEMPORARY DRIVE NUMBER TO DEFAULT
	STA	TEMPDR
	CALL	ADVAN		;SKIP TO NON-BLANK OR END OF LINE
	SDED	CIPTR		;SET PTR TO NON-BLANK OR END OF LINE
	LDAX	D		;END OF LINE?
	ORA	A		;0=YES
	JRZ	SCAN2
	SBI	'A'-1		;CONVERT POSSIBLE DRIVE SPEC TO NUMBER
	MOV	B,A		;STORE NUMBER (A:=0, B:=1, ETC) IN B
	INX	D		;PT TO NEXT CHAR
	LDAX	D		;SEE IF IT IS A COLON (:)
	CPI	':'
	JRZ	SCAN3		;YES, WE HAVE A DRIVE SPEC
	DCX	D		;NO, BACK UP PTR TO FIRST NON-BLANK CHAR
SCAN2:
	LDA	TDRIVE		;SET 1ST BYTE OF FCBDN AS DEFAULT DRIVE
	MOV	M,A
	JR	SCAN4
SCAN3:
	MOV	A,B		;WE HAVE A DRIVE SPEC
	STA	TEMPDR		;SET TEMPORARY DRIVE
	MOV	M,B		;SET 1ST BYTE OF FCBDN AS SPECIFIED DRIVE
	INX	D		;PT TO BYTE AFTER ':'
;
; EXTRACT FILENAME FROM POSSIBLE FILENAME.TYP
;
SCAN4:
	XRA	A		;A=0
	STA	QMCNT		;INIT COUNT OF NUMBER OF QUESTION MARKS IN FCB
	MVI	B,8		;MAX OF 8 CHARS IN FILE NAME
	CALL	SCANF		;FILL FCB FILE NAME
;
; EXTRACT FILE TYPE FROM POSSIBLE FILENAME.TYP
;
	MVI	B,3		;PREPARE TO EXTRACT TYPE
	CPI	'.'		;IF (DE) DELIMITER IS A '.', WE HAVE A TYPE
	JRNZ	SCAN15		;FILL FILE TYPE BYTES WITH <SP>
	INX	D		;PT TO CHAR IN COMMAND LINE AFTER '.'
	CALL	SCANF		;FILL FCB FILE TYPE
	JR	SCAN16		;SKIP TO NEXT PROCESSING
SCAN15:
	CALL	SCANF4		;SPACE FILL
;
; FILL IN EX, S1, S2, AND RC WITH ZEROES
;
SCAN16:
	MVI	B,4		;4 BYTES
SCAN17:
	INX	H		;PT TO NEXT BYTE IN FCBDN
	MVI	M,0
	DJNZ	SCAN17
;
; SCAN COMPLETE -- DE PTS TO DELIMITER BYTE AFTER TOKEN
;
	SDED	CIBPTR
;
; SET ZERO FLAG TO INDICATE PRESENCE OF '?' IN FILENAME.TYP
;
	LDA	QMCNT		;GET NUMBER OF QUESTION MARKS
	ORA	A		;SET ZERO FLAG TO INDICATE ANY '?'
	RET
;
;  SCANF -- SCAN TOKEN PTED TO BY DE FOR A MAX OF B BYTES; PLACE IT INTO
;    FILE NAME FIELD PTED TO BY HL; EXPAND AND INTERPRET WILD CARDS OF
;    '*' AND '?'; ON EXIT, DE PTS TO TERMINATING DELIMITER
;
SCANF:
	CALL	SDELM		;DONE IF DELIMITER ENCOUNTERED - <SP> FILL
	JRZ	SCANF4
	INX	H		;PT TO NEXT BYTE IN FCBDN
	CPI	'*'		;IS (DE) A WILD CARD?
	JRNZ	SCANF1		;CONTINUE IF NOT
	MVI	M,'?'		;PLACE '?' IN FCBDN AND DON'T ADVANCE DE IF SO
	CALL	SCQ		;SCANNER COUNT QUESTION MARKS
	JR	SCANF2
SCANF1:
	MOV	M,A		;STORE FILENAME CHAR IN FCBDN
	INX	D		;PT TO NEXT CHAR IN COMMAND LINE
	CPI	'?'		;CHECK FOR QUESTION MARK (WILD)
	CZ	SCQ		;SCANNER COUNT QUESTION MARKS
SCANF2:
	DJNZ	SCANF		;DECREMENT CHAR COUNT UNTIL 8 ELAPSED
SCANF3:
	CALL	SDELM		;8 CHARS OR MORE - SKIP UNTIL DELIMITER
	RZ			;ZERO FLAG SET IF DELIMITER FOUND
	INX	D		;PT TO NEXT CHAR IN COMMAND LINE
	JR	SCANF3
;
;  FILL MEMORY POINTED TO BY HL WITH SPACES FOR B BYTES
;
SCANF4:
	INX	H		;PT TO NEXT BYTE IN FCBDN
	MVI	M,' '		;FILL FILENAME PART WITH <SP>
	DJNZ	SCANF4
	RET
;
;  INCREMENT QUESTION MARK COUNT FOR SCANNER
;    THIS ROUTINE INCREMENTS THE COUNT OF THE NUMBER OF QUESTION MARKS IN
;    THE CURRENT FCB ENTRY
;
SCQ:
	LDA	QMCNT		;GET COUNT
	INR	A		;INCREMENT
	STA	QMCNT		;PUT COUNT
	RET
;
; CMDTBL (COMMAND TABLE) SCANNER
;   ON RETURN, HL PTS TO ADDRESS OF COMMAND IF CPR-RESIDENT
;   ON RETURN, ZERO FLAG SET MEANS CPR-RESIDENT COMMAND
;
CMDSER:
	LXI	H,CMDTBL	;PT TO COMMAND TABLE
	MVI	C,NCMNDS	;SET COMMAND COUNTER
CMS1:
	LXI	D,FCBFN 	;PT TO STORED COMMAND NAME
	MVI	B,NCHARS	;NUMBER OF CHARS/COMMAND (8 MAX)
CMS2:
	LDAX	D		;COMPARE AGAINST TABLE ENTRY
	CMP	M
	JRNZ	CMS3		;NO MATCH
	INX	D		;PT TO NEXT CHAR
	INX	H
	DJNZ	CMS2		;COUNT DOWN
	LDAX	D		;NEXT CHAR IN INPUT COMMAND MUST BE <SP>
	CPI	' '
	JRNZ	CMS4
	RET			;COMMAND IS CPR-RESIDENT (ZERO FLAG SET)
CMS3:
	INX	H		;SKIP TO NEXT COMMAND TABLE ENTRY
	DJNZ	CMS3
CMS4:
	INX	H		;SKIP ADDRESS
	INX	H
	DCR	C		;DECREMENT TABLE ENTRY NUMBER
	JRNZ	CMS1
	INR	C		;CLEAR ZERO FLAG
	RET			;COMMAND IS DISK-RESIDENT (ZERO FLAG CLEAR)
;
;**** Section 5 ****
; CPR-Resident Commands
;
;
;Section 5A
;Command: DIR
;Function:  To display a directory of the files on disk
;Forms:
;	DIR <afn>	Displays the DIR files
;	DIR <afn> S	Displays the SYS files
;	DIR <afn> A	Display both DIR and SYS files
;
DIR:
	MVI	A,80H		;SET SYSTEM BIT EXAMINATION
	PUSH	PSW
	CALL	SCANER		;EXTRACT POSSIBLE D:FILENAME.TYP TOKEN
	CALL	SLOGIN		;LOG IN DRIVE IF NECESSARY
	LXI	H,FCBFN 	;MAKE FCB WILD (ALL '?') IF NO FILENAME.TYP
	MOV	A,M		;GET FIRST CHAR OF FILENAME.TYP
	CPI	' '		;IF <SP>, ALL WILD
	CZ	FILLQ
	CALL	ADVAN		;LOOK AT NEXT INPUT CHAR
	MVI	B,0		;SYS TOKEN DEFAULT
	JRZ	DIR2		;JUMP; THERE ISN'T ONE
	CPI	SYSFLG		;SYSTEM FLAG SPECIFIER?
	JRZ	GOTSYS		;GOT SYSTEM SPECIFIER
	CPI	SOFLG		;SYS ONLY?
	JRNZ	DIR2
	MVI	B,80H		;FLAG SYS ONLY
GOTSYS:
	INX	D
	SDED	CIBPTR
	CPI	SOFLG		;SYS ONLY SPEC?
	JRZ	DIR2		;THEN LEAVE BIT SPEC UNCHAGNED
	POP	PSW		;GET FLAG
	XRA	A		;SET NO SYSTEM BIT EXAMINATION
	PUSH	PSW 
DIR2:
	POP	PSW		;GET FLAG
DIR2A:
				;DROP INTO DIRPR TO PRINT DIRECTORY
				; THEN RESTART CPR
;
; DIRECTORY PRINT ROUTINE; ON ENTRY, MSB OF A IS 1 (80H) IF SYSTEM FILES EXCL
;
DIRPR:
	MOV	D,A		;STORE SYSTEM FLAG IN D
	MVI	E,0		;SET COLUMN COUNTER TO ZERO
	PUSH	D		;SAVE COLUMN COUNTER (E) AND SYSTEM FLAG (D)
	MOV	A,B		;SYS ONLY SPECIFIER
	STA	SYSTST
	CALL	SEARF		;SEARCH FOR SPECIFIED FILE (FIRST OCCURRANCE)
	CZ	PRNNF		;PRINT NO FILE MSG;REG A NOT CHANGED
;
; ENTRY SELECTION LOOP; ON ENTRY, A=OFFSET FROM SEARF OR SEARN
;
DIR3:
	JRZ	DIR11		;DONE IF ZERO FLAG SET
	DCR	A		;ADJUST TO RETURNED VALUE
	RRC			;CONVERT NUMBER TO OFFSET INTO TBUFF
	RRC
	RRC
	ANI	60H
	MOV	C,A		;OFFSET INTO TBUFF IN C (C=OFFSET TO ENTRY)
	MVI	A,10		;ADD 10 TO PT TO SYSTEM FILE ATTRIBUTE BIT
	CALL	DIRPTR
	POP	D		;GET SYSTEM BIT MASK FROM D
	PUSH	D
	ANA	D		;MASK FOR SYSTEM BIT
SYSTST	EQU	$+1		;POINTER TO IN-THE-CODE BUFFER SYSTST
	CPI	0
	JRNZ	DIR10
	POP	D		;GET ENTRY COUNT (=<CR> COUNTER)
	MOV	A,E		;ADD 1 TO IT
	INR	E
	PUSH	D		;SAVE IT
	ANI	03H		;OUTPUT <CRLF> IF 4 ENTRIES PRINTED IN LINE
	PUSH	PSW
	JRNZ	DIR4
	CALL	CRLF		;NEW LINE
	JR	DIR5
DIR4:
	CALL	PRINT
;
	IF	WIDE
	DB	'  '		;2 SPACES
	DB	FENCE		;THEN FENCE CHAR
	DB	' ',' '+80H	;THEN 2 MORE SPACES
	ENDIF
;
	IF	NOT WIDE
	DB	' '		;SPACE
	DB	FENCE		;THEN FENCE CHAR
	DB	' '+80H		;THEN SPACE
	ENDIF
;
DIR5:
	MVI	B,01H		;PT TO 1ST BYTE OF FILE NAME
DIR6:
	MOV	A,B		;A=OFFSET
	CALL	DIRPTR		;HL NOW PTS TO 1ST BYTE OF FILE NAME
	ANI	7FH		;MASK OUT MSB
	CPI	' '		;NO FILE NAME?
	JRNZ	DIR8		;PRINT FILE NAME IF PRESENT
	POP	PSW
	PUSH	PSW
	CPI	03H
	JRNZ	DIR7
	MVI	A,09H		;PT TO 1ST BYTE OF FILE TYPE
	CALL	DIRPTR		;HL NOW PTS TO 1ST BYTE OF FILE TYPE
	ANI	7FH		;MASK OUT MSB
	CPI	' '		;NO FILE TYPE?
	JRZ	DIR9		;CONTINUE IF SO
DIR7:
	MVI	A,' '		;OUTPUT <SP>
DIR8:
	CALL	CONOUT		;PRINT CHAR
	INR	B		;INCR CHAR COUNT
	MOV	A,B
	CPI	12		;END OF FILENAME.TYP?
	JRNC	DIR9		;CONTINUE IF SO
	CPI	09H		;END IF FILENAME ONLY?
	JRNZ	DIR6		;PRINT TYP IF SO
	MVI	A,'.'		;PRINT DOT BETWEEN FILE NAME AND TYPE
	CALL	CONOUT
	JR	DIR6
DIR9:
	POP	PSW
DIR10:
	CALL	BREAK		;CHECK FOR ABORT
	JRNZ	DIR11
	CALL	SEARN		;SEARCH FOR NEXT FILE
	JR	DIR3		;CONTINUE
DIR11:
	POP	D		;RESTORE STACK
	RET
;
; FILL FCB @HL WITH '?'
;
FILLQ:
	MVI	B,11		;NUMBER OF CHARS IN FN & FT
FQLP:
	MVI	M,'?'		;STORE '?'
	INX	H
	DJNZ	FQLP
	RET
;
;Section 5B
;Command: ERA
;Function:  Erase files
;Forms:
;	ERA <afn>	Erase Specified files and print their names
;
	IF	NOT RAS		;NOT FOR REMOTE-ACCESS SYSTEM
;
ERA:
	CALL	SCANER		;PARSE FILE SPECIFICATION
	CPI	11		;ALL WILD (ALL FILES = 11 '?')?
	JRNZ	ERA1		;IF NOT, THEN DO ERASES
	CALL	PRINTC
	DB	'All','?'+80H
	CALL	CONIN		;GET REPLY
	CPI	'Y'		;YES?
	JNZ	RESTRT		;RESTART CPR IF NOT
	CALL	CRLF		;NEW LINE
ERA1:
	CALL	SLOGIN		;LOG IN SELECTED DISK IF ANY
	XRA	A		;PRINT ALL FILES (EXAMINE SYSTEM BIT)
	MOV	B,A		;NO SYS-ONLY OPT TO DIRPR
	CALL	DIRPR		;PRINT DIRECTORY OF ERASED FILES
	LXI	D,FCBDN 	;DELETE FILE SPECIFIED
	CALL	DELETE
	RET			;REENTER CPR
;
	ENDIF			;RAS
;
;Section 5C
;Command: LIST
;Function:  Print out specified file on the LST: Device
;Forms:
;	LIST <ufn>	Print file (NO Paging)
;
LIST:
	MVI	A,0FFH		;TURN ON PRINTER FLAG
	JR	TYPE0
;
;Section 5D
;Command: TYPE
;Function:  Print out specified file on the CON: Device
;Forms:
;	TYPE <ufn>	Print file
;	TYPE <ufn> P	Print file with paging flag	
;
TYPE:
	XRA	A		;TURN OFF PRINTER FLAG
;
; ENTRY POINT FOR CPR LIST FUNCTION (LIST)
;
TYPE0:
	STA	PRFLG		;SET FLAG
	CALL	SCANER		;EXTRACT FILENAME.TYP TOKEN
	JNZ	ERROR		;ERROR IF ANY QUESTION MARKS
	CALL	ADVAN		;GET PGDFLG IF IT'S THERE
	STA	PGFLG		;SAVE IT AS A FLAG
	JRZ	NOSLAS		;JUMP IF INPUT ENDED
	INX	D		;PUT NEW BUF POINTER
	XCHG
	SHLD	CIBPTR
NOSLAS:
	CALL	SLOGIN		;LOG IN SELECTED DISK IF ANY
	CALL	OPENF		;OPEN SELECTED FILE
	JZ	TYPE4		;ABORT IF ERROR
	CALL	CRLF		;NEW LINE
	MVI	A,NLINES-1	;SET LINE COUNT
	STA	PAGCNT
	LXI	H,CHRCNT	;SET CHAR POSITION/COUNT
	MVI	M,0FFH		;EMPTY LINE
	MVI	B,0		;SET TAB CHAR COUNTER
TYPE1:
	LXI	H,CHRCNT	;PT TO CHAR POSITION/COUNT
	MOV	A,M		;END OF BUFFER?
	CPI	80H
	JRC	TYPE2
	PUSH	H		;READ NEXT BLOCK
	CALL	READF
	POP	H
	JRNZ	TYPE3		;ERROR?
	XRA	A		;RESET COUNT
	MOV	M,A
TYPE2:
	INR	M		;INCREMENT CHAR COUNT
	LXI	H,TBUFF 	;PT TO BUFFER
	CALL	ADDAH		;COMPUTE ADDRESS OF NEXT CHAR FROM OFFSET
	MOV	A,M		;GET NEXT CHAR
	ANI	7FH		;MASK OUT MSB
	CPI	1AH		;END OF FILE (^Z)?
	RZ			;RESTART CPR IF SO
;
; OUTPUT CHAR TO CON: OR LST: DEVICE WITH TABULATION
;
	CPI	CR		;RESET TAB COUNT?
	JRZ	TABRST
	CPI	LF		;RESET TAB COUNT?
	JRZ	TABRST
	CPI	TAB		;TAB?
	JRZ	LTAB
	CALL	LCOUT		;OUTPUT CHAR
	INR	B		;INCREMENT CHAR COUNT
	JR	TYPE2L
TABRST:
	CALL	LCOUT		;OUTPUT <CR> OR <LF>
	MVI	B,0		;RESET TAB COUNTER
	JR	TYPE2L
LTAB:
	MVI	A,' '		;<SP>
	CALL	LCOUT
	INR	B		;INCR POS COUNT
	MOV	A,B
	ANI	7
	JRNZ	LTAB
;
; CONTINUE PROCESSING
;
TYPE2L:
	CALL	BREAK		;CHECK FOR ABORT
	JRZ	TYPE1		;CONTINUE IF NO CHAR
	CPI	'C'-'@' 	;^C?
	RZ			;RESTART IF SO
	JR	TYPE1
TYPE3:
	DCR	A		;NO ERROR?
	RZ			;RESTART CPR
TYPE4:
	JMP	ERRLOG
;
; PAGING ROUTINES
;   PAGER COUNTS DOWN LINES AND PAUSES FOR INPUT (DIRECT) IF COUNT EXPIRES
;   PAGSET SETS LINES/PAGE COUNT
;
PAGER:
	PUSH	H
	LXI	H,PAGCNT	;COUNT DOWN
	DCR	M
	JRNZ	PGBAK		;JUMP IF NOT END OF PAGE
	MVI	M,NLINES-2	;REFILL COUNTER
;
PGFLG	EQU	$+1		;POINTER TO IN-THE-CODE BUFFER PGFLG
	MVI	A,0		;0 MAY BE CHANGED BY PGFLG EQUATE
	CPI	PGDFLG		;PAGE DEFAULT OVERRIDE OPTION WANTED?
;
	IF	PGDFLT		;IF PAGING IS DEFAULT
	JRZ	PGBAK		;  PGDFLG MEANS NO PAGING, PLEASE
	ELSE			;IF PAGING NOT DEFAULT
	JRNZ	PGBAK		;  PGDFLG MEANS PLEASE PAGINATE
	ENDIF
;
	CALL	CONIN		;GET CHAR TO CONTINUE
	CPI	'C'-'@' 	;^C
	JZ	RSTCPR		;RESTART CPR
PGBAK:
	POP	H		;RESTORE HL
	RET
;
;Section 5E
;Command: SAVE
;Function:  To save the contents of the TPA onto disk as a file
;Forms:
;	SAVE <Number of Pages> <ufn>
;				Save specified number of pages (start at 100H)
;				from TPA into specified file; <Number of
;				Pages> is in DEC
;	SAVE <Number of Sectors> <ufn> S
;				Like SAVE above, but numeric argument specifies
;				number of sectors rather than pages
;
	IF	NOT RAS		;NOT FOR REMOTE-ACCESS SYSTEM
;
SAVE:
	CALL	NUMBER		;EXTRACT NUMBER FROM COMMAND LINE
	MOV	L,A		;HL=PAGE COUNT
	MVI	H,0
	PUSH	H		;SAVE PAGE COUNT
	CALL	EXTEST		;TEST FOR EXISTENCE OF FILE AND ABORT IF SO
	MVI	C,16H		;BDOS MAKE FILE
	CALL	GRBDOS
	POP	H		;GET PAGE COUNT
	JRZ	SAVE3		;ERROR?
	XRA	A		;SET RECORD COUNT FIELD OF NEW FILE'S FCB
	STA	FCBCR
	CALL	ADVAN		;LOOK FOR 'S' FOR SECTOR OPTION
	INX	D		;PT TO AFTER 'S' TOKEN
	CPI	SECTFLG
	JRZ	SAVE0
	DCX	D		;NO 'S' TOKEN, SO BACK UP
	DAD	H		;DOUBLE IT FOR HL=SECTOR (128 BYTES) COUNT
SAVE0:
	SDED	CIBPTR		;SET PTR TO BAD TOKEN OR AFTER GOOD TOKEN
	LXI	D,TPA		;PT TO START OF SAVE AREA (TPA)
SAVE1:
	MOV	A,H		;DONE WITH SAVE?
	ORA	L		;HL=0 IF SO
	JRZ	SAVE2
	DCX	H		;COUNT DOWN ON SECTORS
	PUSH	H		;SAVE PTR TO BLOCK TO SAVE
	LXI	H,128		;128 BYTES PER SECTOR
	DAD	D		;PT TO NEXT SECTOR
	PUSH	H		;SAVE ON STACK
	CALL	DMASET		;SET DMA ADDRESS FOR WRITE (ADDRESS IN DE)
	LXI	D,FCBDN 	;WRITE SECTOR
	MVI	C,15H		;BDOS WRITE SECTOR
	CALL	BDOSB		;SAVE BC
	POP	D		;GET PTR TO NEXT SECTOR IN DE
	POP	H		;GET SECTOR COUNT
	JRNZ	SAVE3		;WRITE ERROR?
	JR	SAVE1		;CONTINUE
SAVE2:
	LXI	D,FCBDN 	;CLOSE SAVED FILE
	CALL	CLOSE
	INR	A		;ERROR?
	JRNZ	SAVE4
SAVE3:
	CALL	PRNLE		;PRINT 'NO SPACE' ERROR
SAVE4:
	CALL	DEFDMA		;SET DMA TO 0080
	RET			;RESTART CPR
;
; Test File in FCB for existence, ask user to delete if so, and abort if he
;  choses not to
;
EXTEST:
	CALL	SCANER		;EXTRACT FILE NAME
	JNZ	ERROR		;'?' IS NOT PERMITTED
	CALL	SLOGIN		;LOG IN SELECTED DISK
	CALL	SEARF		;LOOK FOR SPECIFIED FILE
	LXI	D,FCBDN		;PT TO FILE FCB
	RZ			;OK IF NOT FOUND
	PUSH	D		;SAVE PTR TO FCB
	CALL	PRINTC
	DB	'Delete File','?'+80H
	CALL	CONIN		;GET RESPONSE
	POP	D		;GET PTR TO FCB
	CPI	'Y'		;KEY ON YES
	JNZ	RSTCPR		;RESTART IF NO
	PUSH	D		;SAVE PTR TO FCB
	CALL	DELETE		;DELETE FILE
	POP	D		;GET PTR TO FCB
	RET
;
	ENDIF			;RAS
;
;Section 5F
;Command: REN
;Function:  To change the name of an existing file
;Forms:
;	REN <New ufn>=<Old ufn>	Perform function
;
	IF	NOT RAS		;NOT FOR REMOTE-ACCESS SYSTEM
;
REN:
	CALL	EXTEST		;TEST FOR FILE EXISTENCE AND RETURN
				; IF FILE DOESN'T EXIST; ABORT IF IT DOES
	LDA	TEMPDR		;SAVE CURRENT DEFAULT DISK
	PUSH	PSW		;SAVE ON STACK
REN0:
	LXI	H,FCBDN 	;SAVE NEW FILE NAME
	LXI	D,FCBDM
	LXI	B,16		;16 BYTES
	LDIR
	CALL	ADVAN		;ADVANCE CIBPTR
	CPI	'='		;'=' OK
	JRNZ	REN4
REN1:
	XCHG			;PT TO CHAR AFTER '=' IN HL
	INX	H
	SHLD	CIBPTR		;SAVE PTR TO OLD FILE NAME
	CALL	SCANER		;EXTRACT FILENAME.TYP TOKEN
	JRNZ	REN4		;ERROR IF ANY '?'
	POP	PSW		;GET OLD DEFAULT DRIVE
	MOV	B,A		;SAVE IT
	LXI	H,TEMPDR	;COMPARE IT AGAINST CURRENT DEFAULT DRIVE
	MOV	A,M		;MATCH?
	ORA	A
	JRZ	REN2
	CMP	B		;CHECK FOR DRIVE ERROR
	MOV	M,B
	JRNZ	REN4
REN2:
	MOV	M,B
	XRA	A
	STA	FCBDN		;SET DEFAULT DRIVE
	LXI	D,FCBDN 	;RENAME FILE
	MVI	C,17H		;BDOS RENAME FCT
	CALL	GRBDOS
	RNZ
REN3:
	CALL	PRNNF		;PRINT NO FILE MSG
REN4:
	JMP	ERRLOG
;
	ENDIF			;RAS
;
;Section 5G
;Command: USER
;Function:  Change current USER number
;Forms:
;	USER <unum>	Select specified user number;<unum> is in DEC
;
USER:
	CALL	USRNUM		;EXTRACT USER NUMBER FROM COMMAND LINE
	MOV	E,A		;PLACE USER NUMBER IN E
	CALL	SETUSR		;SET SPECIFIED USER
RSTJMP:
	JMP	RCPRNL		;RESTART CPR
;
;Section 5H
;Command: DFU
;Function:  Set the Default User Number for the command/file scanner
;	     (MEMLOAD)
;Forms:
;	DFU <unum>	Select Default User Number;<unum> is in DEC
;
DFU:
	CALL	USRNUM		;GET USER NUMBER
	STA	DFUSR		;PUT IT AWAY
	JR	RSTJMP		;RESTART CPR (NO DEFAULT LOGIN)
;
;Section 5I
;Command: JUMP
;Function:  To Call the program (subroutine) at the specified address
;	     without loading from disk
;Forms:
;	JUMP <adr>		Call at <adr>;<adr> is in HEX
;
	IF	NOT RAS		;NOT FOR REMOTE-ACCESS SYSTEM
;
JUMP:
	CALL	HEXNUM		;GET LOAD ADDRESS IN HL
	JR	CALLPROG	;PERFORM CALL
;
	ENDIF			;RAS
;
;Section 5J
;Command: GO
;Function:  To Call the program in the TPA without loading
;	     loading from disk. Same as JUMP 100H, but much
;	     more convenient, especially when used with
;	     parameters for programs like STAT. Also can be
;	     allowed on remote-access systems with no problems.
;
;Form:
;	GO <parameters like for COMMAND>
;
	IF	NOT RAS		;ONLY IF RAS
;
GO:	LXI	H,TPA		;Always to TPA
	JR	CALLPROG	;Perform call
;
	ENDIF			;END OF GO FOR RAS
;
;Section 5K
;Command: COM file processing
;Function:  To load the specified COM file from disk and execute it
;Forms:
;	<command>
;
COM:
	LDA	FCBFN		;ANY COMMAND?
	CPI	' '		;' ' MEANS COMMAND WAS 'D:' TO SWITCH
	JRNZ	COM1		;NOT <SP>, SO MUST BE TRANSIENT OR ERROR
	LDA	TEMPDR		;LOOK FOR DRIVE SPEC
	ORA	A		;IF ZERO, JUST BLANK
	JZ	RCPRNL
	DCR	A		;ADJUST FOR LOG IN
	STA	TDRIVE		;SET DEFAULT DRIVE
	CALL	SETU0D		;SET DRIVE WITH USER 0
	CALL	LOGIN		;LOG IN DRIVE
	JMP	RCPRNL		;RESTART CPR
COM1:
	LDA	FCBFT		;FILE TYPE MUST BE BLANK
	CPI	' '
	JNZ	ERROR
	LXI	H,COMMSG	;PLACE DEFAULT FILE TYPE (COM) INTO FCB
	LXI	D,FCBFT		;COPY INTO FILE TYPE
	LXI	B,3		;3 BYTES
	LDIR
	LXI	H,TPA		;SET EXECUTION/LOAD ADDRESS
	PUSH	H		;SAVE FOR EXECUTION
	CALL	MEMLOAD		;LOAD MEMORY WITH FILE SPECIFIED IN CMD LINE
	POP	H		;GET EXECUTION ADDRESS
	RNZ			;RETURN (ABORT) IF LOAD ERROR
;
; CALLPROG IS THE ENTRY POINT FOR THE EXECUTION OF THE LOADED
;   PROGRAM;ON ENTRY TO THIS ROUTINE, HL MUST CONTAIN THE EXECUTION
;   ADDRESS OF THE PROGRAM (SUBROUTINE) TO EXECUTE
;
CALLPROG:
	SHLD	EXECADR		;PERFORM IN-LINE CODE MODIFICATION
	CALL	DLOGIN		;LOG IN DEFAULT DRIVE
	CALL	SCANER		;SEARCH COMMAND LINE FOR NEXT TOKEN
	LXI	H,TEMPDR	;SAVE PTR TO DRIVE SPEC
	PUSH	H
	MOV	A,M		;SET DRIVE SPEC
	STA	FCBDN
	LXI	H,FCBDN+10H	;PT TO 2ND FILE NAME
	CALL	SCANX		;SCAN FOR IT AND LOAD IT INTO FCBDN+16
	POP	H		;SET UP DRIVE SPECS
	MOV	A,M
	STA	FCBDM
	XRA	A
	STA	FCBCR
	LXI	D,TFCB		;COPY TO DEFAULT FCB
	LXI	H,FCBDN 	;FROM FCBDN
	LXI	B,33		;SET UP DEFAULT FCB
	LDIR
	LXI	H,CIBUFF
COM4:
	MOV	A,M		;SKIP TO END OF 2ND FILE NAME
	ORA	A		;END OF LINE?
	JRZ	COM5
	CPI	' '		;END OF TOKEN?
	JRZ	COM5
	INX	H
	JR	COM4
;
; LOAD COMMAND LINE INTO TBUFF
;
COM5:
	MVI	B,0		;SET CHAR COUNT
	LXI	D,TBUFF+1	;PT TO CHAR POS
COM6:
	MOV	A,M		;COPY COMMAND LINE TO TBUFF
	STAX	D
	ORA	A		;DONE IF ZERO
	JRZ	COM7
	INR	B		;INCR CHAR COUNT
	INX	H		;PT TO NEXT
	INX	D
	JR	COM6
;
; RUN LOADED TRANSIENT PROGRAM
;
COM7:
	MOV	A,B		;SAVE CHAR COUNT
	STA	TBUFF
	CALL	CRLF		;NEW LINE
	CALL	DEFDMA		;SET DMA TO 0080
	CALL	SETUD		;SET USER/DISK
;
; EXECUTION (CALL) OF PROGRAM (SUBROUTINE) OCCURS HERE
;
EXECADR	EQU	$+1		;CHANGE ADDRESS FOR IN-LINE CODE MODIFICATION
	CALL	TPA		;CALL TRANSIENT
	CALL	DEFDMA		;SET DMA TO 0080, IN CASE
				;PROG CHANGED IT ON US
	CALL	SETU0D		;SET USER 0/DISK
	CALL	LOGIN		;LOGIN DISK
	JMP	RESTRT		;RESTART CPR
;
; TRANSIENT LOAD ERROR
;
COM8:
	POP	H		;CLEAR RETURN ADDRESS
	CALL	RESETUSR	;RESET CURRENT USER NUMBER
				;  RESET MUST BE DONE BEFORE LOGIN
ERRLOG:
	CALL	DLOGIN		;LOG IN DEFAULT DISK
ERRJMP:
	JMP	ERROR
;
;Section 5L
;Command: GET
;Function:  To load the specified file from disk to the specified address
;Forms:
;	GET <adr> <ufn>	Load the specified file at the specified page;
;			<adr> is in HEX
;
	IF	NOT RAS		;NOT FOR REMOTE-ACCESS SYSTEM
;
GET:
	CALL	HEXNUM		;GET LOAD ADDRESS IN HL
	PUSH	H		;SAVE ADDRESS
	CALL	SCANER		;GET FILE NAME
	POP	H		;RESTORE ADDRESS
	JRNZ	ERRJMP		;MUST BE UNAMBIGUOUS
;
; FALL THRU TO MEMLOAD
;
	ENDIF			;RAS
;
; LOAD MEMORY WITH THE FILE WHOSE NAME IS SPECIFIED IN THE COMMAND LINE
;   ON INPUT, HL CONTAINS STARTING ADDRESS TO LOAD
;
MEMLOAD:
	CALL	MLOAD		;USER MEMORY LOAD SUBROUTINE
	PUSH	PSW		;SAVE RETURN STATUS
	CALL	RESETUSR	;RESET USER NUMBER
	POP	PSW		;GET RETURN STATUS
	RET

;
;  MEMORY LOAD SUBROUTINE
;	EXIT POINTS ARE A SIMPLE RETURN WITH THE ZERO FLAG SET IF NO ERROR,
; A SIMPLE RETURN WITH THE ZERO FLAG RESET (NZ) IF MEMORY FULL, OR A JMP TO
; COM8 IF COM FILE NOT FOUND
;
MLOAD:
	SHLD	LOADADR		;SET LOAD ADDRESS
	CALL	GETUSR		;GET CURRENT USER NUMBER
	STA	TMPUSR		;SAVE IT FOR LATER
	STA	TSELUSR 	;TEMP USER TO SELECT
;
;   MLA is a reentry point for a non-standard CP/M Modification
; This is the return point for when the .COM (or GET) file is not found the
; first time, Drive A: is selected for a second attempt
;
MLA:
	CALL	SLOGIN		;LOG IN SPECIFIED DRIVE IF ANY
	CALL	OPENF		;OPEN COMMAND.COM FILE
	JRNZ	MLA1		;FILE FOUND - LOAD IT
;
; ERROR ROUTINE TO SELECT USER 0 IF ALL ELSE FAILS
;
DFUSR	EQU	$+1		;MARK IN-THE-CODE VARIABLE
	MVI	A,DEFUSR	;GET DEFAULT USER
TSELUSR	EQU	$+1		;MARK IN-THE-CODE VARIABLE
	CPI	DEFUSR		;SAME?
	JRZ	MLA0		;JUMP IF
	STA	TSELUSR		;ELSE PUT DOWN NEW ONE
	MOV	E,A
	CALL	SETUSR		;GO SET NEW USER NUMBER
	JR	MLA		;AND TRY AGAIN
;
; ERROR ROUTINE TO SELECT DRIVE A: IF DEFAULT WAS ORIGINALLY SELECTED
;
MLA0:
	LXI	H,TEMPDR	;GET DRIVE FROM CURRENT COMMAND
	XRA	A		;A=0
	ORA	M
	JNZ	COM8		;ERROR IF ALREADY DISK A:
	MVI	M,1		;SELECT DRIVE A:
	JR	MLA
;
; FILE FOUND -- PROCEED WITH LOAD
;
MLA1:
LOADADR	EQU	$+1		;MEMORY LOAD ADDRESS (IN-LINE CODE MOD)
	LXI	H,TPA		;SET START ADDRESS OF MEMORY LOAD
ML2:
	MVI	A,ENTRY/256-1	;GET HIGH-ORDER ADR OF JUST BELOW CPR
	CMP	H		;ARE WE GOING TO OVERWRITE THE CPR?
	JRC	PRNLE		;ERROR IF SO
	PUSH	H		;SAVE ADDRESS OF NEXT SECTOR
	XCHG			;... IN DE
	CALL	DMASET		;SET DMA ADDRESS FOR LOAD
	LXI	D,FCBDN 	;READ NEXT SECTOR
	CALL	READ
	POP	H		;GET ADDRESS OF NEXT SECTOR
	JRNZ	ML3		;READ ERROR OR EOF?
	LXI	D,128		;MOVE 128 BYTES PER SECTOR
	DAD	D		;PT TO NEXT SECTOR IN HL
	JR	ML2
;
ML3:
	DCR	A		;LOAD COMPLETE
	RZ			;OK IF ZERO, ELSE FALL THRU TO PRNLE
;
; LOAD ERROR
;
PRNLE:
	CALL	PRINTC
	DB	'Ful','l'+80H
	MVI	A,1		;SET NON-ZERO TO INDICATE ERROR
	ORA	A		;SET FLAG
	RET
;
	END
