;  PROGRAM NAME:  HELP
;  AUTHOR:  RICHARD CONN
;  DATE:  6 July 1982
;  VERSION:  1.4
;  PREVIOUS VERSIONS, Latest version first:
;
; 16/Jul/82  Changed default HLP file to THIS-SYS and modified
;       internal messages for RCPM use. Version 1.4. Bill Bolton
;
; 06/Jun/82 Modifications for RCPM system use. Line noise
;       was resulting in false answers to "go on" prompts.
;       Test for <CR> response changed to accept only <CR>.
;       Messages tightened up and an alternative end of
;       category message added for the "all categories"
;       condition. Console input changed to Direct Console
;       I/O (BDOS 6) making CP/M 2.2 or later mandatory.
;       Version 1.3. Bill Bolton
;
; 22/Jun/81 Added code for standard or modified CP/M.  Changed "*"
;       in Col 1 to ";".  Changed labels so that the program would
;       assemble with other assemblers. Version 1.2 Ted Shapin.
;
; 06/Oct/80 Version 1.1 (changes undocumented). Richard Conn.
;
; 18/Nov/79 Version 1.0 Original release. Richard Conn.
;
;
;****************************************************************
;*                                                              *
;*  HELP -- DISPLAY HELP FILE INFORMATION TO USER ON CON:       *
;*                                                              *
;*      THE HELP COMMAND IS OF THE GENERAL FORM:                *
;*              HELP <FILENAME>.<EXT>                           *
;*                                                              *
;*      <FILENAME>.<EXT> IS OPTIONAL; IF OMITTED COMPLETELY,    *
;* 'THIS-SYS.HLP' IS ASSUMED; IF JUST <EXT> IS OMITTED,         *
;* EXTENSION IS ASSUMED TO BE '.HLP'                            *
;*                                                              *
;*      THE HELP COMMAND DISPLAYS THE INFORMATION IN A HELP     *
;* FILE TO THE USER.  THERE ARE TWO BASIC TYPES OF HELP FILES - *
;* (1) INDEXED AND (2) NON-INDEXED.  INDEXED HELP FILES ARE     *
;* THOSE WHICH CONTAIN SEVERAL SECTIONS; THE INDIVIDUAL MAY     *
;* READ ALL OF SUCH A HELP FILE OR JUST SELECTED SECTIONS OF    *
;* THIS FILE.  NON-INDEXED HELP FILES CONTAIN ONLY ONE SECTION. *
;*      STRUCTURALLY SPEAKING, HELP FILES CONSIST OF TWO PARTS: *
;* THE HEADER PART AND THE INFORMATION PART.  THE INFORMATION   *
;* PART OF A HELP FILE BEGINS WITH A LINE WHOSE FIRST CHARACTER *
;* IS A COLON.  THE TITLE OF THE INFORMATION SECTION IS ON THIS *
;* LINE.  THE INFORMATION SECTION CONTINUES UNTIL THE NEXT      *
;* INFORMATION SECTION (LINE STARTING WITH A COLON) OR THE END  *
;* OF THE FILE IS ENCOUNTERED.  THE HEADER PART CONSISTS OF A   *
;* GROUP OF LINES BEFORE THE FIRST INFORMATION SECTION.  IF THE *
;* FIRST LINE OF A HELP FILE STARTS WITH A COLON, THEN THERE IS *
;* NO HEADER PART, AND THE HELP FILE IS DUMPED AS ONE           *
;* INFORMATION SECTION.                                         *
;*      THERE MUST BE THE SAME NUMBER OF LINES IN THE HEADER    *
;* PART AS THERE ARE INFORMATION SECTIONS.  IF NOT, A HELP      *
;* FILE ERROR WILL BE ISSUED IF THE HELP COMMAND ATTEMPTS TO    *
;* READ BEYOND THE END OF THE HELP FILE IN ITS SEARCH FOR AN    *
;* INFORMATION SECTION.                                         *
;*                                                              *
;****************************************************************
;
;
;****************************************************************
;*                                                              *
;*  THE HELP PROGRAM IS COMPLETELY TRANSPORTABLE BETWEEN CP/M   *
;*  SYSTEMS.                                                    *
;*                                                              *
;****************************************************************
;
;
;
;****************************************************************
;*  CP/M AND BASIC CHARACTER DEFINITIONS                        *
;****************************************************************

BIAS    EQU     0               ; STANDARD CP/M
;BIAS   EQU     4200H           ; OFFSET CP/M

