;
;  PROGRAM:  MENU
;  AUTHOR:  RICHARD CONN
;  VERSION:  3.2
;  DATE:  10 June 84
;  PREVIOUS VERSIONS:  3.1 (28 Mar 84), 3.0 (18 Mar 84)
;  DERIVATION. MENU 1.4 for ZCPR2
;
VERS    EQU     32

;
;       MENU is the ZCPR3 Menu Processor.  It loads, looks for the MENU.MNU
; file, and then displays it to the user (optionally) and prompts him for
; a single-character command.  The ZCPR3 Multiple Command Line Buffer must
; be installed for MENU to work, and MENU uses this buffer to chain to the
; programs selected by the user and return to itself at the proper place.
;
;       MENU supports multiple menus within one MENU.MNU file.  When a command
; is invoked, MENU returns to the menu the command came from.
;
;       MENU will ONLY RUN on ZCPR3 systems with the Multiple Command Line
; Buffer Option enabled.
;

;
;  Menu Constants
;
sysmenu         equ     0               ;System Menu Enabled? 0=no, 1=yes

;  1 Special Menu Command Chars
RNM             EQU     '>'             ;NEXT MENU
RNMP            EQU     '.'             ;NEXT MENU PRIME (ALTERNATE)
RLM             EQU     '<'             ;LAST MENU
RLMP            EQU     ','             ;LAST MENU PRIME (ALTERNATE)
RFM             EQU     '*'             ;FIRST MENU
;
       if      sysmenu
RSM             EQU     '$'             ;SYSTEM MENU (PASSWORD REQUIRED)
                                       ; THIS IS SAME AS CONTROL CHAR
       endif           ;sysmenu

;  2 Internal Menu Control Chars
MCMD            EQU     ':'             ;COMMAND TO JUMP TO ANOTHER MENU
PCHAR           EQU     '"'             ;INDICATES AUTO PROMPT FOR SPECIFIC CMD
MINDIC          EQU     '#'             ;MENU SECTION INDICATOR
MFIRST          EQU     '%'             ;FIRST MENU INDICATOR
GOPTION         EQU     '-'             ;GLOBAL OPTION INDICATOR
WOPTION         EQU     '!'             ;ACTIVATES WAIT UPON RETURN

;  3 Menu Option Chars
COPTION         EQU     'C'             ;DISPLAY COMMAND LINE TO USER
DOPTION         EQU     'D'             ;DISPLAY MENU TO USER
POPTION         EQU     'P'             ;PAGE OUT MENU DISPLAY TO USER
XOPTION         EQU     'X'             ;DISABLE ZCPR3 RETURN

;  4 Miscellaneous
IBUFSZ          EQU     254             ;SIZE OF INPUT LINE BUFFER
VARFLAG         EQU     '$'             ;VARIABLE FLAG
                                       ;(FOLLOWED BY D,U,Fn,Nn,Tn)
CMDSEP          EQU     ';'             ;ZCPR3 COMMAND SEPARATOR

;
;  Enter/Exit Standout Mode (Recommended that these values not be changed)
;
DIM                     EQU     'A'-'@' ; ^A TO ENTER STANDOUT
NOTDIM                  EQU     'B'-'@' ; ^B TO EXIT STANDOUT

;
;  MACRO Library of Definitions
;
       MACLIB  Z3BASE.LIB

;
;  ZCPR3 CONSTANTS
;
wboot   equ     0
bentry  equ     5
fcb     equ     5ch
tbuff   equ     80h
BEL     equ     7
CR      equ     0dh
LF      equ     0ah
CTRLC   equ     'C'-'@'
TAB     equ     'I'-'@'
CTRLZ   equ     'Z'-'@'

;
; MACROS TO PROVIDE Z80 EXTENSIONS
;   MACROS INCLUDE:
;
;       BR      - JUMP RELATIVE
;       BRC     - JUMP RELATIVE IF CARRY
;       BRNC    - JUMP RELATIVE IF NO CARRY
;       BRZ     - JUMP RELATIVE IF ZERO
;       BRNZ    - JUMP RELATIVE IF NO ZERO
;       BJNZ    - DECREMENT B AND JUMP RELATIVE IF NO ZERO
;       PUTRG   - SAVE REGISTERS
;       GETRG   - RESTORE REGISTERS
;

;
;
; Z80 MACRO EXTENSIONS
;
BR      MACRO   ?N      ;;JUMP RELATIVE
       IF      I8080   ;;8080/8085
       JMP     ?N
       ELSE            ;;Z80
       .Z80
       JR      ?N
       .8080
       ENDIF           ;;I8080
       ENDM
;
BRC     MACRO   ?N      ;;JUMP RELATIVE ON CARRY
       IF      I8080   ;;8080/8085
       JC      ?N
       ELSE            ;;Z80
       .Z80
       JR      C,?N
       .8080
       ENDIF           ;;I8080
       ENDM
;
BRNC    MACRO   ?N      ;;JUMP RELATIVE ON NO CARRY
       IF      I8080   ;;8080/8085
       JNC     ?N
       ELSE            ;;Z80
       .Z80
       JR      NC,?N
       .8080
       ENDIF           ;;I8080
       ENDM
;
BRZ     MACRO   ?N      ;;JUMP RELATIVE ON ZERO
       IF      I8080   ;;8080/8085
       JZ      ?N
       ELSE            ;;Z80
       .Z80
       JR      Z,?N
       .8080
       ENDIF           ;;I8080
       ENDM
