;
;  PROGRAM:  ZEX
;  AUTHOR:  RICHARD CONN (DERIVED FROM EX, WHICH WAS WRITTEN BY SOMEONE ELSE)
;  VERSION:  1.3
;  DATE:  6 JAN 83
;  PREVIOUS VERSIONS:  1.0 (12 NOV 82), 1.1 (8 DEC 82), 1.2 (3 JAN 83)
;
VERS	EQU	13

;
;   ZEX 1.0 -- EX 1.2.1 implemented for ZCPR2 by Richard Conn
;		DATE:  12 NOV 82
;	Extensions to EX are:
;		Multiple command buffer is preserved, and any commands
;			following the ZEX command are executed after
;			the ZEX command file is completed (ZEX T;DIR
;			will execute commands in T.SUB and then run DIR)
;		ZCPR2 Search Path is following when looking for the
;			file specified to ZEX
;		Command File Type may be SUB or ZEX
;		Added ^* form to simply ring the bell
;		Added ^/ form to act like ^? but ring bell periodically
;		Added ^" form to allow user input in the middle of a
;			command file operation
;		Major rewrite of EX to improve readability and to impose
;			a structured organization on the code for maintenance
;			purposes
;		Major change in the abort system so that the multiple command
;			line buffer of ZCPR2 will be properly cleared on
;			abort; without this change, ZEX would crash the system
;			in attempting to abort out of the ^/ and ^? forms;
;			EX will probably always crash a ZCPR2 system with
;			multiple commands enabled if an abort from ^? is
;			attempted
;
;   EX12.ASM - An enhanced version of EXEC and EX.
;
;   START   05-09-82
;
;   DATE    08-11-82  *LAST MAJOR CHANGE
;
;   HISTORY:
;
;   ZEX 1.0 11-12-82  modify for use under ZCPR2; main change is to place
;			rest of multiple command line at end of SUB file
;
;   1.2.1   09-16-82  fix for MBASIC execution under EX 1.2 .
;
;   1.2     08-11-82  added '^:' EX runtime re-execute logic function,
;			    '^?' EX runtime wait for carriage return,
;			    logic to prevent input/EX buffer overlap,
;			    logic to insure (Xsub Already Present),
;			    logic to prevent EX runtime recursion loop,
;			and prompt character logic	   [Larry Steeger]
;
;   1.1     08-06-82  added ';;' EX comment's support,
;			    '^.' print suppression function,
;			    '^<...^>' immediate display support,
;			    '^#' EX message suppression function,
;			    '^$' default parameter support,
;			and '^|' cr,lf generation function [Larry Steeger]
;
;   1.0     08-03-82  corrected $^ error and ^<lowercase> error [Larry Steeger]
;
;   ?	    06-19-82  added missing TRUE and FALSE equates [Ron Fowler]
;
;   ?	    05-17-82  corrected last cold boot no active message
;

;
;	EX12.COM IS AN ENHANCEMENT OF EXEC.COM AND EX.COM
;
;	OPTIONS:
;
;	EX <subfile> <parameters> cr
;
;	EX cr
;
;	 ^<?> WILL GIVE CONTROL CHARACTER <?>
;
;	 | WILL BE CR
;
;	 ^| WILL BE CR,LF
;
;	 ^: WILL CAUSE EX TO RE-EXECUTE THE .SUB FILE FROM THE BEGINNING
;
;	 ^? WILL CAUSE EX TO WAIT FOR A CARRIAGE RETURN
;	    (^C WILL ABORT EX AT THIS POINT ALSO)
;
;	 ^/ WILL CAUSE ZEX TO RING THE BELL AND WAIT FOR A CARRIAGE RETURN
;	    (^C WILL ABORT ZEX AT THIS POINT ALSO)
;
;	 ^* WILL CAUSE ZEX TO RING THE BELL
;
;	 ^" WILL CAUSE ZEX TO STOP PROVIDING INPUT UNTIL THE NUICH CHAR IS
;	    OUTPUT
;
;	 ^$ WILL CAUSE THE REST OF THE LINE TO BE TREATED AS A
;	    SET OF DEFAULT PARAMETERS SEPARATED BY BLANKS TO BE
;	    USED IF THE USER HAS NOT PROVIDED ONE ON EX'S COMMAND LINE.
;
;	 ^# WILL TOGGLE PRINT SUPPRESSION OF EX MESSAGES
;
;	 ^. WILL START PRINT SUPPRESSION OF ALL CHARACTERS
;	    FROM .SUB FILE UNTIL A SUBSEQUENT ^. IS ENCOUNTERED
;
;	 ;; WILL INDICATE THAT THE ;; AND ALL CHARACTERS FOLLOWING IT
;	    UNTIL A LF IS ENCOUNTERED ARE NOT INCLUDED IN EX'S
;	    TEXT BUFFER
;	    (I.E. AN EX ONLY COMMENT)
;
;	 ^<  WILL START IMMEDIATE DISPLAY OF CHARACTERS FROM
;	    THE .SUB FILE UNTIL ^> IS ENCOUNTERED
;	    (I.E. DISPLAY ONLY .SUB INPUT)
;
;	 $<1-9> WILL REPLACE PARAMETER<1-9> IN TEXT FROM THE COMMAND LINE
;
;	 $$ WILL GIVE $
;
;	 $^ WILL GIVE ^
;
;	 $| WILL GIVE |
;
;	 |,cr,lf,1ah will eat last from | to end of buffer
;
;	 ^C FROM CONSOLE WILL ABORT EX
;
FALSE	EQU	0
TRUE	EQU	NOT FALSE
;
;  GENERAL EQUATES
;
BELL	EQU	7
CTRLZ	EQU	1AH	;^Z
DELAY	EQU	6000H	;DELAY CONSTANT FOR TIMER LOOP
CR	EQU	0DH
LF	EQU	0AH
;
;  ZEX MONITOR COMMAND BYTES
;
PSUP	EQU	80H	;^. PRINT SUPPRESS FLAG
IMON	EQU	81H	;^< IMMEDIATE MODE START
IMOFF	EQU	82H	;^> IMMEDIATE MODE STOP
MSUP	EQU	83H	;^# EX MESSAGE SUPPRESS FLAG
CRWAIT	EQU	84H	;^? EX RUNTIME WAIT FOR CR FLAG
REXEC	EQU	85H	;^: EX RUNTIME RE-EXECUTE FLAG
CRBWAIT	EQU	86H	;^/ EX RUNTIME RING BELL AND WAIT FOR CR FLAG
RNG	EQU	87H	;^* EX RUNTIME RING BELL
UICH	EQU	88H	;^" USER INPUT COMMAND CHAR SEQUENCE
DNUICH	EQU	'>'+80H	;USER INPUT TERMINATION CHAR
;
;  CP/M CONSTANTS
;
WARM	EQU	0
BDISK	EQU	4
BDOS	EQU	5
DFCB	EQU	5CH
BUFF	EQU	80H
;
;	NOTE: ZEX10.LIB IS CREATED BY THE ZEX10.SUB GENERATION PROCESS
;
	MACLIB	ZEX10
;
$-PRINT
	IF	BASE
$+PRINT
;
;	START OF EX INITIATOR CODE SEGMENT
;
	ORG	100H
;
;  Branch to Start of Program
;
	jmp	start

;
;******************************************************************
;
;  SINSFORM -- ZCPR2 Utility Standard General Purpose Initialization Format
;
;	This data block precisely defines the data format for
; initial features of a ZCPR2 system which are required for proper
; initialization of the ZCPR2-Specific Routines in SYSLIB.
;

;
;  EXTERNAL PATH DATA
;
EPAVAIL:
	DB	0FFH	; IS EXTERNAL PATH AVAILABLE? (0=NO, 0FFH=YES)
EPADR:
	DW	40H	; ADDRESS OF EXTERNAL PATH IF AVAILABLE

;
;  INTERNAL PATH DATA
;
INTPATH:
	DB	0,0	; DISK, USER FOR FIRST PATH ELEMENT
			; DISK = 1 FOR A, '$' FOR CURRENT
			; USER = NUMBER, '$' FOR CURRENT
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0
	DB	0,0	; DISK, USER FOR 8TH PATH ELEMENT
	DB	0	; END OF PATH

;
;  MULTIPLE COMMAND LINE BUFFER DATA
;
MCAVAIL:
	DB	0FFH	; IS MULTIPLE COMMAND LINE BUFFER AVAILABLE?
MCADR:
	DW	0FF00H	; ADDRESS OF MULTIPLE COMMAND LINE BUFFER IF AVAILABLE

;
;  DISK/USER LIMITS
;
MDISK:
	DB	4	; MAXIMUM NUMBER OF DISKS
MUSER:
	DB	31	; MAXIMUM USER NUMBER

;
;  FLAGS TO PERMIT LOG IN FOR DIFFERENT USER AREA OR DISK
;
DOK:
	DB	0FFH	; ALLOW DISK CHANGE? (0=NO, 0FFH=YES)
UOK:
	DB	0FFH	; ALLOW USER CHANGE? (0=NO, 0FFH=YES)

;
;  PRIVILEGED USER DATA
;
PUSER:
	DB	10	; BEGINNING OF PRIVILEGED USER AREAS
PPASS:
	DB	'chdir',0	; PASSWORD FOR MOVING INTO PRIV USER AREAS
	DS	41-($-PPASS)	; 40 CHARS MAX IN BUFFER + 1 for ending NULL

;
;  CURRENT USER/DISK INDICATOR
;
CINDIC:
	DB	'$'	; USUAL VALUE (FOR PATH EXPRESSIONS)

;
;  DMA ADDRESS FOR DISK TRANSFERS
;
DMADR:
	DW	80H	; TBUFF AREA

;
;  NAMED DIRECTORY INFORMATION
;
NDRADR:
	DW	00000H	; ADDRESS OF MEMORY-RESIDENT NAMED DIRECTORY
NDNAMES:
	DB	64	; MAX NUMBER OF DIRECTORY NAMES
DNFILE:
	DB	'NAMES   '	; NAME OF DISK NAME FILE
	DB	'DIR'		; TYPE OF DISK NAME FILE

;
;  REQUIREMENTS FLAGS
;
EPREQD:
	DB	0FFH	; EXTERNAL PATH?
MCREQD:
	DB	0FFH	; MULTIPLE COMMAND LINE?
MXREQD:
	DB	000H	; MAX USER/DISK?
