*
*  PROGRAM NAME:  HELP2
*  AUTHOR:  RICHARD CONN
*  DATE:  23 MAY 83
*  VERSION:  4.1
*  PREVIOUS VERSIONS:  4.0 (14 JAN 83), 3.2 (6 JAN 83)
*  PREVIOUS VERSIONS:  3.1 (8 DEC 82), 3.0 (11 OCT 82), 2.0 (30 OCT 81)
*  PREVIOUS VERSIONS:  1.8 (18 OCT 81), 1.7 (10 SEP 81), 1.6 (9 SEP 81)
*  PREVIOUS VERSIONS:  1.5 (9 SEP 81), 1.4 (8 SEP 81), 1.3 (8 SEP 81)
*  PREVIOUS VERSIONS:  1.2 (7 SEP 81), 1.1 (6 OCT 80), 1.0 (18 NOV 79)
*  NOTE:  FOR USE WITH ZCPR 2.0 AND LATER
*
VERS	EQU	41

;
;	This program is Copyright (c) 1982, 1983 by Richard Conn
;	All Rights Reserved
;
;	ZCPR2 and its utilities, including this one, are released
; to the public domain.  Anyone who wishes to USE them may do so with
; no strings attached.  The author assumes no responsibility or
; liability for the use of ZCPR2 and its utilities.
;
;	The author, Richard Conn, has sole rights to this program.
; ZCPR2 and its utilities may not be sold without the express,
; written permission of the author.
;

;
;	HELP supports an online documentation system under ZCPR2.  Refer
; to the file HELP.HLP for more details.
;

;
;  CP/M Constants
;
FALSE		EQU	0
TRUE		EQU	NOT FALSE

UDFLAG		EQU	4	; ADDRESS OF USER/DISK FLAG
BDOS		EQU	5	; ADDRESS OF BDOS ENTRY POINT
FCB		EQU	5CH	; ADDRESS OF FILE CONTROL BLOCK
BUFF		EQU	80H	; ADDRESS OF DMA BUFFER

CR		EQU	0DH	; <CR>
LF		EQU	0AH	; <LF>
FF		EQU	'L'-40H	; CTRL-L = FORM FEED
CTRLZ		EQU	'Z'-40H	; CTRL-Z
CTRLC		EQU	'C'-40H	; CTRL-C

;
;  HELP Control Characters
;
SECT$CHAR	EQU	':'	; DEFINED TO BE COLON
BACKUP$CHAR	EQU	'L'	; BACK UP TO PREVIOUS FRAME CHAR
START$CHAR	EQU	'S'	; JUMP TO START OF INFORMATION CHAR
MENU$CHAR	EQU	'M'	; CHAR TO ABORT TO MENU
CPM$ABORT$CHAR	EQU	CTRLC	; CHAR TO EXIT
LEVEL$RET$CHAR	EQU	'^'	; RETURN TO PREVIOUS HELP LEVEL
ROOT$CHAR	EQU	'.'	; RETURN TO ROOT OF HELP
PRINT$CHAR	EQU	'P'	; PRINT CURRENT INFORMATION SECTION

;
;  Lines/Screen on CRT
;
LINES$PER$SCREEN	EQU	24	; ASSUME 24 LINES/SCREEN

;
;  Number of File Names per Line
;
NAMES$PER$LINE		EQU	6	; 6 NAMES (SPACE = 6*11 COLS)

;
;  Set this to TRUE if all files must be of type HLP
;
FORCE$HLP		EQU	TRUE 	; TRUE IF FILES MUST BE OF TYPE HLP

;
;  Number of Nodes (Levels) in Help File Tree
;
HELP$MAX		EQU	10	; DEFAULT = 10 (SPACE=11*HELP$MAX)


;
; MACROS TO PROVIDE Z80 EXTENSIONS
;   MACROS INCLUDE:
;
$-MACRO 		;FIRST TURN OFF THE EXPANSIONS
;
;	JR	- JUMP RELATIVE
;	JRC	- JUMP RELATIVE IF CARRY
;	JRNC	- JUMP RELATIVE IF NO CARRY
;	JRZ	- JUMP RELATIVE IF ZERO
;	JRNZ	- JUMP RELATIVE IF NO ZERO
;	DJNZ	- DECREMENT B AND JUMP RELATIVE IF NO ZERO
;	LDIR	- MOV @HL TO @DE FOR COUNT IN BC
;	LXXD	- LOAD DOUBLE REG DIRECT
;	SXXD	- STORE DOUBLE REG DIRECT
;
;
;
;	@GENDD MACRO USED FOR CHECKING AND GENERATING
;	8-BIT JUMP RELATIVE DISPLACEMENTS
;
@GENDD	MACRO	?DD	;;USED FOR CHECKING RANGE OF 8-BIT DISPLACEMENTS
	IF (?DD GT 7FH) AND (?DD LT 0FF80H)
	DB	100H	;Displacement Range Error on Jump Relative
	ELSE
	DB	?DD
	ENDIF
	ENDM
;
;
; Z80 MACRO EXTENSIONS
;
JR	MACRO	?N	;;JUMP RELATIVE
	DB	18H
	@GENDD	?N-$-1
	ENDM
;
JRC	MACRO	?N	;;JUMP RELATIVE ON CARRY
	DB	38H
	@GENDD	?N-$-1
	ENDM
;
JRNC	MACRO	?N	;;JUMP RELATIVE ON NO CARRY
	DB	30H
	@GENDD	?N-$-1
	ENDM
;
JRZ	MACRO	?N	;;JUMP RELATIVE ON ZERO
	DB	28H
	@GENDD	?N-$-1
	ENDM
;
JRNZ	MACRO	?N	;;JUMP RELATIVE ON NO ZERO
	DB	20H
	@GENDD	?N-$-1
	ENDM
;
DJNZ	MACRO	?N	;;DECREMENT B AND JUMP RELATIVE ON NO ZERO
	DB	10H
	@GENDD	?N-$-1
	ENDM
;
LDIR	MACRO		;;LDIR
	DB	0EDH,0B0H
	ENDM
;
LDED	MACRO	?N	;;LOAD DE DIRECT
	DB	0EDH,05BH
	DW	?N
	ENDM
;
LBCD	MACRO	?N	;;LOAD BC DIRECT
	DB	0EDH,4BH
	DW	?N
	ENDM
;
SDED	MACRO	?N	;;STORE DE DIRECT
	DB	0EDH,53H
	DW	?N
	ENDM
;
SBCD	MACRO	?N	;;STORE BC DIRECT
	DB	0EDH,43H
	DW	?N
	ENDM
;
; END OF Z80 MACRO EXTENSIONS
;

	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	000H	; MULTIPLE COMMAND LINE?
MXREQD:
	DB	000H	; MAX USER/DISK?
UDREQD:
	DB	000H	; ALLOW USER/DISK CHANGE?
PUREQD:
	DB	000H	; PRIVILEGED USER?
CDREQD:
	DB	000H	; CURRENT INDIC AND DMA?
NDREQD:
	DB	000H	; NAMED DIRECTORIES?
Z2CLASS:
	DB	5	; CLASS 5
	DB	'ZCPR2'
	DS	10	; RESERVED

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

;
;  DEFAULT FILE NAME
;
DEFFN:
	DB	'HELP    '
DEFTYP:
	DB	'HLP'