BDOS    EQU     5+BIAS          ; ADDRESS OF BDOS ENTRY POINT
FCB     EQU     5CH+BIAS        ; ADDRESS OF FILE CONTROL BLOCK
BUFF    EQU     80H+BIAS        ; 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

;****************************************************************
;*  CHARACTER WHICH MARKS START OF INFORMATION SECTION          *
;****************************************************************

SECT$CHAR       EQU     ':'     ; DEFINED TO BE COLON
ALL$CHAR        EQU     '*'     ; ALL OF HELP FILE DESIGNATOR

;****************************************************************
;*  USER CUSTOMIZATION -- LINES PER SCREEN DISPLAY              *
;****************************************************************

LINES$PER$SCREEN        EQU     24      ; ASSUME 24 LINES/SCREEN


;****************************************************************
;*  START OF PROGRAM                                            *
;****************************************************************

       ORG     100H + BIAS

START:
       LXI     H,0     ; GET SP
       DAD     SP
       SHLD    STACK
       XRA     A       ; TURN OFF DEFAULT FILE FLAG
       STA     DFFLG
       LXI     D,HELPMS        ; PRINT OPENING MSG
       CALL    PRINT$MESSAGE
       LXI     H,FCB+1 ; CHECK FOR FILE NAME
       MOV     A,M
       CPI     ' '     ; NONE?
       JZ      DEFAULT$FN
       ORA     A       ; ALSO NONE
       JNZ     START1

;  INSERT 'THIS-SYS.HLP' INTO FCB
DEFAULT$FN:
       DCX     H       ; PT TO FCB
       LXI     D,DEFFN
       MVI     B,12    ; 12 BYTES
       XCHG
       CALL    MOVE    ; MOVE (HL) TO (DE) FOR (B) BYTES
       MVI     A,1     ; TURN ON DEFAULT FILE FLAG
       STA     DFFLG
       JMP     START2

;  CHECK FOR EXTENSION TO FILE NAME
START1:
       LXI     H,FCB+9 ; CHECK FOR EXTENSION
       MOV     A,M
       CPI     ' '     ; NONE?
       JZ      DEFAULT$EXT
       ORA     A       ; NONE ALSO
       JNZ     START2

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

;  OPEN FILE
START2:
       LXI     D,FCB   ; PT TO FCB
       MVI     C,15    ; OPEN FILE
       CALL    BDOS
       CPI     255     ; NOT PRESENT?
       JNZ     START3

;  CHECK FOR DEFAULT FILE SEARCH
       LDA     DFFLG   ; GET DEFAULT FILE FLAG
       ORA     A       ; 1=YES, SEARCH FOR DEFAULT FAILED
       JNZ     HELP    ; DISPLAY DEFAULT HELP FILE INFORMATION

;  FILE NOT FOUND -- FATAL ERROR
       LXI     D,ERR1  ; FILE NOT FOUND
       CALL    PRINT$MESSAGE
       RET

;  LOAD HELP FILE INFORMATION
START3:
       LXI     H,HELP$BUF      ; PT TO BUFFER
       SHLD    NEXT$ADR        ; SET PTR

;  READ RECORDS UNTIL EOF
START4:
       CALL    READ$RECORD     ; READ INFO
       ORA     A       ; DONE? 0=NO
       JZ      START4

;
;  START OF HELP PROGRAM
;
HELP:
       LXI     SP,STACK        ; RESET STACK
       LXI     H,HELP$BUF      ; PT TO BUFFER
       MOV     A,M     ; NO HEADER SECTION?
       CPI     SECT$CHAR
       JNZ     HELP1   ; HEADER SECTION EXISTS
       CALL    PRINT$INFO      ; PRINT HELP INFO PTED TO BY HL

;  EXIT POINT FOR ANY EXIT FROM THE REST OF THE HELP PROGRAM
HELP$EXIT:
       LHLD    STACK   ; GET CP/M SP
       SPHL
       RET             ; DONE

