* PROGRAM NAME:  SUB
* AUTHOR:  RICHARD CONN (From SuperSUB Ver 1.1 by Ron Fowler)
* VERSION:  2.3
* DATE:  6 JAN 83
* PREVIOUS VERSIONS:  2.2 (7 DEC 82), 2.1 (14 NOV 82), 2.0 (11 OCT 82)
* PREVIOUS VERSIONS:  1.4 (10 OCT 81), 1.3 (7 OCT 81)
* PREVIOUS VERSIONS:  1.2 (5 OCT 81), 1.1 (3 OCT 81), 1.0 (1 OCT 81)
* NOTE:  FOR USE WITH ZCPR2 VERSION 2.6 AND LATER
VERS	EQU	23

;
;
;************************************************
;*		EXTENDED SUBMIT FOR		*
;*		     CP/M			*
;************************************************
;
;	SUB is derived from Ron's SuperSUB program; it provides a different
; format for the command line, a command-search hierarchy like CCPZ, a
; resetting of the DMA address, several additional functions, and there are
; several other additions/changes.  Additionally, ZCPR2-specific enhancements,
; such as appending the rest of the multiple command line to the command file
; and allowing multiple commands on a single line, are permitted.
;
; REVISED 09/13/81 (RGF): added control character translation
;			  fixed bug in line number reporting
;
;	VERSION 1.1		 by Ron Fowler
;	2/18/81 (first written)     WESTLAND, MICH.
;
;
; This program is intended as a replacement for the
; SUBMIT program provided with CP/M.  It provides sev-
; eral new facilities:
;	1) Nestable SUBMIT runs
;	2) Interactive entry of SUBMIT job (no need
;	   to use an editor for simple SUBMIT runs)
;	3) Command line entry of small SUBMIT jobs
;	4) Ability to enter blank lines in an edited
;	   SUBMIT file
;	5) User customization of number of parameters
;	   and drive to send $$$.SUB to
;
; For full details along with examples, see the ac-
; companying documentation file.
;			--Ron Fowler
;
;
; DEFINE BOOLEANS
;
FALSE	EQU	0
TRUE	EQU	NOT FALSE
;
;************************************************************
;
;		--  User customizable options --
;
CURIND	EQU	'$'	;CURRENT USER INDICATOR

FORCE$SUB	EQU	FALSE	;TRUE IF SUBMITTED FILE MUST BE OF TYPE .SUB
TIME$CONST	EQU	0C000H	;DELAY FOR RINGING BELL
DFLT$USER	EQU	0	;DEFAULT USER NUMBER TO SEARCH FOR .SUB FILE
DFLT$DISK	EQU	0	;DEFAULT DISK TO SEARCH FOR .SUB FILE (0=A)
NPAR		EQU	20	;NUMBER OF ALLOWABLE PARAMETERS
QUIET		EQU	FALSE	;SET TO TRUE TO ELIMATE SIGN-ON MSG
CPBASE		EQU	0	;SET TO 4200H FOR HEATH CP/M
OPT		EQU	'/'	;OPTION DELIMITER CHAR
PDELIM		EQU	'$'	;PARAMETER DELIMITER
;
;
;
;************************************************************
;
; CP/M DEFINITIONS
;
FGCHAR	EQU	1	;GET CHAR FUNCTION
FPCHAR	EQU	2	;PRINT CHAR FUNCTION
DIRIOF	EQU	6	;DIRECT CONSOLE I/O
PRINTF	EQU	9	;PRINT STRING FUNCTION
RDBUF	EQU	10	;READ CONSOLE BUFFER
DETVERS	EQU	12	;GET VERSION NUMBER
LOGIN	EQU	14	;LOG IN DISK
OPENF	EQU	15	;OPEN FILE FUNCTION
CLOSEF	EQU	16	;CLOSE FILE FUNCTION
DELETF	EQU	19	;DELETE FILE FUNCTION
READF	EQU	20	;READ RECORD FUNCTION
WRITEF	EQU	21	;WRITE RECORD FUNCTION
MAKEF	EQU	22	;MAKE (CREATE) FILE FUNCTION
GETDSK	EQU	25	;RETURN CURRENT DISK
SETDMA	EQU	26	;SET DMA ADDRESS
UCODE	EQU	32	;GET/SET USER CODE
;
UDFLAG	EQU	CPBASE+4
BDOS	EQU	CPBASE+5
;
FCB	EQU	5CH	;DEFAULT FILE CONTROL BLOCK
FCBEX	EQU	12	;FCB OFFSET TO EXTENT FIELD
FCBRC	EQU	15	;FCB OFFSET TO RECORD COUNT
FCBNR	EQU	32	;FCB OFFSET TO NEXT RECORD
FN	EQU	1	;FCB OFFSET TO FILE NAME
FT	EQU	9	;FCB OFFSET TO FILE TYPE
TBUF	EQU	CPBASE+80H	;DEFAULT BUFFER
TPA	EQU	CPBASE+100H	;TRANSIENT PROGRAM AREA
;
PUTCNT	EQU	TBUF	;COUNTER FOR OUTPUT CHARS
;
; DEFINE SOME TEXT CHARACTERS
;
CTRLC	EQU	3	;^C
BEL	EQU	7	;RING BELL
CR	EQU	13	;CARRIAGE RETURN
LF	EQU	10	;LINE FEED
TAB	EQU	9

	ORG	TPA
;
;  Branch to Start of Program
;
	JMP	SUBMIT

;
;******************************************************************
;
;  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	0FFH	; MAX USER/DISK?
UDREQD:
	DB	0FFH	; ALLOW USER/DISK CHANGE?
PUREQD:
	DB	0FFH	; PRIVILEGED USER?
CDREQD:
	DB	0FFH	; CURRENT INDIC AND DMA?
NDREQD:
	DB	0FFH	; NAMED DIRECTORIES?
Z2CLASS:
	DB	0	; CLASS 0
	DB	'ZCPR2'
	DS	10	; RESERVED

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

;
;  Start of Program
;
SUBMIT:
	LXI	H,0	;SAVE STACK IN CASE
	DAD	SP	;  ONLY HELP REQUESTED
	SHLD	SPSAVE	;(NOT OTHERWISE USED)
	LXI	SP,STACK
	MVI	C,SETDMA	;SET DMA ADDRESS
	LXI	D,TBUF	;SET DMA TO TBUF
	CALL	BDOS
	CALL	START