;
;  Start of Program
;
START:
	LXI	H,0	; GET SP
	DAD	SP
	SHLD	STACK
	LXI	SP,STACK	; NEW STACK
	MVI	E,0FFH		; GET CURRENT USER NUMBER
	MVI	C,32
	CALL	BDOS
	STA	CUR$USER	; CURRENT USER NUMBER
	LDA	BDOS+2	; BASE PAGE OF BDOS
	SUI	10	; 2K + 2 PAGES
	STA	TPA$END
	XRA	A	; A=0
	STA	DFFLG	; TURN OFF DEFAULT FILE FLAG
	STA	HELP$LEVEL	; SET HELP LEVEL TO 0 (NO RETURN FILE)
	LXI	D,HELPMS	; PRINT OPENING MSG
	CALL	PRINT$MESSAGE
	LXI	H,FCB+1	; CHECK FOR FILE NAME
	MOV	A,M
	CPI	' '	; NONE?
	JRNZ	START1

*  INSERT 'HELP.HLP' INTO FCB OR CLEAR FCB
	LXI	D,DEFFN	; PT TO DEFAULT NAME
	MVI	B,11	; 11 BYTES
	XCHG
	CALL	MOVE	; MOVE (HL) TO (DE) FOR (B) BYTES
	MVI	A,1	; TURN ON DEFAULT FILE FLAG
	STA	DFFLG

*  START/RESTART HELP PROGRAM (START ON INITIAL ENTRY, RESTART ON NODE LOAD)
START1:
	LXI	SP,STACK	; SET STACK POINTER

*  CLEAR NON-NAME/TYPE BYTES IN FCB
	LXI	H,FCB	; INITIAL ZERO
	MVI	M,0	; STORE 0 FOR DRIVE (CURRENT LOGGED-IN)
	LXI	D,12	; SKIP TO EXTENT
	DAD	D
	MVI	B,24	; FILL 24 BYTES
FCB$FILL:
	MVI	M,0	; ZERO FILL
	INX	H	; PT TO NEXT
	DJNZ	FCB$FILL

*  CHECK FOR WILD CARDS IN FILE NAME -- ERROR IF SO
	LXI	H,FCB+1	; PT TO FIRST BYTE OF FILE NAME
	MVI	B,11	; 11 BYTES
FCB$WILD$CK:
	MOV	A,M	; GET BYTE
	ANI	7FH	; MASK
	CPI	'?'	; WILD?
	JRZ	FCB$WILD$ERROR
	INX	H	; PT TO NEXT
	DJNZ	FCB$WILD$CK

*  CHECK FOR FILE TYPE
	LXI	H,FCB+9	; CHECK FOR FILE TYPE

	IF	NOT FORCE$HLP	; IF FILE TYPE MAY NOT BE HLP
	MOV	A,M	; CHECK FOR FILE TYPE SPECIFIED
	CPI	' '	; NONE?
	JRNZ	START2
	ENDIF

*  PLACE DEFAULT FILE TYPE OF '.HLP' IN FCB
DEFAULT$EXT:
	LXI	D,DEFTYP	; DEFAULT FILE TYPE
	MVI	B,3
	XCHG
	CALL	MOVE	; MOVE (HL) TO (DE) FOR (B) BYTES

*  OPEN FILE
START2:
	LDA	DFFLG	; CHECK FOR DEFAULT
	ORA	A	; 0=NO
	JNZ	HELP$DEF	; DISPLAY DEFAULT HELP INFO WITH FILE NAMES
	LXI	H,INTPATH	; SET BASE ADDRESS OF PATH
	LDA	EPAVAIL		; EXTERNAL PATH AVAILABLE
	ORA	A		; 0=NO
	JRZ	START2A
	LHLD	EPADR		; GET ADDRESS OF EXTERNAL PATH
START2A:
	LXI	D,FCB		; PT TO FCB
	CALL	FNDFILE		; SEARCH FOR FILE
	JNZ	START3		; FILE FOUND, SO PROCESS
	LDA	EPAVAIL		; SEARCH ALONG EXTERNAL PATH?
	ORA	A		; 0=NO
	JRZ	START2B
	LXI	H,INTPATH	; SELECT INTERNAL PATH THEN
	LXI	D,FCB		; PT TO FCB
	CALL	FNDFILE		; SEARCH
	JNZ	START3		; FILE FOUND

*  FILE NOT FOUND -- FATAL ERROR
START2B:
	LXI	D,ERR1	; FILE NOT FOUND
	CALL	PRINT$MESSAGE
	JMP	HELP$EXIT

*  FILE CONTAINS WILD CARDS -- FATAL ERROR
FCB$WILD$ERROR:
	LXI	D,WILD$ERR	; WILD CARD
	CALL	PRINT$MESSAGE
	JMP	HELP$EXIT

*
*  DISPLAY DEFAULT HELP FILE INFORMATION
*
HELP$DEF:
	LXI	D,HELP$DEF$MSG	; PRINT MESSAGE
	CALL	PRINT$MESSAGE
	CALL	SET$COUNT	; SET COUNTER
	CALL	RESET$SYSTEM	; RETURN HOME
	CALL	PRINT$HELP$FILES	; PRINT HELP FILES AT HOME
	LDA	EPAVAIL		; EXTERNAL PATH?
	LHLD	EPADR		; EXTERNAL PATH ADDRESS
	ORA	A		; 0=NO EXTERNAL PATH
	CNZ	HDFF1
	CALL	RESET$SYSTEM	; RETURN HOME
	LXI	H,INTPATH	; FOLLOW INTERNAL PATH
	CALL	HDFF1		; SEARCH IT
	CALL	RESET$SYSTEM	; RESET CALLING DISK/USER NUMBER
	LXI	D,HELP$EDEF$MSG	; PRINT END MESSAGE
	CALL	PRINT$MESSAGE
	CALL	CHAR$IN		; GET ANY CHAR FOR RESPONSE
	CPI	CTRLC		; ABORT?
	JZ	HELP$EXIT
	XRA	A		; TURN OFF DEFAULT
	STA	DFFLG
	LXI	H,DEFFN		; SET DEFAULT FILE NAME
	LXI	D,FCB+1
	MVI	B,11		; 11 BYTES
	CALL	MOVE
	JMP	START2		; PROCESS DEFAULT HELP FILE

;
; MAIN SEARCH LOOP
;
HDFF1:
	LDA	CINDIC		;GET CURRENT DISK INDICATOR
	MOV	B,A		;... IN B
	MOV	A,M		;GET DRIVE
	ANI	7FH		;MASK MSB
	ORA	A		;0=DONE=COMMAND NOT FOUND
	RZ			;END OF PATH?
;
; LOOK FOR COMMAND IN DIRECTORY PTED TO BY HL; DRIVE IN A
;
	CMP	B
	JRNZ	HDFF2		;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
HDFF2:
	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
	INX	H		;PT TO NEXT PATH ELEMENT
	ANI	7FH		;MASK OUT MSB
	CMP	B
	JRNZ	HDFF3		;DO NOT SELECT CURRENT USER IF SO
	LDA	CUR$USER	;GET ORIGINAL USER NUMBER
HDFF3:
	MOV	E,A		;SELECT USER
	MVI	C,32
	CALL	BENTRY
	PUSH	H		;SAVE PATH PTR
	CALL	PRINT$HELP$FILES
	POP	H		;GET PATH PTR
	JR	HDFF1

*
*  SET FILE NAME COUNTER
*
SET$COUNT:
	MVI	A,NAMES$PER$LINE	; NUMBER OF FILE NAMES/LINE
	STA	NAME$COUNT
	RET

