
*************************************************************************
*									*
*  Z C P R 2 -- Z80-Based Command Processor Replacement, Version 2.0	*
*									*
*  Copyright (c) 1982, 1983 by Richard Conn				*
*  All Rights Reserved							*
*									*
*  ZCPR2 was written by Richard Conn, who assumes no responsibility	*
*  or liability for its use.  ZCPR2 is released to the public   	*
*  domain for non-commercial use only.					*
*									*
*  The public is encouraged to freely copy and use this program for	*
*  non-commercial purposes.  Any commercial use of ZCPR2 is prohibited	*
*  unless approved by the author, Richard Conn, in writing.		*
*									*
*  This is Mod 0.3 to the RELEASE VERSION of ZCPR2.			*
*									*
*************************************************************************

;
;  ZCPR2 -- CP/M Z80 Command Processor Replacement (ZCPR) Version 2.0
;
;	ZCPR2 is based upon ZCPR
;
;	ZCPR2 was an individual effort by Richard Conn, with comments,
; recommendations, and some beta testing by the following people:
;		Frank Wancho
;		Charlie Strom
;		Hal Carter
;
;	Extensive documentation on ZCPR2 and the utilities in the ZCPR2
; System can be found in the following manuals:
;		ZCPR2 Concepts Manual
;		ZCPR2 Installation Manual
;		ZCPR2 User's Guide
;		ZCPR2 Rationale
;
;******** Structure Notes ********
;
;	ZCPR2 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 ZCPR2
;
;    1		Buffers
;
;    2		CPR Starting Modules
;			CPR1	CPR	CONT	RESTRT	RS1
;			CAPBUF	RSTCPR	RCPRNL	ERROR	PRNNF
;
;    3		Utilities
;			CRLF	CONOUT	CONIN	LCOUT	LSTOUT
;			PAGER	READF	READ	BDOSB	PRINTC
;			PRINT	PRIN1	GETDRV	DEFDMA	DMASET
;			RESET	BDOSJP	LOGIN	OPENF	OPEN
;			GRBDOS	CLOSE	SEARF	SEAR1	SEARN
;			SUBKIL	DELETE	GETUSR	SETUSR
;
;     4		CPR Utilities
;			SETUD	UCASE	REDBUF	BREAK	SDELM
;			ADVAN	SBLANK	ADDAH	NUMBER	NUMERR
;			HEXNUM	DIRPTR	SLOGIN	DLOGIN	SCANLOG
;			SCANER	SCANX	SCANF	CMDSER
;
;     5		CPR-Resident Commands and Functions
;     5A		DIR	DIRPR	PRFN	GETSBIT	FILLQ
;     5B		ERA
;     5C		LIST
;     5D		TYPE
;     5E		SAVE	EXTEST
;     5F		REN
;     5G		JUMP
;     5H		GO
;     5I		COM	CALLPROG
;     5J		GET	MLOAD	PRNLE	PATH
;
;
FALSE	EQU	0
TRUE	EQU	NOT FALSE
;
;  The following MACLIB statement loads all the user-selected equates
; which are used to customize ZCPR2 for the user's working environment.
;
	MACLIB	ZCPRHDR
;
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
;	EXX	- EXCHANGE BC, DE, HL WITH BC', DE', HL'
;
;
;
;	@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		;;RANGE ERROR
	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
;
EXX	MACRO		;;EXCHANGE PRIMARY AND ALTERNATE REGISTERS
	DB	0D9H
	ENDM
;
; END OF Z80 MACRO EXTENSIONS
;
;
;**** Section 0 ****
;
	ORG	CPRLOC
