#TITLE  "TURBODOS 1.4X MENU UTILITY"
       #PAGE   132,66
       MODULE  "TDMENU"
;
;       +----------------------------------+
;       | MENU UTILITY FOR 16-BIT TURBODOS |
;       +----------------------------------+
;
CFUNC   ==      0XE0                    ; C-FUNCTION INTERRUPT
TFUNC   ==      0XDF                    ; T-FUNCTION INTERRUPT
;
       LOC     Data#
;
;       +-----------------------+
;       | TERMINAL CONFIG BLOCK |
;       +-----------------------+
;
ESC     ==      0X1B
;
CCURUP:: BYTE   2,ESC,'A',0,0,0,0       ; CURSOR UP
CCURDN:: BYTE   2,ESC,'B',0,0,0,0       ; CURSOR DOWN
CCURLF:: BYTE   2,ESC,'D',0,0,0,0       ; CURSOR LEFT
CCURRT:: BYTE   2,ESC,'C',0,0,0,0       ; CURSOR RIGHT
CHOMEC:: BYTE   2,ESC,'H',0,0,0,0       ; HOME CURSOR
CURAD1:: BYTE   2,ESC,'F',0,0,0,0       ; CURSOR ADDRESSING STRING 1
CURAD2:: BYTE   0XFF                    ; FF=R/C, 00=C/R
CURAD3:: BYTE   0XFF                    ; FF=BINARY, 00=ASCII
CURAD4:: BYTE   ' '                     ; BINARY OFFSET
CDISON:: BYTE   3,ESC,"-1",0,0,0        ; CURSOR DISPLAY ON
CDISOF:: BYTE   3,ESC,"-0",0,0,0        ; CURSOR DISPLAY OFF
CLEOLN:: BYTE   2,ESC,'K',0,0,0,0       ; CLEAR TO END OF LINE
CLRSCR:: BYTE   2,ESC,'E',0,0,0,0       ; CLEAR SCREEN AND HOME
CINHIL:: BYTE   3,ESC,"dA",0,0,0        ; INTO HIGHLIGHTING
COTHIL:: BYTE   3,ESC,"d@",0,0,0        ; OUT OF HIGHLIGHTING
CINREV:: BYTE   3,ESC,"dP",0,0,0        ; INTO REVERSE
COTREV:: BYTE   3,ESC,"d@",0,0,0        ; OUT OF REVERSE
CLINES:: BYTE   24                      ; NUMBER OF LINES
CCOLUM:: BYTE   80                      ; NUMBER OF COLUMNS
KEYUP::  BYTE   'P'-0X40                ; KEY: CURSOR UP
KEYDN::  BYTE   'N'-0X40                ; KEY: CURSOR DOWN
KEYXIT:: BYTE   'X'-0X40                ; KEY: EXIT TO O/S
NPRIVX:: BYTE   0                       ; NONPRIV MAY EXIT IF NZ
;
;       +------------------------+
;       | SYSTEM MESSAGE SECTION |
;       +------------------------+
;
BANNER:: BYTE   "YOUR MENU TITLE HERE$" ; CUSTOMIZABLE MENU HEADER
       RES     60                      ; UP TO 80 CHARS
CRLFLF: BYTE    "\r\n"
CRLF:   BYTE    "\r\n$"
MENTER: BYTE    "Enter selection [ ]\8\8$"
MINVME: BYTE    "\r\n\n\7FATAL ERROR: Menu not "
       BYTE    "terminated or too large!\r\n\n$"
TOOMNL: BYTE    "\R\N\NTESTING: TOO MANY LINES\R\N\N\N$"
SEP:    BYTE    "  =  $"
PRTC:   BYTE    "PRESS ANY KEY TO RETURN TO MENU:$"
;
;       +----------------------+
;       | DATA STORAGE SECTION |
;       +----------------------+
;
NMBLNS: WORD    0                       ; NUMBER OF MENU LINES
PRIV:   BYTE    0                       ; PRIVILEGE FLAG
;
CMDLN:  BYTE    0,"\\"
       RES     160