;  PRINT HEADER INFORMATION AND SELECT AN OPTION
HELP1:
       MVI     A,0
       STA     ALLFLAG         ; RESET ALL CATEGORIES FLAG
       CALL    PRINT$HEADER    ; PRINT HEADER
       LXI     D,PROMPT$MESSAGE        ; PRINT PROMPT
       CALL    PRINT$MESSAGE
       CALL    CHAR$IN         ; GET RESPONSE
       CPI     CTRLC           ; RETURN TO CP/M
       JZ      HELP$EXIT
       CALL    CHAR$OUT        ; DISPLAY ENTERED CHAR
       CPI     ALL$CHAR        ; ALL OF HELP FILE?
       JZ      HELP$ALL
       ANI     0DFH    ; CAPITALIZE
       PUSH    PSW     ; SAVE CHAR
       CALL    CRLF1
       POP     PSW     ; GET CHAR
       SUI     'A'-1           ; ADJUST FOR COUNT
       MOV     B,A             ; SAVE COUNT
       JZ      BAD$RESPONSE
       JNC     HELP2

;  INVALID RESPONSE
BAD$RESPONSE:
       LXI     D,ERR2  ; INVALID RESPONSE
       CALL    PRINT$MESSAGE
       JMP     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?
       JNC     BAD$RESPONSE
       LHLD    FIRST$ENTRY     ; GET PTR TO FIRST ENTRY

;  PRINT INFORMATION WHEN COUNT IS ZERO
HELP3:
       DCR     B       ; COUNT DOWN
       JNZ     HELP4
       INX     H       ; SKIP OVER COLON
       CALL    PRINT$INFO      ; PRINT INFO PTED TO BY HL
       JMP     HELP1

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

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

;  PRINT ALL OF HELP FILE
HELP$ALL:
       STA     ALLFLAG         ; FOR LATER
       CALL    CRLF1           ; START ON NEW LINE
       LHLD    FIRST$ENTRY     ; PT TO FIRST ENTRY
       CALL    SET$LINE$CNT    ; SET LINE COUNT
;  EXECUTE UNTIL A CTRL-Z IS ENCOUNTERED
HA1:
       INX     H       ; SKIP OVER COLON
       CALL    PI1     ; PRINT INFO W/OUT LINE CNT INFO
       MOV     A,M     ; GET LAST CHAR
       CPI     CTRLZ
       JNZ     HA1
       JMP     HELP



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

;
;  INPUT CHAR; CHAR IS IN A
;
CHAR$IN:
       PUSH    B
       PUSH    D
       PUSH    H
       MVI     C,6     ; DIRECT CONSOLE I/O
       MVI     E,0FFH  ; INPUT
       CALL    BDOS
       POP     H
       POP     D
       POP     B
       ORA     A       ; CHARACTER?
       JZ      CHAR$IN ; NO, KEEP LOOKING
       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
       DCR     B       ; COUNT DOWN
       JNZ     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
       LXI     D,BUFF  ; PT TO BUFFER TO LOAD FROM
       MVI     B,128   ; NUMBER OF BYTES TO MOVE
       XCHG
       CALL    MOVE
       XCHG
       SHLD    NEXT$ADR        ; PT TO NEXT LOAD ADDRESS
       POP     PSW     ; GET RETURN CODE
       RET

;
;  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
       CPI     CR      ; EOL?
       JZ      CRLF
       CPI     LF      ; LINE FEED? (WS FILE)
       JZ      CRLF0
       CALL    CHAR$OUT        ; PRINT CHAR
       INX     H       ; PT TO NEXT
       JMP     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
       LXI     D,PAGEMS
       CALL    PRINT$MESSAGE   ; PRINT PAGE MESSAGE
NXTLOOP:
       CALL    CHAR$IN ; GET RESPONSE
       ANI     0DFH    ; CAPITALIZE
       CPI     'A'     ; ABORT?
       JZ      HELP    ; YES, START OVER
       CPI     CTRLC   ; CP/M ABORT
       JZ      HELP$EXIT
       CPI     CR      ; NEXT PAGE?
       JNZ     NXTLOOP ; NO, TRY AGAIN
       CALL    SET$LINE$CNT
       CALL    CRLF1   ; NEW LINE
       RET

;
;  PRINT CR AND LF ONLY
;
CRLF1:
       MVI     A,CR    ; PRINT CR
       CALL    CHAR$OUT
       MVI     A,LF    ; PRINT LF
       CALL    CHAR$OUT
       RET

;
;  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
;
PRINT$HEADER:
       LXI     H,HELP$BUF
       CALL    SET$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
       CPI     SECT$CHAR
       JZ      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
       JMP     PH1

;  SAVE PTR TO FIRST ENTRY
PH2:
       SHLD    FIRST$ENTRY
       RET

;
;  PRINT AN INFORMATION SECTION
;
PRINT$INFO:
       CALL    SET$LINE$CNT