;
;  ENTRY POINTS INTO ZCPR2
;
;    IF MULTCMD (MULTIPLE COMMANDS ON ONE LINE) is FALSE:
;    If ZCPR2 is entered at location CPRLOC (at the JMP to CPR), then
; the default command in CMDLIN will be processed.  If ZCPR2 is entered
; at location CPRLOC+3 (at the JMP to CPR1), then the default command in
; CMDLIN will NOT be processed.
;    NOTE:  Entry into ZCPR2 at CPRLOC is permitted, but in order for this
; to work, CMDLIN MUST be initialized to contain the command line (ending in 0)
; and the C register MUST contain a valid User/Disk Flag
; (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.  The NXTCHR (NeXT CHaRacter pointer)
; is located to be compatable with such programs (if they determine the buffer
; length from the byte at BUFSIZ [CPRLOC + 6]), but under ZCPR2
; this is no longer necessary.  ZCPR2 automatically initializes
; this buffer pointer in all cases if MULTCMD is not enabled.
;
;    IF MULTCMD is TRUE:
;    Entry at CPR or CPR1 has the same effect.  Multiple command processing
; will still continue.
;    Hence, if MULTCMD is FALSE, a user program need only load the buffer
; CMDLIN with the desired command line, terminated by a zero, in order to
; have this command line executed.  If MULTCMD is TRUE, a user program must
; load this buffer as before, but he must also set the NXTCHR pointer to
; point to the first character of the command line.
;    NOTE:  ***** (BIG STAR) ***** Programs such as SYNONYM3 will fail if
; multiple commands are enabled, but this feature is so very useful that I
; feel it is worth the sacrifice.  The ZCPR2 utilities of STARTUP and MENU
; require multiple commands, and this feature also permits simple chaining
; of programs to be possible under the ZCPR2 environment.
;
;	Enjoy using ZCPR2!
;			Richard Conn
;
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
; CMDLIN.  This command line is terminated by a binary zero.  ZCPR2 then
; parses, interprets, and executes the command line.
;   Case is not significant in the command line.  ZCPR2 converts all lower-case
; letters to upper-case.
;   If MULTCMD is TRUE, then the user must set a pointer to the first
; character of the command line into the buffer NXTCHR.  If MULTCMD is FALSE,
; no action other than placing a zero-terminated command line into the buffer
; starting at CMDLIN is required on the part of the user.
;
	IF	MULTCMD		;MULTIPLE COMMANDS ALLOWED?
;
; For Multiple Commands, the command line buffer (CMDLIN) is located external
; to ZCPR2 so that it is not overlayed during Warm Boots; the same is true
; for NXTCHR, the 2nd key buffer.  BUFSIZ and CHRCNT are not important and
; are provided so the BDOS READLN function can load CMDLIN directly and
; a user program can see how much space is available in CMDLIN for its text.
;
NXTCHR	EQU	CLBASE		;NXTCHR STORED EXTERNALLY (2 bytes)
BUFSIZ	EQU	NXTCHR+2	;BUFSIZ STORED EXTERNALLY (1 byte)
CHRCNT	EQU	BUFSIZ+1	;CHRCNT STORED EXTERNALLY (1 byte)
CMDLIN	EQU	CHRCNT+1	;CMDLIN STORED EXTERNALLY (long)
;
	ELSE
;
; If no multiple commands are permitted, these buffers are left internal
; to ZCPR2 so that the original CCP command line facility (as used by
; programs like SYNONYM3) can be left intact.
;
BUFLEN	EQU	80		;MAXIMUM BUFFER LENGTH
BUFSIZ:
	DB	BUFLEN		;MAXIMUM BUFFER LENGTH
CHRCNT:
	DB	0		;NUMBER OF VALID CHARS IN COMMAND LINE
CMDLIN:
	DB	'               '	;DEFAULT (COLD BOOT) COMMAND
	DB	0			;COMMAND STRING TERMINATOR
	DS	BUFLEN-($-CMDLIN)+1	;TOTAL IS 'BUFLEN' BYTES
;
NXTCHR:
	DW	CMDLIN		;POINTER TO COMMAND INPUT BUFFER
;
	ENDIF		;MULTCMD
;

;
; FILE TYPE FOR COMMAND
;
COMMSG:
	COMTYP			;USE MACRO FROM ZCPRHDR.LIB
;
	IF	SUBON		;IF SUBMIT FACILITY ENABLED ...
;
; SUBMIT FILE CONTROL BLOCK
;
SUBFCB:
	DB	1		;DISK NAME SET TO DEFAULT TO DRIVE A:
	DB	'$$$'		;FILE NAME
	DB	'     '
	SUBTYP			;USE MACRO FROM ZCPRHDR.LIB
	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
;
	ENDIF		;SUBON
;
; COMMAND FILE CONTROL BLOCK
;
	IF	EXTFCB		;MAY BE PLACED EXTERNAL TO ZCPR2
;
FCBDN	EQU	FCBADR		;DISK NAME
FCBFN	EQU	FCBDN+1		;FILE NAME
FCBFT	EQU	FCBFN+8		;FILE TYPE
FCBDM	EQU	FCBFT+7		;DISK GROUP MAP
FCBCR	EQU	FCBDM+16	;CURRENT RECORD NUMBER
;
	ELSE			;OR INTERNAL TO ZCPR2
;
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
;
	ENDIF		;EXTFCB
;

;
; LINE COUNT BUFFER
;
PAGCNT:
	DB	NLINES-2	;LINES LEFT ON PAGE

;
; CPR COMMAND NAME TABLE
;   EACH TABLE ENTRY IS COMPOSED OF THE 4-BYTE COMMAND AND 2-BYTE ADDRESS
;
CMDTBL:
	CTABLE		;DEFINE COMMAND TABLE VIA MACRO IN ZCPRHDR FILE
;
NCMNDS	EQU	($-CMDTBL)/(NCHARS+2)
;

;
;**** Section 2 ****
; ZCPR2 STARTING POINTS
;
; START ZCPR2 AND DON'T PROCESS DEFAULT COMMAND STORED IF MULTIPLE COMMANDS
; ARE NOT ALLOWED
;
CPR1:
;
	IF	NOT MULTCMD	;IF MULTIPLE COMMANDS NOT ALLOWED
;
	XRA	A		;SET END OF COMMAND LINE SO NO DEFAULT COMMAND
	STA	CMDLIN		;FIRST CHAR OF BUFFER
;
	ENDIF		;NOT MULTCMD
;
; START ZCPR2 AND POSSIBLY PROCESS DEFAULT COMMAND
;
; NOTE ON MODIFICATION BY Ron Fowler:  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
;
	IF	NOT MULTCMD	;ONLY ONE COMMAND PERMITTED
;
	LXI	H,CMDLIN	;SET PTR TO BEGINNING OF COMMAND LINE
	SHLD	NXTCHR
;
	ENDIF		;NOT MULTCMD
;
	PUSH	B
	MOV	A,C		;C=USER/DISK NUMBER (SEE LOC 4)
	RAR			;EXTRACT USER NUMBER
	RAR
	RAR
	RAR
	ANI	0FH
	STA	CURUSR		;SET USER
	CALL	SETUSR		;SET USER NUMBER
	CALL	RESET		;RESET DISK SYSTEM
;
	IF	SUBON		;IF SUBMIT FACILITY ENABLED
;
	STA	RNGSUB		;SAVE SUBMIT CLUE FROM DRIVE A:
;
	ENDIF		;SUBON
;
	POP	B
	MOV	A,C		;C=USER/DISK NUMBER (SEE LOC 4)
	ANI	0FH		;EXTRACT CURRENT DISK DRIVE
	STA	CURDR		;SET IT
	CNZ	LOGIN		;LOG IN DEFAULT DISK IF NOT ALREADY LOGGED IN
	CALL	SETUD		;SET USER/DISK FLAG
	CALL	DEFDMA		;SET DEFAULT DMA ADDRESS
;
	IF	SUBON		;CHECK FOR $$$.SUB IF SUBMIT FACILITY IS ON
;
	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
	CNZ	SEAR1
	STA	RNGSUB		;SET FLAG (0=NO $$$.SUB)
;
	ENDIF		;SUBON
;
	IF	MULTCMD
;
;  TEST FOR NEXT COMMAND IN CONT LOOP IF MULTIPLE COMMAND LINE BUFFER
;	IS ENABLED
;
CONT:
;
	ENDIF		;MULTCMD
;
	LHLD	NXTCHR		;PT TO NEXT CHARACTER TO PROCESS
	MOV	A,M		;GET IT
	CPI	3		;RESTART IF ^C
	JRZ	RESTRT
	ORA	A		;0 IF NO COMMAND LINE PRESENT
	JRNZ	RS1
;
	IF	NOT MULTCMD
;
;  TEST FOR ANY DEFAULT COMMAND BEFORE CONT LOOP IS
;	ENTERED IF MULTIPLE COMMAND LINE BUFFER IS DISABLED
;
CONT:
;
	ENDIF		;NOT MULTCMD
;
; PROMPT USER AND INPUT COMMAND LINE FROM HIM
;
RESTRT:
	LXI	SP,STACK	;RESET STACK
;
; PRINT PROMPT (DU>)
;
	CALL	CRLF		;PRINT PROMPT
;
	IF	DUPRMPT		;IF DRIVE IN PROMPT
	LDA	CURDR		;CURRENT DRIVE IS PART OF PROMPT
	ADI	'A'		;CONVERT TO ASCII A-P
	CALL	CONOUT
;
	LDA	CURUSR		;GET USER NUMBER
;
	IF	SUPRES		;IF SUPPRESSING USR # REPORT FOR USR 0
;
	ORA	A
	JRZ	RS000
;
	ENDIF		;SUPRES
;
	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
;
	ENDIF		;DUPRMPT
;
; READ INPUT LINE FROM USER OR $$$.SUB
;
RS000:
	LXI	H,CMDLIN	;SET POINTER TO FIRST CHAR IN COMMAND LINE
	SHLD	NXTCHR		;POINTER TO NEXT CHARACTER TO PROCESS
	MVI	M,0		;ZERO OUT COMMAND LINE IN CASE OF WARM BOOT
	PUSH	H		;SAVE PTR
	CALL	REDBUF		;INPUT COMMAND LINE FROM USER (OR $$$.SUB)
	POP	H		;GET PTR
	MOV	A,M		;CHECK FOR COMMENT LINE
	CPI	COMMENT		;BEGINS WITH COMMENT CHAR?
	JRZ	RESTRT		;INPUT ANOTHER LINE IF SO
	ORA	A		;NO INPUT?
	JRZ	RESTRT
;
; PROCESS INPUT LINE; HL PTS TO FIRST LETTER OF COMMAND
;
RS1:
	LXI	SP,STACK	;RESET STACK
;
	IF	MULTCMD		;MULTIPLE COMMANDS ALLOWED?
;
	MOV	A,M		;GET FIRST CHAR OF COMMAND
	CPI	CMDSEP		;IS IT A COMMAND SEPARATOR?
	JRNZ	RS2
	INX	H		;SKIP IT IF IT IS
	SHLD	NXTCHR		;SET PTR BACK
;
	ENDIF		;MULTCMD
;
; SET POINTER FOR MULTIPLE COMMAND LINE PROCESSING TO FIRST CHAR OF NEW CMND
;
RS2:
	SHLD	CMDCH1		;SET PTR TO FIRST CHAR OF NEW COMMAND LINE
;
; CAPITALIZE COMMAND LINE
;
CAPBUF:
	MOV	A,M		;CAPITALIZE COMMAND CHAR
	CALL	UCASE
	MOV	M,A
	INX	H		;PT TO NEXT CHAR
	ORA	A		;EOL?
	JRNZ	CAPBUF
	CALL	SCANER		;PARSE COMMAND NAME FROM COMMAND LINE
	JRNZ	ERROR		;ERROR IF COMMAND NAME CONTAINS A '?'
	LXI	D,RSTCPR	;PUT RETURN ADDRESS OF COMMAND
	PUSH	D		;ON THE STACK
COLON	EQU	$+1		;FLAG FOR IN-THE-CODE MODIFICATION
	MVI	A,0		;COMMAND OF THE FORM 'DU:COMMAND'?
	ORA	A		;0=NO
	JNZ	COM		;PROCESS AS COM FILE IF NOT
	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 CURRENT USER/DISK
;
; 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
	CPI	' '		;ANY CHAR?
	JZ	CONT		;CONTINUE WITH NEXT COMMAND IF NO ERROR

;
; INVALID COMMAND -- PRINT IT
;
ERROR:
	CALL	CRLF		;NEW LINE
CURTOK	EQU	$+1		;POINTER FOR IN-THE-CODE MODIFICATION
	LXI	H,0		;PT TO BEGINNING OF COMMAND LINE
ERR1:
	MOV	A,M		;GET CHAR
	CPI	' '+1		;SIMPLE '?' IF <SP> OR LESS
	JRC	ERR2
	CALL	CONOUT		;PRINT COMMAND CHAR
	INX	H		;PT TO NEXT CHAR
	JR	ERR1		;CONTINUE
ERR2:
	CALL	PRINT		;PRINT '?'
	DB	'?'+80H
ERR3:
	CALL	DLOGIN		;PANIC RESTORE OF DEFAULT USER/DISK
;
	IF	SUBON		;IF SUBMIT FACILITY IS ON
;
	CALL	SUBKIL		;TERMINATE ACTIVE $$$.SUB IF ANY
;
	ENDIF		;SUBON
;
	JMP	RESTRT		;RESTART CPR

;
; 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
	JR	CONOUT
;
CONIN:
	MVI	C,1	;INPUT CHAR
	CALL	BDOS	;GET INPUT CHAR WITH ^S PROCESSING AND ECHO
	JMP	UCASE	;CAPITALIZE
;
CONOUT:
	EXX
	MVI	C,2
OUTPUT:
	MOV	E,A
	CALL	BDOS
	EXX
	RET
;
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:
	EXX		;SAVE REGS
	MVI	C,5
	JR	OUTPUT
LC1:
	POP	PSW	;GET CHAR
	PUSH	PSW
	CALL	CONOUT	;OUTPUT TO CON:
	POP	PSW
	CPI	LF	;CHECK FOR PAGING
	RNZ
;
; 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	PAGER1		;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	PAGER1		;  PGDFLG MEANS NO PAGING, PLEASE
;
	ELSE			;IF PAGING NOT DEFAULT
;
	JRNZ	PAGER1		;  PGDFLG MEANS PLEASE PAGINATE
;
	ENDIF		;PGDFLG
;
	PUSH	B		;SAVE REG
	CALL	BIOS+9		;BIOS CONSOLE INPUT ROUTINE
	POP	B		;GET REG
	CPI	'C'-'@' 	;^C
	JZ	RSTCPR		;RESTART CPR
PAGER1:
	POP	H		;RESTORE HL
	RET
;
; READ FILE BLOCK FUNCTION
;
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 CHAR WITH MSB SET) PTED TO BY RET ADR
; START WITH <CRLF>
;
PRINTC:
	CALL	CRLF		;NEW LINE