;
BRNZ    MACRO   ?N      ;;JUMP RELATIVE ON NO ZERO
       IF      I8080   ;;8080/8085
       JNZ     ?N
       ELSE            ;;Z80
       .Z80
       JR      NZ,?N
       .8080
       ENDIF           ;;I8080
       ENDM
;
BJNZ    MACRO   ?N      ;;DECREMENT B AND JUMP RELATIVE ON NO ZERO
       IF      I8080   ;;8080/8085
       DCR     B
       JNZ     ?N
       ELSE            ;;Z80
       .Z80
       DJNZ    ?N
       .8080
       ENDIF           ;;I8080
       ENDM
;
PUTRG   MACRO
       PUSH    H       ;;SAVE REGISTERS IN ORDER
       PUSH    D
       PUSH    B
       ENDM
;
GETRG   MACRO
       POP     B       ;;RESTORE REGISTERS IN ORDER
       POP     D
       POP     H
       ENDM
;
; END OF Z80 MACRO EXTENSIONS
;

;
;  Externals from SYSLIB
;
       ext     z3vinit,cls,stndout,stndend
       ext     getcl1,putcl,getsh2,qshell,retud,getefcb,shpush,shpop
       ext     getshm,putshm,moveb,getfn2,pfn1,getcrt,getzrun,putzex,putcst
       ext     eprint,cin,cout,caps,crlf,pafdc,madc,bline,initfcb,sksp
       ext     f$open,f$close,f$read,codend,hmovb

;
; Environment Definition
;
       if      z3env ne 0
;
; External ZCPR3 Environment Descriptor
;
       jmp     start
       db      'Z3ENV' ;This is a ZCPR3 Utility
       db      1       ;External Environment Descriptor
z3eadr:
       dw      z3env
start:
       lhld    z3eadr  ;pt to ZCPR3 environment
;
       else
;
; Internal ZCPR3 Environment Descriptor
;
       MACLIB  SYSENV.LIB
z3eadr:
       jmp     start
       SYSENV
start:
       lxi     h,z3eadr        ;pt to ZCPR3 environment
       endif

;
; Start of Program -- Initialize ZCPR3 Environment
;
       call    z3vinit ;initialize the ZCPR3 Env and the VLIB Env
       jmp     strt
;
;  This is the FCB which defines the default name of the MENU.MNU file
;
       if      sysmenu
ppass:
       db      'SYSTEM          ',0    ;system password
       endif           ;sysmenu
;
menufcb:
       db      0               ;FCB for MENU.MNU
       db      'MENU    '
       db      'MNU'
       ds      4
scratch:                        ;this doubles as a scratch area
       ds      16              ;buffer definition is at end of program
       ds      4               ;36 bytes total

;
;  Start of Program
;
strt:
;
; Check for Shell Stack
;
       call    getsh2  ;get shell status
       brnz    strt0   ;skip over shell init
       call    eprint
       db      ' No Shell Stack',0
       ret
;
; See if Command Line Available
;
strt0:
       call    getcl1  ;get line
       brnz    strt01
       call    eprint
       db      ' No Command Line',0
       ret
;
; See if this program was invoked as a shell
;
strt01:
       call    qshell  ;find out from ZCPR3 environment
       push    psw     ;save status
       xra     a       ;A=0
       call    putcst  ;put command status (normal = 0)
       pop     psw     ;restore status
       jz      menu    ;do not push onto stack if invoked as a shell
;
; Set Name of Shell from External FCB if Possible or From Default if Not
;
setshn:
       call    retud   ;get run address
       lxi     h,shdisk        ;pt to shell disk
       mov     a,b     ;get disk
       adi     'A'     ;convert to letter
       mov     m,a     ;set disk letter
       inx     h       ;pt to user 10's
       mov     a,c     ;get user number
       mvi     b,10    ;subtract 10's
       mvi     d,'0'   ;set char
setshn1:
       sub     b       ;subtract
       brc     setshn2
       inr     d       ;increment digit
       br      setshn1
setshn2:
       add     b       ;get 1's
       mov     m,d     ;set 10's digit for user
       inx     h       ;pt to 1's digit
       adi     '0'     ;compute 1's digit
       mov     m,a     ;set 1's digit
       call    getefcb ;get ptr to external fcb
       brz     strt02  ;no external FCB, so use default name
       inx     h       ;pt to program name
       lxi     d,shname        ;pt to string
       mvi     b,8     ;8 chars
       call    moveb   ;copy into buffer
;
; Check for File Name and Set It If Given
;
strt02:
       lxi     h,fcb+1 ;pt to file name
       lxi     d,menufcb+1
       mvi     b,11    ;11 chars
       mov     a,m     ;get first char
       cpi     ' '
       cnz     moveb   ;copy if one present
;
; Set File Name in MENUFCB into Line
;
       lxi     h,menufcb+1     ;set shell file name
       lxi     d,shfile        ;pt to shell file
       mvi     b,8     ;8 chars
strt03:
       mov     a,m     ;get next char
       cpi     ' '     ;done?
       brz     strt04
       stax    d       ;put char
       inx     d       ;pt to next
strt04:
       inx     h       ;pt to next
       bjnz    strt03
       mvi     a,'.'   ;put dot
       stax    d
       inx     d       ;pt to next
       mvi     b,3     ;file type
strt05:
       mov     a,m     ;copy
       stax    d
       inx     h       ;pt to next
       inx     d
       bjnz    strt05
       xra     a       ;store zero
       stax    d