;
;	SIGN ON MESSAGE
;
	IF	NOT QUIET
	DB	'SUB, Version ',VERS/10+'0','.',(VERS MOD 10)+'0','$'
	ENDIF
;
START:
	POP	D	;RETRIEVE STRING POINTER
	MVI	C,PRINTF
	CALL	BDOS	;PRINT THE SIGN-ON
	LDA	FCB+1	;ANYTHING ON CMD LINE?
	CPI	' '
	JZ	HELP	;NO, GO PRINT HELP
	CALL	INITVAR	;INITIALIZE THE VARIABLE AREA
	CALL	GETPAR	;GET COMMAND LINE PARAMETERS AND EXTRACT OPTION
	CALL	ABORT	;PERFORM ABORT IF FLAG SET
	CALL	SETUP	;SET UP READ OF SUBMIT FILE
	CALL	RDFILE	;READ THE SUBMIT FILE
	CALL	WRSET	;SET UP WRITE OF "$$$.SUB"
	CALL	WRSUB	;WRITE "$$$.SUB"
	JMP	CPBASE	;GO START THE SUBMIT
;
;
;	SETUP SETS UP THE FILE CONTROL BLOCK
;	FOR READING IN THE .SUB TEXT FILE
;
SETUP:
	LXI	H,FCB+FT	;LOOK AT FIRST CHAR OF
	MOV	A,M		;FILE TYPE.  IF IT IS
	CPI	' '		;BLANK, THEN GO MOVE
	JZ	PUTSUB		;"SUB" INTO FT FIELD

	IF	FORCE$SUB	;FILE TYPE MUST BE OF .SUB
	LXI	D,SUBTYP	;FILE TYPE MUST BE .SUB
	MVI	B,3		;3 BYTES
	CALL	COMPAR
	JNZ	NOTFND	;FILE NOT FOUND IF NO TYPE MATCH
	ENDIF

	RET		;  IF NOT BLANK, THEN ACCEPT ANY FILE TYPE
;
;	MOVE "SUB" INTO THE FILE TYPE
;
PUTSUB:
	XCHG		;BY CONVENTION, MOVE FROM
	LXI	H,SUBTYP	; @HL TO @DE
	MVI	B,3
	CALL	MOVE
	RET
;
; MOVE # BYTES IN B REGISTER FROM @HL TO @DE
;
MOVE:
	MOV	A,M	;PICK UP
	STAX	D	;PUT DOWN
	INX	H	;I'M SURE
	INX	D	; YOU'VE SEEN THIS
	DCR	B	; BEFORE...
	JNZ	MOVE	;100 TIMES AT LEAST
	RET		;I KNOW I HAVE!
;
; GETPAR MOVES THE SUBSTITUTION PARAMETERS SPECIFIED
; IN THE COMMAND LINE INTO MEMORY, AND STORES THEIR
; ADDRESSES IN THE PARAMETER TABLE.  THIS ALLOWS
; SUBSTITUTION OF $1, $2, ETC., IN THE SUBMIT COMMANDS
; WITH THEIR ACTUAL VALUES SPECIFED IN THE COMMAND
; LINE.
;
GETPAR:
	XRA	A	;A=0
	STA	AFLAG	;TURN OFF ABORT COMMAND
	LXI	H,TBUF+1	;WHERE WE FIND THE COMMAND TAIL
	CALL	SCANTO	;SKIP SUBMIT FILE NAME
	STA	OPTION	;FIRST CHAR OF CMD LINE IS OPTION
	RC		;LINE ENDED?
	CPI	OPT	;NO, CHECK OPTION
	JNZ	GLP0	;NOT KEYBOARD INP, READ FILE
	INX	H	;POINT PAST '/'
	MOV	A,M	;GET OPTION CHAR
	CPI	'A'	;ABORT COMMAND
	JZ	GPARABT
	CPI	'D'	;DO COMMAND
	JZ	GPARDO
	CPI	'I'	;INTERACTIVE MODE
	JZ	GPARINT	;SKIP TO EOL AND RETURN
	JMP	HELP	;HELP OTHERWISE
GPARABT:
	MVI	A,0FFH	;TURN ON ABORT FLAG
	STA	AFLAG
	INX	H	;GET POSSIBLE BELL OPTION
	MOV	A,M
	CPI	'B'	;BELL OPTION
	RNZ
	MVI	A,0FFH	; SET BELL FLAG
	STA	BELL$FLAG
	RET
GPARINT:
	XRA	A	;TURN OFF COMMAND LINE INPUT
	STA	CLFLAG
	RET
GPARDO:
	INX	H	;SKIP TO <SP>
	MOV	A,M	;GET CHAR
	CPI	' '+1	;LOOK FOR <SP> OR LESS
	JNC	GPARDO
SLSCAN:
	SHLD	CLPTR	;SAVE CMD LINE PTR
	MOV	A,M	;KBD IS SOURCE, GET EOL FLAG
	STA	CLFLAG	;SAVE AS EOL FLAG
	CPI	' '	;ALLOW SPACES AFTER '/'
	RNZ		;GOT NON-BLANK, DONE
	INX	H	;ELSE CONTINUE SCAN
	JMP	SLSCAN
GLP0:
	MOV	A,M	;INPUT IS FROM A .SUB FILE..THIS
	INX	H	;  CODE SKIPS OVER THE NAME OF
	ORA	A	;  THE SUB FILE TO GET TO THE
	RZ		;  COMMAND LINE PARAMETERS
	CPI	' '
	JZ	GLP
	CPI	TAB
	JNZ	GLP0
GLP:
	CALL	SCANTO	;PASS UP THE BLANKS
	RC		;CY RETURNED IF END OF CMD LINE
	CALL	PUTPAR	;NOW PUT THE PARAMETER INTO MEM
	RC		;CY RETURNED IF END OF CMD LINE
	JMP	GLP	;GET THEM ALL
;
; SCANTO SCANS PAST BLANKS TO THE FIRST NON-BLANK. IF
; END OF COMMAND LINE FOUND, RETURNS CARRY SET.
;
SCANTO:
	MOV	A,M
	INX	H
	ORA	A	;SET FLAGS ON ZERO
	STC		;IN CASE ZERO FOUND (END OF CMD LIN)
	RZ
	CPI	' '
	JZ	SCANTO	;SCAN PAST BLANKS
	CPI	TAB	;DO TABS TOO, JUST FOR
	JZ	SCANTO	;  GOOD MEASURE
	DCX	H	;FOUND CHAR, POINT BACK TO IT
	ORA	A	;INSURE CARRY CLEAR
	RET