;
PRINT:
	XTHL			;GET PTR TO STRING
	CALL	PRIN1		;PRINT STRING
	XTHL			;RESTORE HL AND RET ADR
	RET
;
; PRINT STRING (ENDING IN 0 OR BYTE WITH MSB SET) PTED TO BY HL
;
PRIN1:
	MOV	A,M		;GET NEXT BYTE
	INX	H		;PT TO NEXT BYTE
	ORA	A		;END OF STRING?
	RZ			;STRING TERMINATED BY BINARY 0
	PUSH	PSW		;SAVE FLAGS
	ANI	7FH		;MASK OUT MSB
	CALL	CONOUT		;PRINT CHAR
	POP	PSW		;GET FLAGS
	RM			;STRING TERMINATED BY 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
;
	IF	SUBON		;ENABLE ONLY IF SUBMIT FACILITY IS ENABLED
;
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
;
	ENDIF		;SUBON
;
DELETE:
	MVI	C,13H
	JR	BDOSJP	;SAVE MORE SPACE
;
;  GET/SET USER NUMBER
;
GETUSR:
	MVI	A,0FFH		;GET CURRENT USER NUMBER
SETUSR:
	MOV	E,A		;USER NUMBER IN E
	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 ****
; ZCPR2 UTILITIES
;
; SET USER/DISK FLAG TO CURRENT USER AND DEFAULT DISK
;
SETUD:
	CALL	GETUSR		;GET NUMBER OF CURRENT USER
	ANI	0FH		;MASK SURE 4 BITS
	ADD	A		;PLACE IT IN HIGH NYBBLE
	ADD	A
	ADD	A
	ADD	A
	LXI	H,CURDR		;MASK IN CURRENT DRIVE NUMBER (LOW NYBBLE)
	ORA	M		;MASK IN
	STA	UDFLAG		;SET USER/DISK NUMBER
	RET
;
; CONVERT CHAR IN A TO UPPER CASE
;
UCASE:
	ANI	7FH		;MASK OUT MSB
	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:
;
	IF	SUBON		;IF SUBMIT FACILITY IS ENABLED, CHECK FOR IT
;
	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,CHRCNT 	;COPY LAST RECORD (NEXT SUBMIT CMND) TO CHRCNT
	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,CMDLIN	;PRINT COMMAND LINE FROM $$$.SUB
	CALL	PRIN1
	CALL	BREAK		;CHECK FOR ABORT (ANY CHAR)
	RNZ			;IF NO ^C, RETURN TO CALLER AND RUN
	CALL	SUBKIL		;KILL $$$.SUB IF ABORT
	JMP	RESTRT		;RESTART CPR
;
; INPUT COMMAND LINE FROM USER CONSOLE
;
RB1:
	CALL	SUBKIL		;ERASE $$$.SUB IF PRESENT
;
	ENDIF		;SUBON
;
	MVI	A,CPRMPT	;PRINT PROMPT
	CALL	CONOUT
	MVI	C,0AH		;READ COMMAND LINE FROM USER
	LXI	D,BUFSIZ
	CALL	BDOS
;
; STORE ZERO AT END OF COMMAND LINE
;
	LXI	H,CHRCNT	;PT TO CHAR COUNT
	MOV	A,M		;GET CHAR COUNT
	INX	H		;PT TO FIRST CHAR OF COMMAND LINE
	CALL	ADDAH		;PT TO AFTER LAST CHAR OF COMMAND LINE
	MVI	M,0		;STORE ENDING ZERO
	RET
;
; CHECK FOR ANY CHAR FROM USER CONSOLE; RET W/ZERO SET IF NONE
;
BREAK:
	EXX			;SAVE REGS
	CALL	BIOS+6		;CONSOLE STATUS CHECK
	ORA	A		;SET FLAGS
	CNZ	BIOS+9		;GET INPUT CHAR WITH ^S PROCESSING
	CPI	'S'-'@'		;PAUSE IF ^S
	CZ	BIOS+9		;GET NEXT CHAR
	EXX			;RESTORE REGS
	CPI	'C'-'@'		;CHECK FOR ABORT
	RET

;
; CHECK TO SEE IF DE PTS TO DELIMITER; IF SO, RET W/ZERO FLAG SET
;
SDELM:
	LDAX	D
	ORA	A		;0=DELIMITER
	RZ
	CPI	' '+1		;DELIM IF <= <SP>
	JRC	ZERO
	CPI	'='		;'='=DELIMITER
	RZ
	CPI	5FH		;UNDERSCORE=DELIMITER
	RZ
	CPI	'.'		;'.'=DELIMITER
	RZ
	CPI	':'		;':'=DELIMITER
	RZ
	CPI	','		;','=DELIMITER
	RZ
	CPI	';'		;';'=DELIMITER
	RZ
	CPI	'<'		;'<'=DELIMITER
	RZ
	CPI	'>'		;'>'=DELIMITER
;
	IF	MULTCMD		;MULTIPLE COMMANDS ALLOWED?
;
	RZ
	CPI	CMDSEP		;COMMAND SEPARATOR
;
	ENDIF		;MULTCMD
;
	RET
ZERO:
	XRA	A	;SET ZERO FLAG
	RET

;
; ADVANCE INPUT PTR TO FIRST NON-BLANK AND FALL THROUGH TO SBLANK
;
ADVAN:
	LDED	NXTCHR	;PT TO NEXT CHAR
;
; SKIP STRING PTED TO BY DE (STRING ENDS IN 0 OR CMDSEP) UNTIL END OF STRING
;   OR NON-DELIM ENCOUNTERED (BEGINNING OF TOKEN)
;
SBLANK:
	LDAX	D	;GET CHAR
	ORA	A	;ZERO?
	RZ