;
; Set Menu Number
;
       mvi     b,1     ;shell message 1
       xra     a       ;menu 0
       call    putshm  ;set message

;
; Push Name of Shell onto Stack
;
       lxi     h,shdisk        ;pt to name of shell
       call    shpush  ;push shell onto stack
       brnz    strt2
;
; Shell Successfully Installed
;
       call    eprint
       db      ' Shell Installed',0
       ret
;
; Shell Stack Push Error
;
strt2:
       cpi     2       ;shell stack full?
       brnz    strt3
;
; Shell Stack is Full
;
       call    eprint
       db      ' Shell Stack Full',0
       ret
;
; Shell Stack Entry Size is too small for command line
;
strt3:
       call    eprint
       db      ' Shell Stack Entry Size',0
       ret
;
; Check for ZEX Execution and Pass ZEX if So
;
menu:
       call    getzrun         ;is ZEX running?
       brz     runmenu         ;process menu if not
       br      menuz1          ;skip new line
menuzex:
       call    crlf            ;new line
menuz1:
       call    eprint
       db      'Menu> ',0
       mvi     a,1             ;tell ZEX that it is prompted
       call    putzex
       call    codend          ;set up buffer
       mvi     m,ibufsz        ;set size
       mvi     a,0FFH          ;capitalize
       call    bline           ;get line from ZEX
       xra     a               ;A=0
       call    putzex          ;resume ZEX normally
       call    sksp            ;skip over leading spaces
       mov     a,m             ;check for comment
       cpi     ';'
       brz     menuzex
       xchg                    ;DE pts to command line
       xra     a               ;don't display command
       sta     cpflag
       jmp     runcmnd         ;run command pted to by DE
;
; Begin Menu Processing
;
runmenu:
       call    eprint
       db      'MENU  Version '
       db      (vers/10)+'0','.',(vers mod 10)+'0',0
;
; Check for Wait Flag and Wait if So
;
       mvi     b,0             ;get shell message 0
       call    getshm
       ani     80h             ;check for wait flag
       cnz     sak             ;Strike Any Key
;
; Open Menu File
;
       lxi     h,fcb           ;copy FCB into MENU FCB
       lxi     d,menufcb
       mvi     b,36            ;36 bytes
       push    d               ;save ptr
       call    moveb
       pop     d               ;pt to MENU.MNU FCB
       call    initfcb         ;init fcb
       call    f$open          ;open file
       brz     menu1           ;abort if no menu
       call    eprint
       db      CR,LF,' File ',0
       lxi     d,menufcb+1
       call    pfn1
       call    eprint
       db      ' Not Found',0
       jmp     shpop
;
;  Load MENU.MNU from disk
;
menu1:
       call    codend          ;get address of buffer for menu load
mload:
       lxi     d,menufcb       ;pt to FCB
       call    f$read          ;read in next block
       ora     a               ;error?
       brnz    mloaddn         ;load done if error
       lxi     d,tbuff         ;copy from TBUFF into memory pted to by HL
       xchg                    ;HL is source, DE is dest
       mvi     b,128           ;128 bytes
       call    hmovb
       lhld    bentry+1        ;get address of top of TPA
       mov     a,h             ;set to bottom of ZCPR3
       sui     10
       cmp     d               ;about to overflow ZCPR3?
       brnc    mload1  ;continue if not
       call    eprint
       db      CR,LF,' TPA Full',0
       ret
mload1:
       xchg                    ;HL pts to next byte to load to
       br      mload           ;continue load


;
;  Init Flags and Clear MSB of all bytes in Menu File
;
mloaddn:
       call    f$close         ;close input file
       mvi     m,CTRLZ         ;ensure EOF mark
       lxi     d,80H           ;pt to next block
       dad     d
       shld    ibuff           ;set ptr to input line buffer
       mvi     m,ibufsz        ;set size
       dad     d               ;allow 256 bytes
       dad     d
       shld    expline         ;set ptr to expand line
       xra     a               ;A=0
       sta     cflag           ;turn off command display
       sta     dflag           ;turn off menu display
       sta     pflag           ;disallow paging
       sta     cpmok           ;turn off ZCPR3 return flag
       call    codend          ;pt to beginning of file
       push    h               ;save ptr
menul1:
       mov     a,m             ;get byte
       ani     7FH             ;mask out MSB
       mov     m,a             ;put byte
       inx     h               ;pt to next
       cpi     CTRLZ           ;EOF?
       brnz    menul1          ;continue if not
;
;  Mark all Menu Sections
;
       pop     h               ;HL pts to first byte of menu
       mvi     b,0FFH          ;set menu counter
;
;  Skip to Next Menu
;
menul2:
       mov     a,m             ;get byte
       cpi     CTRLZ           ;error?
       jz      mstrerr         ;structure error if so
       cpi     MINDIC          ;menu indicator (start of menu?)
       brnz    menul4
       ori     80H             ;beginning of menu found -- set MSB
       mov     m,a             ;put byte
       inr     b               ;increment menu count
       inx     h               ;pt to next
       mov     a,m             ;get byte
       cpi     MINDIC          ;menu indicator (end of menu?)
       brz     menul5          ;done if so
       cpi     CTRLZ           ;error?
       jz      mstrerr
;
       if      sysmenu
       cpi     RSM             ;system menu indicator?
       brnz    menul3
       mov     a,b             ;set system menu number
       sta     smeno
       mvi     a,0FFH          ;set flag
       sta     smenfl          ;system menu present
       dcx     h               ;back up to beginning of menu
       shld    smenadr         ;start address
       inx     h               ;pt to RSM
       endif           ;sysmenu