UDREQD:
	DB	000H	; ALLOW USER/DISK CHANGE?
PUREQD:
	DB	000H	; PRIVILEGED USER?
CDREQD:
	DB	0FFH	; CURRENT INDIC AND DMA?
NDREQD:
	DB	000H	; NAMED DIRECTORIES?
Z2CLASS:
	DB	4	; CLASS 4
	DB	'ZCPR2'
	DS	10	; RESERVED

;
;  END OF SINSFORM -- STANDARD DEFAULT PARAMETER DATA
;
;******************************************************************
;

;
;  END USER INPUT COMMAND CHAR
;
TNUICH:
	DB	DNUICH	;INIT TO DEFAULT VALUE
;
;  INITIAL COMMAND LINE AREA
;
	DB	0FFH	;SIZE OF COMMAND LINE
DBUFF	EQU	$
	DB	0FFH	;SIZE OF BUFFER
	DS	100H	;SPACE FOR COMMAND LINE
;
;  START OF ZEX
;
START:
	NOP		;REPLACED WITH RET TO PREVENT REENTRY
	LDA	DFCB+1	;CHECK FOR HELP REQUEST
	CPI	'/'	;HELP?
	JZ	HELP
	LXI	H,0
	DAD	SP
	SHLD	CCPSTK	;CCP STACK PTR
	LXI	SP,CCPSTK	;USER STACK AREA
	MVI	A,0C9H	; (8080 RET)
	STA	START	;PREVENT RE-ENTRANCE BY ZCPR
	LXI	H,BUFF	;COPY INPUT LINE INTO DBUFF
	LXI	D,DBUFF
	MVI	B,128	;SIZE OF BUFFER
	CALL	MOVE
	LXI	D,SIGNON	;LOGO
	CALL	PRINT
	CALL	EXACTV	;CHECK FOR RECURSION
	CALL	ZRELOC	;RELOCATE ZEX MODULE
	CALL	ZPARMS	;EXTRACT PARAMETERS FROM COMMAND LINE
	LDA	DFCB+1	;CHECK TO SEE IF SUB FILE PRESENT
	CPI	' '	;<SP>=NO
	PUSH	PSW	;SAVE FLAG
	CNZ	OPENSB	;OPEN AND LOAD SUB FILE IF PRESENT OR ABORT
	POP	PSW	;GET FLAG
	CZ	INPUTSB	;INPUT COMMANDS FROM USER
;
;  HL NOW POINTS TO BYTE AFTER LOADED TEXT
;
	CALL	ZMCL	;STORE REST OF MULTIPLE COMMAND LINE
	CALL	ZLINES	;COPY AND PROCESS COMMAND LINES
;
;  SET UP FOR ZEX EXECUTION AND RUN; HL PTS TO BOTTOM OF DATA AREA
;
	PUSH	H	;SAVE PTR TO END OF DATA
	LHLD	RELSTRT	;GET PTR TO START OF ZEX
	SHLD	GOADR	;SET ADDRESS TO RUN TO
	MOV	D,H	;DE IS START OF DATA AREA ALSO
	MOV	E,L
	DCX	H	;PT TO START OF DATA AREA
	XTHL		;HL IS PTR TO END OF DATA, (SP) IS BEGINNING OF DATA
			;  FROM TOP DOWN
	MVI	M,0FFH	;SET UP END OF DATA
	DCX	H
	MVI	M,'X'	;SET UP EX RECURSION ID
	DCX	H
	MVI	M,'E'
	DCX	H
	MVI	M,'Z'	;ZEX BACKWARDS (FROM TOP DOWN)
	DCX	H
	LDA	BDOS+2	;SET UP BDOS JUMP TO PROTECT DATA
	MOV	M,A
	DCX	H
	LDA	BDOS+1
	MOV	M,A
	DCX	H
	MVI	M,JMP
	POP	D	;GET START OF BUFFER ADDRESS IN DE
	LXI	B,0	;ASSUME NO MULTIPLE COMMANDS
	LDA	MCAVAIL	;GET FLAG
	ORA	A	;0=NONE
	JZ	GOTOZEX	;NO MULTIPLE COMMANDS, SO BC=0
	PUSH	H	;SAVE HL
	LHLD	MCADR	;GET ADDRESS OF MULTIPLE COMMAND BUFFER
	MOV	B,H	;... IN BC
	MOV	C,L
	POP	H
;
;  ZEX MONITOR ENTRY PARAMETERS --
;	HL	TOP OF MEMORY ADDRESS
;	DE	START OF BUFFER ADDRESS
;	BC	ADDRESS OF MULTIPLE COMMAND BUFFER OR 0 IF NONE
;	A	END OF USER INPUT CHAR
;
GOTOZEX:
	LDA	TNUICH	;GET END OF USER INPUT CHAR
GOADR	EQU	$+1
	JMP	$

;
;  PRINT HELP MESSAGE FOR ZEX
;
HELP:
	LXI	D,SIGNON	;PRINT BANNER
	CALL	PRINT
	LXI	H,HMSG	;PRINT MESSAGE
	CALL	HPRINT
	MVI	C,1	;GET CHAR
	CALL	BDOS
	CPI	'C'-'@'	;^C?
	RZ
	LXI	D,CRLFS
	CALL	PRINT
	LXI	D,SIGNON
	CALL	PRINT
	LXI	H,HMSG0
	CALL	HPRINT
	MVI	C,1	;GET CHAR
	CALL	BDOS
	CPI	'C'-'@'	;^C?
	RZ
	LXI	D,CRLFS
	CALL	PRINT
	LXI	D,SIGNON
	CALL	PRINT
	LXI	H,HMSG1
HPRINT:
	MOV	A,M	;GET CHAR
	ORA	A	;DONE?
	RZ
	INX	H	;PT TO NEXT
	PUSH	H	;SAVE PTR
	MOV	E,A	;CHAR IN E
	MVI	C,2	;CONSOLE OUTPUT
	CALL	BDOS
	POP	H	;GET PTR
	JMP	HPRINT

HMSG:
	DB	CR,LF,LF
	DB	'ZEX is an indirect command file processing facility, similar'
	DB	CR,LF,'to the standard CP/M SUBMIT facility and the other '
	DB	'ZCPR2'
	DB	CR,LF,'indirect command file processing facility, SUB2.'
	DB	CR,LF,'The major difference between the SUBMIT-like programs'
	DB	CR,LF,'and ZEX is that ZEX is memory-based, storing a command'
	DB	CR,LF,'processor and the interpreted command file itself in'
	DB	CR,LF,'memory, just under ZCPR2.'
	DB	CR,LF,LF
	DB	'There is a basic tradeoff to be considered when using ZEX'
	DB	CR,LF,'as opposed to SUB2.  Since ZEX is memory-based, it '
	DB	'offers'
	DB	CR,LF,'the advantage of greatly increased operating speed '
	DB	'over'
	DB	CR,LF,'SUB2.  Also, ZEX intercepts all input unless user '
	DB	'input'
	DB	CR,LF,'is explicitly enabled (the ^" command), in which case'
	DB	' the user'
	DB	CR,LF,'is allowed to enter text from his console during a '
	DB	'ZEX run.'
	DB	CR,LF,'SUB2, on the other hand, inputs from the command file'
	DB	' only'
	DB	CR,LF,'at the command line prompt unless XSUB is active.  ZEX'
	DB	' acts like'
	DB	CR,LF,'SUB2 and XSUB combined.  The only bad feature of ZEX is'
	DB	' that'
	DB	CR,LF,'ZEX significantly shortens the TPA work area, so '
	DB	'programs which'
	DB	CR,LF,'require a lot of memory may have trouble running under'
	DB	' ZEX.'
	DB	CR,LF,'This is why SUB2 is provided to the ZCPR2 user along '
	DB	'with ZEX.'
	DB	CR,LF,LF,'Strike Any Key to Continue, ^C to Abort - ',0

HMSG0:
	DB	CR
	DB	LF,LF,'ZEX is invoked by one of the following command lines --'
	DB	CR,LF
	DB	CR,LF,'	ZEX <subfile> <parameters>'
	DB	CR,LF,'or'
	DB	CR,LF,'	ZEX'
	DB	CR,LF
	DB	CR,LF,'The first form executes the indicated command file'
	DB	CR,LF,'(<subfile> may be of type ZEX or SUB, and if a ZEX and'
	DB	' SUB both'
	DB	CR,LF,'exist, the ZEX file is used), passing to it the '
	DB	'indicated '
	DB	CR,LF,'parameters, similar to the way SUBMIT is used.'
	DB	CR,LF
	DB	CR,LF,'The second form allows the user to enter a series of '
	DB	'commands.'
	DB	CR,LF,'ZEX presents the user with a prompt like "n:", where'
	DB	CR,LF,'n is a line number, and the user may enter any command'
	DB	CR,LF,'line.  Input is terminated by simply striking the'
	DB	CR,LF,'RETURN key.'
	DB	CR,LF,LF,'ZEX can be aborted by ^C from console.'
	DB	CR,LF,LF,'The following screen displays the ZEX control codes.'
	DB	CR,LF,LF,'Strike Any Key to Continue, ^C to Abort - ',0

HMSG1:
	DB	CR,LF,LF
	DB	'	**** ZEX Embedded Command Processing Facility ****'
	DB	CR,LF,LF
	DB	'ZEX supports an enhanced command processing facility which '
	DB	'includes the'
	DB	CR,LF,'following escaped character commands which may be'
	DB	CR,LF,'embedded in the text of the command file or user'
	DB	CR,LF,'input and will be executed after the command run begins'
	DB	' --'
	DB	CR,LF
	DB	CR,LF,'	Cmd Meaning		Cmd Meaning'
	DB	CR,LF,'	 |  insert <CR>		^|  insert <CR> <LF>'
	DB	CR,LF,'	^:  rerun command file	^.  suppress print of chars'
	DB	CR,LF,'	^#  toggle ZEX msgs	^$  define default params'
	DB	CR,LF,'	^?  wait for user <CR>	^/  ring and wait for <CR>'
	DB	CR,LF,'	^*  ring bell		^"  allow user input'
	DB	CR,LF,'	^<  display chars only	^>  stop display'
	DB	CR,LF,'	;;  ZEX comment		$n  1<=n<=9 for param'
	DB	CR,LF,'	$$  =$			$^  =^'
	DB	CR,LF,'	$|  =|			^c  insert ctrl char c'
	DB	CR,LF,LF,LF,LF,0