;
	IF	MULTCMD	;MULTIPLE COMMANDS ALLOWED?
;
	CPI	CMDSEP	;COMMAND SEPARATOR?
	RZ
;
	ENDIF		;MULTCMD
;
	CALL	SDELM	;SKIP OVER DELIMITER
	RNZ
	INX	D	;ADVANCE TO NEXT CHAR
	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
NUM0A:
	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
	CPI	':'		;DONE IF COLON
	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			;*2
	JRC	NUMERR
	RLC			;*4
	JRC	NUMERR
	RLC			;*8
	JRC	NUMERR
	ADD	C		;*9
	JRC	NUMERR
	ADD	C		;*10
	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
;
SLOGIN:
	XRA	A		;A=0 FOR DEFAULT DISK
	STA	FCBDN		;SELECT DEFAULT DISK SINCE USER/DISK
				;  SPECIFICALLY SELECTED BY THIS ROUTINE
TEMPDR	EQU	$+1		;POINTER FOR IN-THE-CODE MODIFICATION
	MVI	A,0		;2ND BYTE (IMMEDIATE ARG) IS TEMPDR
	ORA	A		;0=CURRENT DRIVE
	JRNZ	SLOG1
	LDA	CURDR		;LOG IN CURRENT DRIVE
	INR	A		;ADD 1 FOR NEXT DCR
SLOG1:
	DCR	A		;ADJUST FOR PROPER DISK NUMBER (A=0)
	CALL	LOGIN		;LOG IN NEW DRIVE
TEMPUSR	EQU	$+1		;POINTER FOR IN-THE-CODE MODIFICATION
	MVI	A,0		;2ND BYTE IS USER TO BE SELECTED
	JMP	SETUSR		;LOG IN NEW USER

;
; CHECK FOR SPECIFIED DRIVE AND LOG IN DEFAULT DRIVE IF SPECIFIED<>DEFAULT
;
DLOGIN:
CURDR	EQU	$+1		;POINTER FOR IN-THE-CODE MODIFICATION
	MVI	A,0		;PREP TO LOG IN CURRENT DRIVE
	CALL	LOGIN		;LOGIN CURRENT DRIVE
CURUSR	EQU	$+1		;POINTER FOR IN-THE-CODE MODIFICATION
	MVI	A,0		;PREP TO LOG IN CURRENT USER NUMBER
	JMP	SETUSR		;LOG IN NEW USER

;
;  ROUTINE TO CHECK FOR A WHEEL BYTE AS NON-ZERO
;    IF WHEEL BYTE IS ZERO, THEN ABORT (POP STACK AND RETURN)
;
;
	IF	WHEEL		;WHEEL FACILITY?
;
WHLCHK:
	LDA	WHLADR		;GET WHEEL BYTE
	ORA	A		;ZERO?
	RNZ			;OK IF NOT
	JMP	ERROR		;PROCESS AS ERROR
;
	ENDIF		;WHEEL
;

;
; EXTRACT TOKEN FROM COMMAND LINE AND PLACE IT INTO FCBDN;
;   FORMAT FCBDN FCB IF TOKEN RESEMBLES FILE NAME AND TYPE (FILENAME.TYP);
;   ON INPUT, NXTCHR PTS TO CHAR AT WHICH TO START SCAN;
;   ON OUTPUT, NXTCHR PTS TO CHAR AT WHICH TO CONTINUE AND ZERO FLAG IS RESET
;     IF '?' IS IN TOKEN
;
; ENTRY POINTS:
;	SCANLOG - LOAD TOKEN INTO FIRST FCB AND LOG IN TEMP USER/DISK
;	SCANER - LOAD TOKEN INTO FIRST FCB
;	SCANX - LOAD TOKEN INTO FCB PTED TO BY HL
;
SCANLOG:
	CALL	SCANER		;DO SCAN
	PUSH	PSW		;SAVE FLAG
	CALL	SLOGIN		;LOG IN TEMPORARY USER/DISK
	POP	PSW		;GET FLAG
	RET
SCANER:
	LXI	H,FCBDN 	;POINT TO FCBDN
SCANX:
	XRA	A		;A=0
	STA	TEMPDR		;SET TEMPORARY DRIVE NUMBER TO DEFAULT
	MOV	M,A		;SET FIRST BYTE OF FCBDN AS DEFAULT DRIVE
	STA	COLON		;SET NO COLON FLAG
	LDA	CURUSR		;GET CURRENT USER
	STA	TEMPUSR		;SET TEMPUSR
	CALL	ADVAN		;SKIP TO NON-BLANK OR END OF LINE
	SDED	CURTOK		;SET PTR TO NON-BLANK OR END OF LINE
	MVI	B,11		;PREP FOR POSSIBLE SPACE FILL
	JRZ	SCAN4		;DONE IF EOL
;
;  SCAN TOKEN FOR DU: FORM, WHICH MEANS WE HAVE A USER/DISK SPECIFICATION
;    DE PTS TO NEXT CHAR IN LINE, HL PTS TO FCBDN
;
	PUSH	D		;SAVE PTR TO FIRST CHAR
	CALL	SDELM		;CHECK FOR DELIMITER AND GET FIRST CHAR
	CPI	'A'		;IN LETTER RANGE?
	JRC	SCAN1
	CPI	'P'+1		;IN LETTER RANGE?
	JRC	SCAN1A
SCAN1:
	CPI	'0'		;CHECK FOR DIGIT RANGE
	JRC	SCAN2
	CPI	'9'+1		;IN DIGIT RANGE?
	JRNC	SCAN2
SCAN1A:
	INX	D		;PT TO NEXT CHAR
	CALL	SDELM		;CHECK FOR DELIMITER; IF NOT, CHECK FOR DIGIT
	JR	SCAN1
SCAN2:
	POP	D		;RESTORE PTR TO FIRST CHAR
	CPI	':'		;WAS DELIMITER A COLON?
	JRNZ	SCAN3		;DONE IF NO COLON
	STA	COLON		;SET COLON FOUND
;
;  SCAN FOR AND EXTRACT USER/DISK INFO
;    ON ENTRY, HL PTS TO FCBDN, DE PTS TO FIRST CHAR, AND A CONTAINS FIRST CHAR
;
	LDAX	D		;GET FIRST CHAR
	CPI	'A'		;CONVERT POSSIBLE DRIVE SPEC TO NUMBER
	JRC	SUD1		;IF LESS THAN 'A', MUST BE DIGIT
;
;  SET DISK NUMBER (A=1)
;
	SUI	'A'-1		;CONVERT DRIVE NUMBER TO 1-16
	CPI	MAXDISK+1	;WITHIN RANGE?
	JNC	ERROR		;INVALID DISK NUMBER
	STA	TEMPDR		;SET TEMPORARY DRIVE NUMBER
	MOV	M,A		;SET FCBDN
	INX	D		;PT TO NEXT CHAR
	LDAX	D		;SEE IF IT IS A COLON (:)
	CPI	':'
	JRZ	SUD2		;DONE IF NO USER NUMBER (IT IS A COLON)
;
;  SET USER NUMBER
;
SUD1:
	PUSH	H		;SAVE PTR TO FCBDN
	XCHG			;HL PTS TO FIRST DIGIT
	CALL	NUM0A		;GET NUMBER
	XCHG			;DE PTS TO TERMINATING COLON
	POP	H		;GET PTR TO FCBDN
	CPI	MAXUSR+1	;WITHIN LIMIT?
	JNC	ERROR
;
	IF	USERON		;ALLOW USER CHANGE ONLY IF USER IS ALLOWED
;
	STA	TEMPUSR		;SAVE USER NUMBER
;
	ENDIF
;
SUD2:
	INX	D		;PT TO CHAR AFTER COLON