;
;       +------------------------+
;       | MENU SCREEN DEFINITION |
;       +------------------------+
;
; THE FOLLOWING WORKSPACE CONTAINS THE SCREEN DEFINITIONS WHICH MUST
; BE DEFINED IN THE MENU.PAR FILE IN STRICT ACCORDANCE WITH THE
; SAMPLE PROVIDED BELOW. THE TOTAL CHARACTER COUNT MUST NEVER
; EXCEED 8K!
;
SCREEN::
       BYTE    "A|DISPLAY DIRECTORY|DIR\\MENU W",0
       BYTE    "B|DISPLAY DRIVE STATS|DRIVE\\MENU W",0
       BYTE    "C|DISPLAY DRIVE A DIRECTORY|DIR A:\\MENU W",0
       BYTE    "D|LOG ONTO MASTER|MASTER",0
       BYTE    0XFF
       RES     8192-(.-SCREEN)
;
; EXPLANATION OF EXAMPLE:
; THE VERTICAL BAR IS A FIELD DELIMITER, THE SINGLE QUOTE IS
; A PROMPT DELIMITER.
; A DEFINITION LINE MUST CONTAIN 4 FIELDS:
; 1. THE RESPONSE CHARACTER WHICH MUST BE ONE SINGLE CHARACTER
;    AND MAY BE ANY CHARACTER OTHER THAN 0X00,"'",OR "|"
; 2. THE SELECTION TITLE, WHICH MAY BE A STRING OF UP TO 40
;    CHARACTERS
; 3. THE COMMAND
;    (THE COMMAND MAY BE FOLLOWED IMMEDIATELY BY A STRING ENCLOSED
;    IN SINGLE QUOTES. IN SUCH A CASE, THE STRING WILL BE DISPLAYED
;    BEFORE COMMAND EXECUTION AND A RESPONSE WILL BE SOLICITED FROM
;    THE USER WHICH WILL BE PASSED ALONG WITH THE COMMAND.
;    THE RESPONSE WILL ONLY BE PARSED FOR A BACKSLASH CHARACTER,
;    WHICH IN THIS CONTEXT IS ILLEGAL AND WILL BE IGNORED.
; 4. THE ENDMARK, WHICH IS A NUL (0X00) AND DELIMITS DEFINITION
;    LINES.
;
; THE SPECIAL DEFINITION LINE 0XFF DENOTES THE END OF ALL DEFINITIONS
; AND THUS THE END OF THE MENU AND MUST BE PRESENT.
;
       RES     100                     ; ENOUGH STACK SPACE
STACK   ==      .
;
       LOC     Code#
;
;       +------------------+
;       | START OF PROGRAM |
;       +------------------+
;
START:  MOV     AX,DS                   ; GET DATA SEGMENT
       MOV     SS,AX                   ; SET UP STACK SEGMENT
RESTRT: MOV     SP,&STACK               ;   AND OFFSET
       CALL    INIPGM                  ; INITIALIZE THE PROGRAM
       CMP     0X5D,=BYTE 'W'          ; WAIT?
       JNZ     __NWT                   ;   NO
       MOV     CX,=23*256+45           ; BOTTOM LINE, END OF LINE
       CALL    GOTOXY
       CALL    INREV                   ; REVERSE ON
       MOV     DX,&PRTC                ; SEND MESSAGE
       CALL    PRINTM
       CALL    GETCH
       CALL    OUTREV                  ; REVERSE OFF
__NWT:  CALL    CLS                     ; CLEAR THE SCREEN
       MOV     DX,&CRLF                ; SPACE DOWN A LINE
       CALL    PRINTM
       MOV     BX,&BANNER              ; POINT TO BANNER
       CALL    CENTER                  ; PRINT CENTERED
       CMP     NMBLNS,=20      ; TESTING: OVER 20?
       JNA     __LO
       MOV     CX,=16*256+30
       CALL    GOTOXY
       MOV     DX,&TOOMNL
       CALL    PRINTM
       JMP     EXIT
;
__LO:   CALL    DSPMNU                  ; DISPLAY MENU SELECTIONS
       MOV     CX,=4*256+20            ; CURSOR POSITION
;
; THE FOLLOWING LOOP IS USED TO SELECT AN ITEM
; CX PRESERVED THROUGHOUT TO HOLD SCREEN ADDRESS
;
SELECT: CALL    GOTOXY                  ; PLACE CURSOR ON SELECTION
       CALL    GETCH                   ; GET A CHARACTER
       CMP     AL,KEYUP                ; KEY UP?
       JZ      __KUP                   ;   YES
       CMP     AL,KEYDN                ; KEY DOWN?
       JZ      __KDN                   ;   YES
       CMP     AL,=0X0D                ; RETURN?
       JNZ     __KER                   ;   NO, ERROR
       JMP     EXECUT                  ;     ELSE EXECUTE COMMAND
;
__KER:  CMP     AL,KEYXIT               ; EXIT KEY?
       JNZ     __NXK                   ;   NO
       CMP     NPRIVX,=0               ; CHECK NONPR FLAG
       JNZ     __EX1                   ; EXIT IF SET
       CMP     PRIV,=0                 ; PRIV FLAG SET?
       JNZ     __EX1                   ;   YES
__NXK:  MOV     AL,=0X07                ; BELL
       CALL    PUTCH
       JMPS    SELECT
;
__EX1:  JMP     EXIT                    ; SPRINGBOARD
;
; KEY UP
;
__KUP:  DEC     CH                      ; DECREMENT LINE COUNT
       CMP     CH,=3                   ; PAST TOP?
       JNZ     SELECT                  ;   NO, LOOP ON
       MOV     CH,BYTE NMBLNS          ;     ELSE LOAD MAXLINE
       ADD     CH,=3                   ;     ADD OFFSET
       JMPS    SELECT                  ;     AND LOOP
;
; KEY DOWN
;
__KDN:  INC     CH                      ; INCREMENT LINE COUNT
       MOV     AX,NMBLNS               ; GET LINE COUNT
       ADD     AL,=4                   ; ADD OFFSET
       CMP     AL,CH                   ; BELOW BOTTOM?
       JNZ     SELECT                  ;   NO, CONTINUE
       MOV     CH,=4                   ;     ELSE LOAD TOP
       JMPS    SELECT                  ;     AND LOOP
;
; COME HERE TO EXECUTE A PROGRAM
;
EXECUT: SUB     CH,=3                   ; NORMALIZE COMMAND POINTER
       MOV     SI,&SCREEN              ; POINT TO SCREEN STRING
__EXF:  DEC     CH                      ; DECREMENT COUNTER
       JZ      __FND                   ; EXIT LOOP IF FOUND
__F0:   INC     SI
       CMP     [SI],=BYTE 0
       JZ      __EXF
       JMPS    __F0
;
__FND:  INC     SI                      ; STEP PAST EOL
       INC     SI                      ; STEP PAST COMMAND BYTE
__FVB:  INC     SI
       CMP     [SI],=BYTE '|'          ; STEP TO SECOND DELIMITER
       JNZ     __FVB                   ; LOOP TILL FOUND
       INC     SI                      ; POINT TO COMMAND STRING
       MOV     DI,&CMDLN+2             ; POINT TO COMMAND LINE
       MOV     AL,=1                   ;   CONSTRUCTION AREA
__ML:   MOVS    BYTE                    ;   MOVE A BYTE
       INC     AL
       CMP     [SI],=BYTE 0            ; CHECK FOR END
       JNZ     __ML                    ; LOOP IF NOT
       MOV     CMDLN,AL
       MOV     CL,=18                  ; ELSE SEND COMMAND LINE
       MOV     DX,&CMDLN
       INT     TFUNC                   ; DO IT
       MOV     CL,=17                  ; ENABLE AUTOLOAD
       MOV     DL,=0XFF
       INT     TFUNC
       CALL    CLS                     ; CLEAR THE SCREEN
       JMP     EXITX                   ; EXIT AND EXECUTE


;
EXIT:   MOV     CX,=22*256+0            ; PLACE CURSOR ON BOTTON OF SCREEN
       CALL    GOTOXY
       MOV     CL,=17                  ; DISABLE AUTOLOAD
       MOV     DL,=0
       INT     TFUNC
EXITX:  MOV     CL,=0
       INT     CFUNC
;
;       +-------------+
;       | SUBROUTINES |
;       +-------------+
;
;       DISPLAY MENU SCREEN
;       -------------------
;
DSPMNU: MOV     SI,&SCREEN              ; POINT TO SELECTIONS
       MOV     CX,=4*256+20            ; CURSOR POSITION
__DSLP: CALL    GOTOXY
       MOV     AL,[SI]
       CMP     AL,=0XFF                ; END OF MENU?
       JNZ     __X
       RET                             ; YES, RETURN
;
__X:    CALL    PUTCH                   ; DISPLAY RESPONSE CHARACTER
       INC     SI
       INC     SI                      ; POINT TO DESCRIPTION
       MOV     DX,&SEP                 ; DISPLAY SEPARATOR
       CALL    PRINTM
__DSDL: MOV     AL,[SI]
       CMP     AL,='|'                 ; DELIMITER?
       JZ      __DSDX                  ;   YES, EXIT
       CALL    PUTCH
       INC     SI
       JMPS    __DSDL
;
__DSDX: INC     SI
       CMP     [SI],=BYTE 0
       JNZ     __DSDX
       INC     SI
       INC     CH                      ; NEXT LINE
       JMPS    __DSLP
;
;       CENTER PRINT STRING AT BX
;       -------------------------
;
CENTER: PUSH    BX                      ; SAVE STRING POINTER
       XOR     CX,CX                   ; SET COUNTER TO 0
__CCL:  CMP     [BX],=BYTE '$'          ; END MARK?
       JZ      __CCE                   ;   YES
       INC     BX
       INC     CL
       JMPS    __CCL
;
__CCE:  MOV     AL,CCOLUM               ; GET NUMBER OF COLUMNS
       SUB     AL,CL                   ; SUBTRACT NUMBER OF CHARACTERS
       SHR     AL,=1                   ; DIVIDE BY 2
       MOV     CL,AL                   ; PUT INTO COUNTER REGISTER
       MOV     AL,=' '                 ; SPACE
__CCS:  CALL    PUTCH                   ; PRINT A SPACE
       LOOP    __CCS                   ; UNTIL DONE
       POP     DX                      ; GET STRING POINTER
       JMP     PRINTM                  ; NOW PRINT IT
;
;       POSITION CURSOR
;       ---------------
;
GOTOXY: PUSH    SI
       MOV     SI,&CURAD1              ; SEND LEAD-IN
       CALL    TERCMD
       CMP     CURAD2,=0               ; COL/ROW?
       JNZ     __NCR                   ; NO, ROW/COL
       XCHG    CH,CL                   ; ELSE SWAP
__NCR:  MOV     AL,CH                   ; GET ADDR
       ADD     AL,CURAD4               ; ADD OFFSET
       CALL    PUTCH                   ; OUTPUT IT
       MOV     AL,CL                   ; GET SECOND ADDR
       ADD     AL,CURAD4               ; ADD OFFSET
       CALL    PUTCH                   ; OUTPUT IT
       POP     SI
       RET
;
;       TURN REVERSE ON
;       ---------------
;
INREV:  MOV     SI,&CINREV
       JMPS    TERCMD
;
;       TURN REVERSE OFF
;       ----------------
;
OUTREV: MOV     SI,&COTREV
       JMPS    TERCMD
;
;       CLEAR THE SCREEN
;       ----------------
;
CLS:    MOV     SI,&CLRSCR              ; POINT TO CLEAR SCREEN CODE
;
;       SEND TERMINAL COMMAND
;       ---------------------
;
TERCMD: PUSH    AX                      ; SAVE ACCUMULATOR
       PUSH    CX                      ; SAVE COUNTER
       MOV     CL,[SI]                 ; GET NUMBER OF BYTES
       XOR     CH,CH                   ; MAKE 16 BITS
__TCL:  INC     SI                      ; POINT TO NEXT BYTE
       MOV     AL,[SI]                 ; GET BYTE
       CALL    PUTCH                   ; OUTPUT CHARACTER
       LOOP    __TCL                   ; REPEAT UNTIL DONE
       POP     CX                      ; RESTORE REGISTERS
       POP     AX
       RET
;
;       OUTPUT CHARACTER IN AL
;       ----------------------
;
PUTCH:  PUSH    AX                      ; SAVE MOST REGISTERS
       PUSH    BX
       PUSH    CX
       PUSH    DX
       PUSH    SI
       MOV     CL,=2
       MOV     DL,AL
       INT     CFUNC
       POP     SI
       POP     DX
       POP     CX
       POP     BX
       POP     AX
       RET
;
; GET CHARACTER INTO AL
;
GETCH:  PUSH    BX                      ; SAVE MOST REGISTERS
       PUSH    CX
       PUSH    DX
       PUSH    SI
       MOV     CL,=3
       INT     CFUNC
       POP     SI
       POP     DX
       POP     CX
       POP     BX
       RET
;
;       PRINT STRING AT DX
;       ------------------
;
PRINTM: PUSH    BX
       PUSH    CX
       PUSH    DX
       PUSH    SI
       MOV     CL,=9
       INT     CFUNC
       POP     SI
       POP     DX
       POP     CX
       POP     BX
       RET
;
;       INITIALIZE PROGRAM
;       ------------------
;
INIPGM: MOV     BX,CS                   ; SET UP REGISTERS
       MOV     DX,&ABORT               ;   FOR ABORT TRAP
       MOV     CL,=8
       INT     TFUNC
       MOV     CL,=12                  ; GET PRIVILEGE STATUS
       INT     TFUNC
       MOV     PRIV,CH                 ; SAVE IT
       MOV     CL,=17                  ; ENABLE AUTOLOAD
       MOV     DL,=0XFF
       INT     TFUNC
;
; NOW DETERMINE TOTAL NUMBER OF MENU SELECTIONS
; (SIMPLY COUNT NULL CHARACTERS IN THE DEFINITION SPACE)
;
       MOV     SI,&SCREEN              ; POINT TO SCREEN DEF SPACE
       XOR     AX,AX                   ; CLEAR COUNTER
       MOV     CX,=8192                ; MAX NUMBER OF BYTES
__NCL:  CMP     [SI],=BYTE 0X00         ; DEF LINE END?
       JZ      __DLE                   ;   YES
       CMP     [SI],=BYTE 0XFF         ; SCREEN END?
       JZ      __SCE                   ;   YES
__C:    INC     SI                      ; INCREMENT POINTER
       LOOP    __NCL                   ;   AND CONTINUE
       MOV     DX,&MINVME              ; COMPLAIN
       CALL    PRINTM
       JMP     EXIT                    ; AND QUIT
;
__DLE:  INC     AX                      ; BUMP COUNTER
       JMPS    __C
;
__SCE:  MOV     NMBLNS,AX               ; STORE NUMBER OF LINES
       RET                             ; AND RETURN
;
ABORT:  RETF
       END
RINTM: PUSH    BX
       PUSH    CX
       PUSH    DX
       PUSH    SI
       MOV     CL,=9
       INT     CFUNC
       POP     SI
       POP     DX
       P