PI1:
       CALL    PRINT$LINE      ; PRINT LINE FROM INFO FILE
       MOV     A,M     ; DONE?
       CPI     CTRLZ   ; EOF?
       JZ      PI2
       CPI     SECT$CHAR       ; NEXT SECTION
       JZ      PI2
       CPI     FF      ; FORM FEED?
       JNZ     PI1
       CALL    FORM$FEED       ; FEED SCREEN
       JMP     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
       DCR     B       ; COUNT DOWN
       JNZ     FEED$LOOP
       RET

;  END OF INFO
PI2:
       CALL    CRLF1   ; NEW LINE
       LDA     LINE$CNT        ; COUNT DOWN
       DCR     A
       STA     LINE$CNT
       JNZ     PI2
       LDA     ALLFLAG
       ORA     A               ; DISPLAY ALL CATEGORIES?
       LXI     D,CONTMS
       JNZ     PI2CONT         ; YES
       LXI     D,ENDMS         ; PRINT END OF INFORMATION MSG
PI2CONT:
       CALL    PRINT$MESSAGE
PI2LOOP:
       CALL    CHAR$IN ; GET ANY CHAR
       CPI     CTRLC   ; CP/M ABORT
       JZ      HELP$EXIT
       CPI     CR      ; BACK TO MENU
       JNZ     PI2LOOP ; NO, KEEP LOOKING
       CALL    CRLF1   ; NEW LINE
       CALL    SET$LINE$CNT    ; RESET LINE COUNT IN CASE OF ALL
       RET

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

HELPMS:
       DB      'HELP V1.4',CR,LF,'$'
ENDMS:
       DB      '+ End of Category +'
       DB      '       Type CTRL-C=CP/M, <CR>=Menu --$'
CONTMS:
       DB      '+ End of Category +'
       DB      '       Type CTRL-C=CP/M, <CR>=Next Category -$'

SELECTMS:
       DB      CR,LF,'  HELP File Selections are --',CR,LF,'$'
DEFFN:
       DB      0,'THIS-SYS'
DEFEXT:
       DB      'HLP'
PAGEMS:
       DB      '       Type "A"=Abort, CTRL-C=CP/M, <CR>=More -$'
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,'$'
PROMPT$MESSAGE:
       DB      CR,LF,'Type CTRL-C to exit to CP/M, "*" to select'
       DB      ' all, or category -$'

ALLFLAG:
       DS      1       ; DISPLAY ALL CATEGORIES FLAG
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
DFFLG:
       DS      1       ; DEFAULT FILE FLAG (0=NOT SEARCH FOR, 1=YES)
NEXT$ADR:
       DS      2       ; NEXT LOAD ADDRESS
       DS      80      ; STACK SPACE
STACK:
       DS      2       ; CP/M STACK PTR


;
;  DEFAULT HELP MESSAGE
;
HELP$BUF:
       DB      ':The HELP Subsystem for Online Documentation',CR,LF
       DB      '     This  is HELP,  the Online Documentation  Subsystem.  The',CR,LF
       DB      'purpose  of  HELP is to allow you to interactively  query  the',CR,LF
       DB      '*.HLP  files  on this system to get information  summaries  on',CR,LF
       DB      'various aspects of the system, CP/M, applications programs and',CR,LF
       DB      'languages.',CR,LF
       DB      CR,LF
       DB      '     When  you  type  ''HELP'',  a search is done for  the  file',CR,LF
       DB      '''THIS-SYS.HLP''.   If found, the contents of this HELP File are',CR,LF
       DB      'displayed for you;  if not found, the HELP Information you are',CR,LF
       DB      'now reading is displayed.',CR,LF
       DB      CR,LF
       DB      '     If  you  desire information on a specific topic and  have',CR,LF
       DB      'found  a  HELP File in the system directory with  a  promising',CR,LF
       DB      'name (ie, CPM.HLP is a HELP File on CP/M), try the command:',CR,LF
       DB      CR,LF
       DB      '               HELP d:topic',CR,LF
       DB      'Where:',CR,LF
       DB      '     "d:"      is the disk the HELP File resides on (optional)',CR,LF
       DB      '     "topic"   is the name of the HELP File (like CPM.HLP).',CR,LF
       DB      '',CR,LF
       DB      'Please refer to the HELP File "HELP.HLP" for more information.',CR,LF
       DB      CTRLZ           ; END OF FILE

       END