*
*  PRINT NAMES OF HELP FILES
*
PRINT$HELP$FILES:
	LXI	H,FCB		; MAKE FCB = *.HLP
	MVI	M,0		; BEGINNING 0 FOR DEFAULT DISK
	MVI	B,8		; FILL 8 ?'S
	INX	H		; PT TO FIRST CHAR
PHF1:
	MVI	M,'?'		; '?' FILL
	INX	H		; PT TO NEXT
	DJNZ	PHF1
	LXI	D,DEFTYP	; COPY DEFAULT FILE TYPE
	MVI	B,3		; 3 BYTES
	XCHG			; EXCHANGE
	CALL	MOVE		; COPY
	XCHG			; RESTORE PTR
	MVI	B,24		; FILL REST WITH 0'S
PHF2:
	MVI	M,0		; 0 FILL
	INX	H		; PT TO NEXT
	DJNZ	PHF2

*  SEARCH FOR FIRST FILE
	LXI	D,FCB		; PT TO FCB
	MVI	C,17		; SEARCH FOR FIRST
	CALL	BDOS
	CPI	0FFH		; NONE?
	RZ			; DONE IF SO

*  PRINT CURRENT AND SEARCH FOR NEXT
PHF3:
	CALL	PRINT$HFN	; PRINT HELP FILE NAME
	LXI	D,FCB		; PT TO FCB
	MVI	C,18		; SEARCH FOR NEXT
	CALL	BDOS
	CPI	0FFH		; DONE?
	JRNZ	PHF3
	RET

*
*  PRINT NAME OF FILE WHOSE BUFF OFFSET IS IN A
*
PRINT$HFN:
	RRC			; A=A*32
	RRC
	RRC			; RATHER THAN 5 LEFT, I DO 3 RIGHT (NEAT, HUH?)
	ANI	60H		; MASK ALL BUT INTERESTING PART
	LXI	H,BUFF		; PT TO BUFFER
	ADD	L		; PT TO ENTRY
	MOV	L,A
	MOV	A,H
	ACI	0
	MOV	H,A		; HL PTS TO ENTRY
	INX	H		; HL PTS TO HELP FILE NAME
	MVI	B,8		; 8 CHARS
PHFN1:
	MOV	A,M		; GET CHAR
	ANI	7FH		; MASK
	INX	H		; PT TO NEXT
	CALL	CHAR$OUT	; PRINT IT
	DJNZ	PHFN1
	MVI	A,' '		; TRAILING SPACES
	CALL	CHAR$OUT	; 3 OF THEM
	CALL	CHAR$OUT
	CALL	CHAR$OUT
	LDA	NAME$COUNT	; COUNT DOWN
	DCR	A
	STA	NAME$COUNT
	RNZ
	CALL	SET$COUNT	; RESET COUNT
	CALL	CRLF		; NEW LINE
	RET

*
*  LOAD HELP FILE INFORMATION
*
START3:
	LXI	D,FCB		; OPEN FILE
	MVI	C,15		; BDOS FILE OPEN
	CALL	BDOS
	LXI	H,HELP$BUF	; PT TO BUFFER
	SHLD	NEXT$ADR	; SET PTR

*  READ RECORDS UNTIL EOF
START4:
	CALL	READ$RECORD	; READ INFO
	JRZ	START4	; NOT DONE IF ZERO RETURN
	LXI	D,FCB	; CLOSE FILE
	MVI	C,16	; CLOSE
	CALL	BDOS
	CALL	RESET$SYSTEM	; RESTORE CURRENT DISK AND USER IF CHANGED

*
*  START OF HELP PROGRAM
*
HELP:
	LXI	SP,STACK	; RESET STACK
	MVI	A,0	; SET NO FRAME
	STA	FRAME$NUMBER
	LXI	H,HELP$BUF	; PT TO BUFFER
	MOV	A,M	; NO HEADER SECTION?
	ANI	7FH	; MASK OUT MSB
	CPI	SECT$CHAR
	JRNZ	HELP1	; HEADER SECTION EXISTS
	CALL	PRINT$INFO	; PRINT HELP INFO PTED TO BY HL
	LDA	HELP$LEVEL	; CHECK TO SEE IF WE ARE NOT AT LEVEL 0
	ORA	A	; 0=LEVEL 0
	JRZ	HELP$EXIT	; ABORT IF SO
	JMP	LEVEL$RETURN	; GO TO PREVIOUS LEVEL IF NOT

*  EXIT POINT FOR ANY EXIT FROM THE REST OF THE HELP PROGRAM
HELP$EXIT:
	CALL	RESET$SYSTEM	; RESET CALLING DISK/USER NUMBER
	LHLD	STACK	; GET CP/M SP
	SPHL
	RET		; DONE

*  PRINT HEADER INFORMATION AND SELECT AN OPTION
HELP1:
	CALL	PRINT$HEADER	; PRINT HEADER
	PUSH	B	; SAVE C (NUMBER OF VALID SELECTIONS)
	CALL	CRLF1	; NEW LINE
	CALL	PR$LEVEL	; PRINT LEVEL NUMBER
	LXI	D,PROMPT1$MESSAGE	; PRINT PROMPT
	CALL	PRINT$MESSAGE
	LXI	D,PROMPT2$MESSAGE	; LEVEL COMMAND
	LDA	HELP$LEVEL	; CURRENT LEVEL = 0?
	ORA	A	; SET FLAGS
	JRZ	HELP1A
	CALL	PRINT$MESSAGE
HELP1A:
	LXI	D,PROMPT3$MESSAGE
	CALL	PRINT$MESSAGE
	POP	B	; GET C
	CALL	CHAR$IN		; GET RESPONSE
	CPI	CTRLC		; EXIT
	JRZ	HELP$EXIT
	CPI	ROOT$CHAR	; GO TO ROOT
	JZ	GO$ROOT
	CPI	LEVEL$RET$CHAR	; RETURN TO PREVIOUS LEVEL
	JZ	LEVEL$RETURN
	PUSH	PSW	; SAVE CHAR
	CALL	CRLF1
	POP	PSW	; GET CHAR
	SUI	'A'-1		; ADJUST FOR COUNT
	MOV	B,A		; SAVE COUNT
	JRZ	BAD$RESPONSE
	JRNC	HELP2

*  INVALID RESPONSE
BAD$RESPONSE:
	LXI	D,ERR2	; INVALID RESPONSE
	CALL	PRINT$MESSAGE
	JR	HELP1

*  VALID RESPONSE -- LOOK FOR AND PRINT INFORMATION SECTION
HELP2:
	INR	C	; 1 MORE THAN NUMBER OF POSSIBLE SELECTIONS
	CMP	C	; GREATER THAN NUMBER OF POSSIBLE SELECTIONS?
	JRNC	BAD$RESPONSE
	LHLD	FIRST$ENTRY	; GET PTR TO FIRST ENTRY

*  PRINT INFORMATION WHEN COUNT IS ZERO
HELP3:
	DJNZ	HELP4
	CALL	PRINT$INFO	; PRINT INFO PTED TO BY HL
	JR	HELP1

*  LOCATE NEXT INFORMATION SECTION
HELP4:
	MOV	A,M	; <CTRL-Z>?
	ANI	7FH	; MASK OUT MSB
	INX	H	; PT TO NEXT BYTE
	CPI	CTRLZ
	JZ	HELP$ERR	; HELP FILE FORMAT ERROR
	CPI	LF	; LINE FEED (WS FILE)?
	JRZ	HELP5
	CPI	CR	; <CR>?
	JRNZ	HELP4
	INX	H	; 1ST BYTE OF NEXT LINE