;
; EXTRACT FILENAME FROM POSSIBLE FILENAME.TYP
;   DE PTS TO NEXT CHAR TO PROCESS, HL PTS TO FCBDN
;
SCAN3:
	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
	LDAX	D		;GET LAST CHAR WHICH STOPPED SCAN
	CPI	'.'		;IF (DE) DELIMITER IS A '.', WE HAVE A TYPE
	JRNZ	SCAN4		;FILL FILE TYPE BYTES WITH <SP>
	INX	D		;PT TO CHAR IN COMMAND LINE AFTER '.'
	CALL	SCANF		;FILL FCB FILE TYPE
	JR	SCAN5		;SKIP TO NEXT PROCESSING
SCAN4:
	CALL	SCANF4		;SPACE FILL
;
; FILL IN EX, S1, S2, AND RC WITH ZEROES
;
SCAN5:
	MVI	B,4		;4 BYTES
	XRA	A		;A=0
	CALL	SCANF5		;FILL WITH ZEROES
;
; SCAN COMPLETE -- DE PTS TO DELIMITER BYTE AFTER TOKEN
;
	SDED	NXTCHR
;
; SET ZERO FLAG TO INDICATE PRESENCE OF '?' IN FILENAME.TYP
;
QMCNT	EQU	$+1		;POINTER FOR IN-THE-CODE MODIFICATION
	MVI	A,0		;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 FCB AND DON'T ADVANCE DE IF SO
	CALL	SCQ		;SCANNER COUNT QUESTION MARKS
	JR	SCANF2
SCANF1:
	MOV	M,A		;STORE FILENAME CHAR IN FCB
	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:
	MVI	A,' '		;<SP> FILL
SCANF5:
	INX	H		;PT TO NEXT BYTE IN FCB
	MOV	M,A		;FILL WITH BYTE IN A
	DJNZ	SCANF5
	RET
;
;  INCREMENT QUESTION MARK COUNT FOR SCANNER
;    THIS ROUTINE INCREMENTS THE COUNT OF THE NUMBER OF QUESTION MARKS IN
;    THE CURRENT FCB ENTRY
;
SCQ:
	PUSH	H		;SAVE HL
	LXI	H,QMCNT		;GET COUNT
	INR	M		;INCREMENT
	POP	H		;GET HL
	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
	MOV	A,C		;CHECK NUMBER OF COMMANDS
	ORA	A		;IF NONE, THEN ABORT
	JRZ	CMS5
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
CMS5:
	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
;Notes:
;	The flag SYSFLG defines the letter used to display both DIR and
;		SYS files (A in the above Forms section)
;	The flag SOFLG defines the letter used to display only the SYS
;		files (S in the above Forms section)
;	The flag WIDE determines if the file names are spaced further
;		apart (WIDE=TRUE) for 80-col screens
;	The flag FENCE defines the character used to separate the file
;		names
;
	IF	DIRON		;DIR ENABLED
;
DIR:
	CALL	SCANLOG		;EXTRACT POSSIBLE D:FILENAME.TYP TOKEN AND LOG
	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,80H		;PREPARE FOR DIR-ONLY SELECTION
	JRZ	DIRDN		;THERE IS NO FLAG, SO DIR ONLY
	MVI	B,1		;SET FOR BOTH DIR AND SYS FILES
	CPI	SYSFLG		;SYSTEM AND DIR FLAG SPECIFIER?
	JRZ	GOTFLG		;GOT SYSTEM SPECIFIER
	CPI	SOFLG		;SYS ONLY?
	JRNZ	DIRDN
	DCR	B		;B=0 FOR SYS FILES ONLY
GOTFLG:
	INX	D		;PT TO CHAR AFTER FLAG
DIRDN:
	SDED	NXTCHR		;SET PTR FOR NEXT PASS
				;DROP INTO DIRPR TO PRINT DIRECTORY
				; THEN RESTART CPR
;
	ENDIF			;DIRON
;
; DIRECTORY PRINT ROUTINE; ON ENTRY, B REG IS SET AS FOLLOWS:
;	0 FOR ONLY SYSTEM FILES, 80H FOR ONLY DIR FILES, 1 FOR BOTH
;
	IF	DIRON OR ERAON
;
DIRPR:
	MOV	A,B		;GET FLAG
	STA	SYSTST		;SET SYSTEM TEST FLAG
	MVI	E,0		;SET COLUMN COUNTER TO ZERO
	PUSH	D		;SAVE COLUMN COUNTER (E)
	CALL	SEARF		;SEARCH FOR SPECIFIED FILE (FIRST OCCURRANCE)
	JRNZ	DIR3
	CALL	PRNNF		;PRINT NO FILE MSG; REG A NOT CHANGED
	XRA	A		;SET ZERO FLAG
	POP	D		;RESTORE DE
	RET
;
; ENTRY SELECTION LOOP; ON ENTRY, A=OFFSET FROM SEARF OR SEARN
;
DIR3:
	CALL	GETSBIT		;GET AND TEST FOR TYPE OF FILES
	JRZ	DIR6
	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
	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
;
	ELSE
;
	DB	' '		;SPACE
	DB	FENCE		;THEN FENCE CHAR
	DB	' '+80H		;THEN SPACE
;
	ENDIF			;WIDE
;
DIR5:
	MVI	B,01H		;PT TO 1ST BYTE OF FILE NAME
	MOV	A,B		;A=OFFSET
	CALL	DIRPTR		;HL NOW PTS TO 1ST BYTE OF FILE NAME
	CALL	PRFN		;PRINT FILE NAME
DIR6:
	CALL	BREAK		;CHECK FOR ABORT
	JRZ	DIR7
	CALL	SEARN		;SEARCH FOR NEXT FILE
	JRNZ	DIR3		;CONTINUE IF FILE FOUND
DIR7:
	POP	D		;RESTORE STACK
	MVI	A,0FFH		;SET NZ FLAG
	ORA	A
	RET
;
	ENDIF			;DIRON OR ERAON
;
;  PRINT FILE NAME PTED TO BY HL
;
PRFN:
	MVI	B,8	;8 CHARS
	CALL	PRFN1
	MVI	A,'.'	;DOT
	CALL	CONOUT
	MVI	B,3	;3 CHARS
PRFN1:
	MOV	A,M	; GET CHAR
	INX	H	; PT TO NEXT
	CALL	CONOUT	; PRINT CHAR
	DCR	B	; COUNT DOWN
	JRNZ	PRFN1
	RET
;
; AFTER A SEARCH, RETURN NZ SET IF DESIRED TYPE OF FILE FOUND, Z IF NOT
;   THIS ALGORITHM LOOKS AT THE SYSTEM BIT OF THE LOCATED FILE; THIS
;   BIT IS SET TO 1 IF THE FILE IS A SYSTEM FILE AND 0 IF NOT A SYSTEM
;   FILE.  THE FOLLOWING EXCLUSIVE OR MASKS ARE APPLIED TO RETURN Z OR NZ
;   AS REQUIRED BY THE CALLING PROGRAM:
;
;	SYSTEM BYTE: X 0 0 0  0 0 0 0   (AFTER 80H MASK, X=1 IF SYS, 0 IF DIR)
;
;	SYS-ONLY   : 0 0 0 0  0 0 0 0   (XOR 0 = 0 if X=0, = 80H if X=1)
;	DIR-ONLY   : 1 0 0 0  0 0 0 0   (XOR 80H = 80h if X=0, = 0 if X=1)
;	BOTH       : 0 0 0 0  0 0 0 1   (XOR 1 = 81H or 1H, NZ in both cases)
;
GETSBIT:
	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		;A=SYSTEM BYTE
	ANI	80H		;LOOK AT ONLY SYSTEM BIT
SYSTST	EQU	$+1		;IN-THE-CODE VARIABLE
	XRI	0		; IF SYSTST=0, SYS ONLY; IF SYSTST=80H, DIR
				; ONLY; IF SYSTST=1, BOTH SYS AND DIR
	RET			;NZ IF OK, Z IF NOT OK