;
;  Skip out Menu Display
;
menul3:
       call    lskipt          ;skip to beginning of next line
       brz     menul4          ;found menu indicator
       cpi     CTRLZ           ;error?
       jz      mstrerr
       br      menul3          ;continue if not
;
;  Skip to Next Menu
;
menul4:
       call    lskip           ;skip to beginning of next menu
       br      menul2
;
;  Check Menu Options
;
menul5:
       call    codend          ;pt to beginning of file
       mov     a,m             ;check for option
       cpi     GOPTION         ;global option char?
       jnz     mfile           ;if no global option, scan for menu files
       inx     h               ;pt to option char
option:
       mov     a,m             ;get option char
       call    caps            ;capitalize
       inx     h               ;pt to next
       cpi     CR              ;done?
       brz     optdn
       cpi     COPTION         ;display command?
       brz     optc
       cpi     DOPTION         ;display menu?
       brz     optd
       cpi     POPTION         ;paging?
       brz     optp
       cpi     XOPTION         ;exit OK?
       jnz     mstrerr         ;option error if not
;
;  Disable Exit to ZCPR3
;
       mvi     a,0FFH          ;turn flag off
       sta     cpmok
       br      option
;
;  Process Paging Option
;
optp:
       mvi     a,0FFH          ;set flag
       sta     pflag
       br      option
;
;  Process Display Menu Option
;
optd:
       mvi     a,0FFH          ;set flag
       sta     dflag
       br      option
;
;  Process Display Command Option
;
optc:
       mvi     a,0FFH          ;set flag
       sta     cflag
       br      option

;
;  Option Processing Done
;
optdn:
       inx     h               ;skip LF

;
;  Check for Menu Display
;
mfile:
       mov     a,m             ;get first byte
       ani     7FH             ;mask
       cpi     MINDIC          ;start of menu?
       jnz     mstrerr

;
;  Check and Set First Menu
;
       shld    mstart          ;save start address of first menu item
       mvi     m,MFIRST+80H    ;set first char of first menu

;
;  Entry Point for Menu Display
;       On entry, HL pts to first byte of current menu
;
dmenu:
       mvi     b,1             ;shell message 1 contains menu number
       call    getshm          ;get menu number flag
       cnz     mchc0           ;skip to proper menu
       shld    cstart          ;save start address of current menu
       lda     cflag           ;copy display command flag for temp use
       sta     cpflag
       lda     dflag           ;copy display menu flag for temp use
       sta     dpflag
       lda     pflag           ;copy paging flag for temp use
       sta     ppflag
       inx     h               ;pt to first char after menu indicator char
dispm1:
       mov     a,m             ;get char
       call    caps            ;capitalize
       inx     h               ;pt to next
       cpi     CR              ;end of options?
       brz     dispm2
;
       if      sysmenu
       cpi     RSM             ;system menu?
       brz     dispm1          ;ok if so
       endif           ;sysmenu
;
       cpi     COPTION         ;command display?
       brz     dispmc
       cpi     DOPTION         ;display?
       brz     dispmd
       cpi     POPTION         ;paging?
       brz     dispmp
       cpi     XOPTION         ;ZCPR3 return?
       jnz     mstrerr         ;error if not
;
;  Toggle ZCPR3 Return Option
;
       lda     cpmok           ;get flag
       cma                     ;toggle
       sta     cpmok
       br      dispm1
;
;  Toggle Paging Option
;
dispmp:
       lda     ppflag          ;get flag
       cma                     ;toggle
       sta     ppflag
       br      dispm1
;
;  Toggle Display Menu Option
;
dispmd:
       lda     dpflag          ;get flag
       cma                     ;toggle
       sta     dpflag
       br      dispm1
;
;  Toggle Display Command Option
;
dispmc:
       lda     cpflag          ;get flag
       cma                     ;toggle
       sta     cpflag
       br      dispm1
;
;  Done with Menu-Specific Option Processing
;
dispm2:
       call    lskip           ;skip to LF
       lda     dpflag          ;display menu?
       ora     a               ;0=no
       brz     dispm8          ;skip over menu if not
       call    getnlines       ;get line count in A
       sta     pagcnt          ;set count
       lda     ppflag          ;paging?
       ora     a               ;0=no
       push    psw             ;save flag
       cnz     cls             ;clear screen if so
       pop     psw             ;get flag
       cz      crlf            ;else new line
;
;  Print Next Line of Menu if not Starting with ESCAPE Char (MINDIC)
;
dispm3:
       mov     a,m             ;get first char of line
       ani     7FH             ;mask
       cpi     MINDIC          ;done?
       brz     dispm4
       call    expand          ;expand line pted to by HL
       push    h               ;save ptr to next line
       xchg                    ;HL pts to expanded line
       call    lprintx         ;print line pted to by HL ending in <CR>
       pop     h               ;pt to next line
       br      dispm3
;
;  Done with Menu Display -- Page it out
;
dispm4:
       call    lskip           ;skip to first char of next line (option char)
       shld    optstrt         ;set start address of options
       lda     pagcnt          ;number of remaining lines
       mov     b,a             ;count in B
       ora     a               ;ok?
       brz     dispm6          ;don't do anything if already there
       lda     ppflag          ;page?
       ora     a               ;0=No
       brz     dispm6
;
;  Page Loop for Menu Display
;
dispm5:
       call    crlf            ;new line
       bjnz    dispm5