;
; PUTPAR PUTS THE PARAMETER POINTED TO BY HL INTO
; MEMORY POINTED TO BY "TXTPTR".  ALSO STORES THE
; ADDRESS OF THE PARAMETER INTO THE PARAMETER TABLE
; FOR EASY ACCESS LATER, WHEN WE WRITE $$$.SUB
;
PUTPAR:
	PUSH	H	;SAVE POINTER TO PARM
	LHLD	TXTPTR	;NEXT FREE MEMORY
	XCHG		;  INTO DE
	LHLD	TBLPTR	;NEXT FREE AREA OF TABLE
	MOV	A,M	;NON-ZERO IN TABLE
	ORA	A	; INDICATES TABLE
	JNZ	PAROVF	; TABLE OVERFLOW (TOO MANY PARMS)
	MOV	M,E	;STORE THE PARM ADRS
	INX	H
	MOV	M,D
	INX	H
	SHLD	TBLPTR	;SAVE TABLE PNTR FOR NEXT TIME
	POP	H	;GET BACK PARM POINTER
	PUSH	D	;SAVE FREE MEM POINTER BECAUSE
			;  WE WILL HAVE TO HAVE IT BACK
			;  LATER TO STORE THE LENGTH
	INX	D	;POINT PAST LENGTH STORAGE
	MVI	B,0	;INITIALIZE LENGTH OF PARM
PPLP:
	MOV	A,M	;GET NEXT BYTE OF PARM
	INX	H
	ORA	A	;TEST FOR END OF CMD LINE
	JZ	PP2	;JUMP IF END
	CPI	' '	;TEST FOR END OF COMMAND
	JZ	PP2
	CPI	TAB	;TAB ALSO ENDS COMMAND
	JZ	PP2
	STAX	D	;PUT PARAMETER BYTE-BY-BYTE
	INX	D	;INTO FREE MEMORY
	INR	B	;BUMP LENGTH
	JMP	PPLP
PP2:
	XCHG
	SHLD	TXTPTR	;NEW FREE MEMORY POINTER
	POP	H	;REMEMBER OUR LENGTH POINTER?
	MOV	M,B	;STORE THE LENGTH
	XCHG		;HAVE TO RETN HL > CMD LINE
	ORA	A	;NOW RETURN END OF LINE FLAG
	STC
	RZ		;RETURN CY IF ZERO (EOL MARK)
	CMC
	RET
;
;
;	ABORT CHECKS TO SEE IF THE ABORT FLAG IS SET AND
;	EXECUTES THE ABORT FUNCTION IF SO
;
;
ABORT:
	LDA	AFLAG	;GET THE FLAG
	ORA	A	;0=NO
	RZ
	LXI	D,ABMSG	;PRINT ABORT MESSAGE
	MVI	C,PRINTF
	CALL	BDOS
	CALL	CHARINB	;GET RESPONSE
	CPI	'A'	;ABORT?
	JZ	ABORT0	;RETURN TO CP/M
	CPI	CTRLC	;ABORT?
	JNZ	ABORT1	;RETURN TO CP/M
ABORT0:
	LXI	D,SUBFCB	;DELETE SUBMIT FILE
	MVI	C,DELETF
	CALL	BDOS
	LXI	D,ABMSG1	;PRINT DONE MESSAGE
	MVI	C,PRINTF
	CALL	BDOS
	JMP	CPBASE	;RETURN TO CP/M
ABORT1:
	LXI	D,ABMSG2	;PRINT CONTINUATION MESSAGE
	MVI	C,PRINTF
	CALL	BDOS
	JMP	CPBASE	; RETURN TO CP/M
ABMSG:
	DB	CR,LF,'Abort SUB File'
	DB	CR,LF,'Do you wish to abort execution?'
	DB	CR,LF,'  Enter A or ^C to Abort or anything else to '
	DB	'continue - $'
ABMSG1:
	DB	'Execution Aborted$'
ABMSG2:
	DB	'Continuing Execution$'
;
;	INPUT CHAR FROM CON:; RING BELL EVERY SO OFTEN IF FLAG SET
;
CHARINB:
	LDA	BELL$FLAG	; GET FLAG
	ORA	A		; 0=NO
	JZ	CHARIN
	PUSH	H		; SAVE HL
CHARINB$LOOP:
	LXI	H,TIME$CONST	; GET TIME CONSTANT
CHARINB$LOOP1:
	DCX	H		; COUNT DOWN
	MOV	A,H
	ORA	L
	JNZ	CHARINB$LOOP1
	MVI	E,0FFH		; REQUEST STATUS
	MVI	C,DIRIOF	; DIRECT I/O
	CALL	BDOS
	ORA	A		; ANY INPUT?
	JNZ	CHARINB$DONE
	MVI	E,BEL		; RING BELL
	MVI	C,2		; OUTPUT TO CON:
	CALL	BDOS
	JMP	CHARINB$LOOP
CHARINB$DONE:
	POP	H		; RESTORE HL
	JMP	CHARIN1

;
;	INPUT CHAR FROM CON:; CAPITALIZE IT AND ECHO <CRLF>
;
CHARIN:
	MVI	C,FGCHAR	;GET CHAR
	CALL	BDOS
CHARIN1:
	CALL	UCASE		;CAPITALIZE
	PUSH	PSW		;SAVE IT
	CALL	CRLF		;NEW LINE
	POP	PSW		;GET IT
	RET
;
;	RDFILE READS THE .SUB FILE SPECIFIED
;	IN THE SUBMIT COMMAND INTO MEMORY
;
RDFILE:
	LXI	H,0	;INIT LINE NUMBER
	SHLD	LINNUM
	LDA	OPTION	;USING A FILE?
	CPI	OPT	;OPT OPTION TELLS
	JNZ	RDFILE1	;JUMP IF NOT
	LXI	D,RDLMSG	;READING LINE MESSAGE
	MVI	C,PRINTF
	CALL	BDOS
	JMP	LINE
RDFILE1:
	LXI	D,RDFMSG	;READING FILE MESSAGE
	MVI	C,PRINTF
	CALL	BDOS