;
; 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
;	ERA <afn> V	Erase Specified files and print their names, but ask
;				for verification before Erase is done
;Notes:
;	Several Key Flags affect this command:
;		ERAV - If TRUE, the V option is enabled, and the character
;			which turns it on (the V) is defined by ERDFLG
;		ERAOK - If TRUE, the OK? prompt is enabled
;	If ERAOK is FALSE, the verification feature is disabled regardless
;		of what value ERAV has
;	If ERAOK is TRUE, then:
;		If ERAV is TRUE, verification is requested only if the V
;			flag (actual letter defined by ERDFLG) is in the
;			command line
;		If ERAV is FALSE, verification is always requested, and a
;			V flag in the command line will cause an error
;			message to be printed (V?) after the ERA is completed
;
	IF	ERAON		;ERA ENABLED?
;
ERA:
;
	IF	WERA		;WHEEL FACILITY ENABLED?
;
	CALL	WHLCHK		;CHECK FOR IT
;
	ENDIF		;WERA
;
	CALL	SCANLOG		;PARSE FILE SPECIFICATION AND LOG IN USER/DISK
;
	IF	ERAV AND ERAOK	;V FLAG AND OK? ENABLED?
;
	CALL	ADVAN		;GET ERAFLG IF IT'S THERE
	STA	ERAFLG		;SAVE IT AS A FLAG
	JRZ	ERA1		;JUMP IF INPUT ENDED
	INX	D		;PUT NEW BUF POINTER
ERA1:
	XCHG			;PUT PTR IN HL
	SHLD	NXTCHR		;SET PTR TO BYTE FOR NEXT COMMAND PROCESSING
;
	ENDIF			;ERAV
;
	MVI	B,1		;DISPLAY ALL MATCHING FILES
	CALL	DIRPR		;PRINT DIRECTORY OF ERASED FILES
	RZ			;ABORT IF NO FILES
;
	IF	ERAOK		;PRINT PROMPT
;
	IF	ERAV		;TEST VERIFY FLAG
;
ERAFLG	EQU	$+1		;ADDRESS OF FLAG
	MVI	A,0		;2ND BYTE IS FLAG
	CPI	ERDFLG		;IS IT A VERIFY OPTION?
	JRNZ	ERA2		;SKIP PROMPT IF IT IS NOT
;
	ENDIF			;ERAV
;
	CALL	PRINTC
	DB	'OK to Erase','?'+80H
	CALL	CONIN		;GET REPLY
	CPI	'Y'		;YES?
	RNZ			;ABORT IF NOT
;
	ENDIF			;ERAOK
;
ERA2:
	LXI	D,FCBDN 	;DELETE FILE SPECIFIED
	CALL	DELETE
	RET			;REENTER CPR
;
	ENDIF			;ERAON
;
;Section 5C
;Command: LIST
;Function:  Print out specified file on the LST: Device
;Forms:
;	LIST <ufn>	Print file (NO Paging)
;Notes:
;	The flags which apply to TYPE do not take effect with LIST
;
	IF	LTON		;LIST AND TYPE ENABLED?
;
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	
;Notes:
;	The flag PGDFLG defines the letter which toggles the paging
;		facility (P in the forms section above)
;	The flag PGDFLT determines if TYPE is to page by default
;		(PGDFLT=TRUE if TYPE pages by default); combined with
;		PGDFLG, the following events occur --
;			If PGDFLT = TRUE, PGDFLG turns OFF paging
;			If PGDFLT = FALSE, PGDFLG turns ON paging
;
TYPE:
	XRA	A		;TURN OFF PRINTER FLAG
;
; ENTRY POINT FOR CPR LIST FUNCTION (LIST)
;
TYPE0:
	STA	PRFLG		;SET FLAG
;
	IF	WLT	;WHEEL ON?
;
	CALL	WHLCHK		;CHECK WHEEL BYTE
;
	ENDIF		;WLT
;
	CALL	SCANLOG		;EXTRACT FILENAME.TYP TOKEN AND LOG USER/DISK
	JNZ	ERROR		;ERROR IF ANY QUESTION MARKS
	CALL	ADVAN		;GET PGDFLG IF IT'S THERE
	STA	PGFLG		;SAVE IT AS A FLAG
	JRZ	TYPE1		;JUMP IF INPUT ENDED
	INX	D		;PUT NEW BUF POINTER
TYPE1:
	SDED	NXTCHR		;SET PTR TO BYTE FOR NEXT COMMAND PROCESSING
	CALL	OPENF		;OPEN SELECTED FILE
	JZ	ERROR		;ABORT IF ERROR
	CALL	CRLF		;NEW LINE
	MVI	A,NLINES-1	;SET LINE COUNT
	STA	PAGCNT
	LXI	B,080H		;SET CHAR POSITION AND TAB COUNT
				;  (B=0=TAB, C=080H=CHAR POSITION)
;
;  MAIN LOOP FOR LOADING NEXT BLOCK
;
TYPE2:
	MOV	A,C		;GET CHAR COUNT
	CPI	80H
	JRC	TYPE3
	PUSH	H		;READ NEXT BLOCK
	PUSH	B
	CALL	READF
	POP	B
	POP	H
	JRNZ	TYPE7		;ERROR?
	MVI	C,0		;SET CHAR COUNT
	LXI	H,TBUFF		;PT TO FIRST CHAR
;
;  MAIN LOOP FOR PRINTING CHARS IN TBUFF
;
TYPE3:
	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	TYPE4
	CPI	LF		;RESET TAB COUNT?
	JRZ	TYPE4
	CPI	TAB		;TAB?
	JRZ	TYPE5
;
;  OUTPUT CHAR AND INCREMENT CHAR COUNT
;
	CALL	LCOUT		;OUTPUT CHAR
	INR	B		;INCREMENT TAB COUNT
	JR	TYPE6
;
;  OUTPUT <CR> OR <LF> AND RESET TAB COUNT
;
TYPE4:
	CALL	LCOUT		;OUTPUT <CR> OR <LF>
	MVI	B,0		;RESET TAB COUNTER
	JR	TYPE6
;
;  TABULATE
;
TYPE5:
	MVI	A,' '		;<SP>
	CALL	LCOUT
	INR	B		;INCR POS COUNT
	MOV	A,B
	ANI	7
	JRNZ	TYPE5
;
; CONTINUE PROCESSING
;
TYPE6:
	INR	C		;INCREMENT CHAR COUNT
	INX	H		;PT TO NEXT CHAR
	CALL	BREAK		;CHECK FOR ABORT
	RZ			;RESTART IF SO
	JR	TYPE2
TYPE7:
	DCR	A		;NO ERROR?
	RZ			;RESTART CPR
	JMP	ERROR
;
	ENDIF			;LTON
;
;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
;Notes:
;	The MULTCMD flag (Multiple Commands Allowed) expands the code slightly,
;		but is required to support multiple commands with SAVE
;	The SECTFLG defines the letter which indicates a sector count
;		(S in the Forms section above)
;
	IF	SAVEON		;SAVE ENABLED?
;
SAVE:
;
	IF	WSAVE	;WHEEL FACILITY?
;
	CALL	WHLCHK		;CHECK FOR WHEEL BYTE
;
	ENDIF		;WSAVE
;
	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	NXTCHR		;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:
	JMP	DEFDMA		;SET DMA TO 0080 AND RESTART CPR
;
	ENDIF			;SAVEON
;
; Test File in FCB for existence, ask user to delete if so, and abort if he
;  choses not to
;
	IF	SAVEON OR RENON	;FOR SAVE AND REN FUNCTIONS
;
EXTEST:
	CALL	SCANLOG		;EXTRACT FILE NAME AND LOG IN USER/DISK
	JNZ	ERROR		;'?' IS NOT PERMITTED
	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	'Erase',' '+80H
	LXI	H,FCBFN		;PT TO FILE NAME FIELD
	CALL	PRFN		;PRINT IT
	MVI	A,'?'		;PRINT QUESTION
	CALL	CONOUT
	CALL	CONIN		;GET RESPONSE
	POP	D		;GET PTR TO FCB
	CPI	'Y'		;KEY ON YES
	JNZ	ERR3		;RESTART AS ERROR IF NO
	PUSH	D		;SAVE PTR TO FCB
	CALL	DELETE		;DELETE FILE
	POP	D		;GET PTR TO FCB
	RET
;
	ENDIF			;SAVEON OR RENON