HELP5:
	MOV	A,M	; GET CHAR
	ANI	7FH	; MASK OUT MSB
	CPI	SECT$CHAR	; NEW SECTION?
	JRZ	HELP3	; CONTINUE LOOP IF SO
	CPI	CTRLZ	; EOF?
	JRNZ	HELP4	; CONTINUE IF NOT

*  ERROR -- REACHED END OF HELP FILE
HELP$ERR:
	LXI	D,ERR3	; FORMAT ERROR
	CALL	PRINT$MESSAGE
	JMP	HELP1


*********************************************************
*							*
*  HELP SUPPORT ROUTINE SECTION				*
*							*
*********************************************************

*
*  RESTORE CURRENT DISK AND CURRENT USER
*
RESET$SYSTEM:
	LDA	UDFLAG	; GET DISK
	ANI	0FH	; GET DISK NUMBER
	MOV	E,A	; DISK IN E
	MVI	C,14	; SELECT DISK
	CALL	BDOS
	LDA	CUR$USER	; SET USER
	MOV	E,A	; USER IN E
	MVI	C,32	; SET USER CODE
	CALL	BDOS
	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
	JRNZ	FF5		;FOUND IT -- RETURN FOUND FLAG
	XCHG			;HL=FCB PTR
	SHLD	FCBPTR		;SAVE IT
	LHLD	PATH		;PT TO PATH FOR FAILURE POSSIBILITY
;
; MAIN SEARCH LOOP
;
FF1:
	LDA	CINDIC		;GET CURRENT USER/DISK INDICATOR
	MOV	B,A		;... IN B
	MOV	A,M		;GET DRIVE
	ANI	7FH		;MASK MSB
	ORA	A		;0=DONE=COMMAND NOT FOUND
	JRNZ	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:
	CMP	B		;CURRENT DISK?
	JRNZ	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
	CMP	B		;CURRENT USER?
	JRNZ	FF4		;DO NOT SELECT CURRENT USER IF SO
	LDA	CUR$USER	;GET ORIGINAL USER NUMBER
FF4:
	MOV	E,A		;SELECT USER
	MVI	C,32
	CALL	BENTRY
	XCHG			;SAVE PTR TO NEXT PATH ELEMENT IN DE
	LHLD	FCBPTR		;GET PTR TO FCB
	XCHG			;... IN DE, PATH PTR IN HL
	MVI	C,17		;SEARCH FOR FIRST
	CALL	BENTRY		;LOOK FOR FILE
	INR	A		;SET FLAG
	JRZ	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
PATH:
	DS	2	;BASE ADDRESS OF PATH

*
*  INPUT CHAR; CHAR IS IN A
*
CHAR$IN:
	PUSH B ! PUSH D ! PUSH H
	MVI	C,1	; READ CHAR
	CALL	BDOS
	POP H ! POP D ! POP B
	PUSH	PSW	; SAVE CHAR
	CALL	CRLF1
	POP	PSW	; RESTORE CHAR
*
*  CAPITALIZE CHAR IN A
*
CAPS:
	ANI	7FH	; MASK OUT MSB
	CPI	61H	; LESS THAN SMALL A?
	RC
	CPI	7BH	; LESS THAN LEFT BRACE?
	RNC
	ANI	5FH	; CAPITALIZE
	RET

*
*  PRINT CHAR IN A ON CON:
*
CHAR$OUT:
	PUSH PSW ! PUSH B ! PUSH D ! PUSH H
	MVI	C,2	; WRITE
	MOV	E,A	; CHAR IN E
	CALL	BDOS
	POP H ! POP D ! POP B ! POP PSW
	RET

*
*  PRINT ERROR MSG PTED TO BY DE; ENDS IN '$'
*
PRINT$MESSAGE:
	PUSH B ! PUSH D ! PUSH H
	MVI	C,9	; PRINT BUFFER
	CALL	BDOS
	POP H ! POP D ! POP B
	RET

*
*  MOVE BYTES PTED TO BY HL TO AREA PTED TO BY DE; B BYTES TO MOVE
*
MOVE:
	MOV	A,M	; GET BYTE
	ANI	7FH	; MASK OFF MSB -- IN CASE A WS FILE
	STAX	D	; PUT BYTE
	INX	H	; PT TO NEXT
	INX	D
	DJNZ	MOVE
	RET

*
*  READ RECORD FROM DISK; NEXT$ADR CONTAINS ADDRESS TO READ TO
*	ON RETURN, BDOS ERROR CODE IS IN A (0=NO ERROR)
*
READ$RECORD:
	MVI	C,20	; READ NEXT RECORD
	LXI	D,FCB	; PT TO FCB
	CALL	BDOS
	PUSH	PSW	; SAVE RETURN CODE
	LHLD	NEXT$ADR	; PT TO LOAD ADDRESS
	LDA	TPA$END	; CHECK AGAINST END PAGE OF TPA
	CMP	H	; IF AT SAME PAGE, YES
	JZ	READ$ERROR
	LXI	D,BUFF	; PT TO BUFFER TO LOAD FROM
	MVI	B,128	; NUMBER OF BYTES TO MOVE
	XCHG
	CALL	MOVE
	XCHG
	MVI	M,CTRLZ	; STORE ENDING CTRLZ IN CASE OF EOF
	POP	PSW	; GET RETURN CODE
	ORA	A	; DONE?  <>0 IF SO

*  READ DONE -- SAVE PTR TO NEXT BLOCK
	SHLD	NEXT$ADR	; SET NEXT ADDRESS
	RET

READ$ERROR:
	LXI	D,READERR
	CALL	PRINT$MESSAGE
	JMP	HELP$EXIT

*
*  PRINT ONE LINE OF INFO SECTION; HL PTS TO LINE UPON ENTRY;
*	HL PTS TO FIRST CHAR OF NEXT LINE UPON EXIT
*
PRINT$LINE:
	MOV	A,M	; GET CHAR
	ANI	7FH	; MASK OUT MSB
	CPI	CR	; EOL?
	JRZ	CRLF
	CPI	LF	; LINE FEED? (WS FILE)
	JRZ	CRLF0
	CPI	CTRLZ	; END OF FILE?
	JRZ	CRLFC	; DONE IF SO
	CALL	CHAR$OUT	; PRINT CHAR
	INX	H	; PT TO NEXT
	JR	PRINT$LINE

*
*  PRINT CRLF, PT TO FIRST CHAR OF NEXT LINE, AND PAGE IF NECESSARY
*
CRLF:
	INX	H	; PT TO LF
CRLF0:
	INX	H	; PT TO 1ST CHAR OF NEXT LINE
CRLFC:
	CALL	CRLF1	; PRINT CRLF
	LDA	LINE$CNT	; GET LINE COUNT
	DCR	A
	STA	LINE$CNT
	RNZ		; OK -- CONTINUE
	MOV	A,M	; SET MSB OF FIRST CHAR OF NEXT LINE
	ORI	80H
	MOV	M,A	; MSB IS SET FOR LATER BACKUP