;
;  RELOCATE ZEX MODULE INTO HIGH MEMORY JUST BELOW ZCPR2
;
ZRELOC:
	LHLD	RELOCL	;GET RELOC PROGRAM LENGTH
	MOV	B,H	;BC=HL=RELOC PROGRAM LENGTH
	MOV	C,L
	PUSH	B	;SAVE LENGTH FOR FUTURE USE
	LHLD	BDOS+1	;GET BASE
	LXI	D,-806H ;GET BEFORE CCP
	DAD	D
	MOV	A,L	;SUBTRACT RELOC LENGTH
	SUB	C
	MOV	E,A
	MOV	A,H
	SBB	B
	MOV	D,A
	PUSH	D	;SAVE NEW TOP/START TO MOVE TO
	LXI	H,BEGREL	;START OF MOVE
OMOVE:
	MOV	A,B
	ORA	C
	JZ	MOVEND
	DCX	B
	MOV	A,M
	STAX	D
	INX	D
	INX	H
	JMP	OMOVE
;
MOVEND:
	POP	D	;GET START OF MOVED PROGRAM
	POP	B	;LENGTH OF MOVE PROGRAM
	PUSH	D	;SAVE PTR TO START OF PROGRAM
	PUSH	H	;START OF BIT MAP
	MOV	H,D	;MSB OFFSET
	MOV	L,E	;LSB OFFSET
OFFLUP:
	MOV	A,B	;TEST LENGTH
	ORA	C	;IF 0
	JZ	GOTO	;JUMP TO RELOCATED PROGRAM
	DCX	B	;DECREMENT COUNT
	LDA	COUNT
	INR	A
	STA	COUNT
	ANI	07H
	JNZ	OFFBIT	;NO
	XTHL		;YES, GET BIT MAP
	MOV	A,M	;GET NEXT BYTE
	INX	H	;INCREMENT BIT MAP POINTER
	XTHL		;SAVE FOR LATER
	STA	BITMAP	;KEEP BIT OFFSET
OFFBIT:
	LDA	BITMAP
	RAL		;TEST FOR OFFSET
	STA	BITMAP	;SAVE NEW BYTE
	JNC	NOFSET	;NO
	DCX	D	;GET BACK TO LSB
	LDAX	D
	ADD	L
	STAX	D
	INX	D	;MSB
	LDAX	D	;YES
	ADC	H	;ADD IN OFFSET
	STAX	D	;PUT IN MOVED PLACE
NOFSET:
	INX	D	;INCREMENT MOVED POINTER
	JMP	OFFLUP	;CONTINUE WITH RELOCATE
;
GOTO:
	POP	D	;RESTORE STACK
	POP	H	;PT TO FIRST BYTE OF PROGRAM
	SHLD	RELSTRT	;SAVE PTR
	DCX	H	;RELOCATE PROGRAM-1
	SHLD	OUTBUF	;SAVE PTR TO BYTE IN FRONT OF RELOCATED PROGRAM
	RET
;
;  GET PARAMETERS FROM COMMAND LINE
;	TERMINATE EACH PARAMETER WITH A BINARY ZERO, AND SET POINTERS
;	TO EACH PARAMETER
;
ZPARMS:
	LXI	D,DBUFF+1	;TERMINATE COMMAND LINE WITH CR
	PUSH	D
	LDA	DBUFF
	MOV	L,A	;HL = NUMBER OF CHARS IN LINE
	MVI	H,0
	DAD	D	;PT TO AFTER LAST CHAR
	MVI	M,CR	;STORE <CR>
	LXI	H,PRMDMY	;START AT DUMMY PARAMETER FOR .SUB FILE SPEC
	PUSH	H
	LXI	B,PRMPNL+2
	XRA	A
	CALL	FILL	;CLEAR PTR AREA
	POP	H	;GET PTR TO POINTER FOR PARAMETER 0
	POP	D	;GET PTR TO FIRST CHAR IN LINE
	MVI	A,(PRMPNL/2)+1	;NUMBER OF PARAMETERS POSSIBLE, MAX
	STA	PRMMAX	;HIGHEST PARAMETER # + 1 for .SUB SPEC
;
;  PARAMETER EXTRACTION ROUTINE; HL PTS TO FIRST PARAM PTR, DE PTS TO LINE
;
PARMS:
	MVI	B,0	;CLEAR PARAMETER COUNTER
	XCHG
	SHLD	ERRLNE	;SAVE IN CASE OF ERROR
	XCHG
;
PARMSL:
	LDAX	D	;IGNORE LEADING SPACES
	INX	D
	CPI	CR
	JZ	ENDLNE
	CPI	' '
	JZ	PARMSL
	DCX	D	;BACK UP TO 1ST CHAR
	MOV	M,E	;SAVE ADDRESS IN TABLE
	INX	H
	MOV	M,D
	INX	H
	INR	B	;COUNT+1
	LDA	PRMMAX
	CMP	B
	JC	PRMTOO	;TOO MANY ARGUMENTS
;
ENDPRM:
	LDAX	D	;GO TO END OF PARAMETER
	INX	D
	CPI	CR
	JZ	ENDLNE
	CPI	' '	;SKIP UNTIL <SP>
	JNZ	ENDPRM
	XRA	A	;A=0 TO TERMINATE PARAM
	DCX	D	;PT TO <SP> FOLLOWING PARAM
	STAX	D	;TERMINATE PARAMETER
	INX	D	;PT TO CHAR AFTER <SP>
	JMP	PARMSL	;IGNORE SPACES BETWEEN PARAMETERS
ENDLNE:
	XRA	A	;STORE ZERO AFTER LAST PARAMETER
	DCX	D	;PT TO CR
	STAX	D	;TERMINATE LAST PARAMETER
	INX	D	;PT TO AFTER LAST PARAM
	MVI	A,CR	;STORE ENDING CR
	STAX	D
	RET
;
;  INPUT COMMAND LINES FROM USER
;
INPUTSB:
	LXI	H,0
	SHLD	LINES	;START LINE COUNTER
	MVI	A,0FFH	;SET BUFFER LENGTH
	STA	DBUFF-1
	LXI	H,BEGREL	;SET UP OUTPUT BUFFER
	SHLD	INBUF
GETLIN:
	CALL	CRLF
	LHLD	LINES
	INX	H
	SHLD	LINES
	CALL	DECOUT	;PRINT LINE #
	MVI	E,':'	;GET PROMPT
	CALL	OUTCHR
	MVI	E,' '
	CALL	OUTCHR
	LXI	D,DBUFF-1
	MVI	C,10	;READ CONSOLE BUFFER
	CALL	BDOS
	LXI	D,DBUFF
	LDAX	D	;GET LENGTH
	MOV	B,A
	INX	D
	LHLD	INBUF	;GET INPUT POINTER
	ORA	A	;SEE IF END
	RZ		;DONE WITH INPUT
	XCHG
	CALL	MOVE	;MOVE TO INPUT BUFFER
	XCHG
	MVI	M,CR
	INX	H
	MVI	M,LF
	INX	H
	SHLD	INBUF
	JMP	GETLIN
;
;  OPEN AND LOAD SUB FILE
;
OPENSB:
	CALL	PUTUD	;SAVE USER/DISK
;
;  SET UP TO READ ZEX FILE
;
	LXI	D,DFCB+9
	LXI	H,ZEXNAM	;MOVE 'SUB' TO DFCB FILE TYPE
	MVI	B,3
	CALL	MOVE
	XRA	A	;ZERO CR FIELD
	STA	DFCB+32
	LXI	D,BUFF	;SET DMA ADDRESS
	MVI	C,26	;SET DMA
	CALL	BDOS
	LXI	D,DFCB
	LXI	H,INTPATH	;PT TO INTERNAL PATH
	LDA	EPAVAIL	;EXTERNAL PATHS AVAILABLE?
	ORA	A	;0=NO
	JZ	OSB1	;USE INTERNAL PATH
	LHLD	EPADR	;PT TO EXTERNAL PATH
OSB1:
	PUSH	H	;SAVE PATH PTR
	CALL	FNDFILE	;LOOK FOR FILE ALONG PATH AND SAY IF IT IS FOUND
	POP	H	;GET PATH PTR
	JNZ	READSB
;
;  ZEX FILE NOT FOUND -- SET UP TO READ SUB FILE
;
	PUSH	H	;SAVE PATH PTR
	CALL	GETUD	;RESTORE USER/DISK
	LXI	D,DFCB+9	;SET TYPE TO SUB
	LXI	H,SUBNAM
	MVI	B,3
	CALL	MOVE
	XRA	A	;ZERO CR FIELD
	STA	DFCB+32
	POP	H	;PT TO PATH
	LXI	D,DFCB	;PT TO FCB
	CALL	FNDFILE	;LOOK FOR FILE
	JNZ	READSB
RSBERR:
	CALL	GETUD	;RESTORE USER/DISK
	LXI	H,NOSBF2
	LXI	D,DFCB+1
	MVI	B,8	;NAME LENGTH
	CALL	MOVEFN	;MOVE FILE NAME
	MVI	B,3	;TYPE LENGTH
	MVI	M,'.'
	INX	H
	LXI	D,DFCB+9;FILE TYPE POINTER
	CALL	MOVEFN	;MOVE FILE TYPE
	MVI	M,'$'	;END TERMINATER
	JMP	NOSUB
*
*  FNDFILE -- LOOK FOR FILE ALONG ZCPR2 PATH
*  INPUT PARAMETERS:  HL = BASE ADDRESS OF PATH, DE = PTR TO FCB OF FILE
*  OUTPUT PARAMETERS:  A=0 AND ZERO FLAG SET IF NOT FOUND, NZ IF FOUND
*
FNDFILE:
	SHLD	PATH		;SAVE PATH BASE ADDRESS
	MVI	C,17		;SEARCH FOR FIRST
	CALL	BENTRY		;LOOK FOR FILE
	INR	A		;SET FLAG
	JNZ	FF5		;FOUND IT -- RETURN FOUND FLAG
	XCHG			;HL=FCB PTR
	SHLD	FCBPTR		;SAVE IT
	LHLD	PATH		;PT TO PATH FOR FAILURE POSSIBILITY
	MVI	C,32		;GET CURRENT USER
	MVI	E,0FFH
	CALL	BENTRY
	STA	TMPUSR		;SAVE IT FOR LATER