*  CHECK FOR .SUB FILE IN CURRENT USER/CURRENT DISK
	LXI	D,FCB	;WE ARE, OPEN IT
	CALL	INITFCB	;INIT FCB
	LXI	H,INTPATH	;SET ADDRESS OF PATH
	LDA	EPAVAIL	;EXTERNAL PATHS AVAILABLE?
	ORA	A	;0=NO
	JZ	OSB1	;USE INTERNAL PATH
	LHLD	EPADR	;PT TO EXTERNAL PATH
OSB1:
	CALL	FNDFILE	;SEARCH FOR FILE
	JZ	NOTFND	;FILE NOT FOUND
	LXI	D,FCB	;PT TO FCB
	MVI	C,OPENF	;OPEN FILE
	CALL	BDOS
LINE:
	LHLD	LINNUM	;BUMP LINE NUMBER
	INX	H
	SHLD	LINNUM
	LHLD	PREV	;GET PREV PREVIOUS LINE POINTER
	XCHG
	LHLD	TXTPTR	;GET CURRENT FREE MEM POINTER
	SHLD	PREV	;MAKE IT THE PREV LINE (FOR NXT PASS)
	MOV	M,E	;STORE AT BEGIN OF CURRENT LINE,
	INX	H	;  A POINTER TO THE PREVIOUS
	MOV	M,D
	INX	H
	PUSH	H	;LATER WE WILL PUT LENGTH HERE
	INX	H	;SKIP PAST LENGTH
	MVI	C,0	;INITIALIZE LENGTH TO ZERO
LLP:
	CALL	GNB	;GET NEXT BYTE FROM INPUT SOURCE
	JC	EOF	;CY SET IF END OF FILE FOUND
	ANI	7FH	;MASK OUT MSB
	CALL	UCASE	;CONVERT TO UPPER CASE
	CPI	1AH	;SEE IF CPM END OF FILE INDICATOR
	JZ	EOF
	CPI	LF	;IGNORE LINEFEEDS
	JZ	LLP
	CPI	CR	;IF IT'S A CARRIAGE RETURN,
	JZ	EOL	;  THEN DO END OF LINE
	MOV	M,A	;STORE ALL OTHERS INTO MEMORY
	INX	H
	CALL	SIZE	;MAKE SURE NO MEMORY OVERFLOW
	INR	C	;BUMP CHAR COUNT
	JM	LENERR	;MAX OF 128 CHARS PER LINE
	JMP	LLP	;GO DO NEXT CHAR
RDFMSG:
	DB	CR,LF,'Process SUB File$'
RDLMSG:
	DB	CR,LF,'Input SUB File Command Lines$'
;
;	DO END OF LINE SEQUENCE
;
EOL:
	SHLD	TXTPTR	;SAVE FREE MEMORY POINTER
	POP	H	;CURRENT LINE'S LENGTH POINTER
	MOV	M,C	;STORE LENGTH AWAY
	JMP	LINE	;GO DO NEXT LINE
;
;	END OF TEXT FILE
;
EOF:
	SHLD	TXTPTR	;SAVE FREE MEMORY POINTER
	PUSH	B	;SAVE LINE LENGTH
	CALL	ZMCL	;LOAD REST OF MULTIPLE COMMAND LINE
	POP	B	;RESTORE LINE LENGTH
	POP	H	;CURRENT LINE'S LENGTH POINTER
	MOV	M,C	;STORE LENGTH AWAY
	RET		;ALL DONE READING SUB FILE
;
;  COPY MULTIPLE COMMAND LINE INTO MEMORY BUFFER
;
ZMCL:
	LDA	MCAVAIL	;ANY MULTIPLE COMMANDS?
	ORA	A	;0=NO
	RZ
	LHLD	LINNUM	;BUMP LINE NUMBER
	INX	H
	SHLD	LINNUM
	LHLD	PREV	;GET PREV PREVIOUS LINE POINTER
	XCHG
	LHLD	TXTPTR	;GET CURRENT FREE MEM POINTER
	SHLD	PREV	;MAKE IT THE PREV LINE (FOR NXT PASS)
	MOV	M,E	;STORE AT BEGIN OF CURRENT LINE,
	INX	H	;  A POINTER TO THE PREVIOUS
	MOV	M,D
	INX	H
	PUSH	H	;LATER WE WILL PUT LENGTH HERE
	INX	H	;SKIP PAST LENGTH
	MVI	C,0	;INITIALIZE LENGTH TO ZERO
	XCHG		;DE PTS TO NEXT PLACE TO STORE A BYTE
	LHLD	MCADR	;GET ADDRESS OF MULTIPLE COMMAND LINE BUFFER
	MOV	A,M	;GET LOW
	INX	H
	MOV	H,M	;GET HIGH
	MOV	L,A	;HL PTS TO FIRST BYTE OF MULTIPLE COMMAND LINE
	MOV	B,M	;GET FIRST CHAR IN LINE
	MVI	M,0	;CLEAR LINE
	MOV	A,B	;CHECK TO SEE IF FIRST CHAR IS A SEMICOLON (CMD SEP)
	CPI	';'
	JNZ	ZMCL0
	INX	H	;PT TO 2ND CHAR
	MOV	A,M	;FIRST WAS A SEMICOLON, SO GET SECOND
ZMCL0:
	XCHG		;HL PTS TO NEXT BUFFER SPACE, DE PTS TO MC LINE
	JMP	ZMCL1A	;A=FIRST CHAR IN MC LINE
;
;  MAJOR LOOP TO STORE MULTIPLE COMMAND LINE
;
ZMCL1:
	LDAX	D	;GET NEXT BYTE FROM MULTIPLE COMMAND LINE
ZMCL1A:
	ORA	A	;0=EOL
	JZ	ZMCL2
	ANI	7FH	;MASK OUT MSB
	CALL	UCASE	;CONVERT TO UPPER CASE
	MOV	M,A	;STORE CHAR INTO MEMORY
	INX	H	;PT TO NEXT CHAR
	INX	D
	CALL	SIZE	;MAKE SURE NO MEMORY OVFL
	INR	C	;INCR CHAR COUNT
	JM	LENERR	;MAX OF 128 CHARS IN LINE
	JMP	ZMCL1