;
;  Determine if Another Menu Follows
;
dispm6:
       xra     a               ;A=0
       sta     nmenfl          ;set for no next menu
       mov     a,m             ;ok?
       ani     7FH             ;mask
       cpi     CTRLZ           ;error if EOF
       jz      mstrerr
       cpi     MINDIC          ;next menu?
       brnz    dispm7
       inx     h               ;double indicator if end
       mov     a,m
       cpi     MINDIC          ;end?
       brz     dispm9
;
       if      sysmenu
       cpi     RSM             ;system menu = no next menu
       brz     dispm9
       endif           ;sysmenu
;
       mvi     a,0FFH          ;set next menu
       sta     nmenfl
       br      dispm9
dispm7:
       call    lskip           ;skip to next line
       br      dispm6

;
;  Skip over current menu so it is not displayed
;
dispm8:
       call    lskipt          ;skip to beginning of command
       brnz    dispm8
       call    lskip           ;skip over end of display indicator
       shld    optstrt         ;set pointer to options
       br      dispm6          ;determine if next menu available
dispm9:

;
;  Ready for Option Input
;    The following Flags/Values are now set:
;       CPFLAG -- Display Command Flag (0=No, 0FFH=Yes)
;       DPFLAG -- Display Menu Flag (0=No, 0FFH=Yes)
;       OPTSTRT -- Address of First Menu Option
;       NMENFL -- 0 if no next menu, 0FFH if next menu
;       MSTART -- Start Address of MINDIC Before Menu Display
;         (MSTART)=MFIRST with MSB Set
prompt:
       call    stndout         ;begin standout
       mvi     a,0ffh
       sta     pagcnt          ;turn off paging
       sta     dpflag          ;turn on future menu displays
       call    retud           ;get DU
       mov     a,b             ;print D
       adi     'A'
       call    cout
       mov     a,c             ;print U
       call    pafdc
       call    eprint
       db      '> Command (CR=Menu',0
       lda     cpmok           ;OK to return to ZCPR3?
       ora     a               ;0=No
       cnz     prmptc
       lhld    cstart          ;pt to first char
       mov     a,m             ;get it
       ani     7
FH              ;mask
       cpi     MFIRST
       cnz     prmptf          ;print previous menu prompt if not first menu
       lda     nmenfl          ;next menu available?
       ora     a               ;0=No
       cnz     prmptn          ;print next menu prompt
       call    eprint
       db      ') - ',0
       call    stndend         ;end standout
prompt1:
       call    cin             ;get response
       call    caps            ;capitalize
       mov     b,a             ;result in B

;
;  Check for CR
;
       cpi     CR              ;<CR>?
       jz      dispm2          ;reprint menu if so

;
;  Check for Reboot
;
       lda     cpmok           ;ok to abort?
       ora     a               ;0=No
       brz     prmpt0
       mov     a,b             ;get command
       cpi     CTRLC           ;reboot?
       jz      shpop           ;pop shell stack and return to OS if so

;
;  Check for Command to Return to First Menu
;
prmpt0:
       mov     a,m             ;get it
       ani     7FH             ;mask
       cpi     MFIRST
       brz     prmpt1
       mov     a,b             ;get command
       cpi     RFM             ;return to first menu?
       brnz    prmpt1
       lhld    mstart          ;pt to first menu
       mvi     b,1             ;shell message 1 is menu number
       xra     a               ;A=0=menu 0
       jmp     putshm          ;reenter shell at first menu

;
;  Check for Command to go to Next Menu
;
prmpt1:
       lda     nmenfl          ;next menu available?
       ora     a               ;0=No
       brz     prmpt2
       mov     a,b             ;get command
       cpi     RNMP            ;goto next menu?
       brz     rnmx
       cpi     RNM             ;goto next menu?
       brnz    prmpt2
rnmx:
       mvi     b,1             ;shell message 1 is menu number
       call    getshm          ;increment menu number
       inr     a
       jmp     putshm          ;reenter menu system at new menu

;
;  Check for Command to go to Last Menu
;
prmpt2:
       mov     a,m             ;get menu char
       ani     7FH             ;at first menu?
       cpi     MFIRST
       brz     prmpt3          ;skip if at first menu
       mov     a,b             ;get command
       cpi     RLMP            ;goto last menu?
       brz     lstmnu
       cpi     RLM             ;goto last menu?
       brnz    prmpt3
lstmnu:
       mvi     b,1             ;shell message 1 is menu number
       call    getshm          ;decrement menu number
       dcr     a
       jmp     putshm          ;reenter shell at last menu

;
;  Check for Command to goto System Menu
;
prmpt3:
       if      sysmenu
;
       lda     smenfl          ;system menu available?
       ora     a               ;0=No
       brz     prmpt4
       mov     a,b             ;get command
       cpi     RSM             ;system menu?
       brnz    prmpt4
       call    password        ;prompt for and get password
       jnz     prompt          ;reprompt if error
       lhld    smenadr         ;get address of system menu
       lda     smeno           ;set system menu number
       mvi     b,1             ;shell message 1 is menu number
       jmp     putshm          ;reenter shell at system menu
;
       endif           ;sysmenu
;
;  This is where additional functions may be added
;
prmpt4:

;
;  Check for Option Letter
;
       lhld    optstrt         ;pt to first option char
prmptx:
       mov     a,m             ;get it
       call    caps            ;capitalize
       cpi     MINDIC          ;at next menu?
       brz     prmpter
       cmp     b               ;match user selection?
       brz     prmptd
       call    lskip           ;skip to next line
       br      prmptx