;
;Section 5F
;Command: REN
;Function:  To change the name of an existing file
;Forms:
;	REN <New ufn>=<Old ufn>	Perform function
;
	IF	RENON		;REN ENABLED?
;
REN:
;
	IF	WREN		;WHEEL FACILITY?
;
	CALL	WHLCHK		;CHECK FOR WHEEL BYTE
;
	ENDIF		;WREN
;
	CALL	EXTEST		;TEST FOR FILE EXISTENCE AND RETURN
				; IF FILE DOESN'T EXIST; ABORT IF IT DOES
	LDA	TEMPDR		;SAVE SELECTED 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 TO NEXT CHARACTER (NON-DELIM)
	JRZ	REN4		;ERROR IF NONE
;
;  PERFORM RENAME FUNCTION
;
REN1:
	SDED	NXTCHR		;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 SELECTED DRIVE
	MOV	A,M		;DEFAULT?
	ORA	A
	JRZ	REN2
	CMP	B		;CHECK FOR DRIVE ERROR (LIKE REN A:T=B:S)
	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	ERROR
;
	ENDIF			;RENON
;
RSTJMP:
	JMP	RCPRNL		;RESTART CPR
;
;Section 5G
;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	JUMPON		;JUMP ENABLED?
;
JUMP:
;
	IF	WJUMP	;WHEEL FACILITY?
;
	CALL	WHLCHK		;CHECK FOR WHEEL BYTE
;
	ENDIF		;WJUMP
;
	CALL	HEXNUM		;GET LOAD ADDRESS IN HL
	JR	CALLPROG	;PERFORM CALL
;
	ENDIF			;JUMPON
;
;Section 5H
;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	GOON		;GO ENABLED?
;
GO:
;
	IF	WGO	;WHEEL FACILITY?
;
	CALL	WHLCHK		;CHECK FOR WHEEL BYTE
;
	ENDIF		;WGO
;
	LXI	H,TPA		;Always to TPA
	JR	CALLPROG	;Perform call
;
	ENDIF			;GOON
;
;Section 5I
;Command: COM file processing
;Function:  To load the specified COM file from disk and execute it
;Forms:  <command line>
;Notes:
;	COM files are processed as follows --
;		1. File name buffers are initialized and a preliminary
;			error check is done
;		2. MLOAD is used to search for the file along the Path
;			and load it into the TPA
;		3. CALLPROG is used to set up the buffers to be used by
;			the transient (FCB at 5CH, FCB at 6CH, BUFF at 80H)
;			and run the program
;	The flag MULTCMD comes into play frequently here; it mainly serves
;		to save space if MULTCMD is FALSE and enables Multiple
;		Commands on the same line if MULTCMD is TRUE
;
COM:
	LDA	FCBFN		;ANY COMMAND?
	CPI	' '		;' ' MEANS COMMAND WAS 'D:' TO SWITCH
	JRNZ	COM1		;NOT <SP>, SO MUST BE TRANSIENT OR ERROR
;
;  ENTRY POINT TO SELECT USER/DISK
;
;
	IF	WDU	;WHEEL FACILITY?
;
	CALL	WHLCHK		;CHECK FOR WHEEL BYTE
;
	ENDIF		;WDU
;
	LDA	COLON		;LOOK FOR COLON FLAG
	ORA	A		;IF ZERO, JUST BLANK
	RZ			;RETURN TO MAIN ROUTINE IF NOTHING SPECIFIED
;
;  COMMAND IS DU:, SO LOG IN USER/DISK
;
	LDA	TEMPUSR		;GET SELECTED USER
	CPI	10H		;MAKE SURE 4 BITS
	JNC	ERROR		;RANGE ERROR?
	STA	CURUSR		;SET CURRENT USER
	CALL	SLOGIN		;LOG IN USER/DISK AS IF TEMPORARILY
;
;  NOW, MAKE LOGIN PERMANENT
;
	LDA	TEMPDR		;GET SELECTED DRIVE
	ORA	A		;IF 0 (DEFAULT), NO CHANGE
	JRZ	COM0
	DCR	A		;ADJUST FOR LOG IN
	STA	CURDR		;SET CURRENT DRIVE
COM0:
	JMP	SETUD		;SET CURRENT USER/DISK AND RET THRU DLOGIN
;
;  PROCESS COMMAND
;
COM1:
	LXI	D,FCBFT		;PT TO FILE TYPE
	LDAX	D		;GET FIRST CHAR OF FILE TYPE
	CPI	' '		;MUST BE BLANK, OR ERROR
	JNZ	ERROR
	LXI	H,COMMSG	;PLACE DEFAULT FILE TYPE (COM) INTO FCB
	LXI	B,3		;3 BYTES
	LDIR
	LXI	H,TPA		;SET EXECUTION/LOAD ADDRESS
	PUSH	H		;SAVE FOR EXECUTION
;
	IF	CMDRUN		;COMMAND RUN FACILITY AVAILABLE?
;
	MVI	A,0FFH		;USE IT IF AVAILABLE
;
	ENDIF		;CMDRUN
;
	CALL	MLOAD		;LOAD MEMORY WITH FILE SPECIFIED IN CMD LINE
	POP	H		;GET EXECUTION ADDRESS
;
; 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	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 FCB+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
CMDCH1	EQU	$+1		;IN-THE-CODE BUFFER FOR ADDRESS OF 1ST CHAR
	LXI	H,CMDLIN
CALLP1:
	MOV	A,M		;SKIP TO END OF 2ND FILE NAME
	ORA	A		;END OF LINE?
	JRZ	CALLP2
;
	IF	MULTCMD		;MULTIPLE COMMANDS ALLOWED?
;
	CPI	CMDSEP		;COMMAND SEPARATOR?
	JRZ	CALLP2
;
	ENDIF		;MULTCMD
;
	CPI	' '		;END OF TOKEN?
	JRZ	CALLP2
	INX	H
	JR	CALLP1
;
; LOAD COMMAND LINE INTO TBUFF
;
CALLP2:
	MVI	B,0		;SET CHAR COUNT
	LXI	D,TBUFF+1	;PT TO CHAR POS
CALLP3:
	MOV	A,M		;COPY COMMAND LINE TO TBUFF
	STAX	D
	ORA	A		;DONE IF ZERO
	JRZ	CALLP5
;
	IF	MULTCMD		;MULTIPLE COMMANDS ALLOWED?
;
	CPI	CMDSEP		;DONE IF COMMAND SEPARATOR
	JRZ	CALLP4
;
	ENDIF		;MULTCMD
;
	INR	B		;INCR CHAR COUNT
	INX	H		;PT TO NEXT
	INX	D
	JR	CALLP3
;
	IF	MULTCMD		;MULTIPLE COMMANDS ALLOWED?
;
CALLP4:
	XRA	A		;STORE ENDING ZERO
	STAX	D		;INSTEAD OF CMDSEP
;
	ENDIF		;MULTCMD
;
; RUN LOADED TRANSIENT PROGRAM
;
CALLP5:
;
	IF	MULTCMD		;MULTIPLE COMMANDS ALLOWED?
;
	SHLD	NXTCHR		;SAVE PTR TO CONTINUE PROCESSING
;
	ENDIF		;MULTCMD
;
	MOV	A,B		;SAVE CHAR COUNT
	STA	TBUFF
	CALL	CRLF		;NEW LINE
	CALL	DEFDMA		;SET DMA TO 0080
;
; 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
	CALL	DLOGIN		;LOGIN CURRENT USER/DISK
	JMP	CONT		;RESTART CPR AND CONTINUE COMMAND PROCESSING
;
;Section 5J
;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	GETON		;GET ENABLED?
;
GET:
;
	IF	WGET	;WHEEL ON?
;
	CALL	WHLCHK		;CHECK WHEEL BYTE
;
	ENDIF		;WGET
;
	CALL	HEXNUM		;GET LOAD ADDRESS IN HL
	PUSH	H		;SAVE ADDRESS
	CALL	SCANER		;GET FILE NAME
	POP	H		;RESTORE ADDRESS
	JNZ	ERROR		;MUST BE UNAMBIGUOUS
;
; FALL THRU TO MLOAD
;
	IF	CMDRUN		;COMMAND RUN FACILITY AVAILABLE?