;
;  DONE WITH INPUT OF MULTIPLE COMMAND LINE -- SAVE CHAR CNT AND SET PTR
;
ZMCL2:
	SHLD	TXTPTR	;SAVE PTR
	POP	H	;PT TO CHAR COUNT POSITION
	MOV	M,C	;STORE CHAR COUNT
	RET

*
*  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:
	CPI	CURIND		;CURRENT DRIVE SPECIFIED?
	JNZ	FF3		;SKIP DEFAULT DRIVE SELECTION IF SO
	LDA	UDFLAG		;GET DEFAULT USER/DISK
	ANI	0FH		;MASK FOR DEFAULT DISK
	INR	A		;PREP FOR FOLLOWING DCR A
FF3:
	DCR	A		;ADJUST PATH 1 TO 0 FOR A, ETC
	MOV	E,A		;DISK NUMBER IN E
	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
	CPI	CURIND		;CURRENT USER SPECIFIED?
	JNZ	FF4		;DO NOT SELECT CURRENT USER IF SO
	LDA	TMPUSR		;GET ORIGINAL USER NUMBER
FF4:
	MOV	E,A		;SELECT USER
	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
;
;	INITIALIZE KEY FIELDS OF FCB PTED TO BY DE
;
INITFCB:
	PUSH	H	;SAVE HL
	XRA	A	;A=0
	STAX	D	;SET DEFAULT DRIVE
	LXI	H,FCBEX	;PT TO EX FIELD
	DAD	D
	MOV	M,A	;SET EX FIELD TO ZERO
	LXI	H,FCBNR	;PT TO CR FIELD
	DAD	D
	MOV	M,A	;SET CR FIELD TO ZERO
	POP	H	;RESTORE HL
	RET
;
;	GET NEXT BYTE FROM INPUT FILE
;
GNB:
	PUSH	H	;DON'T ALTER ANYBODY
	PUSH	D
	PUSH	B
	LDA	OPTION	;INPUT FROM .SUB FILE?
	CPI	OPT	;TOLD BY ORIG CMD LINE OPTION
	JNZ	NSLASH	;JUMP IF WE ARE
	CALL	GNBKBD	;NO, GET A BYTE FROM KBD INPUT
	JMP	GNBXIT	;THEN LEAVE
NSLASH:
	LDA	IBP	;GET BUFFER POINTER
	ORA	A	;PAST END?
	CM	FILL	;WRAPPED AROUND
	JNC	GNB1	;NO END OF FILE

*  RESTORE CURRENT USER/DISK IF CHANGED
	LDA	UDFLAG	;RESTORE CURRENT DISK
	ANI	0FH	;MASK IN DISK ONLY
	MOV	E,A
	MVI	C,LOGIN	;BDOS FCT
	CALL	BDOS
	LDA	DUSER	;RESTORE CURRENT USER
	MOV	E,A
	MVI	C,UCODE	;BDOS FCT
	CALL	BDOS

	MVI	A,1AH	;FAKE EOF
GNB1:
	MOV	E,A	;PUT IN DE
	MVI	D,0
	INR	A	;POINT TO NEXT
	STA	IBP	;PUT AWAY
	LXI	H,TBUF	;NOW OFFSET INTO BUFR
	DAD	D
	MOV	A,M	;GET CHAR THERE
GNBXIT:
	POP	B	;RESTORE EVERYBODY
	POP	D
	POP	H
	ORA	A	;TURN ON CARRY
	RET
;
;	FILL INPUT BUFFER
;
FILL:
	MVI	C,READF
	LXI	D,FCB
	CALL	BDOS
	ORA	A	;GOT GOOD READ?
	MVI	A,0	;(NEW BUF PTR)
	STC
	RNZ		;RETN CY=EOF
	CMC		;NO EOF, NO CY
	RET
;
; COME HERE TO GET A .SUB CHARACTER WHEN
; WE'RE NOT USING A .SUB FILE ("/" OPTION)
;
GNBKBD:
	LDA	CLFLAG	;USE CP/M CMD LINE?
	ORA	A
	JNZ	GNBCL	;THEN GO DO IT
	LDA	CLCNT	;NOT, CHECK LOCAL
	ORA	A	;  CMD LINE CHAR COUNT
	CM	CLFILL	;REFILL WHEN IT WRAPS BACK
	JC	GKEND	;GOT CARRY (FROM CLFILL), RETURN EOF
	DCR	A	;COUNT DOWN
	STA	CLCNT
	JP	GNBCL	;IF PLUS, BUFFER NOT EMPTY
	MVI	A,CR	;OUT OF CHARS, RETURN A CR
	RET
GKEND:
	MVI	A,1AH	;RETURN EOF
	RET
;
; GET NEXT BYTE OF INPUT FROM CMD LINE @CLPTR
;
GNBCL:
	LHLD	CLPTR	;LOAD THE POINTER
	MOV	A,M	;GET THE CHAR
	INX	H	;BUMP POINTER FOR NEXT TIME
	SHLD	CLPTR
	ORA	A	;PHYSICAL END-OF-LINE
	RNZ		;THIS ONLY NEEDED WHEN INPUT
			;  SOURCE IS ORIG CPM CMD LINE
	MVI	A,1AH	;TRANSLATE THAT TO END OF FILE
	RET
;
; SUBROUTINE TO RE-FILL THE LOCAL COMMAND LINE
;
CLFILL:
	LXI	D,PROMPT	;PRINT A PROMPT
	MVI	C,PRINTF	;USE CP/M FUNCT 9
	CALL	BDOS
	LXI	D,CLBUF ;NOW FILL THE BUFFER
	MVI	C,RDBUF
	CALL	BDOS
	LDA	CLCNT	;RETURN WITH COUNT IN A
	LXI	H,CLTEXT	;RESET THE CMD LINE POINTER
	SHLD	CLPTR
	ORA	A	;SET CY ON LEN NZ
	STC
	RZ
	CMC
	RET
;
;	MAKE SURE NO MEMORY OVERFLOW
;
SIZE:
	LDA	BDOS+2	;HIGHEST PAGE POINTER
	DCR	A	;MAKE IT BE UNDER BDOS
	CMP	H	;CHECK IT AGAINST CURRENT PAGE
	RNC		;NC=ALL OKAY
	JMP	MEMERR	;OTHERWISE ABORT