;
;  Invalid Option
;
prmpter:
       call    eprint
       db      BEL,0
       jmp     prompt1

;
;  Process Option
;
prmptd:
       mov     a,b             ;output user selection
       call    cout
       mvi     b,0             ;shell message 0, bit 7 = wait flag
       call    getshm
       ani     7FH             ;set no wait
       call    putshm
       inx     h               ;pt to first letter of command
       mov     a,m             ;get it
       cpi     MCMD            ;invoke other menu?
       jz      mchcmd          ;menu change command
       cpi     WOPTION         ;turn on wait?
       brnz    prmptg
       mvi     b,0             ;shell message 0, bit 7 = wait flag
       call    getshm
       ori     80h             ;set wait flag
       call    putshm          ;set shell message
       inx     h               ;skip option char
prmptg:
       call    expand          ;expand line, DE pts to result
;
; Run Command Pted to by DE
;
runcmnd:
       call    getcl1          ;get address of command buffer
       mov     b,h             ;... in BC also
       mov     c,l
       mvi     a,4             ;HL=HL+4 for address of first char
       add     l
       mov     l,a
       mov     a,h
       aci     0
       mov     h,a
       mov     a,l             ;store address
       stax    b
       inx     b
       mov     a,h
       stax    b
;
; Copy Command Line in DE into Buffer in HL
;
cmdcpy:
       ldax    d               ;get command letter
       call    caps            ;capitalize it
       ora     a               ;done?
       brz     ccpyd
       cpi     CR              ;done?
       brz     ccpyd
       cpi     PCHAR           ;prompt?
       brz     ccpyp
       mov     m,a             ;store it
       inx     h               ;pt to next
       inx     d
       br      cmdcpy
ccpyd:
       mvi     m,0             ;store ending 0
       jmp     cmddisp         ;optionally display command
;
;  Prompt User for Input and Accept It
;
ccpyp:
       inx     d               ;pt to first char of prompt
       call    crlf            ;new line
ccpyp1:
       ldax    d               ;get char
       cpi     PCHAR           ;end of prompt?
       brz     ccpyp2
       cpi     CR              ;new line?
       brz     ccpyp3
       call    cout            ;echo char
       inx     d               ;pt to next char
       br      ccpyp1          ;continue looping
ccpyp2:
       inx     d               ;pt to char after closing PCHAR
ccpyp3:
       push    d               ;save ptr to next char
       xchg                    ;DE pts to buffer
       mvi     a,0FFH          ;capitalize input from user
       lhld    ibuff           ;input line buffer
       call    bline           ;get input from user
       xchg                    ;HL pts to buffer, DE pts to user input
cmdlp:
       ldax    d               ;get char from user
       ora     a               ;end of input?
       brz     cmdlp1          ;store rest of line
       mov     m,a             ;store char
       inx     h               ;pt to next
       inx     d
       br      cmdlp
cmdlp1:
       pop     d               ;DE pts to next char, HL pts to buffer
       br      cmdcpy          ;resume copying
;
;  Check for Display of Loaded Command and Do So if Set
;
cmddisp:
       lda     cpflag          ;display command?
       ora     a               ;0=No
       rz                      ;return to OS if so to run command
       call    crlf            ;new line
       call    getcl1          ;pt to first char
       mov     e,m             ;get low-order address
       inx     h
       mov     d,m             ;get high-order address
       xchg                    ;HL pts to first char
cmdd1:
       mov     a,m             ;get char
       cpi     CMDSEP          ;done if command separator
       rz
       inx     h               ;pt to next
       call    cout            ;print char
       br      cmdd1

;
;  Menu Change Command -- Jump to Specified Menu
;
mchcmd:
       inx     h               ;pt to menu number
       call    eval            ;convert to decimal number in A
       sta     menuno          ;save menu number
       call    mchc0           ;skip to desired menu to check for it
       lda     menuno          ;get menu number
       mvi     b,1             ;menu number is shell message 1
       jmp     putshm          ;set message and reenter shell

;
;  Entry Point if MENU is Reinvoked
;
mchc0:
       mov     b,a             ;menu number in B
       inr     b               ;add 1 for initial offset
       lhld    mstart          ;pt to first menu
mchc1:
       dcr     b               ;count down
       rz                      ;done if found
mchc2:
       call    lskipt          ;skip to next line
       brnz    mchc2           ;continue if not end of menu display
mchc3:
       call    lskipt          ;skip to next line
       brnz    mchc3           ;continue if not at end of menu commands
       inx     h               ;end of MENU.MNU?
       mov     a,m             ;yes if double MINDIC
       ani     7FH             ;mask
       cpi     MINDIC
       brz     mchcerr         ;error if so
       dcx     h               ;pt to first char
       br      mchc1           ;continue
;
; Premature End of Menu File
;
mchcerr:
       pop     psw             ;clear stack
       jmp     mstrerr         ;menu structure error

;
;  Print Line pted to by HL Ending in <CR>
;    Decrement PAGCNT
;
lprintx:
       call    lprint          ;print without <CR>
       jmp     crlf            ;do <CR> <LF>
;
;  Print Line Pted to by HL; Decrement PAGCNT
;
lprint:
       mvi     b,0             ;set tab counter