;
; MAIN SEARCH LOOP
;
FF1:
	MOV	A,M		;GET DRIVE
	ANI	7FH		;MASK MSB
	ORA	A		;0=DONE=COMMAND NOT FOUND
	JNZ	FF2		;NO ERROR ABORT?
;
; FILE NOT FOUND ERROR
;
	XRA	A		;ZERO FLAG MEANS NOT FOUND
	RET
;
; LOOK FOR COMMAND IN DIRECTORY PTED TO BY HL; DRIVE IN A
;
FF2:
	MOV	E,A		;DISK IN E
	LDA	CINDIC		;CURRENT DRIVE SPECIFIED?
	CMP	E
	JNZ	FF3		;SKIP DEFAULT DRIVE SELECTION IF SO
	LDA	BDISK		;GET DEFAULT USER/DISK
	ANI	0FH		;MASK FOR DEFAULT DISK
	INR	A		;PREP FOR FOLLOWING DCR A
	MOV	E,A		;DISK NUMBER IN E
FF3:
	DCR	E		;ADJUST PATH 1 TO 0 FOR A, ETC
	MVI	C,14		;SELECT DISK FCT
	CALL	BENTRY		;SELECT DRIVE
	INX	H		;PT TO USER NUMBER
	MOV	A,M		;GET USER NUMBER
	ANI	7FH		;MASK OUT MSB
	INX	H		;PT TO NEXT ENTRY IN PATH
	PUSH	H		;SAVE PTR
	MOV	E,A		;SAVE IN E
	LDA	CINDIC		;CURRENT USER SPECIFIED?
	CMP	E		;MATCH?
	JNZ	FF4		;DO NOT SELECT CURRENT USER IF SO
	LDA	TMPUSR		;GET ORIGINAL USER NUMBER
	MOV	E,A		;SELECT USER
FF4:
	MVI	C,32
	CALL	BENTRY
	LHLD	FCBPTR		;GET PTR TO FCB
	XCHG			;... IN DE
	MVI	C,17		;SEARCH FOR FIRST
	CALL	BENTRY		;LOOK FOR FILE
	POP	H		;GET PTR TO NEXT PATH ENTRY
	INR	A		;SET FLAG
	JZ	FF1		;CONTINUE PATH SEARCH IF SEARCH FAILED
;
; FILE FOUND -- PERFORM SYSTEM TEST AND PROCEED IF APPROVED
;
FF5:
	MVI	A,0FFH		;SET OK RETURN
	ORA	A
	RET

;
;  BDOS ROUTINE
;
BENTRY:
	PUSH	H	;SAVE REGS
	PUSH	D
	PUSH	B
	CALL	BDOS
	POP	B	;GET REGS
	POP	D
	POP	H
	RET

* BUFFERS
FCBPTR:
	DS	2	;POINTER TO FCB FOR FILE SEARCH
TMPUSR:
	DS	1	;CURRENT USER NUMBER
PATH:
	DS	2	;BASE ADDRESS OF PATH
;
;  PUTUD -- SAVE AWAY CURRENT USER/DISK
;  GETUD -- RESTORE CURRENT USER/DISK
;
PUTUD:
	MVI	E,0FFH	;GET CURRENT USER
	MVI	C,32	;BDOS
	CALL	BDOS
	STA	CUSER	;SAVE CURRENT USER AWAY
	MVI	C,25	;GET CURRENT DISK
	CALL	BDOS
	STA	CDISK
	RET
GETUD:
	LDA	CDISK	;GET CURRENT DISK
	MOV	E,A	;... IN E
	MVI	C,14	;SELECT DISK
	CALL	BDOS
	LDA	CUSER	;GET CURRENT USER
	MOV	E,A	;... IN E
	MVI	C,32	;SELECT USER
	CALL	BDOS
	RET
CDISK:
	DS	1	;CURRENT DISK NUMBER
CUSER:
	DS	1	;CURRENT USER NUMBER
;
;  OPEN AND READ SUB FILE
;
READSB:
	MVI	C,15	;OPEN FILE
	CALL	BDOS	;BDOS
	INR	A	;ERROR?
	JZ	RSBERR
;
;  READ IN AND STORE SUB FILE
;
READTX:
	LHLD	INBUF	;GET PTR TO NEXT BYTE
	XCHG		;SET PTR IN DE
	LXI	H,80H	;GET SECTOR OFFSET
	DAD	D	;HL PTS TO FOLLOWING BLOCK TO BE READ, DE PTS TO
	SHLD	INBUF	;  BLOCK TO READ; SAVE PTR TO FOLLOWING BLOCK
	MVI	C,26	;SET DMA ADDRESS
	CALL	BDOS
	LXI	D,DFCB
	MVI	C,20	;READ SEQUENTIAL
	CALL	BDOS
	ORA	A
	JZ	READTX	;READ COMPLETE .SUB FILE
	CALL	GETUD	;RESTORE CURRENT USER/DISK
	LHLD	INBUF	;MAKE SURE BUFFER'S TERMINATED
	LXI	D,-100H	;PT TO FIRST BYTE OF LAST BLOCK READ
	DAD	D
	MVI	B,80H	;LOOK AT AT MOST 80H BYTES
SKIP1A:
	MOV	A,M	;GET BYTE
	CPI	CTRLZ	;EOF?
	JZ	SKIP1B
	INX	H	;PT TO NEXT
	DCR	B	;COUNT DOWN
	JNZ	SKIP1A
;  HL NOW POINTS TO AFTER LAST VALID CHAR IN FILE
SKIP1B:
	SHLD	INBUF	;SET PTR
	RET		;DONE WITH NO ERROR

;
;  THIS PART OF THE CODE STORES THE REST OF THE COMMAND LINE AS PART OF THE
;  COMMAND FILE FOR ZCPR2; ON ENTRY, HL PTS TO NEXT AVAILABLE BYTE
;
ZMCL:
	XCHG		;BUFFER PTED TO BY DE
	LHLD	MCADR	;GET BASE ADDRESS OF MULTIPLE COMMAND LINE
	MOV	A,M	;GET LOW
	INX	H
	MOV	H,M	;GET HIGH
	MOV	L,A	;HL PTS TO NEXT CHAR IN MULTIPLE COMMAND LINE
	XCHG		;DE PTS TO NEXT CHAR IN COMMAND LINE, HL PTS TO BUF END
	LDA	MCAVAIL	;MULTIPLE COMMANDS ENABLED?
	ORA	A	;0=NO
	JZ	ENDSTR	;TERMINATE FILE; HL PTS TO NEXT BYTE
	LDAX	D	;GET FIRST BYTE
	MOV	B,A	;SAVE FIRST BYTE IN B
	XRA	A	;A=0
	STAX	D	;CLEAR COMMAND LINE
	INX	D	;PT TO NEXT BYTE
	MOV	A,B	;GET FIRST BYTE
	CPI	';'	;SEPARATION CHAR?
	JNZ	CMCMD1	;PROCESS IF NOT
;
;  LOOP TO STORE REST OF MULTIPLE COMMAND LINE INTO LOADED FILE
;
CMCMD:
	LDAX	D	;GET BYTE FROM LINE
CMCMD1:
	ORA	A	;EOL IF ZERO
	JZ	CMEND	;READ IN FILE; HL PTS TO NEXT AVAILABLE BYTE
	MOV	M,A	;STORE BYTE
	INX	H	;PT TO NEXT
	INX	D
	JMP	CMCMD
CMEND:
	MVI	M,CR	;STORE <CR> <LF>
	INX	H
	MVI	M,LF
	INX	H	;PT TO NEXT AVAILABLE BYTE
;
;  MARK END OF BUFFER AND CONTINUE
;
ENDSTR:
	MVI	M,1AH	;EOF CHARACTER
	SHLD	ENDBUF	;EOB ADDRESS
	MOV	A,L
	SUI	LOW BEGREL+1	;SEE IF BUFFER'S EMPTY
	MOV	A,H
	SBI	HIGH BEGREL
	JC	BUFLOW
	RET
;
;  COPY AND PROCESS COMMAND LINES, PLACING FINAL COMMAND LINE FORM UNDER ZEX
;    RETURN WITH HL PTING TO NEXT AVAILABLE BYTE IN MEMORY BUFFER UNDER ZEX
;
ZLINES:
	XRA	A
	STA	IMFLG1
	STA	IMFLG2
	STA	PRTFLG
	STA	OUTCNT
	LXI	H,1
	SHLD	LINES
	LHLD	OUTBUF	;PT TO BYTE JUST BELOW LOADED ZEX
	SHLD	OUTLNE
	SHLD	BUFSTR
	LXI	D,BEGREL	;PT TO FIRST BYTE OF COMMAND BUFFER
;
;  MAIN COPY LOOP TO COPY BUFFER AT BEGREL TO JUST UNDER EX WITH PROCESSING
;
MOVSTR:
	LDAX	D	;GET NEXT COMMAND BYTE
	INX	D	;PT TO FOLLOWING
	ANI	7FH	;MAKE SURE NO PARITY
	CPI	LF	;NEW LINE?
	JNZ	MOVST0
;
;  NEW LINE -- DON'T STORE <LF> AND INCREMENT LINE COUNT
;
MOVSTX:
	CALL	INCR	;INCREMENT LINE COUNT
	JMP	MOVSTR	;CONTINUE
;
;  BEGIN CHARACTER PROCESSING
;    A CONTAINS CHAR, DE PTS TO BYTE AFTER CHAR, HL PTS TO NEXT BUFFER POS
;
MOVST0:
	CPI	1AH	;END OF INPUT?
	RZ		;DONE IF SO
	CPI	'|'	;CARRIAGE RETURN?
	JNZ	MOVST1	;NOPE
;
;  PROCESS CARRIAGE RETURN FORM (|)
;
	PUSH	D	;SAVE OLD POINTER
	INX	D	;LOOK FOR EOF AFTER | (PT TO LF)
	INX	D	;PT TO POSSIBLE EOF
	LDAX	D	;GET PRESENT LOCATION+2
	POP	D	;GET OLD POINTER
	CPI	1AH	;END OF BUFFER
	RZ		;END, SO NO FOLLOWING <CR>
	MVI	A,CR	;MAKE CHAR A <CR>
	CALL	INCR	;INCREMENT LINES FOR ERRORS
	JMP	MOVST4	;STORE <CR> IN A