;
;	SET UP THE $$$.SUB FILE
;	FOR WRITING
;
WRSET:
	LXI	D,WRSUBMSG
	MVI	C,PRINTF
	CALL	BDOS
	LXI	D,SUBFCB
	MVI	C,OPENF
	CALL	BDOS	;OPEN THE FILE
	INR	A	;CHECK CPM RETURN
	JZ	NONE1	;NONE EXISTS ALREADY
;
;	$$$.SUB EXISTS, SO SET
;	FCB TO APPEND TO IT
;
	LDA	SUBFCB+FCBRC	;GET RECORD COUNT
	STA	SUBFCB+FCBNR	;MAKE NEXT RECORD
	RET
;
;	COME HERE WHEN NO $$$.SUB EXISTS
;
NONE1:
	LXI	D,SUBFCB
	MVI	C,MAKEF
	CALL	BDOS
	INR	A
	JZ	NOMAKE	;0FFH=CAN'T CREATE FILE
	RET
;
WRSUBMSG:
	DB	CR,LF,'Writing SUB Execution File to Disk$'
;
;	WRITE THE "$$$.SUB" FILE
;
WRSUB:
	LHLD	PREV	;THIS CODE SCANS BACKWARD
	MOV	A,H	;  THRU THE FILE STORED IN
	ORA	L	;  MEMORY TO THE FIRST NON-
	JZ	NOTEXT	;  NULL LINE.  IF NONE IS
	MOV	E,M	;  FOUND, ABORTS
	INX	H
	MOV	D,M	;HERE, WE PICK UP PNTR TO PREV LINE
	INX	H	;NOW WE POINT TO LENGTH
	XCHG		;WE NEED TO STORE AWAY
	SHLD	PREV	;  POINTER TO PREV LINE
	XCHG
	MOV	A,M	;NOW PICK UP THE LENGTH
	ORA	A	;SET Z FLAG ON LENGTH
	JNZ	WRNTRY	;GOT LINE W/LENGTH: GO DO IT
	LHLD	LINNUM	;NOTHING HERE, FIX LINE NUMBER
	DCX	H	;(WORKING BACKWARD NOW)
	SHLD	LINNUM
	JMP	WRSUB
WRLOP:
	LHLD	PREV	;GET PREV LINE POINTER
	MOV	A,H
	ORA	L	;IF THERE IS NO PREV LINE
	JZ	CLOSE	;  THEN WE ARE DONE
	MOV	E,M	;ELSE SET UP PREV FOR NEXT
	INX	H	;  PASS THRU HERE
	MOV	D,M
	INX	H
	XCHG		;NOW STORE IT AWAY
	SHLD	PREV
	XCHG
WRNTRY:
	CALL	PUTLIN	;WRITE THE LINE TO THE FILE
	LHLD	LINNUM	;BUMP THE LINE NUMBER
	DCX	H	;DOWN (WORKING BACK NOW)
	SHLD	LINNUM
	JMP	WRLOP
;
;	$$$.SUB IS WRITTEN, CLOSE THE FILE
;
CLOSE:
	LXI	D,SUBFCB
	MVI	C,CLOSEF
	JMP	BDOS
;
;	THIS SUBROUTINE WRITES A LINE
;	TO THE $$$.SUB FILE BUFFER,
;	AND FLUSHES THE BUFFER AFTER
;	THE LINE IS WRITTEN.
;
PUTLIN:
	MOV	A,M	;PICK UP LENGTH BYTE
	INX	H	;POINT PAST IT
	STA	GETCNT	;MAKE A COUNT FOR "GET"
	SHLD	GETPTR	;MAKE A POINTER FOR "GET"
	LXI	H,TBUF+1	;TEXT GOES AFTER LENGTH
	SHLD	PUTPTR	;MAKE POINTER FOR "PUT"
	XRA	A	;INITIALIZE PUT COUNT
	STA	PUTCNT
	MOV	B,L	;COUNT FOR CLEAR LOOP
CLR:
	MOV	M,A	;ZERO OUT BUFFER LOC
	INX	H
	INR	B	;COUNT
	JNZ	CLR
;
;	THIS LOOP COLLECTS CHARACTERS
;	FROM THE LINE STORED IN MEMORY
;	AND WRITES THEM TO THE FILE.
;	IF THE "$" PARAMETER SPECIFIER
;	IS ENCOUNTERED, PARAMETER SUB-
;	STITUTION IS DONE
;
PUTLP:
	CALL	GETCHR	;PICK UP A CHARACTER
	JC	FLUSH	;CY = NO MORE CHAR IN LINE
	CPI	'^'	;CONTROL-CHAR TRANSLATE PREFIX?
	JNZ	NOTCX
	CALL	GETCHR	;YES, GET THE NEXT
	JC	CCERR	;ERROR: EARLY END OF INPUT
	SUI	'@'	;MAKE IT A CONTROL-CHAR
	JC	CCERR	;ERROR: TOO SMALL
	CPI	' '
	JNC	CCERR	;ERROR: TOO LARGE
NOTCX:
	CPI	PDELIM	;PARAMETER SPECIFIER?
	JNZ	STOBYT	;IF NOT, JUST WRITE CHAR
	LDA	OPTION	;CHECK OPTION: '$' DOESN'T
	CPI	OPT	;  COUNT IN OPT MODE
	MVI	A,PDELIM	;(RESTORE THE '$')
	JZ	STOBYT
	CALL	LKAHED	;PEEK AT NEXT CHAR
	JC	PARERR	;LINE ENDING MEANS PARAM ERR
	CPI	PDELIM	;ANOTHER "$"?
	JNZ	SUBS	;IF NOT THEN GO DO SUBSTITUTION
	CALL	GETCHR	;GET THE 2ND "$" (WE ONLY LOOKED
			;  AHEAD BEFORE)
STOBYT:
	CALL	PUTCHR	;WRITE CHAR TO FILE
	JMP	PUTLP
;
;	PARAMETER SUBSTITUTION...LOOKS UP THE
;	PARAMETER # AFTER THE "$" AND PLUGS IT
;	IN IF IT EXISTS.
;
SUBS:
	CALL	NUMTST	;IT BETTER BE A NUMBER
	JC	PARERR	;  OTHERWISE PARAM ERROR
	MVI	B,0	;INITIALIZE PARM #
	JMP	LPNTRY	;WE JOIN LOOP IN PROGRESS...
SUBLP:
	CALL	LKAHED	;LOOK AT NEXT CHAR
	JC	DOSUBS	;IF LINE EMPTY, THEN PLUG IN PARM
	CALL	NUMTST	;CHECK FOR NUMERIC
	JC	DOSUBS	;DONE IF NOT