lprnt0:
       mov     a,m             ;get char
       inx     h               ;pt to next
       ani     7FH             ;mask MSB
       cpi     DIM             ;goto standout mode?
       brz     lprnt3
       cpi     NOTDIM          ;end standout mode?
       brz     lprnt4
       cpi     TAB             ;tabulate?
       brz     lprnt2
       cpi     CR              ;done?
       brz     lprnt1
       call    cout            ;print
       inr     b               ;incr tab counter
       br      lprnt0
lprnt1:
       inx     h               ;pt to first char of next line
       lda     pagcnt          ;count down pages
       dcr     a
       sta     pagcnt
       rnz
       call    getnlines       ;get line count in A
       sta     pagcnt
       call    eprint
       db      CR,LF,'Pause -',0
       br      sak1
lprnt2:
       mvi     a,' '           ;print <SP>
       call    cout
       inr     b               ;incr tab counter
       mov     a,b             ;done?
       ani     7               ;every 8
       brnz    lprnt2
       br      lprnt0
lprnt3:
       call    stndout         ;enter standout mode
       br      lprnt0
lprnt4:
       call    stndend         ;end standout mode
       br      lprnt0
;
;  Strike Any Key Message
;
sak:
       mvi     b,0             ;clear any pending wait
       call    getshm
       ani     7FH             ;mask MSB
       call    putshm
sak1:
       call    stndout         ;goto standout
       call    eprint
       db      ' Strike Any Key - ',0
       call    stndend         ;exit standout
       call    cin             ;get response
       call    crlf            ;new line
       ret

;
;  Prompt for, input, and check password (only one chance)
;    If accepted, return with Zero Flag Set; if not, return with NZ
;
       if      sysmenu
password:
       call    eprint
       db      CR,LF,'Pass? ',0
       lhld    ibuff           ;pt to input line buffer
       xra     a               ;don't capitalize user input
       call    bline           ;get line from user
       lxi     d,ppass         ;pt to system password
pass1:
       ldax    d               ;get sys pass char
       cmp     m               ;ok?
       brnz    passerr         ;error if no match
       inx     h               ;pt to next
       inx     d
       ora     a               ;end of strings?
       brnz    pass1
       ret                     ;return with zero set to show match
passerr:
       call    eprint
       db      CR,LF,' Password Error',0
       call    sak1            ;strike any key
       call    crlf
       mvi     a,0FFH          ;set no zero
       ora     a
       ret
       endif           ;sysmenu
;
;  Skip to Beginning of Next Line and Test First Char for Menu Indicator
;
lskipt:
       call    lskip           ;skip
       mov     a,m             ;get char
       ani     7FH             ;mask
       cpi     MINDIC          ;test
       ret

;
;  Skip to Beginning of Next Line
;
lskip:
       mov     a,m             ;get char
       ani     7FH             ;mask out MSB
       inx     h               ;pt to next
       cpi     LF
       brnz    lskip
       ret

;
;  Print ZCPR3 Return Prompt
;
prmptc:
       call    eprint
       db      ', ^C=Z3',0
       ret
;
;  Print First/Last Menu Chars
;
prmptf:
       call    eprint
       db      ', ',RFM,'=1st Menu, ',RLM,'=Prev Menu',0
       ret
;
;  Print next menu message
;
prmptn:
       call    eprint
       db      ', ',RNM,'=Next Menu',0
       ret

;
;  Menu Structure Error -- FATAL
;    This message is printed to indicate an error in the structure of
; the MENU.MNU file.
;
mstrerr:
       call    eprint
       db      CR,LF,' Structure Error',0
       jmp     shpop

;
; Expand Line Pted to by HL into Scratch Area
;       Return with HL pting to next line, DE pting to current line
;
expand:
       xchg
       lhld    expline         ;pt to buffer
       xchg
exp1:
       mov     a,m             ;get next char
       ani     7fh             ;mask MSB
       stax    d               ;store char
       cpi     CR              ;end of line?
       jz      expx
       inx     h               ;pt to next
       inx     d
       cpi     VARFLAG         ;variable follows?
       brnz    exp1
;
; Variable Identified - Process it
;
       mov     a,m             ;get next char
       inx     h               ;pt to next
       cpi     VARFLAG         ;one variable char?
       brz     exp1            ;resume if double VARFLAG
       dcx     d               ;pt to variable position
       call    caps            ;capitalize variable
       cpi     'D'             ;current disk?
       brz     expdisk
       cpi     'U'             ;current user?
       brz     expuser
       cpi     'F'             ;filename.typ?
       brz     expfile
       cpi     'N'             ;filename?
       brz     expname
       cpi     'T'             ;filetype?
       brz     exptype
       br      exp1            ;resume expansion
;
; Expand Exit
;
expx:
       inx     h               ;pt to line feed
       mov     a,m             ;get it
       cpi     LF              ;line feed?
       brnz    expx1
       inx     h               ;pt to char after line feed
expx1:
       xchg                    ;DE pts to next line
       lhld    expline         ;pt to expanded line
       xchg                    ;HL pts to next line, DE pts to expanded line
       ret

;
; Expand Disk
;
expdisk:
       call    retud           ;get disk in B
       mov     a,b             ;get disk number (A=0)
       adi     'A'             ;convert to ASCII
       stax    d               ;store letter
       inx     d               ;pt to next
       br      exp1            ;resume expansion
;
; Expand User
;
expuser:
       call    retud           ;get user in C
       mov     a,c             ;get user number
       mvi     b,10            ;subtract 10's
       mvi     c,'0'           ;set char
expu1:
       sub     b               ;-10
       brc     expu2
       inr     c               ;increment digit
       br      expu1