;
;  CHECK FOR NON-CR FORMS
;    AT THIS POINT, DE PTS TO NEXT CHAR IN LINE AND HL PTS TO NEXT
;    BYTE IN BUFFER (MOVING DOWN)
;
MOVST1:
	MOV	C,A	;SAVE CHAR IN C
	LDA	IMFLG1
	CPI	IMON	;IMMEDIATE MODE ON ?
	MOV	A,C	;GET CHAR BACK
	JZ	MOVST2	;YES..SKIP EX COMMENT PROCESSING
	CPI	';'	;FIRST ';'?
	JZ	EXCOMM	;PROCESS POSSIBLE EX COMMENT
MOVST2:
	CPI	'^'	;CONTROL CHAR?
	JZ	MOVST5	;CONVERT CONTROL CHARACTERS
	CPI	'$'	;PARAMETER OR CONTROL CHAR?
	CZ	GTPARM	;SUBSTITUTE COMMAND PARAMETER OR CONTROL CHAR.
MOVST3:
	STA	LCHR	;SAVE LAST CHAR ENTERED
	CPI	CR	;=CR?
	JNZ	MOVST4
	MOV	C,A	;SAVE CHAR TEMPORARILY
	LDA	OUTCNT	;GET CHAR OUTPUT FLAG
	ORA	A	;ANY CHAR?
	MOV	A,C
	JZ	MOVSTR	;NO..USE INPUT CR ONLY IF OTHER NON-CONTROL
;			     CHARACTERS IN CURRENT LINE
;  PLACE CHAR IN BUFFER
;    CHAR IN A, HL PTS TO BUFFER LOC
;
MOVST4:
	CALL	CHRSTR	;ADD TO BUFFER
	CALL	CNTINC	;INCREMENT COUNT
	JMP	MOVSTR
;
;  PREFIX WAS AN UPARROW (^), SO PROCESS CONTROL CHARS
;
MOVST5:
	CALL	GETCMD	;VALIDATE CONTROL CHARACTERS
	CPI	':'
	JZ	REXC	;RE-EXECUTE
	CPI	'?'
	JZ	GCRW	;CR WAIT
	CPI	'/'
	JZ	GCRBW	;RING BELL AND WAIT FOR <CR>
	CPI	'"'
	JZ	UISET	;USER INPUT
	CPI	'*'
	JZ	GRNG	;CONTINUALLY RING BELL WHILE WAITING FOR <CR>
	CPI	'|'
	JZ	GCRLF	;CR,LF GENERATION
	CPI	'$'
	JZ	PRMDEF	;DEFAULT PARAMETERS' LINE
	CPI	'.'
	JZ	PRTSUP	;PRINT SUPPRESS TOGGLE
	CPI	'#'
	JZ	MSGSUP	;MESSAGE SUPPRESS TOGGLE
	CPI	'<'
	JZ	IMPRTY	;IMMEDIATE MODE START
	CPI	'>'
	JZ	IMPRTN	;IMMEDIATE MODE STOP
	JMP	MOVST3	;OTHER CONTROL CODES
;
REXC:
	MVI	A,REXEC	;CONVERT '^:' TO RE-EXECUTE FLAG
	JMP	MOVST3
;
GCRW:
	MVI	A,CRWAIT	;CONVERT '^?' TO CRWAIT FLAG
	JMP	MOVST3
;
GCRBW:
	MVI	A,CRBWAIT	;CONVERT '^/' TO CRBWAIT FLAG
	JMP	MOVST3
;
;  ALLOW USER INPUT FROM NOW ON, BUT FIRST SKIP OUT REST OF LINE
;
UISET:
	LDAX	D		;GET NEXT CHAR
	ANI	7FH		;MASK IT
	CPI	LF		;DONE?
	JZ	UISET1
	CPI	1AH		;EOF?
	JZ	UISET1
	INX	D		;PT TO NEXT CHAR
	JMP	UISET		;CONTINUE SKIPPING
UISET1:
	MVI	A,UICH		;CONTROL CHAR
	JMP	MOVST3
;
GRNG:
	MVI	A,RNG		;CONVERT '^*' TO RNG FLAG
	JMP	MOVST3
;
GCRLF:
	MVI	A,CR	;GENERATE CR & LF
	CALL	CHRSTR
	MVI	A,LF
	CALL	CHRSTR
	STA	LCHR
	JMP	MOVSTR
;
PRMDEF:
	PUSH	H
	LXI	H,PRMDFP
	PUSH	H
	LXI	B,PRMDFL
	XRA	A
	CALL	FILL	;CLEAR PTR TABLE
	POP	H
	MVI	A,PRMDFL/2
	STA	PRMMAX	;HIGHEST PARAMETER #
	CALL	PARMS	;BUILD DEFAULT PARAMETERS PTRS
	POP	H
	INX	D	;SKIP CR
	MVI	A,LF
	JMP	MOVSTX	;CONTINUE AT EOL
;
;  CHECK TO SEE IF PREVIOUS CHAR WAS ALSO A ; AND FLUSH AS ZEX COMMENT IF SO
;
EXCOMM:
	PUSH	H
	LXI	H,LCHR	;PT TO PREVIOUS CHAR
	CMP	M	; DOUBLE ;?
	MOV	M,A	;STORE CURRENT CHAR AS PREVIOUS CHAR
	POP	H
	JNZ	MOVST3	;NO...CONTINUE
	MOV	C,A	;SAVE CHAR
	LDA	PRTFLG
	CPI	PSUP
	MOV	A,C
	JZ	MOVST3	;PRINT SUPPRESS
	LDA	IMFLG1
	CPI	IMON
	MOV	A,C
	JZ	MOVST3	;IMMEDIATE MODE
	INX	H	;YES..IGNORE PREVIOUS ;
	PUSH	H
	LXI	H,LCHR
	LDA	OUTCNT
	DCR	A	;DROP 1 CHAR.
	STA	OUTCNT
EXCOML:
	LDAX	D	;IGNORE CHARACTERS UNTIL EOF OR LF
	INX	D
	CPI	1AH	;EOF
	JZ	EXCOMX
	CPI	LF	;LINE FEED
	JNZ	EXCOML
	MOV	M,A
	LDA	OUTCNT
	ORA	A	;ANY CHAR. ON THIS LINE?
	JZ	EXCOM2	;NO...SKIP CR
EXCOM1:
	POP	H	;YES..FORCE CR
	MVI	A,CR
	CALL	CHRSTR
	MVI	A,LF
	JMP	MOVSTX	;CONTINUE
;
EXCOM2:
	POP	H
	MVI	A,LF
	JMP	MOVSTX	;CONTINUE
;
EXCOMX:
	POP	H
	RET		;RETURN TO MAIN FLOW, WITH HL PTING TO NEXT BYTE
;
MSGSUP:
	MVI	A,MSUP	;CONVERT '^#' TO MESSAGE SUPPRESS FLAG
	JMP	MOVST3
;
PRTSUP:
	MVI	A,PSUP	;CONVERT '^.' TO PRINT SUPPRESS FLAG
	PUSH	H
	LXI	H,PRTFLG
	CMP	M	;ALREADY ON?
	JNZ	PRTSST	;NO...SET FLAG
	XRA	A	;YES..CLEAR FLAG
PRTSST:
	MOV	M,A	;SET/RESET FLAG
	POP	H
	MVI	A,PSUP
	JMP	MOVST3
;
IMPRTY:
	MVI	A,IMON	;CONVERT '^<' TO IMMEDIATE MODE START
	STA	LCHR
	PUSH	H
	LXI	H,IMFLG1
	CMP	M	;ALREADY ON?
	POP	H
	JZ	MOVSTR	;YES..
	STA	IMFLG1
	STA	IMFLG2
	JMP	MOVST3	;NO...
;
IMPRTN:
	MVI	A,IMOFF ;CONVERT '^>' TO IMMEDIATE MODE STOP
	STA	LCHR
	PUSH	H
	LXI	H,IMFLG2
	CMP	M	;ALREADY OFF?
	POP	H
	JZ	MOVSTR	;YES..
	STA	IMFLG2
	STA	IMFLG1
	JMP	MOVST3	;NO...
;
;  PLACE CHAR IN BUFFER; A=CHAR, HL PTS TO BUFFER LOC
;
CHRSTR:
	PUSH	PSW	;CHECK FOR INPUT/EX BUFFER OVERLAP
	PUSH	D
	PUSH	H
	LHLD	ENDBUF
	XCHG
	POP	H
	MOV	A,L
	CMP	E
	JNZ	CHRSTX	;LSB<>
	MOV	A,H
	CMP	D
	JZ	OVERL	;MSB=, OVERLAP WILL OCCUR/ABORT EX
;
CHRSTX:
	POP	D	;ADD CHAR. TO EX'S BUFFER
	POP	PSW
	MOV	M,A	;STORE CHAR
	DCX	H	;PT TO NEXT LOCATION (MOVING DOWN)
	RET
;
;  CHECK TO SEE IF ZEX IS ALREADY ACTIVE, AND ABORT IF SO
;
EXACTV:
	LHLD	BDOS+1	;CHECK FOR EX RECURSION
	INX	H	;SKIP JMP
	INX	H	;SKIP LOW
	INX	H	;SKIP HIGH
	MVI	A,'Z'	;LOOK FOR ZEX STORED JUST ABOVE THE BDOS JMP
	CMP	M
	RNZ
	INX	H
	MVI	A,'E'
	CMP	M
	RNZ		;NOT 'E'
	INX	H
	MVI	A,'X'
	CMP	M
	RNZ		;NOT 'X'
	INX	H
	MVI	A,0FFH
	CMP	M
	RNZ		;NOT 0FFH
	LXI	D,EXACT
	CALL	PRINT	;ZEX ALREADY PRESENT
;
;  ABORT AND RETURN TO ZCPR2
;
CCPRET:
	LHLD	CCPSTK	;RESTORE STACK
	SPHL
	RET		;RETURN TO CCP
;
;	ERROR EXITS
;
GETERR:
	LXI	D,CMDER ;CONTROL CHARACTER INVALID
	CALL	PRINT
	JMP	LINE	;PRINT LINE # AND LINE AND EXIT
;
NUMERR:
	LXI	D,NONUM	;EXCESSIVE NUMBER
	CALL	PRINT
	JMP	LINE	;PRINT LINE # AND LINE AND EXIT