LPNTRY:
	CALL	GETCHR	;NOW REMOVE THE CHAR FROM INPUT STREAM
	SUI	'0'	;REMOVE ASCII BIAS
	MOV	C,A	;SAVE IT
	MOV	A,B	;OUR ACCUMULATED COUNT
	ADD	A	;MULTIPLY  BY TEN
	ADD	A
	ADD	B
	ADD	A
	ADD	C	;THEN ADD IN NEW DIGIT
	MOV	B,A	;RESTORE COUNT
	JMP	SUBLP
;
;	PERFORM THE SUBSTITUTION
;
DOSUBS:
	MOV	A,B	;GET PARM #
	DCR	A	;MAKE ZERO RELATIVE
	JM	PARERR	;OOPS
	CALL	LOOKUP	;LOOK IT UP IN PARM TABLE
	JC	PARERR	;IT'S NOT THERE
	MOV	B,A	;LENGTH IN B
SUBLP1:
	INR	B	;TEST B FOR ZERO
	DCR	B
	JZ	PUTLP	;DONE
	MOV	A,M	;GET CHAR OF REAL PARAMETER
	INX	H	;POINT PAST FOR NEXT TIME
	PUSH	H	;SAVE REAL PARM POINTER
	CALL	PUTCHR	;PUT IT IN THE FILE
	POP	H	;GET BACK REAL PARM POINTER
	DCR	B	;COUNTDOWN
	JMP	SUBLP1
;
;	COME HERE WHEN A LINE IS FINISHED,
;	AND WE NEED TO WRITE THE BUFFER TO DISK
;
FLUSH:
	LXI	D,SUBFCB
	MVI	C,WRITEF
	CALL	BDOS
	ORA	A
	JNZ	WRERR	;CPM RETURNED A WRITE ERROR
	RET
;
;	GETCHR GETS ONE CHAR FROM
;	LINE STORED IN MEMORY
;
GETCHR:
	LXI	H,GETCNT
	MOV	A,M	;PICK UP COUNT
	DCR	A	;REMOVE THIS CHAR
	STC		;PRESET ERROR
	RM		;RETURN CY IF OUT OF CHARS
	MOV	M,A	;UPDATE COUNT
	LHLD	GETPTR	;CURRENT CHAR POINTER
	MOV	A,M	;PICK UP CHAR
	INX	H	;BUMP POINTER
	SHLD	GETPTR	;PUT IT BACK
	CMC		;TURN CARRY OFF
	RET
;
;	PUTCHR PUTS ONE CHAR TO
;	THE OUTPUT BUFFER
;
PUTCHR:
	LXI	H,PUTCNT
	INR	M	;INCREMENT COUNT
	JM	LENERR	;LINE WENT TO > 128 CHARS
	LHLD	PUTPTR	;GET BUFFER POINTER
	ANI	7FH	;MASK OUT MSB
	MOV	M,A	;PUT CHAR THERE
	INX	H	;BUMP POINTER
	SHLD	PUTPTR	;PUT IT BACK
	RET		;ALL DONE
;
;	LOOK AHEAD ONE CHAR IN
;	THE INPUT STREAM.  SET
;	CARRY IF NONE LEFT.
;
LKAHED:
	LDA	GETCNT
	ORA	A	;SEE IF COUNT IS DOWN TO ZERO
	STC		;PRE SET INDICATOR
	RZ
	MOV	A,M	;PICK UP CHAR
	CMC		;TURN OFF CARRY FLAG
	RET
;
;	LOOK UP PARAMETER WITH NUMBER IN
;	A REG. RETURN A=LENGTH OF PARM,
;	AND HL => PARAMETER
;
LOOKUP:
	CPI	NPAR
	JNC	PAROVF	;PARM # TOO HIGH
	MOV	L,A
	MVI	H,0	;NOW HAVE 16 BIT NUMBER
	DAD	H	;DOUBLE FOR WORD OFFSET
	LXI	D,TABLE
	DAD	D	;DO THE OFFSET
	MOV	E,M	;GET ADDRESS OF PARM
	INX	H
	MOV	D,M
	MOV	A,D	;ANYTHING THERE?
	ORA	E
	JNZ	LKUPOK
	XRA	A	;NO, ZERO LENGTH
	RET
LKUPOK:
	XCHG		;NOW IN DE
	MOV	A,M	;PICK UP LENGTH
	INX	H	;POINT PAST LENGTH
	RET
;
;	UTILITY COMPARE SUBROUTINE
;
COMPAR:
	LDAX	D
	CMP	M
	RNZ
	INX	H
	INX	D
	DCR	B
	JNZ	COMPAR
	RET
;
;	NUMERIC TEST UTILITY SUBROUTINE
;
NUMTST:
	CPI	'0'
	RC
	CPI	'9'+1
	CMC
	RET
;
;DECIMAL OUTPUT ROUTINE
;
DECOUT:
	PUSH	B
	PUSH	D
	PUSH	H
	LXI	B,-10
	LXI	D,-1
;
DECOU2:
	DAD	B
	INX	D
	JC	DECOU2
	LXI	B,10
	DAD	B
	XCHG
	MOV	A,H
	ORA	L
	CNZ	DECOUT
	MOV	A,E
	ADI	'0'
	CALL	TYPE
	POP	H
	POP	D
	POP	B
	RET
;
; PRINT CR, LF ON CONSOLE
;
CRLF:
	MVI	A,CR
	CALL	TYPE
	MVI	A,LF
;
; PRINT CHAR IN A ON CONSOLE
;
TYPE:
	PUSH	H	;SAVE REGS
	PUSH	D
	PUSH	B
	MOV	E,A	;PUT IN E FOR CP/M
	MVI	C,FPCHAR
	CALL	BDOS	;PRINT IT
	POP	B	;RESTORE ALL
	POP	D
	POP	H
	RET
;
; CONVERT CHAR IN A TO UPPER CASE
;
UCASE:
	CPI	'a'	;VALIDATE CASE
	RC
	CPI	'z'+1
	RNC
	ANI	5FH	;GOT LC, CONV TO UC
	RET
;
;	ERROR HANDLERS
;
WRERR:
	CALL	ERRXIT
	DB	'Disk Full$'
NOMAKE:
	CALL	ERRXIT
	DB	'Directory Full$'