FRAME$PAUSE:
	CALL	PR$LEVEL	; PRINT LEVEL NUMBER
	LDA	FRAME$NUMBER	; INCREMENT FRAME NUMBER
	INR	A
	STA	FRAME$NUMBER
	LXI	D,PAGEMS
	CALL	PRINT$MESSAGE	; PRINT PAGE MESSAGE
	LXI	D,PAGE1MS	; NOT LEVEL 0?
	LDA	HELP$LEVEL	; GET LEVEL NUMBER
	ORA	A	; SET FLAGS
	JRZ	FP1
	CALL	PRINT$MESSAGE
FP1:
	LXI	D,PAGE2MS
	CALL	PRINT$MESSAGE
	CALL	CHAR$IN	; GET RESPONSE
	CPI	MENU$CHAR	; ABORT?
	JZ	HELP	; START OVER IF SO
	CPI	CPM$ABORT$CHAR	; ABORT TO OS
	JZ	HELP$EXIT
	CPI	PRINT$CHAR	; PRINT INFO SECTION?
	JZ	LIST1$INFO
	CPI	ROOT$CHAR	; GO TO ROOT
	JZ	GO$ROOT
	CPI	LEVEL$RET$CHAR	; RETURN TO HIGHER LEVEL
	JZ	LEVEL$RETURN
	CPI	BACKUP$CHAR	; BACK UP?
	JZ	FRAME$BACKUP
	CPI	START$CHAR	; JUMP TO START OF INFO
	JZ	INFO$START
FRAME$RESUME:
	SHLD	START$OF$FRAME
	CALL	SET$LINE$CNT
	CALL	CRLF1	; NEW LINE
	RET

*  JUMP TO START OF INFORMATION
INFO$START:
	LHLD	START$OF$INFO	; PT TO START OF INFO
	MVI	A,1		; RESET FRAME COUNT
	STA	FRAME$NUMBER
	JMP	FRAME$RESUME	; CONTINUE PROCESSING

*  BACK UP TO PREVIOUS FRAME
FRAME$BACKUP:
	CALL	BOI$CHECK	; AT BEGINNING OF INFORMATION?
	JRNZ	FB1		; CONTINUE IF NOT
	JMP	FRAME$PAUSE
FB1:
	DCX	H	; BACK UP UNTIL BYTE WITH MSB SET IS FOUND
	MOV	A,M	; GET BYTE
	ANI	80H
	JRZ	FB1
	LDA	FRAME$NUMBER	; DECREMENT FRAME NUMBER
	DCR	A		; BACK UP TO CURRENT FRAME NUMBER
	DCR	A		; BACK UP TO PREVIOUS FRAME NUMBER
	STA	FRAME$NUMBER
	JMP	FRAME$RESUME	; CONTINUE PROCESSING
*
*  PRINT CR AND LF ONLY
*
CRLF1:
	MVI	A,CR	; PRINT CR
	CALL	CHAR$OUT
	MVI	A,LF	; PRINT LF
	JMP	CHAR$OUT

*
*  SET LINE$CNT VARIABLE TO SCREEN SIZE
*
SET$LINE$CNT:
	MVI	A,LINES$PER$SCREEN-1
	STA	LINE$CNT
	RET

*
*  PRINT THE HEADER SECTION AND LOAD FIRST$ENTRY PTR
*    ON RETURN, C=NUMBER OF POSSIBLE SELECTIONS
*
PRINT$HEADER:
	MVI	A,0	; SET NO FRAME
	STA	FRAME$NUMBER
	LXI	H,HELP$BUF
	CALL	SET$LINE$CNT
	LDA	LINE$CNT
	DCR	A
	STA	LINE$CNT
	MVI	A,'A'	; INIT SELECTION CHAR
	STA	SEL$CHAR
	LXI	D,SELECTMS
	CALL	PRINT$MESSAGE
	MVI	C,0	; COUNT NUMBER OF SELECTIONS

* PRINT LINE UNTIL FIRST INFORMATION SECTION FOUND
PH1:
	MOV	A,M	; GET CHAR
	ANI	7FH	; MASK OUT MSB
	CPI	SECT$CHAR
	JRZ	PH2
	CPI	CTRLZ	; EOF? -- ABORT
	JZ	HELP$EXIT
	INR	C	; INCREMENT SELECTION COUNT
	LDA	SEL$CHAR	; DISPLAY SELECTION CHAR
	CALL	CHAR$OUT
	INR	A	; INCR CHAR
	STA	SEL$CHAR
	MVI	A,'.'
	CALL	CHAR$OUT
	MVI	A,' '
	CALL	CHAR$OUT
	CALL	PRINT$LINE	; PRINT HEADER LINE
	JR	PH1

*  SAVE PTR TO FIRST ENTRY
PH2:
	SHLD	FIRST$ENTRY
	LDA	LINE$CNT	; GET COUNT OF REMAINING LINES
	MOV	B,A		; ... IN B
	ORA	A		; ANY LEFT?
	RZ
PH3:
	CALL	CRLF1		; NEW LINE
	DJNZ	PH3
	RET

*
*  PRINT AN INFORMATION SECTION
*    INFORMATION SECTION IS PTED TO BY HL
*
PRINT$INFO:
	SHLD	START$OF$INFO	; SET START OF INFORMATION POINTER
	CALL	LOAD$NODE	; LOAD NEW NODE IF DUAL SECT$CHAR
	SHLD	START$OF$FRAME	; SET FRAME POINTER
	MOV	A,M	; SET MSB
	ORI	80H
	MOV	M,A
	CALL	SET$LINE$CNT
	MVI	A,1		; A=1
	STA	FRAME$NUMBER	; SET FRAME NUMBER
PI1:
	CALL	PRINT$LINE	; PRINT LINE FROM INFO FILE
	MOV	A,M	; DONE?
	ANI	7FH	; MASK OUT MSB
	CPI	CTRLZ	; EOF?
	JRZ	PI2
	CPI	SECT$CHAR	; NEXT SECTION
	JRZ	PI2
	CPI	FF	; FORM FEED?
	JRNZ	PI1
	INX	H	; PT TO CHAR AFTER FORM FEED
	CALL	FORM$FEED	; FEED SCREEN
	JR	PI1

*  FORM FEED SCREEN
FORM$FEED:
	LDA	LINE$CNT	; GET LINE COUNT
	MOV	B,A	; ... IN B
FEED$LOOP:
	PUSH	B	; SAVE B
	CALL	CRLFC	; NEW LINE
	POP	B	; GET B
	DJNZ	FEED$LOOP
	RET

*  END OF INFO
PI2:
	MOV	A,M	; SET MSB OF NEXT BYTE
	ORI	80H
	MOV	M,A
PI2A:
	CALL	CRLF1	; NEW LINE
	LDA	LINE$CNT	; COUNT DOWN
	DCR	A
	STA	LINE$CNT
	JRNZ	PI2A
PI2$MSG:
	CALL	PR$LEVEL	; PRINT LEVEL NUMBER
	LDA	FRAME$NUMBER	; INCREMENT FRAME NUMBER
	INR	A
	STA	FRAME$NUMBER
	LXI	D,ENDMS		; PRINT END OF INFORMATION MSG
	CALL	PRINT$MESSAGE
	LXI	D,PAGE1MS	; PRINT LEVEL UP MESSAGE OPTIONALLY
	LDA	HELP$LEVEL	; GET CURRENT HELP LEVEL
	ORA	A	; SET FLAGS
	JRZ	PI2$MSG1
	CALL	PRINT$MESSAGE