;
NODEFP:
	LXI	D,NOPRM ;UNKNOWN PARAMETER
	CALL	PRINT
	JMP	LINE	;PRINT LINE # AND LINE AND EXIT
;
PRMERR:
	LXI	D,PMERR
	CALL	PRINT
	JMP	LINE	;PRINT LINE # AND LINE AND EXIT
;
PRMTOO:
	LXI	D,TOOARG;TOO MANY PARAMETER ARGUMENTS
	CALL	PRINT
	LHLD	ERRLNE
	CALL	EPRT	;PRINT PARAMETER LINE
	JMP	CCPRET
;
BUFLOW:
	LXI	D,BUFMTY;TEXT BUFFER EMPTY
	CALL	PRINT
	JMP	CCPRET
;
NOSUB:
	LXI	D,NOSBF1;.SUB FILE NOT FOUND
	CALL	PRINT
	LXI	D,NOTHER
	CALL	PRINT
	JMP	CCPRET
;
OVERL:
	LXI	D,OVERLP;INPUT/EX BUFFER OVERLAP
	CALL	PRINT
	JMP	LINE
;
;	SUBROUTINES
;
;	CONTROL CODES 0-1FH
;	WITH SUPPORT FOR $ . # < >
;
GETCMD:
	LDAX	D	;GET NEXT CHARACTER
	INX	D	;INCREMENT POINTER
	CPI	'|'
	RZ		;CR,LF GENERATION
	CPI	'a'-1	;LOWERCASE?
	JC	GETUPR	;NOPE
	CPI	'z'+1	;a-z?
	JNC	GETERR	;NOPE
	sui	'a'-'A' ;GET TO UPPERCASE
GETUPR:
	CPI	'@'	;0-1FH CONTROL CODE?
	JNC	GETCC
	CPI	':'
	RZ		;RE-EXECUTE
	CPI	'?'
	RZ		;CR WAIT
	CPI	'/'
	RZ		;CR WAIT AND RING BELL
	CPI	'*'
	RZ		;RING BELL
	CPI	'"'
	RZ		;USER INPUT
	CPI	'$'
	RZ		;DEFAULT PARAMETERS' LINE
	CPI	'.'
	RZ		;PRINT SUPPRESS TOGGLE
	CPI	'#'
	RZ		;MESSAGE SUPPRESS TOGGLE
	CPI	'<'
	RZ		;IMMEDIATE MODE START
	CPI	'>'
	RZ		;IMMEDIATE MODE STOP
	JMP	GETERR
GETCC:
	SUI	'@'	;GET CONTROL CODE
	RNC
	JMP	GETERR
;
;  EXTRACT PARAMETER ELEMENT WHOSE $N SPECIFICATION IS POINTED TO BY DE
;    DE PTS TO CHAR AFTER THE $
;    BUFFER TO PLACE RESULTING PARAMETER IS PTED TO BY HL
;
GTPARM:
	LDAX	D	;GET CHAR AFTER THE $
	INX	D	;PT TO NEXT CHAR
	CPI	'$'	;IF DOUBLE $, THEN STORE AS $
	RZ
	CPI	'^'	;UP ARROW
	RZ
	CPI	'|'	;CARRIAGE RETURN
	RZ
	CPI	'1'	;CHECK FOR VALID DIGIT (1-9)
	JC	PRMERR
	CPI	'9'+1	;RANGE ERROR?
	JNC	PRMERR
	SUI	'1'	;GET ACTUAL # (ZERO RELATIVE)
	ADD	A	;DOUBLE FOR OFFSET
	STA	PRMNUM
	PUSH	D	;SAVE PTRS
	PUSH	H
	LXI	H,PRMPNT	;PT TO PARAMETER PTR TABLE
	CPI	PRMPNL-1	;PARAMETER NUMBER WITHIN RANGE?
	JNC	NOPARM	;> HIGHEST #
	MOV	E,A
	MVI	D,0
	DAD	D
	MOV	E,M	;GET PARAMETER POINTER
	INX	H
	MOV	D,M
	POP	H	;RESTORE PTR TO NEXT BYTE IN OUTPUT BUFFER BELOW ZEX
	MOV	A,E	;ANY PARAM?
	ORA	D
	JZ	NOPARM	;NO PARAMETER PRESENT, TRY DEFAULTS
;
;  MOVE PARAMETER PTED TO BY DE INTO BUFFER BELOW ZEX, 1ST BYTE PTED TO BY HL
;
MOVPRM:
	LDAX	D	;GET PARAMETER CHAR
	INX	D	;PT TO NEXT
	ORA	A	;DONE?
	JZ	ENDPAR
	MOV	M,A	;STORE IN BUFFER
	DCX	H	;MOVE DOWN
	JMP	MOVPRM
;
;  PARAMETER PLACED IN MEMORY -- CONTINUE
;
ENDPAR:
	POP	D	;GET PTR TO NEXT CHAR IN LINE
	INX	H	;PT TO LAST CHAR AND GET IT
	MOV	A,M	;UPON RETURN, LAST CHAR STORED NORMALLY SO THAT
	RET		;  OVERFLOW CHECK MAY BE DONE
;
;  NO PARAMETER PTED TO
;
NOPARM:
	PUSH	H
	LXI	H,PRMDFP	;TRY DEFAULT PARAMETERS
	LDA	PRMNUM
	CPI	PRMDFL-1
	JNC	NUMERR	;> HIGHEST #
	MOV	E,A
	MVI	D,0
	DAD	D
	MOV	E,M	;GET PARAMETER POINTER
	INX	H
	MOV	D,M
	POP	H
	MOV	A,E
	ORA	D
	JZ	NODEFP	;NO PARAMETER PRESENT
	JMP	MOVPRM	;MOVE PARAMETER TO BUFFER
;
MOVEFN:
	LDAX	D
	CPI	' '	;SEE IF SPACE
	RZ
	MOV	M,A
	INX	D	;INCREMENT POINTERS
	INX	H
	DCR	B
	JNZ	MOVEFN
	RET
;
;  INCREMENT LINE COUNT, AND AFFECT ONLY HL (MUST NOT AFFECT A)
;
INCR:
	PUSH	H	;SAVE OUTPUT POINTER
	LHLD	LINES
	INX	H	;INCREMENT LINE COUNTER
	SHLD	LINES
	LXI	H,LCHR	;CLEAR LAST CHARACTER
	MVI	M,0
	LXI	H,OUTCNT;CLEAR CHARACTER COUNT
	MVI	M,0
	MOV	L,E	;DE=HL
	MOV	H,D
	SHLD	BEGLIN
	POP	H
	SHLD	OUTLNE	;SAVE NEW OUTPUT LINE
	RET
;
CNTINC:
	CPI	' '	;CONTROL CHARACTER?
	RC		;YES..
	CPI	UICH	;USER INPUT CHAR?
	JZ	CNTIN1
	ANI	80H	;SPECIAL CONTROL?
	RNZ		;YES..
	LDA	PRTFLG
	CPI	PSUP	;PRINT SUPPRESS FLAG?
	RZ		;YES..
	LDA	IMFLG1
	CPI	IMON	;IMMEDIATE MODE?
	RZ		;YES..
CNTIN1:
	LDA	OUTCNT
	INR	A
	STA	OUTCNT
	RET
;
PRINT:
	MVI	C,9	;PRINT STRING AT (DE)
	CALL	BDOS
	RET
;
EPRT:
	MOV	A,M	;PRINT PARAMETER LINE AT (HL)
	CPI	CR
	RZ
	CPI	0
	JNZ	EPRT1
	MVI	A,' '
EPRT1:
	INX	H
	PUSH	H
	MOV	E,A
	MVI	C,2
	CALL	BDOS
	POP	H
	JMP	EPRT
;
CRLF:
	LXI	D,CRLFS ;PRINT CR/LF
	CALL	PRINT
	RET
;
LINE:
	LXI	D,LINEM ;PRINT LINE # AND LINE IN ERROR AND EXIT
	CALL	PRINT
	LHLD	LINES
	CALL	DECOUT	;PRINT LINE #
	CALL	CRLF
	LHLD	BEGLIN
	PUSH	H	;SAVE BEGGING POINTER
FINDCR:
	MOV	A,M
	INX	H
	CPI	1AH	;END OF BUFFER
	JZ	FOUND
	CPI	CR
	JNZ	FINDCR
FOUND:
	MVI	M,0	;END OF STRING
	POP	H	;START OF STRING
	CALL	PRNTHL	;PRINT BAD LINE
	JMP	CCPRET	;THATS ALL FOLKS
;
PRNTHL:
	MOV	A,M	;PRINT LINE AT (HL)
	INX	H
	ORA	A
	RZ
	MOV	E,A
	PUSH	H	;SAVE POINTER
	CALL	OUTCHR
	POP	H	;GET POINTER BACK
	JMP	PRNTHL
;
OUTCHR:
	MVI	C,2	;PRINT CHARACTER IN E
	JMP	BDOS
;
DECOUT:
	PUSH	H	;PRINT DECIMAL LINE NUMBER
	PUSH	D
	PUSH	B
	LXI	B,-10	;RADIX FOR CONVERSION
	LXI	D,-1	;THIS BECOMES NO DIVIDED BY RADIX
DX:
	DAD	B	;SUBTRACT 10
	INX	D
	JC	DX
	LXI	B,10
	DAD	B	;ADD RADIX BACK IN ONCE
	XCHG
	MOV	A,H
	ORA	L	;TEST FOR ZERO
	CNZ	DECOUT	;RECURSIVE CALL
	MOV	A,E
	ADI	'0'	;CONVERT FROM BCD TO HEX
	MOV	E,A	;TO E FOR OUTPUT
	MVI	C,2
	CALL	BDOS
	POP	B	;RESTORE REGISTERS
	POP	D
	POP	H
	RET
;
MOVE:
	MOV	A,M	;MOVE STRING AT (HL) TO (DE) FOR LENGTH IN B
	INX	H
	STAX	D
	INX	D
	DCR	B
	JNZ	MOVE
	RET
;
FILL:
	PUSH	D	; FILL STORAGE AT (HL) WITH CHARACTER IN A
	MOV	E,A	; FOR LENGTH IN BC
	MOV	A,B
	ORA	C
	MOV	A,E
	POP	D
	RZ
	DCX	B
	MOV	M,A
	INX	H
	JMP	FILL
;
;	WORKING STORAGE AREA
;
SUBNAM:
	DB	'SUB'