;
	XRA	A		;NO CMDRUN IF FACILITY IS THERE
;
	ENDIF			;CMDRUN
;
	ENDIF			;GETON

;
;  MEMORY LOAD SUBROUTINE
;
; LOAD MEMORY WITH THE FILE WHOSE NAME IS SPECIFIED IN THE COMMAND LINE
;   ON INPUT, HL CONTAINS STARTING ADDRESS TO LOAD
;
;	EXIT POINTS ARE A RETURN AND LOG IN CURRENT USER/DISK IF NO ERROR,
; A JMP TO ERROR IF COM FILE NOT FOUND OR A MESSAGE AND ABORT IF MEMORY FULL
;
MLOAD:
;
	IF	CMDRUN	;CMDRUN FACILITY?
;
	STA	CRFLAG	;SAVE FLAG
;
	ENDIF		;CMDRUN
;
	SHLD	LOADADR		;SET LOAD ADDRESS
;
;   MLA is a reentry point for a non-standard CP/M Modification
; The PATH command-search is implemented by this routine
;
MLA:
;
	IF	DRVPREFIX	;IF DRIVE PREFIX ALLOWED ...
;
	MVI	A,DRVPFATT	;SET FLAG PER USER SPEC FOR SYS/NON-SYS
	STA	SYSTST		;TEST FLAG IN GETSBIT
	CALL	SLOGIN		;LOOK UNDER TEMPORARY USER/DISK
	CALL	SEARF		;LOOK FOR FILE
MLARUN:
	LXI	H,PATH		;PT TO PATH FOR FAILURE POSSIBILITY
	JRNZ	MLA4		;FOUND IT -- LOAD IT AND RUN
;
	ELSE			;NO DRIVE PREFIX
;
MLARUN:
	LXI	H,PATH		;POINT TO PATH
;
	ENDIF		;DRVPREFIX
;
MLA0:
	MOV	A,M		;GET DRIVE
	ORA	A		;0=DONE=COMMAND NOT FOUND
;
	IF	CMDRUN		;COMMAND RUN FACILITY
;
	JRNZ	NOCRUN		;NOT READY FOR CMD RUN YET
CRFLAG	EQU	$+1		;POINTER FOR IN-THE-CODE MODIFICATION
	MVI	A,0		;CHECK CRFLAG
	ORA	A		;0=NO
	JZ	ERROR		;PROCESS AS ERROR IF CMD RUN EXHAUSTED
;
	IF	ROOTONLY	;ONLY LOOK FOR EXT COMMAND PROCESSOR AT ROOT
;
	PUSH	H
;
	ENDIF		;ROOTONLY
;
	XRA	A		;DO NOT REENTER THIS CODE
	STA	CRFLAG		;SET ZERO FOR NO ENTRY
	LHLD	CMDCH1		;GET PTR TO FIRST CHAR OF COMMAND
	DCX	H		;PT TO CHAR COUNT
	MVI	M,' '		;STORE LEADING SPACE
	SHLD	CMDCH1		;POINT TO LEADING SPACE AS FIRST CHAR
	SHLD	NXTCHR		;NEXT CHAR IS FIRST CHAR OF COMMAND
	LXI	H,CFCB		;SET CFCB AS COMMAND
	LXI	D,FCBDN		;... BY COPYING IT INTO FCBDN
	LXI	B,12		;ONLY 12 BYTES REQUIRED
	LDIR
;
	IF	ROOTONLY	;LOOK FOR EXT COMMAND PROCESSOR AT ROOT ONLY?
;
	JR	MLA3RT
;
	ELSE			;FOLLOW PATH LOOKING FOR EXT COMMAND PROCESSOR
;
	XRA	A		;A=0
	JR	MLARUN		;NOW TRY THE RUN
;
	ENDIF		;ROOTONLY
;
CFCB:
	CMDFCB			;FCB DEFINING INITIAL COMMAND
NOCRUN:
;
	ELSE
;
	JZ	ERROR		;TRANSIENT LOAD ERROR -- FILE NOT FOUND
;
	ENDIF		;CMDRUN
;
; LOOK FOR COMMAND IN DIRECTORY PTED TO BY HL; DRIVE IN A
;
	CPI	CURIND		;CURRENT DRIVE SPECIFIED?
	JRNZ	MLA1		;SKIP DEFAULT DRIVE SELECTION IF SO
	LDA	CURDR		;GET CURRENT DRIVE
	INR	A		;SET A=1
MLA1:
	STA	TEMPDR		;SELECT DIFFERENT DRIVE IF NOT CURRENT
	MVI	A,1		;PREPARE TO ACCEPT BOTH SYSTEM AND DIR FILES
	STA	SYSTST		;TEST FLAG IS 1 FOR BOTH
	INX	H		;PT TO USER NUMBER
	MOV	A,M		;GET USER NUMBER
	INX	H		;PT TO NEXT ENTRY IN PATH
	PUSH	H		;SAVE PTR
	ANI	7FH		;MASK OUT SYSTEM BIT
	CPI	CURIND		;CURRENT USER SPECIFIED?
	JRNZ	MLA2		;DO NOT SELECT CURRENT USER IF SO
	LDA	CURUSR		;GET CURRENT USER NUMBER
MLA2:
	STA	TEMPUSR		;SET TEMPORARY USER NUMBER
	CMA			;FLIP BITS SO SYSTEM BIT IS 0 IF SYS-ONLY
	ANI	80H		;MASK FOR ONLY NOT OF SYSTEM BIT TO SHOW
	JRNZ	MLA3		;DON'T SET FLAG IS ORIGINALLY SYSTEM BIT=0
	STA	SYSTST		;TEST FLAG IS 0 FOR SYS-ONLY, 1 FOR BOTH
MLA3:
	CALL	SLOGIN		;LOG IN PATH-SPECIFIED USER/DISK
MLA3RT:
	CALL	SEARF		;LOOK FOR FILE
	POP	H		;GET PTR TO NEXT PATH ENTRY
	JRZ	MLA0		;CONTINUE PATH SEARCH IF SEARCH FAILED
				;LOAD IF SEARCH SUCCEEDED
;
; FILE FOUND -- PERFORM SYSTEM TEST AND PROCEED IF APPROVED
;
MLA4:
	PUSH	H		;SAVE PTR
	CALL	GETSBIT		;CHECK SYSTEM BIT
	POP	H		;GET PTR
	JRZ	MLA0		;CONTINUE IF NO MATCH
	CALL	OPENF		;OPEN FILE FOR INPUT
LOADADR	EQU	$+1		;MEMORY LOAD ADDRESS (IN-LINE CODE MOD)
	LXI	H,TPA		;SET START ADDRESS OF MEMORY LOAD
MLA5:
	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	MLA6		;READ ERROR OR EOF?
	LXI	D,128		;MOVE 128 BYTES PER SECTOR
	DAD	D		;PT TO NEXT SECTOR IN HL
	JR	MLA5
;
MLA6:
	DCR	A		;LOAD COMPLETE
	JZ	DLOGIN		;OK IF ZERO, ELSE FALL THRU TO PRNLE

;
; LOAD ERROR
;
PRNLE:
	CALL	PRINTC
	DB	'Ful','l'+80H
	CALL	DLOGIN		;RESTORE CURRENT USER/DISK
	JMP	RESTRT		;RESTART ZCPR

;*****

;
;  DEFAULT PATH USED FOR PATH COMMAND-SEARCH
;
	IF	INTPATH		;USE THIS PATH?
;
PATH:
	IPATH			;PATH DEFINED IN ZCPRHDR.LIB
;
	ENDIF		;INTPATH

;*****
	IF	INTSTACK	;INTERNAL STACK
;
;  STACK AREA
;
	DS	48		;STACK AREA
STACK	EQU	$		;TOP OF STACK
;
	ENDIF		;INTSTACK
;

;
;	The following will cause an error message to appear if
; the size of ZCPR2 is over 2K bytes.
;
	IF	($ GT CPRLOC+800H)
ZCPR2ER	EQU	NOVALUE		;ZCPR2 IS LARGER THAN 2K BYTES
	ENDIF

	END