PI2$MSG1:
	LXI	D,PAGE2MS	; PRINT REST OF INFO MESSAGE
	CALL	PRINT$MESSAGE
	CALL	CHAR$IN	; GET ANY CHAR
	CPI	MENU$CHAR	; MENU ABORT
	JZ	HELP
	CPI	CPM$ABORT$CHAR	; ABORT TO OS
	JZ	HELP$EXIT
	CPI	PRINT$CHAR	; PRINT INFORMATION SECTION?
	JZ	LIST1$INFO
	CPI	ROOT$CHAR	; GO TO ROOT
	JZ	GO$ROOT
	CPI	LEVEL$RET$CHAR	; RETURN TO HIGHER LEVEL
	JZ	LEVEL$RETURN
	CPI	BACKUP$CHAR	; BACK UP FROM EOI?
	JZ	PI2$BACKUP
	CPI	START$CHAR	; START OF INFO?
	JZ	PI2$START
	JMP	SET$LINE$CNT	; RESET LINE COUNT IN CASE OF ALL

*  JUMP TO START OF INFO
PI2$START:
	LHLD	START$OF$INFO	; PT TO START OF INFO
	CALL	FRAME$RESUME	; RESET POINTERS
	MVI	A,1		; RESET FRAME COUNT
	STA	FRAME$NUMBER
	JMP	PI1	; CONTINUE PROCESSING

*  BACK UP TO PREVIOUS FRAME
PI2$BACKUP:
	CALL	BOI$CHECK	; AT BEGINNING OF INFORMATION?
	JZ	PI2$MSG
PI2$BACK:
	CALL	FB1	; BACK UP TO PREVIOUS FRAME
	JMP	PI1	; CONTINUE PROCESSING

*
*  CHECK FOR POSITION AT BEGINNING OF INFORMATION SECTION
*    IF SO, PRINT BACKUP ERROR MESSAGE AND RETURN W/ZERO SET
*
BOI$CHECK:
	LHLD	START$OF$INFO	; START ADDRESS
	XCHG			; ... IN DE
	LHLD	START$OF$FRAME	; FRAME ADDRESS
	MOV	A,D		; EQUAL?
	CMP	H
	RNZ
	MOV	A,E
	CMP	L
	RNZ
	LXI	D,BACKERR	; BACKUP ERROR
	CALL	PRINT$MESSAGE
	XRA	A		; ZERO FLAG SET
	STA	FRAME$NUMBER	; SET FRAME NUMBER
	RET

**************************************************************
*  THIS BODY OF CODE LISTS INFORMATION FROM HELP2 TO THE
*  PRINTER
**************************************************************

*
*  LIST ONE LINE OF INFO SECTION; HL PTS TO LINE UPON ENTRY;
*	HL PTS TO FIRST CHAR OF NEXT LINE UPON EXIT
*
LIST$LINE:
	MOV	A,M	; GET CHAR
	ANI	7FH	; MASK OUT MSB
	CPI	CR	; EOL?
	JRZ	LCRLF
	CPI	LF	; LINE FEED? (WS FILE)
	JRZ	LCRLF0
	CPI	CTRLZ	; END OF FILE?
	JRZ	LCRLFC	; DONE IF SO
	CALL	LST$OUT	; PRINT CHAR
	RZ		; ABORT
	INX	H	; PT TO NEXT
	JR	LIST$LINE

*
*  LIST CRLF, PT TO FIRST CHAR OF NEXT LINE, AND PAGE IF NECESSARY
*
LCRLF:
	INX	H	; PT TO LF
LCRLF0:
	INX	H	; PT TO 1ST CHAR OF NEXT LINE
LCRLFC:
	CALL	LCRLF1	; PRINT CRLF
	LDA	LINE$CNT	; GET LINE COUNT
	DCR	A
	STA	LINE$CNT
	JRNZ	LNOABT	; OK -- CONTINUE
LCRLF1:
	MVI	A,CR	; SEND <CRLF> TO PRINTER
	CALL	LST$OUT
	RZ		; ABORT
	MVI	A,LF	; FALL THRU TO LST$OUT

*  PRINT CHARACTER IN A ON PRINTER; AFFECT NO REGISTERS
LST$OUT:
	PUSH	H	; SAVE REGS
	PUSH	D
	PUSH	B
	MOV	E,A	; CHAR IN E
	MVI	C,5	; BDOS PRINTER OUTPUT ROUTINE
	CALL	BDOS
	MVI	E,0FFH	; CONDITIONAL INPUT
	MVI	C,6	; DIRECT CONSOLE I/O
	CALL	BDOS
	POP	B	; RESTORE REGS
	POP	D
	POP	H
	CPI	CTRLC	; ABORT?
	RET
LNOABT:
	MVI	A,0FFH	; SET NO ABORT RETURN
	ORA	A	; SET FLAGS
	RET

*
*  LIST THE CURRENT INFORMATION SECTION
*    INFORMATION SECTION IS PTED TO BY START$OF$INFO
*
LIST1$INFO:
	LXI	D,PRINT$WAIT$MSG
	CALL	PRINT$MESSAGE
	LHLD	START$OF$INFO	; PREPARE TO LIST ENTIRE INFO SECTION
	XRA	A		; CLEAR FRAME LIST FLAG
	STA	LFR$FLAG
	CALL	CHAR$IN		; GET USER INPUT
	CPI	'S'		; SCREEN DISPLAY ONLY
	JRNZ	LIST2$INFO
	LHLD	START$OF$FRAME	; LIST CURRENT FRAME ONLY
	MVI	A,0FFH		; SET FLAG
	STA	LFR$FLAG	; LIST FRAME ONLY
LIST2$INFO:
	CPI	CTRLC		; CHECK FOR ^C TO ABORT
	CNZ	LIST$INFO	; DO PRINTOUT
	LHLD	START$OF$FRAME	; RETURN TO FRAME WE WERE ON
	CALL	FRAME$RESUME
	LDA	FRAME$NUMBER	; ADJUST FRAME NUMBERING
	DCR	A
	STA	FRAME$NUMBER
	JMP	PI1		; RESUME AT PI1
*
LIST$INFO:
	LXI	D,PRINT$LMSG	; PRINT ABORT OPTION MESSAGE
	CALL	PRINT$MESSAGE
	CALL	SET$LINE$CNT
LI1:
	CALL	LIST$LINE	; LIST LINE FROM INFO FILE
	CPI	CTRLC	; ABORT?
	RZ
	MOV	A,M	; DONE?
	ANI	7FH	; MASK OUT MSB
	CPI	CTRLZ	; EOF?
	RZ		; RESUME IF AT END OF INFO
	CPI	SECT$CHAR	; NEXT SECTION
	RZ		; RESUME IF AT END OF INFO
	CPI	FF	; FORM FEED?
	JRNZ	LI1
	INX	H	; PT TO CHAR AFTER FORM FEED
	CALL	LFORM$FEED	; FEED PRINTER
	LDA	LFR$FLAG	; LIST FRAME ONLY?
	ORA	A		; 0=NO
	RNZ
	JR	LI1

*  FORM FEED SCREEN
LFORM$FEED:
	CALL	LCRLFC	; NEW LINE
	RET

**************************************************************
*  END OF BODY OF CODE WHICH LISTS INFORMATION FROM HELP2 TO
*  THE PRINTER
**************************************************************

*
*  AT THE BEGINNING OF AN INFORMATION SECTION (HL PTS TO FIRST CHAR)
*    CHECK TO SEE IF ANOTHER SECT$CHAR FOLLOWS, AND, IF SO, LOAD THE
*    SPECIFIED FILE AS A NEW NODE AND BEGIN PROCESSING IT
*
LOAD$NODE:
	INX	H	; PT TO POSSIBLE 2ND SECT$CHAR
	MOV	A,M	; GET IT
	DCX	H	; PREP FOR RETURN
	ANI	7FH	; MASK MSB
	CPI	SECT$CHAR	; ANOTHER ONE?
	RNZ		; PROCESS NORMALLY IF NOT