ZEXNAM:
	DB	'ZEX'
LINEM:
	DB	' Error Line # $'
EXACT:
	DB	CR,LF,'(ZEX Already Present)$'
BUFMTY:
	DB	CR,LF,'Text Buffer Empty$'
OVERLP:
	DB	CR,LF,'Input/ZEX Buffer Overlap$'
NONUM:
	DB	CR,LF,'Parameter Number out of range$'
NOPRM:
	DB	CR,LF,'No Parameter or Default Parameter$'
PMERR:
	DB	CR,LF,'Parameter$'
NOSBF1:
	DB	CR,LF,'File '
NOSBF2:
	DB	'filename.typ$'
NOTHER:
	DB	' not there$'
CMDER:
	DB	CR,LF,'Control character$'
TOOARG:
	DB	CR,LF,'Too many arguments - $'
SIGNON:
	DB	'ZEX, Version '
	DB	VERS/10+'0','.',(VERS MOD 10)+'0','$'
CRLFS:
	DB	CR,LF,'$'
;
	DS	80	;STACK SPACE
CCPSTK:
	DW	0	;CCP STACK PTR
IMFLG1:
	DB	0	;=IMON ENCOUNTERED
IMFLG2:
	DB	0	;=IMOFF ENCOUNTERED
PRTFLG:
	DB	0	;=PSUP ON
LCHR:
	DB	0	;LAST CHARACTER READ
PRMMAX:
	DB	0	;HIGHEST PARAMETER #
PRMNUM:
	DB	0	;CURRENT $<1-9> NUMBER * 2 (ZERO RELATIVE)
ERRLNE:
	DW	0
BITMAP:
	DB	0	;PRESENT OFFSET BIT'S
COUNT:
	DB	0FFH	;PRESENT OFFSET BIT COUNT
BEGLIN:
	DW	BEGREL	;BEGINNING OF OLD LINE POINTER
LINES:
	DW	1
INBUF:
	DW	BEGREL
ENDBUF:
	DW	0	;END OF INPUT BUFFER
OUTCNT:
	DB	0
OUTLNE:
	DW	0
RELSTRT:
	DW	0
OUTBUF:
	DW	0
BUFSTR:
	DW	0
RELOCL:
	DW	0	;LENGTH OF RELOC PROGRAM (FILLED IN BY SID)
PRMDFP: 		;DEFAULT PARAMETER PTRS
	REPT	9
	DW	0
	ENDM
PRMDFL	EQU	$-PRMDFP
PRMDMY:
	DW	0	;DUMMY PARAMETER FOR .SUB FILE SPEC.
PRMPNT: 		;COMMAND LINE PARAMETER PTRS
	REPT	9
	DW	0
	ENDM
PRMPNL	EQU	$-PRMPNT
PATCH:			;PATCH AREA
	REPT	32
	DB	'p'
	ENDM
	REPT	30
	DW	0
	ENDM
;
;	INSURE 8 BYTE BOUNDARY FOR REL.UTL(RELS.UTL)
;
?PLOC	SET	$
	IF	(?PLOC MOD 8) GT 0
?PLOC	SET	(?PLOC AND 0FFF8H)+8 ;GET NEXT 8 BYTE BOUNDARY
	ORG	?PLOC
	ENDIF
;
BEGREL:
	DS	0	;RELOC PROGRAM STARTS HERE (ALSO USED AS BUFFER)
;
	ENDIF
;
;	END OF EX INITIATOR CODE SEGMENT
;
$-PRINT
	IF NOT	BASE
$+PRINT
;
;	START OF EX RELOCATED CODE SEGMENT
;		HL PTS TO TOP OF AVAILABLE MEMORY, DE PTS TO START OF
;		TEXT BUFFER TO BE PROCESSED, BC PTS TO MULTIPLE COMMAND BUFFER
;		OR BC=0 IF NO MULTIPLE COMMANDS
;
	ORG	REL
;
ZEX:
	STA	NUICH	;SAVE END OF USER INPUT CHAR
	SHLD	MEMTOP	;SAVE PTR TO TOP OF MEMORY
	XCHG		;PT TO START OF BUFFER
	SHLD	REVBUF	;PTR TO FIRST CHAR
	SHLD	SAVBUF
	PUSH	H	;SAVE MULTIPLE COMMAND BUFFER LOCATION
	MOV	H,B
	MOV	L,C
	SHLD	EXMBASE
	POP	H
	MOV	A,M	;GET 1ST CHAR
	CPI	MSUP	;1ST CHAR=MESSAGE SUPPRESS?
	JNZ	EX1	;NO...
	DCX	H	;YES..SKIP CHARACTER
	SHLD	REVBUF
	STA	MSUPFL	;SET INITIAL FLAG
EX1:
	LXI	SP,MEMTOP
	LHLD	BDOS+1	;GET WARM JUMP FOR STANDARD CCP
	MOV	A,H
	SUI	8
	MOV	H,A
	MVI	L,3	;SET UP FOR WARM CCP JUMP
	SHLD	CCPJMP
	LHLD	WARM+1	;SAVE WARM BOOT ADDRESS
	SHLD	WARMPT
	LXI	D,BSWARM	;SAVE OLD BIOS JUMPS
	MVI	B,12
	CALL	MOVE	;MOVE BIOS JUMPS
	LHLD	WARMPT
	XCHG
	LXI	H,LOCJMP	;STORE NEW BIOS JUMPS
	MVI	B,12
	CALL	MOVE	;MOVE NEW BIOS JUMPS TO BIOS AREA
;
;	EX RUNTIME BIOS INTERCEPT ROUTINES
;
NWARM:
	LXI	SP,MEMTOP
	LHLD	REVBUF	;SEE IF WE'RE AT BUFFERS END
	MOV	A,M
	CPI	0FFH	;TEST IT
	JZ	WARMR	;WARM RETURN
	XRA	A	;A=0
	STA	UIFLG	;SET NO USER INPUT
	LHLD	WARMPT	;SET WARM BOOT ADDRESS
	SHLD	WARM+1
	LHLD	MEMTOP	;SET BDOS ENTRY ADDRESS
	SHLD	BDOS+1
	LXI	D,BUFF	;DMA ADDRESS
	MVI	C,26	;SET DMA
	CALL	BDOS
	LXI	H,STARTM	;TELL USER WE'RE STILL HERE
	CALL	PMSG
	LDA	BDISK
	MOV	C,A
	LHLD	CCPJMP
	PCHL		;GOTO CONSOLE PROCESSOR
;
;	JMP TABLE TO OVERLAY BIOS WITH NEW ZEX-BASED JUMPS
;
LOCJMP:
	JMP	NWARM	;WARM
	JMP	BCONST	;CONST
	JMP	NCONIN	;CONIN
	JMP	NCONOT	;CONOT
;
;	CONSOLE INPUT INTERCEPT ROUTINE
;
NCONIN:
	LDA	UIFLG	;USER INPUT ACTIVE?
	ORA	A	;0=NO
	JNZ	BCONIN	;GET INPUT VIA BIOS IF USER INPUT ACTIVE
	LXI	H,0
	DAD	SP	;SAVE RETURN STACK LEVEL
	SHLD	CONSTK
	LXI	SP,MEMTOP	;SET USER STACK
NCONNL:
	CALL	BCONST	;GET CONSOLE STATUS
	ORA	A
	JZ	GETBUF	;GET CHARACTER FROM BUFFER
	CALL	BCONIN	;GET CHARACTER
	CPI	'C'-'@' ;SEE IF TERMINATE CHARACTER
	JZ	ZEXABRT
	CPI	'S'-'@' ;13H
	JNZ	NCONEX
	CALL	BCONIN	;WAIT FOR NEXT CHARACTER
	ANI	7FH
	LHLD	REVBUF
	INX	H
	MOV	M,A
	SHLD	REVBUF
	MVI	A,'S'-'@'	;13H
NCONEX:
	LHLD	CONSTK	;RESTORE CALLER'S STACK
	SPHL
	RET
;
;  RETURN NEXT CHAR FROM INPUT BUFFER
;
GETBUF:
	LDA	PSUPFL	;SET PRINT SUPPRESS FLAG FOR NCONOT
	STA	OUTFLG
GBUF1:
	CALL	GETCHR	;GET NEXT CHARACTER
	CPI	UICH	;USER INPUT?
	JZ	UISTRT	;YES..SET USER INPUT PENDING FLAG
	CPI	REXEC	;RE-EXECUTE?
	JZ	REXECR	;YES..RESET BUFFER PTR
	CPI	CRWAIT	;CR WAIT?
	JZ	CRWRTN	;YES..WAIT FOR CR
	CPI	CRBWAIT	;CR WAIT WITH RING BELL?
	JZ	CRBWRTN	;YES..WAIT FOR CR AND RING BELL
	CPI	RNG	;RING BELL?
	JZ	RNGBELL	;YES..JUST RING THE BELL
	CPI	MSUP	;MESSAGE SUPPRESS FLAG?
	JZ	MSUPCK	;YES..TOGGLE FLAG
	CPI	PSUP	;PRINT SUPPRESS ?
	JZ	PSUPCK	;YES..TOGGLE FLAG
	CPI	IMON	;IMMEDIATE MODE START ?
	JZ	IMFLGS	;YES..SET FLAG
	CPI	IMOFF	;IMMEDIATE MODE STOP?
	JZ	IMFLGS	;YES..RESET FLAG
	CPI	CR	;CR?
	JNZ	GETEXT	;NO...EXIT
	XRA	A
	STA	OUTFLG	;YES..RESET PRINT SUPPRESSION
	MVI	A,CR
GETEXT:
	MOV	C,A
	LDA	IMFLG
	CPI	IMON	;IMMEDIATE MODE ?
	MOV	A,C
	JNZ	NCONEX	;NO...RETURN TO CALLER WITH CHAR
	CALL	BCONOT	;YES..IMMEDIATE ECHO TO CONSOLE
	JMP	NCONNL	;...LOOP UNTIL IMOFF
;
;  ^" COMMAND
;
UISTRT:
	MVI	A,0FFH	;TURN ON FLAG
	STA	UIFLG	;SET USER INPUT PENDING
	LHLD	CONSTK	;RESTORE CALLER'S STACK
	SPHL
	JMP	NCONIN	;GET CHAR FROM USER FOR NOW