expu2:
       add     b               ;+10
       adi     '0'             ;convert 1's to ASCII
       mov     b,a             ;B=1's
       mov     a,c             ;get 10's
       stax    d               ;store 10's
       inx     d
       mov     a,b             ;get 1's
       stax    d               ;store 1's
       inx     d               ;pt to next
       br      exp1            ;resume
;
; Expand File
;
expfile:
       call    getfnum         ;get file number
       jz      exp1            ;resume if error
       push    h               ;save ptr to next char
       call    ptfn            ;set ptr to file name
       call    putn            ;put file name
       mvi     a,'.'
       stax    d               ;store dot
       inx     d               ;pt to next
       call    putt            ;put file type
       pop     h               ;restore ptr
       jmp     exp1            ;resume
;
; Expand Name
;
expname:
       call    getfnum         ;get file number
       jz      exp1            ;resume if error
       push    h               ;save ptr to next char
       call    ptfn            ;set ptr to file name
       call    putn            ;put file name
       pop     h               ;restore ptr
       jmp     exp1            ;resume
;
; Expand Type
;
exptype:
       call    getfnum         ;get file number
       jz      exp1            ;resume if error
       push    h               ;save ptr to next char
       call    ptfn            ;set ptr to file name
       mvi     a,8             ;add 8
       add     l
       mov     l,a
       mov     a,h
       aci     0
       mov     h,a
       call    putt            ;put file type
       pop     h
       jmp     exp1            ;resume
;
; Pt to File Name whose Number (1-4) is in A
;
ptfn:
       mov     b,a             ;get number in B
       call    getfn2          ;pt to file name 2
       push    d               ;save DE
       mov     a,b             ;file 0?
       ora     a
       brz     ptfnx
       lxi     d,11            ;size of file name and type
ptfn1:
       dad     d               ;pt to next
       bjnz    ptfn1
ptfnx:
       pop     d               ;restore DE
       ret
;
; Put File Name pted to by HL
;
putn:
       mvi     b,8             ;8 chars
       br      putc
;
; Put File Type pted to by HL
;
putt:
       mvi     b,3             ;3 chars
;
; Copy Chars from HL to DE for up to B bytes -- flush if space
;
putc:
       mov     a,m             ;get next char
       cpi     ' '             ;skip spaces
       brz     putc1
       stax    d               ;put next char
       inx     d               ;pt to next
putc1:
       inx     h               ;pt to next
       bjnz    putc
       ret

;
; Get File Number (1 to 4)
;       If valid number, return with value in A and HL pting to next char
;       If not valid, return with Z and HL pting to last char (F, N, T)
;
getfnum:
       mov     a,m             ;get char
       sui     '1'             ;convert
       brc     getfne          ;error
       cpi     4               ;range?
       brnc    getfne
       inx     h               ;pt to next char
       ret                     ;NZ from CPI 4
getfne:
       dcx     h               ;error return
       xra     a
       ret

;
;  Return Number of Lines on CRT in A
;
getnlines:
       push    h               ;save HL
       call    getcrt          ;get CRT info
       inx     h               ;pt to number of lines
       mov     a,m             ;get count
       pop     h               ;restore HL
       dcr     a               ;subtract 1 for footer
       ret

;
;  Convert char string pted to by HL into decimal number in A
;       On Entry, HL pts to first digit char
;       On Exit, HL pts to after last digit char and A=number
;
eval:
       push    b               ;save BC
       mvi     b,0             ;set value
eval1:
       mov     a,m             ;get digit
       sui     '0'             ;convert to binary
       brc     eval2
       cpi     10              ;range?
       brnc    eval2
       inx     h               ;pt to next digit
       mov     c,a             ;new digit in C
       mov     a,b             ;multiply B by 10
       add     a               ;*2
       add     a               ;*4
       add     b               ;*5
       add     a               ;*10
       add     c               ;add in new digit
       mov     b,a             ;result in B
       br      eval1
eval2:
       mov     a,b             ;result in A
       pop     b               ;restore ptr
       ret

;
;  These buffers overlay the scratch area to save space
;
optstrt equ     scratch         ;Address of First Option in Current Menu
mstart  equ     optstrt+2       ;Address of First Menu
cstart  equ     mstart+2        ;Address of Current Menu
smenfl  equ     cstart+2        ;System Menu Available Flag (0=No)
smeno   equ     smenfl+1        ;System Menu Number
smenadr equ     smeno+1         ;Address of First Byte of System Menu
nmenfl  equ     smenadr+2       ;Next Menu Available Flag (0=No)
menuno  equ     nmenfl+1        ;Number of Menu
pagcnt  equ     menuno+1        ;Paging Counter
cflag   equ     pagcnt+1        ;Display Command Line Flag
dflag   equ     cflag+1         ;Display Menu Flag
pflag   equ     dflag+1         ;Paging Flag

;
;  Buffers
;
ibuff:
       ds      2               ;input line buffer
expline:
       ds      2               ;scratch area to expand lines in
cpflag:
       ds      1               ;Temp Display Command Line Flag
dpflag:
       ds      1               ;Temp Display Menu Flag
ppflag:
       ds      1               ;Temp Paging Flag
cpmok:
       ds      1               ;OK to Return to ZCPR3 (0=No)
tnum:
       ds      41              ;space for chars and ending 0
shdisk:
       db      'A'             ;disk to return to
       db      '00'            ;user to return to
       db      ':;'            ;log in and next command
shname:
       db      'MENU    '      ;program name (filled in at installation)
shfile:
       ds      13              ;file name (12) and ending 0

       end