*  WE HAVE A NEW NODE -- CHECK TO SEE IF WE CAN NEST AGAIN
	LDA	HELP$LEVEL	; GET CURRENT HELP LEVEL
	CPI	HELP$MAX	; AT MAXIMUM?
	JRNZ	LOAD$NODE1
	LXI	D,LEVELERR	; LEVEL ERROR MESSAGE
	CALL	PRINT$MESSAGE
	JMP	HELP$EXIT

*  WE HAVE NOT REACHED LEVEL LIMIT, SO CONTINUE
*  AT THIS TIME, A=HELP LEVEL INDEX AND HL = PTR TO CURRENT SECTION (::)
LOAD$NODE1:

*  SAVE CURRENT HELP FILE NAME FOR RETURN
	INX	H	; PT TO SECTION SECT$CHAR
	INX	H	; NOW POINTING TO FILE NAME
	PUSH	H	; SAVE PTR
	CALL	COMP$HELP$NAME$PTR	; HL=POINTER TO STACK ELT INDEXED BY A
	XCHG		; DE=ADDRESS OF NEXT ELEMENT

*  COPY CURRENT NODE ELEMENT NAME INTO NEXT STACK ELEMENT
	LXI	H,FCB+1	; PT TO FILE NAME
	MVI	B,11	; 11 BYTES
	CALL	MOVE

*  INCREMENT HELP LEVEL
	LDA	HELP$LEVEL	; GET OLD LEVEL
	INR	A	; SET NEW LEVEL
	STA	HELP$LEVEL

*  SET UP FCB FOR NEW FILE
	LXI	D,LOADING$MSG
	CALL	PRINT$MESSAGE
	POP	H	; GET PTR TO NEW FILE NAME
	LXI	D,FCB+1	; PT TO FCB NAME
	MVI	B,8	; 8 CHARS MAX
	CALL	LOAD$FCB	; PLACE INTO FCB WITH ERROR CHECKING
	MVI	A,'.'	; DECIMAL BETWEEN FILE NAME AND TYPE
	CALL	CHAR$OUT
	MVI	B,3	; 3 CHARS MAX FOR TYPE
	CALL	LOAD$FCB	; PLACE INTO FCB WITH ERROR CHECKING
	CALL	CRLF1	; NEW LINE
	JMP	START1	; LOAD NEW HELP FILE

*
*  LOAD FCB PTED TO BY DE WITH "NORMAL" FILE NAME PTED TO BY HL FOR B BYTES
*
LOAD$FCB:
	MOV	A,M	; GET CHAR
	INX	H	; PT TO NEXT
	CPI	'.'	; DONE IF DECIMAL
	JRZ	LOAD$FCB$FILL
	CPI	' '+1	; DONE IF < <SP>
	JRC	LOAD$FCB$FILL
	CALL	CAPS	; CAPITALIZE
	CALL	CHAR$OUT	; PRINT FILE NAME AND TYPE
	STAX	D	; STORE CHAR
	INX	D	; PT TO NEXT
	DJNZ	LOAD$FCB
	MOV	A,M	; CHECK FOR ERROR
	ANI	7FH	; MASK MSB
	INX	H	; PT TO NEXT CHAR
	CPI	'.'	; OK IF '.'
	RZ
	CPI	' '+1	; OK IF <SP>
	RC
	LXI	D,LOADERR
	CALL	PRINT$MESSAGE
	JMP	HELP$EXIT
LOAD$FCB$FILL:
	MOV	C,A	; SAVE CHAR THAT TERMINATED STRING
LOAD$FCB$LOOP:
	MVI	A,' '	; <SP> FILL REST OF FCB
	STAX	D	; STORE <SP>
	INX	D	; PT TO NEXT
	DJNZ	LOAD$FCB$LOOP
	MOV	A,C	; GET CHAR THAT TERMINATED STRING
	RET

*
*  GO TO ROOT
*
GO$ROOT:
	LDA	HELP$LEVEL	; AT ROOT?
	ORA	A	; 0=YES
	JZ	HELP	; RETURN TO HELP
	MVI	A,0	; SET ROOT INDEX
	JR	GORET

*
*  RETURN TO PREVIOUS HELP LEVEL
*
LEVEL$RETURN:
	LDA	HELP$LEVEL	; ARE WE AT THE LOWEST LEVEL?
	ORA	A	; 0=YES
	JRNZ	LRET
	LXI	D,LRETERR
	CALL	PRINT$MESSAGE
	JMP	HELP

*  SET NEW HELP LEVEL
LRET:
	DCR	A	; DOWN 1

*  GO TO HELP LEVEL INDEXED IN A
GORET:
	STA	HELP$LEVEL	; SET NEW HELP LEVEL
	CALL	COMP$HELP$NAME$PTR	; HL=POINTER TO TARGET HELP FILE NAME
	PUSH	H	; SAVE PTR TO FILE NAME
	LXI	D,LOADING$MSG	; PRINT NAME OF FILE TO BE LOADED
	CALL	PRINT$MESSAGE
	MVI	B,8	; 8 CHARS TO FILE NAME
GORET$NAME:
	MOV	A,M	; GET CHAR
	CPI	' '	; END OF NAME?
	INX	H	; PT TO NEXT
	JRZ	GORET$NAME0
	CALL	CHAR$OUT	; PRINT FILE NAME
	DJNZ	GORET$NAME
	JMP	GORET$NAME1
GORET$NAME0:
	DCR	B	; COUNT DOWN
	JRZ	GORET$NAME1
	INX	H	; SKIP NEXT SPACE
	JR	GORET$NAME0
GORET$NAME1:
	MVI	A,'.'	; PRINT DECIMAL
	CALL	CHAR$OUT
	MVI	B,3	; PRINT FILE TYPE
GORET$NAME2:
	MOV	A,M	; GET CHAR
	INX	H	; PT TO NEXT
	CALL	CHAR$OUT	; PRINT IT
	DJNZ	GORET$NAME2
	CALL	CRLF1	; NEW LINE
	POP	H	; GET PTR TO FILE NAME
	LXI	D,FCB+1	; COPY ELEMENT INTO FCB
	MVI	B,11	; 11 BYTES
	CALL	MOVE
	JMP	START1	; LOAD ENTRY

*
*  COMPUTE POINTER TO HELP NAME ENTRY INDEXED BY HELP LEVEL IN A
*
COMP$HELP$NAME$PTR:
	CALL	COMP$OFFSET	; COMPUTE OFFSET IN TABLE
	LXI	D,HELP$NAME$STACK	; PT TO BASE OF HELP NAMES
	DAD	D	; ADD IN OFFSET
	RET

*
*  COMPUTE OFFSET INTO TABLE BASED ON INDEX A
*    OFFSET = A * 11
*
COMP$OFFSET:
	MOV	L,A	; VALUE IN HL
	MVI	H,0
	MOV	E,L	; DE=HL
	MOV	D,H

	DAD	H	; *2
	DAD	H	; *4
	DAD	H	; *8
	DAD	D	; *9
	DAD	D	; *10
	DAD	D	; *11
	RET