MEMERR:
	CALL	ERRXIT
	DB	'Memory Full$'
NOTFND:
	CALL	ERRXIT
	DB	'SUBMIT File Not Found$'
PARERR:
	CALL	ERRXIT
	DB	'Parameter$'
PAROVF:
	CALL	ERRXIT
	DB	'Too Many Parameters: $'
LENERR:
	CALL	ERRXIT
	DB	'Line too Long: $'
NOTEXT:
	CALL	ERRXIT
	DB	'SUBMIT File Empty$'
CCERR:
	CALL	ERRXIT
	DB	'Control Character$'
ERRXIT:
	LXI	D,SUBERR	;PRINT ERROR HERALD
	MVI	C,PRINTF
	CALL	BDOS
	POP	D
	MVI	C,PRINTF
	CALL	BDOS
	LXI	D,ERRMSG	;PRINT 2ND HALF MSG
	MVI	C,PRINTF
	CALL	BDOS
	LHLD	LINNUM		;TELL LINE NUMBER
	CALL	DECOUT
	CALL	CRLF
	LXI	D,SUBFCB	;DELETE THE $$$.SUB FILE
	MVI	C,DELETF
	CALL	BDOS
	JMP	CPBASE
;
SUBERR:
	DB	CR,LF,'SUB Error -- $'
ERRMSG:
	DB	' error on line number: $'
;
; PROMPT FOR COMMAND LINE INPUT
;
PROMPT:
	DB	CR,LF,'Command Line? $'
;
;	INITIALIZE ALL VARIABLES
;
INITVAR:
	LXI	H,VAR
	LXI	B,ENDVAR-VAR
INITLP:
	MVI	M,0	;ZERO ENTIRE VAR AREA
	INX	H
	DCX	B
	MOV	A,B
	ORA	C
	JNZ	INITLP
	LXI	H,TABLE	;INIT PARM TABLE POINTER
	SHLD	TBLPTR
	LXI	H,0FFFFH	;MARK END OF TABLE
	SHLD	ENDTBL
	LXI	H,FREMEM	;FREE MEMORY STARTS TXT AREA
	SHLD	TXTPTR
	MVI	A,80H	;FORCE READ
	STA	IBP
	STA	CLCNT	;FORCE CONSOLE READ
;  GET CURRENT USER NUMBER FOR LATER
	MVI	E,0FFH	;GET USER CODE
	MVI	C,UCODE	;BDOS FCT
	CALL	BDOS
	STA	DUSER	;DEFAULT USER
	RET
;
; PRINT HELP WITH PROGRAM OPTIONS
;
HELP:
	LXI	D,HLPMSG	;PRINT THE HELP STUFF
	MVI	C,PRINTF
	CALL	BDOS
	LHLD	SPSAVE	;THEN RETURN W/NO WARM-BOOT
	SPHL
	RET
;
HLPMSG:
	DB	CR,LF,'How to use SUB --',CR,LF
	DB	CR,LF,'SUB<CR>			- print this HELP message'
	DB	CR,LF,'SUB /A <text>		- Abort of SUBMIT File'
	DB	CR,LF,'SUB /AB <text>		- /A and Ring Bell'
	DB	CR,LF,'SUB /D <cmd lines>	- use SUMMARY (DO) mode'
	DB	CR,LF,'SUB /I<CR>		- go into Interactive mode'
	DB	CR,LF,'SUB <FILE> <PARMS>	- as in standard SUBMIT.COM'
	DB	CR,LF
	DB	CR,LF,'In "/I" (interactive) mode, SUB will prompt you'
	DB	CR,LF,'a line at a time for the SUBMIT job input...logical'
	DB	CR,LF,'lines may be combined on the same input line by sep-'
	DB	CR,LF,'erating them with semicolons.  Example:'
	DB	CR,LF,'  A>SUB /D STAT;DIR'
	DB	CR,LF,'specifies two commands on the same input line.',CR,LF
	DB	CR,LF,'Submitted jobs may be nested...SUB does not erase'
	DB	CR,LF,'any existing submit job (appends to them instead).'
	DB	CR,LF
	DB	CR,LF,'To insert a control character into the output, pre-'
	DB	CR,LF,'fix it with a "^" (works in any mode).'
	DB	CR,LF,'$'
;
;	VARIABLE STORAGE
;
VAR	EQU	$
;
AFLAG:
	DB	0	;ABORT FLAG (0=NO)
TXTPTR:
	DW	0	;FREE MEMORY POINTER
TBLPTR:
	DW	0	;POINTER TO PARM TABLE
DUSER:
	DB	0	;DEFAULT USER NUMBER
LINNUM:
	DW	0	;CURRENT LINE NUMBER
PREV:
	DW	0	;POINTER TO PREV LINE
GETCNT:
	DB	0	;COUNTER FOR 'GET'
GETPTR:
	DW	0	;POINTER FOR 'GET'
PUTPTR:
	DW	0	;POINTER FOR 'PUT'
IBP:
	DB	0	;INPUT BUFFER POINTER
CLPTR:
	DW	0	;COMMAND LINE POINTER
CLFLAG:
	DB	0	;USE CP/M CMD LINE FLAG
BELL$FLAG:
	DB	0	;RING BELL ON ABORT FLAG
OPTION:
	DB	0	;OPT OPTION FLAG STORE
TABLE:
	DS	NPAR*3	;PARAMETER TABLE
ENDTBL:
	DW	0FFFFH	;END OF PARAMETER TABLE
;
ENDVAR	EQU	$
;
; COMMAND LINE BUFFER...NOT INITIALIZED
;
CLBUF:
	DB	128	;BUFFER LENGTH
CLCNT:
	DB	0	;CHARACTER COUNTER
CLTEXT:
	DS	128	;THE BUFFER ITSELF
;
SPSAVE:
	DW	0	;STACK POINTER SAVE
;
;
;	FCB FOR $$$.SUB
;
SUBFCB:
	DB	1	;DRIVE SPECIFIER (A SELECTED)
	DB	'$$$     '
SUBTYP:
	DB	'SUB'
	DW	0,0,0,0	;INITIALIZE REST OF FCB
	DW	0,0,0,0
	DW	0,0,0,0
;
;	STACK AREA
;
	DS	200	;WHY NOT?
STACK	EQU	$
FREMEM	EQU	$
;
	END	SUB