;
;  ^: COMMAND
;
REXECR:
	LHLD	SAVBUF	;START AT TOP OF BUFFER AGAIN
	SHLD	REVBUF
	XRA	A
	STA	IMFLG	;RESET ALL FLAGS
	STA	PSUPFL
	STA	MSUPFL
	STA	UIFLG
	JMP	NCONNL	;...LOOP UNTIL ^C
;
;  ^? COMMAND
;
CRWRTN:
	CALL	BCONIN	;GET INPUT CHAR
	CPI	'C'-'@'
	JZ	ZEXABRT	;=^C
	CPI	CR
	JZ	CRWRTX	;=<CR>
	CPI	' '
	JZ	CRWRTX	;=<SP>
	MVI	C,BELL
	CALL	BCONOT	;<>CR
	JMP	CRWRTN
;
;  ^/ COMMAND
;
CRBWRTN:
	LXI	H,DELAY	;SET COUNTER
CRBWR1:
	PUSH	H	;SAVE COUNTER
	CALL	BCONST	;CHECK STATUS
	POP	H	;GET COUNTER
	ORA	A	;SET FLAGS
	JNZ	CRBWR2
	DCX	H	;COUNT DOWN
	MOV	A,H	;DONE?
	ORA	L
	JNZ	CRBWR1
	MVI	C,BELL	;RING BELL
	CALL	BCONOT
	JMP	CRBWRTN
CRBWR2:
	CALL	BCONIN	;GET CHAR
	CPI	'C'-'@'	;ABORT?
	JZ	ZEXABRT
	CPI	CR	;CONT IF <CR>
	JNZ	CRBWRTN
;
;  ^| COMMAND
;
CRWRTX:
	MOV	C,A	;ECHO CR/LF
	CALL	NCONOT
	MVI	C,LF
	CALL	NCONOT
	JMP	GETBUF
;
;  ^* COMMAND
;
RNGBELL:
	MVI	C,BELL	;RING BELL
	CALL	NCONOT
	JMP	GETBUF
;
;  ^. COMMAND
;
PSUPCK:
	LXI	H,PSUPFL
	CMP	M
	JNZ	PSUPST	;SET FLAGS IF NOT EQUAL
	XRA	A	;ELSE RESET FLAGS
PSUPST:
	MOV	M,A	;SET/RESET SAVED FLAG
	JMP	GETBUF	;AND GET NEXT CHARACTER (SETS EXEC FLAG)
;
;  ^# COMMAND
;
MSUPCK:
	LXI	H,MSUPFL
	CMP	M
	JNZ	MSUPST	;SET FLAGS IF NOT EQUAL
	XRA	A	;ELSE RESET FLAG
MSUPST:
	MOV	M,A	;SET/RESET FLAG
	JMP	GETBUF	;AND GET NEXT CHARACTER
;
;  ^< AND ^> COMMANDS
;
IMFLGS:
	STA	IMFLG	;SET/RESET IMMEDIATE MODE FLAG
	JMP	GETBUF	;GET NEXT CHARACTER
;
;	CONSOLE OUTPUT INTERCEPT ROUTINE
;
NCONOT:
	LDA	OUTFLG	;PRINT SUPPRESSION?
	ORA	A
	RNZ		;YES...IGNORE ECHO
	MOV	A,C
	STA	PMCHR
	LDA	NUICH	;END OF USER INPUT?
	CMP	C
	JNZ	BCONOT
	XRA	A	;A=0
	STA	UIFLG	;CLEAR USER INPUT
	JMP	BCONOT
;
;  GET NEXT CHAR FROM BUFFER AND TERMINATE ZEX IF END OF BUFFER
;
GETCHR:
	LHLD	REVBUF
	MOV	A,M
	DCX	H
	SHLD	REVBUF
	CPI	0FFH	;EOB?
	RNZ		;NO...RETURN
	LHLD	REVBUF
	INX	H	;POINT TO EOB
	SHLD	REVBUF
	CALL	MOVBAK	;MOVE JUMPS BACK
	CALL	BDOSRST	;RESTORE BDOS ADDRESS
	CALL	PMCHRS	;RESTORE PROMPT
	LXI	H,DONEM ;TELL USER WE'RE DONE
	CALL	PMSG
	LHLD	CONSTK	;GET OLD STACK
	SPHL
	JMP	BCONIN
;
;  RESTORE BDOS JMP IF NECESSARY
;
BDOSRST:
	LHLD	MEMTOP	;SEE IF BDOS+1=MEMTOP
	XCHG
	LHLD	BDOS+1
	MOV	A,E
	SUB	L
	MOV	A,D
	SBB	H
	RNZ		;DON'T REPLACE BDOS JUMP
	INX	D	;PT TO BDOS JUMP
	LDAX	D	;GET LOW ADDRESS
	MOV	L,A	;... IN L
	INX	D
	LDAX	D	;GET HIGH ADDRESS
	MOV	H,A	;... IN H
	SHLD	BDOS+1	;RESET BDOS JUMP
	RET
;
;	^C ABORT EXIT
;
ZEXABRT:
	LXI	SP,MEMTOP	;^C ABORTS EX
	LXI	H,ABORTD	;ABORT
	CALL	PMSG
	JMP	WARMX
;
WARMR:
	CALL	PMCHRS	;RESTORE PROMPT
	LXI	H,DONEM ;END MESSAGE
	CALL	PMSG
;
;  ABORT ZEX AND RETURN TO ZCPR2
;
WARMX:
	LXI	H,DONEC	;CLASSIC FINISH
	CALL	PMSG
	CALL	MOVBAK	;MOVE JUMPS BACK
	LHLD	EXMBASE	;MULTIPLE COMMAND LINES ENABLED?
	MOV	A,H	;ANY ON?
	ORA	L
	JZ	WARM	;NONE ON IF ADDRESS IS ZERO, SO JUST WARM BOOT
;
;  THIS SECTION OF CODE CLEARS THE MULTIPLE COMMAND LINE BUFFER
;
	MOV	D,H	;DE PTS TO MULTIPLE COMMAND BUFFER ALSO
	MOV	E,L
	PUSH	H	;SAVE PTR
	LXI	H,4	;PT TO FIRST CHAR OF LINE
	DAD	D
	MVI	M,0	;SET FIRST CHAR OF LINE TO ZERO FOR EOL
	XCHG		;DE PTS TO FIRST CHAR OF LINE
	POP	H	;GET PTR
	MOV	M,E	;STORE ADDRESS OF EMPTY COMMAND (EOL)
	INX	H
	MOV	M,D
	JMP	WARM
;
;	SUBROUTINES
;
MOVBAK:
	LHLD	WARMPT	;MOVE OLD JUMP TABLE BACK TO BIOS
	XCHG
	LXI	H,BSWARM
	MVI	B,12
	CALL	MOVE
	JMP	F121	;CALL 1.2.1 FIX FOR MBASIC	1.1.2
;
MOVE:
	MOV	A,M	;MOVE STRING FROM (HL) TO (DE) FOR LENGTH IN B
	INX	H
	STAX	D
	INX	D
	DCR	B
	JNZ	MOVE
	RET
;
PMCHRS:
	LDA	PMCHR	;SET PROMPT CHAR ONLY IF SPECIAL CHARACTER
	CPI	' '+1
	RC
	CPI	'0'
	JC	PMCHRX
	CPI	'9'+1
	RC
	CPI	'A'
	JC	PMCHRX
	CPI	'Z'+1
	RC
	CPI	'a'
	JC	PMCHRX
	CPI	'z'+1
	RC
PMCHRX:
	STA	DONEC1
	RET
;
PMSG:
	LDA	MSUPFL	;PRINT MESSAGE AT (HL)
	CPI	MSUP	;MESSAGES SUPPRESSED?
	RZ		;YES..EXIT
	PUSH	H
PMSGL:
	POP	H
	MOV	A,M
	CPI	'$'	;EOM?
	RZ		;YES..EXIT
	INX	H
	PUSH	H
	MOV	C,A
	CALL	BCONOT
	JMP	PMSGL
;
;  REPLACE ZEX ROUTINE JUMPS WITH BIOS JUMPS
;
F121:
	LXI	H,BSWARM	; INSURE ONLY BIOS	1.1.2
	LXI	D,NWARM 	;  CALLS FROM NOW ON	1.1.2
	MVI	B,3		;   FOR PROGRAMS	1.1.2
	CALL	MOVE		;    THAT MAY HAVE	1.1.2
	LXI	H,BCONIN	;     COPIED OUR	1.1.2
	LXI	D,NCONIN	;      ADDRESSES AS	1.1.2
	MVI	B,3		;	IF THEY WERE	1.1.2
	CALL	MOVE		;	 IN THE BIOS.	1.1.2
	LXI	H,BCONOT	;  (MBASIC DOES THIS)	1.1.2
	LXI	D,NCONOT	;			1.1.2
	MVI	B,3		;			1.1.2
	JMP	MOVE		;			1.1.2
;
;	WORKING STORAGE AREA
;
ABORTD:
	DB	CR,LF,'(ZEX Aborted)',CR,LF,'$'
STARTM:
	DB	CR,LF,'(ZEX Active)$'
DONEM:
	DB	CR,LF,'(ZEX Completed)',CR,LF
DONEC:
	DB	'By Your Command '
DONEC1:
	DB	'>$'
;
	REPT	12
	DW	0
	ENDM
MEMTOP:
	DW	0
EXMBASE:
	DW	0
REVBUF:
	DW	0
SAVBUF:
	DW	0
CCPJMP:
	DW	0
WARMPT:
	DW	0
;
;	ORIGINAL BIOS JMP TABLE
;
BSWARM:
	JMP	$
BCONST:
	JMP	$
BCONIN:
	JMP	$
BCONOT:
	JMP	$
;
PMCHR:
	DB	0
PSUPFL:
	DB	0
OUTFLG:
	DB	0
NUICH:
	DB	0
IMFLG:
	DB	0
MSUPFL:
	DB	0
UIFLG:
	DB	0
CONSTK:
	DW	0
;
?PLEN	SET	$
	IF (?PLEN MOD 8) GT 0
?PLEN	SET	(?PLEN AND 0FFF8H)+8;GET NEXT BOUNDARY
	ENDIF
;
DRVERL	EQU	?PLEN
;
DRVL8	EQU	DRVERL/8	;LENGTH OF RELOCATION BIT MAP
	ORG	DRVERL
;
	ENDIF
;
;	END OF EX RELOCATED CODE SEGMENT
;
	END