*
*  PRINT LEVEL NUMBER
*
PR$LEVEL:
	LDA	HELP$LEVEL	; DON'T PRINT LEVEL 0
	ORA	A	; 0?
	JRZ	PR$FRAME
	LXI	D,LEVEL$MESSAGE	; PRINT HEADER
	CALL	PRINT$MESSAGE
	LDA	HELP$LEVEL	; GET NUMBER
	CALL	PR$DEC		; PRINT AS DECIMAL
	LXI	D,LEVEL2$MESSAGE	; PRINT END HEADER
	CALL	PRINT$MESSAGE
PR$FRAME:
	LDA	FRAME$NUMBER	; GET CURRENT FRAME NUMBER
	ORA	A	; SET FLAGS
	RZ		; NO FRAME?
	CALL	PR$DEC	; PRINT AS DECIMAL
	LXI	D,LEVEL3$MESSAGE
	CALL	PRINT$MESSAGE
	RET
*  PRINT A AS DECIMAL
PR$DEC:
	PUSH	PSW	; SAVE VALUE
	XRA	A
	STA	LD$SPACE
	POP	PSW	; GET VALUE
	MVI	B,100	; PRINT 100'S
	CALL	PDEC
	MVI	B,10	; PRINT 10'S
	CALL	PDEC
	ADI	'0'	; PRINT 1'S
	JMP	CHAR$OUT
PDEC:
	MVI	C,0	; SET VALUE
PDEC1:
	SUB	B	; SUBTRACT POWER
	JRC	PDEC2
	INR	C	; INCREMENT VALUE
	JR	PDEC1
PDEC2:
	ADD	B	; ADD POWER BACK IN
	MOV	B,A	; SAVE A IN B
	LDA	LD$SPACE	; GET LEADING <SP> FLAG
	ORA	A	; NON-ZERO=PRINT
	JRNZ	PDEC3
	MOV	A,C	; GET DIGIT
	STA	LD$SPACE	; NEW FLAG
	ORA	A	; ZERO?
	JRNZ	PDEC3	; PRINT BYTE IN C
	MVI	A,' '	; PRINT LEADING SPACE
	JR	PDEC4
PDEC3:
	MOV	A,C	; GET VALUE
	ADI	'0'	; CONVERT TO ASCII
PDEC4:
	CALL	CHAR$OUT	; PRINT CHAR
	MOV	A,B	; RESTORE A
	RET

*********************************************************
*  MESSAGE AND BUFFER SECTION				*
*********************************************************

HELPMS:
	DB	'HELP for ZCPR2, Version '
	DB	(VERS/10)+'0','.',(VERS MOD 10)+'0',CR,LF,'$'
HELP$DEF$MSG:
	DB	CR,LF,'Default HELP Facility Invoked'
	DB	CR,LF,'	Available HELP Files are --',CR,LF,'$'
HELP$EDEF$MSG:
	DB	CR,LF,'	Type Any Character for Default Info (^C to Abort) - $'
SELECTMS:
	DB	CR,LF,'  HELP File Selections are --',CR,LF,'$'
ENDMS:
	DB	'EOI '
PAGEMS:
	DB	'^C=Exit $'		; ABORT TO CP/M CHAR
PAGE1MS:
	DB	LEVEL$RET$CHAR,'=Level '	; RETURN TO HIGHER NODE
	DB	ROOT$CHAR,'=Root $'		; RETURN TO ROOT
PAGE2MS:
	DB	MENU$CHAR,'=Menu '	; ABORT TO MENU CHAR
	DB	START$CHAR,'=Start '	; JUMP TO START OF INFORMATION CHAR
	DB	BACKUP$CHAR,'=Last '	; BACK UP TO PREVIOUS FRAME CHAR
	DB	PRINT$CHAR,'=Print '	; PRINT CURRENT INFO SECTION
;	DB	'CR=Next '
	DB	'$'
PRINT$LMSG:
	DB	'Printing in Progress -- Strike ^C to Abort $'
PRINT$WAIT$MSG:
	DB	'Please Set Top-of-Form on Printer'
	DB	CR,LF,'  Strike S to Print this Screen Only, ^C to Abort, or'
	DB	CR,LF,'Any Other Char to Print Entire Information Section - $'
WILD$ERR:
	DB	CR,LF,'HELP FATAL ERROR -- File Name Contains Wild Card$'
ERR1:
	DB	CR,LF,'HELP FATAL ERROR -- File not Found$'
ERR2:
	DB	CR,LF,'HELP ERROR -- Invalid Response',CR,LF,'$'
ERR3:
	DB	CR,LF,'HELP ERROR -- EOF on HELP File',CR,LF,'$'
BACKERR:
	DB	CR,LF,'HELP ERROR -- Not Possible to Backup Before Start of '
	DB	'Info',CR,LF,'$'
LEVELERR:
	DB	CR,LF,'HELP ERROR -- Node Level Limit Reached -- Aborting'
	DB	CR,LF,'$'
LOADERR:
	DB	CR,LF,'HELP ERROR -- Invalid File Name in Load',CR,LF,'$'
LRETERR:
	DB	CR,LF,'HELP ERROR -- No Higher Level to Return to',CR,LF,'$'
READERR:
	DB	CR,LF,'HELP ERROR -- Not Enough Room for HELP File',CR,LF,'$'
LEVEL$MESSAGE:
	DB	'Level $'
LEVEL2$MESSAGE:
	DB	'/ $'
LEVEL3$MESSAGE:
	DB	': $'
PROMPT1$MESSAGE:
	DB	'Type  ^C=Exit$'
PROMPT2$MESSAGE:
	DB	' ',LEVEL$RET$CHAR,'=Level '
	DB	ROOT$CHAR,'=Root$'
PROMPT3$MESSAGE:
	DB	' or Enter Selection $'
LOADING$MSG:
	DB	CR,LF,'Loading HELP File $'

LFR$FLAG:
	DS	1	; LIST FRAME ONLY FLAG (FOR PRINT FUNCTION)
CUR$USER:
	DS	1	; NUMBER OF CURRENT USER
TPA$END:
	DS	1	; END PAGE ADDRESS OF TPA
START$OF$INFO:
	DS	2	; PTR TO START OF CURRENT INFORMATION BLOCK
START$OF$FRAME:
	DS	2	; PTR TO START OF CURRENT FRAME
SEL$CHAR:
	DS	1	; SELECTION TABLE OPTION CHAR
FIRST$ENTRY:
	DS	2	; PTR TO FIRST ENTRY OF INFORMATION SECTION
LINE$CNT:
	DS	1	; LINE COUNT BUFFER
NAME$COUNT:
	DS	1	; COUNT OF FILE NAMES/LINE
DFFLG:
	DS	1	; DEFAULT FILE FLAG (0=NOT SEARCH FOR, 1=YES)
NEXT$ADR:
	DS	2	; NEXT LOAD ADDRESS
LD$SPACE:
	DS	1	; LEADING SPACE FLAG FOR DECIMAL PRINT
HELP$LEVEL:
	DS	1	; NUMBER OF HELP LEVEL CURRENT NODE IS AT (0=BOTTOM)
FRAME$NUMBER:
	DS	1	; NUMBER OF CURRENT FRAME

HELP$NAME$STACK:
	DS	11*HELP$MAX	; STACK OF HELP FILE NAMES OF EACH LEVEL

	DS	80	; STACK SPACE
STACK:
	DS	2	; CP/M STACK PTR

*
*  Help Files are Loaded Here
*
HELP$BUF	equ	$

	END


