Metropoli BBS
VIEWER: menu.asm MODE: TEXT (CP437)
                PAGE    66,132          ;
                NAME    MENU            ;
                TITLE   Menu utility    ;
;
; MENU provides a screen oriented interface for use in constructing batch files
; using menu selection and fill-in-the-blanks approaches.
;
; Use:
;
; MENU [/N/D/E] [screen] [template] [out out out ...]
;
; "screen" is the display file that the user sees.  "screen" contains the text
; to be displayed, the display attributes, and display management commands.
; The logical screen may be longer than a physical screen.
;
; "template" is a file that defines the format of the output file "out".
; "template" consists of the exact text to be sent to "out" except for the
; inclusion of bracketed references.  Text of the form [nnn] is replaced by
;
; "out" is the name of the file to receive the final output.  Multiple out
; files may be specified.
;
; Copyright (c) 1989 Ziff Communications Co.
; Written  May 1989 for PC Magazine by Ronald Q. Smith
;
; Constant definitions.
;
; DOS Interface
;
DOS$            EQU     21H             ; DOS function call INT
; Function values for DOS$
DATE$           EQU     2AH             ; Get date
TIME$           EQU     2CH             ; Get time of day
CREATE$         EQU     3CH             ; Create file
OPEN$           EQU     3DH             ; Open file
CLOSE$          EQU     3EH             ; Close file
RDFILE$         EQU     3FH             ; Read file
WRFILE$         EQU     40H             ; Write file
EXIT$           EQU     4CH             ; Return to DOS
STDIN           EQU     0               ; Standard input handle
STDOUT          EQU     1               ; Standard output handle
ERROUT          EQU     2               ; Handle for error messages
;
; BIOS Interface
;
VIDEO$          EQU     10H             ; CRT handler INT
; Function values for VIDEO$
SMODE$          EQU     0               ; Set video mode
CRS$            EQU     2               ; Set cursor position function
RDCRS$          EQU     3               ; Read cursor position function
PAGE$           EQU     5               ; Set video page
RACH$           EQU     8               ; Read attribute and character
WACH$           EQU     9               ; Write attribute and character
WCHR$           EQU     10              ; Write character
VIDSTAT$        EQU     15              ; Get current video state
EGASTAT$        EQU     17              ; Get EGA status
ALTSEL$         EQU     18              ; Alternate select to EGA
;
KYBD$           EQU     16H             ; Keyboard handler INT
;
; Non-printing ASCII characters
;
BS_CHR          EQU     8               ; Back space
TAB_CHR         EQU     9               ; Tab
LF_CHR          EQU     10              ; Line feed
CR_CHR          EQU     13              ; Carriage return
EOF_CHR         EQU     26              ; End-of-file sentinel character
ESC_CHR         EQU     27              ; Esc
;
; Screen constants.
;
SCR_ATTR        EQU     7               ; White on black default
MONO_SEG        EQU     0B000H          ; Monochrome adapter refresh segment
COLOR_SEG       EQU     0B800H          ; Color/EGA adapter refresh segment
;
; I/O constants.
;
L_INB           EQU     512             ; Input buffer size
L_OUTB          EQU     512             ; Output buffer size
EDIT_FLD        EQU     100             ; Maximum number of input fields
;
; BIOS Data segment
;
BIOS_DATA       SEGMENT AT 40H          ;
                ORG     63H             ;
ADDR_6845       DW      ?               ; I/O address of current display
                ORG     87H             ;
EGA_INFO        DB      ?               ; EGA status byte
EGA_MASK        EQU     00001000B       ; EGA active bit (0=active)
BIOS_DATA       ENDS                    ;
;
; Macros.
;
; DBA creates character,attribute pairs from an attribute value (ATTR) and
; a string (STR).  This macro may be used to create values to be copied
; directly to the screen buffer.
;
DBA             MACRO   ATTR,STR        ; DB with attribute
                  IRPC    C,<&STR>      ;
                    DB      '&C',ATTR   ;
                  ENDM                  ;
                ENDM                    ;
;
; Start of program area.
;
CODE            SEGMENT PARA PUBLIC     ;
                ASSUME  CS:CODE,DS:CODE,ES:CODE,SS:CODE ;
;
; Program Segment Prefix area.
;
MENU_PSP        LABEL   BYTE            ; Start of segment
                ORG     80H             ;
P_CNT           DB      ?               ; Number of bytes of parameter supplied
P_CMD           DB      ?               ; Parameter area
;
; MENU Body
;
                ORG     100H            ;
MENU            PROC                    ;
                JMP     INIT            ; Go to initialization code
;
; Data area.
;
;
; Screen handling.
;
SAVEPAGE        DB      ?               ; Save video page
SAVECRS         DW      ?               ; Save original cursor position
FAST            DB      0               ; If non-zero, fast screen update
VIDEO_SEG       DW      MONO_SEG        ; Default video display segment
STATUS_REG      DW      ?               ; Video status register
SCR_COL         DB      80,0            ; Default screen width
SCR_LIN         DB      25,0            ; Default number of lines
DISP_SIZE       DW      24*80*2         ; Default display area size
DISP_LOC        DW      0FFFFH          ; Save area start for screen
EDIT_LAST       DW      ?               ; Last address in edit buffer
ATTRIBUTE       DB      SCR_ATTR        ; Current screen attribute
DOPT            DW      L_SCHR          ; Length of screen character table
EOPT            DW      L_KEYT          ; Length of editing character table
SHOW            DB      0               ; Show fields if set
SCRN_END        DW      ?               ; Location of end of screen
;
; Input field handling.
;
EDIT_NUM        DW      0               ; Index to next input area def
EDIT_AREA       DW      EDIT_FLD DUP(0,0) ; Start and end of input areas
                DW      0,0             ; Last DEF_AREA entry
;
; Constant fields.
;
CONST_NUM       DW      0               ; Index to next constant definition
CONST_AREA      DW      EDIT_FLD DUP(0,0) ; Start and end of constant areas
;
; Defining new input fields.
;
DEF_NUM         DW      0               ; Index to next definition
DEF_AREA        EQU     EDIT_AREA+4     ; Start and end of input areas
                                        ; Only use one real input area
                                        ; when defining new ones
;
; Handles and other I/O related.
;
SCREENFILE      DW      ?               ; Screen file handle
TEMPLFILE       DW      ?               ; Template file handle
IN_CNT          DW      0               ; Characters remaining in input buffer
IN_INDX         DW      ?               ; Next character in input buffer
NUM_OUT         DW      0               ; Number of output files
OUT_HNDL        LABEL   WORD            ;
                DW      64 DUP(0)       ; Output file handles
OUTFILE         DW      ?               ; Current output file handle
OUT_INDX        DW      0               ; Output buffer index
;
; Messages
;
CRLF            DB      CR_CHR,LF_CHR   ;
BIG_MSG         DB      "Screen file is too long." ;
L_BIG           EQU     $-BIG_MSG       ;
SCRN_ERR        DB      'Error reading screen file.' ;
L_SERR          EQU     $-SCRN_ERR      ;
INP_MSG         DB      "Screen file contains unbalanced []'s or too " ;
                DB      'many fields.'  ;
L_INP           EQU     $-INP_MSG       ;
CONST_MSG       DB      "Screen file contains unbalanced {}'s or too " ;
                DB      'many constants.' ;
L_CONST         EQU     $-CONST_MSG     ;
ANSI_MSG        DB      'Illegal ANSI (ESC) sequence.  Only ESC [#;...#m ' ;
                DB      'is allowed.  #=0-47.' ;
L_ANSI          EQU     $-ANSI_MSG      ;
TMPL_ERR        DB      'Error reading template file.' ;
L_TMPL          EQU     $-TMPL_ERR      ;
TMPL_MSG        DB      "Template file contains unbalanced []'s, {}'s, " ;
                DB      "<>'s or a bad field number." ;
L_TBAD          EQU     $-TMPL_MSG      ;
OUT_MSG         DB      'Error writing output file.' ;
L_OUT           EQU     $-OUT_MSG       ;
Q_MSG           DB      'Do you want to quit? YN' ;
L_Q             EQU     $-Q_MSG         ;
ROOM_MSG        DB      'Too many fields' ;
L_ROOM          EQU     $-ROOM_MSG      ;
BAL_MSG         DB      'No field start' ;
L_BAL           EQU     $-BAL_MSG       ;
NEST_MSG        DB      'Fields may not be nested' ;
L_NEST          EQU     $-NEST_MSG      ;
COLOR_MSG       DB      'Text='         ;
TCLR            DB      '   Bkgr='      ;
BCLR            DB      '  Blink='      ;
BLNK            DB      ' '             ;
L_COLOR         EQU     $-COLOR_MSG     ;
;
; Date and time values
;
DOW             DW      0               ; Day of week index (0-6)*3
MON             DW      0               ; Month index (0-12)*3
MONTH           DW      'MM'            ; Month
DAY             DW      'DD'            ; Day
CENTURY         DW      'CC'            ; Century
YEAR            DW      'YY'            ; Year
HOUR            DW      'hh'            ; Hours
MINUTE          DW      'mm'            ; Minutes
SECOND          DW      'ss'            ; Seconds
DOW_TAB         LABEL   BYTE            ; Day of week abbreviations
                DB      'Sun'           ;
                DB      'Mon'           ;
                DB      'Tue'           ;
                DB      'Wed'           ;
                DB      'Thu'           ;
                DB      'Fri'           ;
                DB      'Sat'           ;
MON_TAB         LABEL   BYTE            ; Month abbreviations
                DB      'Jan'           ;
                DB      'Feb'           ;
                DB      'Mar'           ;
                DB      'Apr'           ;
                DB      'May'           ;
                DB      'Jun'           ;
                DB      'Jul'           ;
                DB      'Aug'           ;
                DB      'Sep'           ;
                DB      'Oct'           ;
                DB      'Nov'           ;
                DB      'Dec'           ;
TD_TAB          LABEL   WORD            ; Pattern table for time/date
                DW      'DW'            ;
                DW      'MN'            ;
                DW      'MM'            ;
                DW      'DD'            ;
                DW      'CC'            ;
                DW      'YY'            ;
                DW      'hh'            ;
                DW      'mm'            ;
                DW      'ss'            ;
L_TD            EQU     ($-TD_TAB)/2    ; Number entries
TD_LOC          DW      DOW             ;
                DW      MON             ;
                DW      MONTH           ;
                DW      DAY             ;
                DW      CENTURY         ;
                DW      YEAR            ;
                DW      HOUR            ;
                DW      MINUTE          ;
                DW      SECOND          ;
;
; Key handling tables.
;
KEY_TAB         LABEL   WORD            ; Keys requiring special handling
                DW      CR_CHR          ; Carriage return
                DW      LF_CHR          ; Ctrl Enter
                DW      TAB_CHR         ; Tab
                DW      ESC_CHR         ; Esc
                DW      BS_CHR          ; Backspace
                DW      15*256          ; Back tab
                DW      71*256          ; Home
                DW      72*256          ; Up arrow
                DW      73*256          ; PgUp
                DW      75*256          ; Left arrow
                DW      77*256          ; Right arrow
                DW      79*256          ; End
                DW      80*256          ; Down arrow
                DW      81*256          ; PgDn
                DW      82*256          ; Ins
                DW      83*256          ; Del
                DW      117*256         ; Ctrl End
                DW      119*256         ; Ctrl Home
                DW      59*256          ; F1
L_KEYT          EQU     ($-KEY_TAB)/2   ; Number entries if not editing screen
                DW      60*256          ; F2
                DW      61*256          ; F3
                DW      62*256          ; F4
                DW      63*256          ; F5
                DW      64*256          ; F6
                DW      65*256          ; F7
                DW      66*256          ; F8
                DW      67*256          ; F9
                DW      68*256          ; F10
                DW      131*256         ; Alt =
L_EDIT          EQU     ($-KEY_TAB)/2   ; Number of entries if editing screen
KEY_VECT        LABEL   WORD            ; Vector for key processing routines
                DW      EDIT_PUT        ;
                DW      CR_SUB          ;
                DW      CENTER_SUB      ;
                DW      TAB_SUB         ;
                DW      ESC_SUB         ;
                DW      BS_SUB          ;
                DW      BTAB_SUB        ;
                DW      HOME_SUB        ;
                DW      UP_SUB          ;
                DW      PGUP_SUB        ;
                DW      LEFT_SUB        ;
                DW      RIGHT_SUB       ;
                DW      END_SUB         ;
                DW      DOWN_SUB        ;
                DW      PGDN_SUB        ;
                DW      INS_SUB         ;
                DW      DEL_SUB         ;
                DW      CEND_SUB        ;
                DW      CHOME_SUB       ;
                DW      HELP_SUB        ;
                DW      SETND_SUB       ;
                DW      FSHOW_SUB       ;
                DW      FDEL_SUB        ;
                DW      CNSTRT_SUB      ;
                DW      CNEND_SUB       ;
                DW      FSTRT_SUB       ;
                DW      FEND_SUB        ;
                DW      ATTR_SUB        ;
                DW      COLOR_SUB       ;
                DW      FORCE_SUB       ;
;
; Search and vector table for screen file conversion.
;
SCR_CHR         LABEL   BYTE            ;
                DB      CR_CHR,LF_CHR,ESC_CHR ;
L_DOPT          EQU     $-SCR_CHR       ; Length if display only
                DB      '\[]{}'         ;
L_SCHR          EQU     $-SCR_CHR       ;
SCR_VECT        LABEL   WORD            ;
                DW      SCR_PUT         ; If no match, just put character
                DW      SCR_CR          ;
                DW      SCR_LF          ;
                DW      SCR_ANSI        ;
                DW      SCR_FORCE       ;
                DW      SCR_LBRK        ;
                DW      SCR_RBRK        ;
                DW      SCR_LBRACE      ;
                DW      SCR_RBRACE      ;
;
; Search and vector table for pattern replacement.
;
REPL_CHR        LABEL   BYTE            ;
                DB      '\?=|]'        ;
L_RCHR          EQU     $-REPL_CHR      ;
REPL_VECT       LABEL   WORD            ;
                DW      REPL_PUT        ; If no match, just put character
                DW      COPY_FORCE      ;
                DW      COPY_ONE        ;
                DW      COPY_LINE       ;
                DW      COPY_STOP       ;
                DW      COPY_REST       ;
;
; Search and vector table for template file processing.
;
TMPL_CHR        LABEL   BYTE            ;
                DB      '\[]{}<>'       ;
L_TCHR          EQU     $-TMPL_CHR      ;
TMPL_VECT       LABEL   WORD            ;
                DW      TMPL_PUT        ; If no match, just put character
                DW      TMPL_FORCE      ;
                DW      TMPL_SENT       ;
                DW      TMPL_ERROR      ;
                DW      TMPL_CONST      ;
                DW      TMPL_ERROR      ;
                DW      TMPL_FILE       ;
                DW      TMPL_ERROR      ;
;
; Attribute tables for ANSI sequence.
;
ATTR_MASK       LABEL   BYTE            ;
                DB      0               ; White on black
                DB      0FFH            ; Bold
                DB      0FFH            ;
                DB      0FFH            ;
                DB      0F8H            ; Underline
                DB      0FFH            ; Blink
                DB      0FFH            ;
                DB      077H            ; Reverse video
                DB      070H            ; Invisible
                DB      29-8 DUP(0FFH)  ; Unused
                DB      070H            ; Black foreground
                DB      070H            ; Red foreground
                DB      070H            ; Green foreground
                DB      070H            ; Yellow foreground
                DB      070H            ; Blue foreground
                DB      070H            ; Magenta foreground
                DB      070H            ; Cyan foreground
                DB      070H            ; White foreground
                DB      39-37 DUP(0FFH) ; Unused
                DB      0FH             ; Black background
                DB      0FH             ; Red background
                DB      0FH             ; Green background
                DB      0FH             ; Yellow background
                DB      0FH             ; Blue background
                DB      0FH             ; Magenta background
                DB      0FH             ; Cyan background
                DB      0FH             ; White background
L_ATTR          EQU     $-ATTR_MASK     ; Number of attribute values
ATTR_VAL        LABEL   BYTE            ;
                DB      7               ; White on black
                DB      8               ; Bold on
                DB      0               ;
                DB      0               ;
                DB      1               ; Underline
                DB      80H             ; Blink
                DB      0               ;
                DB      0               ; Reverse
                DB      0               ; Hidden
                DB      29-8 DUP(0)     ; Unused
                DB      0               ; Black foreground
                DB      4               ; Red foreground
                DB      2               ; Green foreground
                DB      6               ; Yellow foreground
                DB      1               ; Blue foreground
                DB      5               ; Magenta foreground
                DB      3               ; Cyan foreground
                DB      7               ; White foreground
                DB      39-37 DUP(0)    ; Unused
                DB      0               ; Black background
                DB      40H             ; Red background
                DB      20H             ; Green background
                DB      60H             ; Yellow background
                DB      10H             ; Blue background
                DB      50H             ; Magenta background
                DB      30H             ; Cyan background
                DB      70H             ; White background
;
; Status line.
;
C10             DB      10,0            ; Constant 10
C100            DB      100             ; Constant 100
STAT_ATTR       EQU     1FH             ; White on blue
STATUS_MSG      DB      'COL='          ;
STAT_COL        DB      'xxx, ROW='     ;
STAT_ROW        DB      'xxx, LN='      ;
STAT_LIN        DB      'xxx'           ;
                DB      ' '             ;
STAT_INS        DB      ' '             ;
                DB      ' '             ;
STAT_Q          DB      ' '             ;
                DB      71-($-STATUS_MSG) DUP(' ') ;
Q_LNG           EQU     $-STAT_Q        ; Max length of messages
                DB      'F1 = Help'     ;
                DB      80 DUP(' ')     ; Status lines up to 160 characters
;
; Menu for selecting colors and attributes.
;
COLOR_MNU       LABEL   WORD            ;
                DBA %(SCR_ATTR OR 8),<                 COLORS                 >
                DBA     SCR_ATTR,<                                        > ;
                DBA     SCR_ATTR,<Select text color(0-15) background(0-7) > ;
                DBA     SCR_ATTR,<       and blink(Y,N)                   > ;
                DBA     SCR_ATTR,<Press Enter when done                   > ;
                DBA     SCR_ATTR,<                                        > ;
                DBA          70H,< 0 = Black          > ;
                DBA            8,< 8 = Dark grey      > ;
                DBA            1,< 1 = Blue           > ;
                DBA            9,< 9 = Bright blue    > ;
                DBA            2,< 2 = Green          > ;
                DBA           10,<10 = Bright green   > ;
                DBA            3,< 3 = Cyan           > ;
                DBA           11,<11 = Bright cyan    > ;
                DBA            4,< 4 = Red            > ;
                DBA           12,<12 = Bright Red     > ;
                DBA            5,< 5 = Magenta        > ;
                DBA           13,<13 = Bright Magenta > ;
                DBA            6,< 6 = Brown          > ;
                DBA           14,<14 = Yellow         > ;
                DBA            7,< 7 = White          > ;
                DBA           15,<15 = Bright white   > ;
L_CTAB          EQU     ($-COLOR_MNU)/80        ; Number of lines in menu
;
; HELP display
;
HELP_MNU        LABEL   WORD                    ;
                DBA %(SCR_ATTR OR 8),<            KEY DEFINITIONS             >
                DBA     SCR_ATTR,<                                        > ;
                DBA     SCR_ATTR,<Press Enter when done                   > ;
                DBA     SCR_ATTR,<                                        > ;
                DBA     SCR_ATTR,<Esc = Quit          > ;
                DBA     SCR_ATTR,<Ctrl Enter = Done   > ;
                DBA     SCR_ATTR,<F1 = Key definition > ;
                DBA     SCR_ATTR,<Enter = Field done  > ;
                DBA     SCR_ATTR,<Tab = Next field    > ;
                DBA     SCR_ATTR,<ShiftTab = Tab back > ;
                DBA     SCR_ATTR,<Home = Field start  > ;
                DBA     SCR_ATTR,<End = Field end     > ;
                DBA     SCR_ATTR,<Ctrl Home = Top     > ;
                DBA     SCR_ATTR,<Ctrl End = Bottom   > ;
                DBA     SCR_ATTR,<Ins = Insert mode   > ;
                DBA     SCR_ATTR,<Del = Delete char   > ;
L_HELP          EQU     ($-HELP_MNU)/80 ; Number of lines if not editing
                DBA     SCR_ATTR,<Alt= = Insert key   > ;
                DBA     SCR_ATTR,<F2 = Set end screen > ;
                DBA     SCR_ATTR,<F3 = Display Fields > ;
                DBA     SCR_ATTR,<F4 = Delete Field   > ;
                DBA     SCR_ATTR,<F5 = Start Constant > ;
                DBA     SCR_ATTR,<F6 = End Constant   > ;
                DBA     SCR_ATTR,<F7 = Start Field    > ;
                DBA     SCR_ATTR,<F8 = End Field      > ;
                DBA     SCR_ATTR,<F9 = Select color   > ;
                DBA     SCR_ATTR,<F10 = Color char    > ;
L_HMNU          EQU     ($-HELP_MNU)/80 ; Number of lines in menu
;
; Code area.
;
BEGIN:                                  ;
;
; Copy screen file to editing buffer.
; Editing buffer is layed out in screen format.
;
                MOV     DI,OFFSET EDIT_LOC ;
                MOV     AH,SCR_ATTR     ; Default attribute
                MOV     BX,SCREENFILE   ;
                CMP     DOPT,L_DOPT     ; If /D option, allow STDIN
                JE      SCR_LINE        ;
                OR      BX,BX           ; Any screen file?
                JZ      SCR_DONE        ; No, Just use an empty file
;
; Get next character and check for special cases.
; For CR, treat as end of line and skip an immediately following LF.
; For LF, treat as end of line.
; For ESC, process ANSI SGR sequence.
; For \, use next character even if it is a special character.
; For [], build input field
; For {}, build constant field
; For anything else, just use the character.
;
SCR_LINE:                               ;
                MOV     DX,WORD PTR SCR_COL ; Number of columns on screen
SCR_GET:                                ;
                CALL    GET_CHR         ;
                JZ      SCR_END         ; If no more characters
SCR_GOT:                                ;
                MOV     CX,DOPT         ; Length for display type
                MOV     SI,OFFSET SCR_CHR ;
                CALL    TBL_SRCH        ; Find character type
                JMP     SCR_VECT[SI]    ; Branch on character type
;
; Just put the character and the current attribute in the display.
;
SCR_PUT:                                ;
                CMP     DI,EDIT_LAST    ; Buffer full?
                JA      SCR_ERROR       ; File too big
                STOSW                   ; Store character
                DEC     DX              ; Starting a new line on screen?
                JNS     SCR_GET         ; No
                ADD     DX,WORD PTR SCR_COL ; Chars remaining in new line
                JMP     SCR_GET         ;
;
; Got a line feed (LF) which always means end of record.
;
SCR_LF:                                 ;
                CALL    SCR_PAD         ; Close out line
                JMP     SCR_LINE        ; and start another
;
; Got a CR which means end of record.  Must check for a line feed following
; and skip it if found.
;
SCR_CR:                                 ;
                CALL    SCR_PAD         ; Close out line
                MOV     DL,SCR_COL      ; Start new line
                CALL    GET_CHR         ; Get next character
                JZ      SCR_DONE        ; No more characters
                CMP     AL,LF_CHR       ; Is it a LF?
                JNE     SCR_GOT         ; No, go process it
                JMP     SCR_GET         ; Yes, go get next character
;
; End of file
;
SCR_END:                                ;
                CMP     DL,SCR_COL      ; Started a line?
                JAE     SCR_DONE        ; No
                CALL    SCR_PAD         ; Fill out line to end
SCR_DONE:                               ;
                JMP     EDIT_RDY        ;
;
; Insert spaces into screen line for remainder of line.
; DX = space count
;
SCR_PAD:                                ;
                MOV     CX,DX           ; Remaining character count
                MOV     AL,' '          ; Space character.
        REP     STOSW                   ;
                MOV     DX,CX           ; No characters remaining
                CMP     DI,EDIT_LAST    ; Overflow?
                JA      SCR_ERROR       ; Yes
                RET                     ;
;
; Expanded file is too big.
;
SCR_ERROR:                              ;
                MOV     DX,OFFSET BIG_MSG ;
                MOV     CX,L_BIG        ;
                JMP     ERROR_DSP       ;
;
; Found a \.  Put the next character no matter what it is.
;
SCR_FORCE:                              ;
                CALL    GET_CHR         ;
                JZ      SCR_END         ;
                JMP     SCR_PUT         ;
;
; Found a [.  Start input field.
;
SCR_LBRK:                               ;
                MOV     SI,EDIT_NUM     ; Loc of next input area control
                CMP     SI,EDIT_FLD*4   ; Already too many?
                JAE     INP_ERROR       ; No
                CMP     EDIT_AREA[SI],0 ; Already started an input area?
                JNZ     INP_ERROR       ;
                MOV     EDIT_AREA[SI],DI ; Save start of input area
                JMP     SCR_GET         ; Go get next character
;
; Found a ].  End input field.
;
SCR_RBRK:                               ;
                MOV     SI,EDIT_NUM     ;
                CMP     SI,EDIT_FLD*4   ; Already too many?
                JAE     INP_ERROR       ; No
                CMP     EDIT_AREA[SI],0 ; Start of area found?
                JZ      INP_ERROR       ; No, error
                MOV     EDIT_AREA[SI+2],DI ; Save end of area
                ADD     EDIT_NUM,4      ; Move to next area
                JMP     SCR_GET         ;
INP_ERROR:                              ; Unbalanced or too many []'s
                MOV     DX,OFFSET INP_MSG ;
                MOV     CX,L_INP        ;
                JMP     ERROR_DSP       ; Go display error message
;
; Found an ANSI sequence.  Form new attribute value.
;
SCR_ANSI:                               ;
                CALL    GET_CHR         ; We have the Esc, look for [
                JZ      ANSI_ERROR      ;
                CMP     AL,'['          ; ESC [ ?
                JNE     ANSI_ERROR      ; No
                PUSH    DX              ;
                MOV     CL,4            ;
SCR_ALP:                                ;
                CALL    DTB             ; Get binary value
                CMP     DX,L_ATTR       ; Legal attribute value?
                JAE     ANSI_ERROR      ; No, error
                MOV     SI,DX           ;
                AND     AH,ATTR_MASK[SI] ; Mask attribute to be changed out
                OR      AH,ATTR_VAL[SI] ; Put in new value
                CMP     DL,7            ; Reverse video?
                JNE     SCR_INV         ; No
                ROL     AH,CL           ;
SCR_INV:                                ;
                CMP     DL,8            ; Canceled(invisible)?
                JNE     SCR_SEP         ; No
                MOV     DH,AH           ; Make foreground=background
                SHR     DH,CL           ;
                OR      AH,DH           ;
SCR_SEP:                                ;
                CMP     AL,';'          ; Sub-field separator?
                JE      SCR_ALP         ; Yes, continue
                CMP     AL,'m'          ; End of SGR sequence?
                JNE     ANSI_ERROR      ; No
                POP     DX              ;
                JMP     SCR_GET         ; Yes
ANSI_ERROR:                             ; Not a supported Esc sequence
                MOV     DX,OFFSET ANSI_MSG ;
                MOV     CX,L_ANSI       ;
                JMP     ERROR_DSP       ;
;
; { indicating beginning of constant area.
;
SCR_LBRACE:                             ;
                MOV     SI,CONST_NUM    ; Loc of next constant area control
                CMP     SI,EDIT_FLD*4   ; Already too many?
                JAE     CONST_ERROR     ; No
                CMP     CONST_AREA[SI],0 ; Already started a constant area?
                JNZ     CONST_ERROR     ;
                MOV     CONST_AREA[SI],DI ; Save start of constant area
                JMP     SCR_GET         ; Go get next character
;
; Found a }.  End constant area.
;
SCR_RBRACE:                             ;
                MOV     SI,CONST_NUM    ;
                CMP     SI,EDIT_FLD*4   ; Already too many?
                JAE     CONST_ERROR     ; No
                CMP     CONST_AREA[SI],0 ; Start of area found?
                JZ      CONST_ERROR     ; No, error
                MOV     CONST_AREA[SI+2],DI ; Save end of area
                ADD     CONST_NUM,4     ; Move to next area
                JMP     SCR_GET         ;
CONST_ERROR:                            ;
                MOV     CX,L_CONST      ;
                MOV     DX,OFFSET CONST_MSG ;
                JMP     ERROR_DSP       ;
;
; Check for unbalanced []'s and {}'s.
;
EDIT_RDY:                               ;
                MOV     SI,EDIT_NUM     ;
                CMP     SI,EDIT_FLD*4   ; Area full?
                JAE     EDIT_BRACE      ; Yes, everything is OK
                CMP     EDIT_AREA[SI],0 ; Started next field?
                JZ      EDIT_BRACE      ; No
                JMP     INP_ERROR       ; Yes, error
EDIT_BRACE:                             ;
                MOV     SI,CONST_NUM    ;
                CMP     SI,EDIT_FLD*4   ; Full?
                JAE     EDIT_OK         ;
                CMP     CONST_AREA[SI],0 ; Started next constant.
                JNZ     CONST_ERROR     ;
EDIT_OK:                                ;
                DEC     DI              ; Update end of file
                DEC     DI              ;
;
; If the /E option is set, copy all existing input field definitions to the
; New field area and set the entire display to a single input field.
;
                CMP     EOPT,L_EDIT     ; Editing a screen file?
                JNE     EDIT_NORM       ; No normal file editing
                MOV     CX,EDIT_NUM     ; Length to copy
                MOV     SI,OFFSET EDIT_AREA-1 ; Copy input definition to
                ADD     SI,CX           ;
                MOV     DI,OFFSET DEF_AREA-1 ; new input definitions
                ADD     DI,CX           ;
                MOV     DEF_NUM,CX      ;
                STD                     ; Overlapping fields
        REP     MOVSB                   ;
                CLD                     ;
                MOV     EDIT_NUM,4      ; Define a single input field
                MOV     EDIT_AREA,OFFSET EDIT_LOC ; covering the entire file
                MOV     DI,EDIT_LAST    ;
                MOV     EDIT_AREA+2,DI  ;
                MOV     SCRN_END,DI     ; Default end of screen definition
EDIT_NORM:                              ;
                MOV     EDIT_LAST,DI    ;
                CMP     DI,OFFSET EDIT_LOC-2 ; Empty file?
                JA      EDIT_NMT        ; No
                JMP     CENTER_SUB      ; Yes, just exit
EDIT_NMT:                               ;
;
; Display first screen of file.
;
                CALL    CHOME_SUB       ; Display first page
;
; Now get a key and process editing commands.
;
EDIT_FILE:                              ;
                XOR     AH,AH           ;
                INT     KYBD$           ; Read next character
                OR      AL,AL           ; Extended key?
                JZ      EDIT_XTND       ; Yes
                XOR     AH,AH           ; Clear scan code
EDIT_XTND:                              ;
;
; Determine type of key and switch to proper routine to handle that key type.
;
                PUSH    SI              ;
                MOV     CX,EOPT         ;
                MOV     SI,OFFSET KEY_TAB ;
                CALL    WTBL_SRCH       ; Find matching entry
                MOV     BX,SI           ;
                POP     SI              ;
                CALL    KEY_VECT[BX]    ; Go to key routine
                JMP     EDIT_FILE       ;
;
; Put character in file and display.  All normal keys.
;
EDIT_PUT:                               ;
                CMP     AL,0            ; Don't put NULs
                JZ      EDIT_STATUS     ;
EDIT_FORCE:                             ;
                CALL    FIND_AREA       ; Are we in an input field?
                JC      EDIT_STATUS     ; No, do nothing
                CMP     STAT_INS,' '    ; Are we inserting?
                JE      EDIT_NINS       ; No, just store the character
;
; Insert a character in this input area.
;
                PUSH    DI              ;
                PUSH    SI              ;
                MOV     DI,EDIT_AREA[BX+2] ; Amount to move
                DEC     DI              ;
                DEC     DI              ;
                CMP     SI,DI           ; At end of area?
                JAE     EDIT_NMV        ; Yes, don't move anything
                MOV     CX,DI           ;
                SUB     CX,SI           ;
                LEA     SI,[DI-2]       ;
                SHR     CX,1            ;
                STD                     ;
        REP     MOVSW                   ; Copy characters
                CLD                     ;
EDIT_NMV:                               ;
                POP     SI              ;
                POP     DI              ;
                MOV     [SI],AL         ;
                CALL    DISP_SCREEN     ; Display the modified field
                JMP     SHORT RIGHT_SUB ;
;
; Put character in file buffer and display.
;
EDIT_NINS:                              ;
                MOV     [SI],AL         ; Put key in file
                MOV     AH,[SI+1]       ;
                CALL    WRITE_SCREEN    ;
                DEC     DI              ; Don't move cursor here
                DEC     DI              ;
;
; Increment cursor position.
;
RIGHT_SUB:                              ; Right arrow
                MOV     AX,1            ;
;
; General logic to move the cursor.
; If it would move outside the file, it is moved the maximum amount.
; If it moves outside the current screen, the screen is moved.
; This logic is only good for moves of <8000H bytes.
; Entered with AX = Amount to move cursor, positive or negative.
;
MOVE_CRS:                               ;
                CWD                     ; Extend sign
                SHL     AX,1            ; Make into word offset
                ADD     AX,SI           ; Adjust file pointer
                ADC     DX,0            ;
                JS      CHOME_SUB       ; Negative is before start
                JNZ     CEND_SUB        ; Too large, so after end
                CMP     AX,OFFSET EDIT_LOC ; Before start of file?
                JB      CHOME_SUB       ; Yes
                CMP     AX,EDIT_LAST    ; Beyond end?
                JA      CEND_SUB        ; Yes
;
; Move to a specific display position.
; Because we could be moving 16384 (8000H) bytes or more, we must handle the
; calculations with double precision arithmetic.
; Enter with AX = New display position.
;
MOVE_TO:                                ;
                XCHG    SI,AX           ; New file position
                SUB     AX,SI           ; Distance from old to new
                SBB     DX,DX           ; Sign extend distance to high order
                SUB     DI,AX           ; New cursor index
                ADC     DX,0            ; Slight cheat knowing sign of DI
                JS      MOVE_FWD        ; DI>64K, move screen forward
                JNZ     MOVE_BACK       ; DI<0, move screen backward
                CMP     DI,DISP_SIZE    ; Off end of screen?
                JAE     MOVE_FWD        ; Must move screen forward
                CALL    FIND_AREA       ; Get next input field
                JNC     EDIT_STATUS     ; Already in one
                MOV     AX,EDIT_AREA[BX] ; Start of next area
                SUB     AX,SI           ; Offset to start of next area
                JB      EDIT_STATUS     ; Don't move back to area
                MOV     CX,AX           ;
                ADD     CX,DI           ; Cursor index of next area
                SHR     AX,1            ;
                CMP     CX,DISP_SIZE    ; Still on screen?
                JB      MOVE_CRS        ; Yes, move cursor to input area
;
; Update status line with cursor position and move video cursor.
;
EDIT_STATUS:                            ;
                CALL    STAT_CLR        ; Clear any message in status line
                CALL    STATUS          ; Display status line
                XOR     BH,BH           ; Video page 0
                MOV     AX,DI           ;
                SHR     AX,1            ;
                DIV     SCR_COL         ; Compute new cursor position
                XCHG    AL,AH           ;
                MOV     DX,AX           ;
                MOV     AH,CRS$         ; Update cursor position
                INT     VIDEO$          ;
                RET                     ;
;
; Ctrl Home = Go to top of display.
;
CHOME_SUB:                              ; Ctrl Home
                MOV     SI,OFFSET EDIT_LOC ; Move back to start
;
; Move display back until new position is in first line on screen.
;
MOVE_BACK:                              ;
                XOR     DI,DI           ; Cursor line is top of screen
                JMP     SHORT MOVE_SCRN ;
;
; Move display forward until new position is in last line on screen.
;
MOVE_FWD:                               ;
                MOV     DI,DISP_SIZE    ;
                SUB     DI,WORD PTR SCR_COL ; Cursor is in last line
                SUB     DI,WORD PTR SCR_COL ;
MOVE_SCRN:                              ;
                LEA     AX,[SI-(OFFSET EDIT_LOC - OFFSET MENU_PSP)] ;
                SHR     AX,1            ;
                CWD                     ; May be more than 256 lines
                DIV     WORD PTR SCR_COL ; Compute new column position
                SHL     DX,1            ; Remainder is column
                ADD     DI,DX           ; New cursor position
                CALL    DISP_SCREEN     ; Redisplay screen
                XOR     AX,AX           ; Find field position
                JMP     MOVE_CRS        ;
;
; Ctrl End = Go to end of display.
;
CEND_SUB:                               ; Ctrl End
                MOV     AX,EDIT_LAST    ; Move to end of display area
                JMP     MOVE_TO         ;
;
; CR = Go to next line in a multi-line field, or to next field, or exit
; if in last line of last field.
;
CR_SUB:                                 ; CR
                CALL    FIND_AREA       ; Find which area we are in
                JC      CR_TAB          ; Not in an area
                MOV     AX,DI           ;
                SHR     AX,1            ;
                DIV     SCR_COL         ; Offset to start of next line
                MOV     AL,SCR_COL      ;
                SUB     AL,AH           ;
                XOR     AH,AH           ;
                SHL     AX,1            ;
                ADD     AX,SI           ;
                CMP     AX,EDIT_AREA[BX+2] ; Beyond end of area?
                JB      GO_MTO          ; No, just move to next line
                ADD     BX,4            ;
CR_TAB:                                 ; Go to next area or quit
                CMP     BX,EDIT_NUM     ; In last field?
                JAE     GO_CENTER       ; Yes
                CMP     SI,EDIT_AREA[BX] ; After last field?
                JB      TAB_SUB         ; No, move to next field
GO_CENTER:                              ;
                JMP     CENTER_SUB      ; Yes, end of entry
;
; Down arrow = Move down one line.
;
DOWN_SUB:                               ; Down arrow
                MOV     AX,WORD PTR SCR_COL ; Move one line
                JMP     MOVE_CRS        ;
;
; Up arrow = Move up one line.
;
UP_SUB:                                 ; Up arrow
                MOV     AX,WORD PTR SCR_COL ; Move up one line
                NEG     AX              ;
                JMP     MOVE_CRS        ;
;
; PgUp = Move up one page.
;
PGUP_SUB:                               ; PgUp
                MOV     AX,DISP_SIZE    ;
                SHR     AX,1            ;
                NEG     AX              ; Move back one page
                JMP     MOVE_CRS        ; Move to next input area if any
;
; PgDn = Move down one page.
;
PGDN_SUB:                               ; PgDn
                MOV     AX,DISP_SIZE    ; Move ahead one screen
                SHR     AX,1            ;
                JMP     MOVE_CRS        ;
;
; Tab = Move to start of next field.
;
TAB_SUB:                                ; TAB
                CMP     EDIT_NUM,0      ; Any input areas?
                JZ      GO_EST          ; No, nothing to do
                CALL    FIND_AREA       ; Find current area
                JC      TAB_OK          ; Point to next area
                ADD     BX,4            ; Move to next area
                CMP     BX,EDIT_NUM     ; Wrap around?
                JB      TAB_OK          ;
                XOR     BX,BX           ;
TAB_OK:                                 ;
                MOV     AX,EDIT_AREA[BX] ; Move to start of next area
GO_MTO:                                 ;
                JMP     MOVE_TO         ;
;
; Home and Back tab = Move to start of this field.  If at start, move to
; start of previous field.
;
HOME_SUB:                               ; Home
BTAB_SUB:                               ; Back tab
                CALL    FIND_AREA       ; Find current area
                JC      BTAB_AREA       ; Not in an area, just use previous
                MOV     AX,EDIT_AREA[BX] ;
                CMP     AX,SI           ; Already at start of area?
                JNE     GO_MTO          ; No, move to start of area
BTAB_AREA:                              ;
                CMP     EDIT_NUM,0      ; Any input areas?
                JZ      GO_EST          ; No
                OR      BX,BX           ; First area?
                JNZ     BACK_AREA       ; No
                MOV     BX,EDIT_NUM     ; Yes, move to last area
BACK_AREA:                              ;
                MOV     AX,EDIT_AREA[BX-4] ; Start of previous area
                JMP     MOVE_TO         ;
GO_EST:                                 ;
                JMP     EDIT_STATUS     ;
;
; Left arrow = Move cursor left one space.
;
LEFT_SUB:                               ; Left arrow
                MOV     AX,-1           ; Back one space
                JMP     MOVE_CRS        ;
;
; End = Move to end of this field.  Moves to first space after last non-space.
;
END_SUB:                                ; End
                CALL    FIND_AREA       ; Find area
                CMP     BX,EDIT_NUM     ; Any areas?
                JAE     GO_EST          ; No, nothing to do
                CALL    END_FIND        ; Find end of next area
                CMP     AX,SI           ; Would we move?
                JNE     GO_MTO          ; Yes, so move
                CALL    TAB_SUB         ; Move to end of next area
                CALL    FIND_AREA       ;
                CALL    END_FIND        ;
                JMP     MOVE_TO         ;
;
; Find end of area.
;
END_FIND:                               ;
                PUSH    DI              ;
                MOV     DX,EDIT_AREA[BX] ; Start of area
                MOV     DI,EDIT_AREA[BX+2] ; End of area
                MOV     AH,[DI-1]       ; Attribute for first position
                CALL    TRIM            ; Find last non-blank + 2
                CMP     DI,EDIT_AREA[BX+2] ; Non-blank in last position
                JB      EF_LAST         ; No
                DEC     DI              ; Go to last position then
                DEC     DI              ;
EF_LAST:                                ;
                MOV     AX,DI           ; Position to move to
                POP     DI              ;
                RET                     ;
;
; Ins = Toggle insert mode.
;
INS_SUB:                                ;
                XOR     STAT_INS,' ' XOR '^' ; Toggle insert mode
                JMP     EDIT_STATUS     ; Go display updated status
;
; Backspace = Move back one space and delete the character there.
; If at first character of the field, do nothing.
;
BS_SUB:                                 ; Backspace
                CALL    FIND_AREA       ; Find current input area
                JC      LEFT_SUB        ; Not in area, just move left
                CMP     SI,EDIT_AREA[BX] ; Are we at start?
                JNE     BS_LFT          ; No, move left
                JMP     EDIT_STATUS     ; Nothing to do
BS_LFT:                                 ;
                CALL    LEFT_SUB        ;
;
; Del = Delete the character under the cursor.
;
DEL_SUB:                                ;
                CALL    FIND_AREA       ; Find the area we are in
                JC      GO_EST          ; Not in an area
                MOV     CX,EDIT_AREA[BX+2] ;
                DEC     CX              ; Last character of field
                DEC     CX              ;
                PUSH    CX              ; Save last position
                SUB     CX,SI           ;
                PUSH    DI              ;
                PUSH    SI              ;
                MOV     DI,SI           ;
                INC     SI              ;
                INC     SI              ;
        REP     MOVSB                   ; Copy characters
                POP     SI              ;
                POP     DI              ;
                POP     BX              ;
                MOV     BYTE PTR [BX],' ' ; Put a blank in the last spot
                CALL    DISP_SCREEN     ; Display the field
                RET                     ;
;
; F1 = Help.  Display key definitions.
;
HELP_SUB:                               ;
                MOV     AX,OFFSET HELP_MNU ;
                MOV     CX,L_HELP       ; Length if not editing
                CMP     EOPT,L_EDIT     ; Editing a screen file?
                JNE     HELP_NRM        ; No
                MOV     CX,L_HMNU       ;
HELP_NRM:                               ;
                CALL    DISP_MNU        ; Display the menu
HELP_WT:                                ;
                XOR     AH,AH           ;
                INT     KYBD$           ;
                CMP     AL,ESC_CHR      ;
                JE      HELP_QUIT       ;
                CMP     AL,CR_CHR       ;
                JNE     HELP_WT         ; Wait for a CR
HELP_QUIT:                              ;
                CALL    DISP_SCREEN     ; Refresh the screen
                JMP     EDIT_STATUS     ;
;
; F2 = Send end of screen definition location.
;
SETND_SUB:                              ;
                MOV     SCRN_END,SI     ; Set new end of definition loc
                JMP     EDIT_STATUS     ;
;
; F3 = Toggle field and constant display.
;
FSHOW_SUB:                              ;
                XOR     SHOW,1          ; Toggle show flag
                CALL    DISP_SCREEN     ; Redisplay this screen
                RET                     ;
;
; F4 = Erase field or constant definition at this location.
;
FDEL_SUB:                               ; Erase definition
                XOR     BX,BX           ; Loop through all definitions
FDEL_LP:                                ;
                CMP     BX,DEF_NUM      ;
                JAE     NO_FDEF         ;
                CMP     SI,DEF_AREA[BX] ; Start of this field?
                JNE     NO_FDEF         ; No
                MOV     AX,OFFSET DEF_AREA ;
                CALL    FD_RMV          ; Remove entry
                SUB     DEF_NUM,4       ; Reduce count of entries
                JMP     FDEL_LP         ; Check next entry
NO_FDEF:                                ;
                CMP     BX,CONST_NUM    ;
                JAE     NO_FCON         ;
                CMP     SI,CONST_AREA[BX] ; Start of this constant?
                JNE     NO_FCON         ; No
                MOV     AX,OFFSET CONST_AREA ;
                CALL    FD_RMV          ; Remove entry
                SUB     CONST_NUM,4     ; Reduce count
                JMP     NO_FDEF         ; Check next entry
NO_FCON:                                ;
                ADD     BX,4            ;
                CMP     BX,EDIT_FLD*4   ; More fields to check?
                JB      FDEL_LP         ; Yes
                CALL    DISP_SCREEN     ; Update display
                RET                     ;
FD_RMV:                                 ;
                PUSH    DI              ; Remove this entry
                PUSH    SI              ;
                MOV     CX,EDIT_FLD*4-4 ;
                SUB     CX,BX           ; Length to move
                MOV     DI,BX           ;
                ADD     DI,AX           ;
                LEA     SI,[DI+4]       ;
        REP     MOVSB                   ; Copy entries
                XOR     AX,AX           ;
                STOSW                   ;
                STOSW                   ; Clear last entry
                POP     SI              ;
                POP     DI              ;
                RET                     ;
;
; F5 = Start constant area.
;
CNSTRT_SUB:                             ; Define new constant area
                MOV     BX,CONST_NUM    ; Pointer to next field definition
                CMP     BX,EDIT_FLD*4   ; Area full?
                JAE     DEF_ERROR       ; Yes
                CMP     CONST_AREA[BX],0 ; Already started the area?
                JNZ     DEF_NEST        ; Yes, cannot nest
                MOV     CONST_AREA[BX],SI ; Save start of area
                CALL    DISP_SCREEN     ;
                RET                     ;
;
; F6 = End of constant area.
;
CNEND_SUB:                              ; End constant area
                MOV     BX,CONST_NUM     ;
                CMP     BX,EDIT_FLD*4   ; Full?
                JAE     DEF_ERROR       ;
                CMP     CONST_AREA[BX],0 ; Started area?
                JZ      DEF_UNBAL       ; No
                LEA     AX,[SI+2]       ;
                MOV     CONST_AREA[BX+2],AX ; Save end of area
                ADD     CONST_NUM,4     ;
                CALL    DISP_SCREEN     ;
                RET                     ;
;
; F7 = Start new field.
;
FSTRT_SUB:                              ; F7
                MOV     BX,DEF_NUM      ; Pointer to next field definition
                CMP     BX,EDIT_FLD*4   ; Area full?
                JAE     DEF_ERROR       ; Yes
                CMP     DEF_AREA[BX],0  ; Already started the area?
                JNZ     DEF_NEST        ; Yes, cannot nest
                MOV     DEF_AREA[BX],SI ; Save start of area
                CALL    DISP_SCREEN     ;
                RET                     ;
DEF_ERROR:                              ;
                MOV     AX,OFFSET ROOM_MSG ;
                MOV     CX,L_ROOM       ;
                CALL    STAT_MSG        ; Display error in status line
                JMP     EDIT_FILE       ;
DEF_NEST:                               ;
                MOV     AX,OFFSET NEST_MSG ;
                MOV     CX,L_NEST       ;
                CALL    STAT_MSG        ;
                JMP     EDIT_FILE       ;
;
; F8 = End of new field.
;
FEND_SUB:                               ;
                MOV     BX,DEF_NUM      ;
                CMP     BX,EDIT_FLD*4   ; Full?
                JAE     DEF_ERROR       ;
                CMP     DEF_AREA[BX],0  ; Started area?
                JZ      DEF_UNBAL       ; No
                LEA     AX,[SI+2]       ;
                MOV     DEF_AREA[BX+2],AX ; Save end of area
                ADD     DEF_NUM,4       ;
                CALL    DISP_SCREEN     ;
                RET                     ;
DEF_UNBAL:                              ;
                MOV     AX,OFFSET BAL_MSG ;
                MOV     CX,L_BAL        ;
                CALL    STAT_MSG        ;
                JMP     EDIT_FILE       ;
;
; F9 = Select new colors.
;
ATTR_SUB:                               ;
;
; Display color menu.
;
                MOV     CX,L_CTAB       ; Number of lines
                MOV     AX,OFFSET COLOR_MNU ;
                CALL    DISP_MNU        ; Display attribute menu
;
; Display status line showing current colors as defaults.
;
                MOV     AL,[SI+1]       ; Get text color
                AND     AL,0FH          ;
                CALL    BTD             ;
                MOV     WORD PTR TCLR,AX ;
                MOV     AL,[SI+1]       ;
                MOV     AH,'N'          ; Blink Value
                TEST    AL,80H          ; Is it blinking?
                JZ      NOT_BLINK       ; No
                MOV     AH,'Y'          ;
NOT_BLINK:                              ;
                MOV     BLNK,AH         ;
                MOV     CL,4            ;
                SHR     AL,CL           ;
                AND     AL,7            ; Background color
                CALL    BTD             ;
                MOV     BCLR,AH         ;
;
; Get new color values.
;
                PUSH    SI              ;
                MOV     DH,SCR_LIN      ;
                DEC     DH              ;
COLOR_ST:                               ;
                MOV     DL,OFFSET STAT_Q - OFFSET STATUS_MSG ;
                ADD     DL,OFFSET TCLR - OFFSET COLOR_MSG ;
                MOV     SI,OFFSET TCLR  ;
                XOR     BH,BH           ;
COLOR_LP:                               ;
                MOV     AX,OFFSET COLOR_MSG ; Put in status line
                MOV     CX,L_COLOR      ;
                CALL    STAT_MSG        ;
                MOV     AH,CRS$         ;
                INT     VIDEO$          ; Set cursor position
                XOR     AH,AH           ;
                INT     KYBD$           ; Get next input
                CMP     AL,'0'          ; Digit?
                JB      NOT_CDIG        ; No
                CMP     AL,'9'          ;
                JA      NOT_CDIG        ;
                CMP     SI,OFFSET BCLR  ; Blink field?
                JA      COLOR_LP        ; Yes, no digits
                MOV     [SI],AL         ; Put digit
                CMP     SI,OFFSET TCLR  ; Moving to another field?
                JA      PUT_CLR2        ;
                INC     SI              ;
                INC     DL              ;
                JMP     COLOR_LP        ;
PUT_CLR2:                               ;
                CMP     SI,OFFSET BCLR  ; Moving to blink field?
                JAE     PUT_BKGR        ; Yes
                ADD     SI,OFFSET BCLR - OFFSET TCLR - 1 ;
                ADD     DL,OFFSET BCLR - OFFSET TCLR - 1 ;
                JMP     COLOR_LP        ;
PUT_BKGR:                               ;
                ADD     SI,OFFSET BLNK - OFFSET BCLR ;
                ADD     DL,OFFSET BLNK - OFFSET BCLR ;
                JMP     COLOR_LP        ;
NOT_CDIG:                               ;
                CMP     AL,ESC_CHR      ; Quit?
                JE      CLR_QUIT        ;
                CMP     AL,CR_CHR       ; Done?
                JE      CLR_DONE        ; Yes
                CMP     AL,BS_CHR       ; Backspace?
                JE      CLR_BS          ; Yes
                CMP     SI,OFFSET BLNK  ; Blink field?
                JNE     COLOR_LP        ; No
                AND     AL,NOT 20H      ;
                CMP     AL,'N'          ;
                JE      PUT_BLNK        ;
                CMP     AL,'Y'          ;
                JNE     COLOR_LP        ;
PUT_BLNK:                               ;
                MOV     [SI],AL         ;
                JMP     COLOR_LP        ;
;
; We have the color request.  Convert the values and check if legal.
;
CLR_DONE:                               ;
                MOV     AL,TCLR         ; Convert foreground color to binary
                SUB     AL,'0'          ;
                MUL     C10             ;
                ADD     AL,TCLR+1       ;
                SUB     AL,'0'          ;
                MOV     AH,BCLR         ; Convert background color
                SUB     AH,'0'          ;
                CMP     AL,15           ; Legal colors?
                JA      GO_CST          ; No
                CMP     AH,7            ;
                JA      GO_CST          ;
                MOV     CL,4            ; Put in attribute format
                SHL     AH,CL           ;
                OR      AL,AH           ;
                CMP     BLNK,'Y'        ; Blink?
                JNE     NOT_BLNK        ; No
                OR      AL,80H          ;
NOT_BLNK:                               ;
                MOV     ATTRIBUTE,AL    ; Save new attribute
CLR_QUIT:                               ;
                POP     SI              ;
                CALL    DISP_SCREEN     ; Redisplay screen
                JMP     EDIT_STATUS     ; Redisplay cursor
;
; Handle backspacing in color information.
;
CLR_BS:                                 ;
                CMP     SI,OFFSET TCLR+1 ; In first field?
                JA      BS_BKGR         ; No
                JB      GO_CLRLP        ;
                DEC     SI              ;
                DEC     DL              ;
GO_CLRLP:                               ;
                JMP     COLOR_LP        ;
BS_BKGR:                                ;
                CMP     SI,OFFSET BCLR  ; In background?
                JA      BS_BLNK         ; No
                SUB     SI,OFFSET BCLR - OFFSET TCLR - 1 ;
                SUB     DL,OFFSET BCLR - OFFSET TCLR - 1 ;
                JMP     COLOR_LP        ;
BS_BLNK:                                ;
                SUB     SI,OFFSET BLNK - OFFSET BCLR ;
                SUB     DL,OFFSET BLNK - OFFSET BCLR ;
                JMP     COLOR_LP        ;
GO_CST:                                 ;
                JMP     COLOR_ST        ;
;
; F10 = Put new attribute in character and advance cursor.
;
COLOR_SUB:                              ;
                MOV     AH,ATTRIBUTE    ;
                MOV     [SI+1],AH       ; Put new attribute
                MOV     AL,[SI]         ;
                JMP     EDIT_NINS       ; Redisplay the character position
;
; Alt= = Put next key value whatever it is.
;
FORCE_SUB:                              ;
                XOR     AH,AH           ;
                INT     KYBD$           ; Get next key stroke
                JMP     EDIT_FORCE      ; and force into file
;
; Ctrl Enter = Done
;
CENTER_SUB:                             ; Ctrl Enter = Done
                POP     AX              ; Throw away return
                CALL    REST_VID        ; Restore screen
                CALL    GET_TIME        ; Initialize date & time variables
;
; Read template file.
;
                MOV     BX,TEMPLFILE    ; Read template file
                MOV     IN_CNT,0        ; Start new buffer
                MOV     AH,SCR_ATTR     ; Starting attribute for ANSI conversion
                MOV     DX,1            ; First field for screen file editing
                CMP     EOPT,L_EDIT     ; Editing a screen file?
                JE      DO_INP          ; Yes, go copy the screen
                CMP     BX,0            ; Any template file?
                JZ      GO_DONE         ; No
;
; Scan template file looking for.  Check character type and handle as needed.
;
TMPL_LP:                                ;
                CALL    GET_CHR         ; Get next character
                JNZ     TMPL_OK         ; Got a character
GO_DONE:                                ;
                JMP     WF_DONE         ;
TMPL_OK:                                ;
                MOV     CX,L_TCHR       ;
                MOV     SI,OFFSET TMPL_CHR ;
                CALL    TBL_SRCH        ; Find character type
                JMP     TMPL_VECT[SI]   ; Switch on character type
TMPL_PUT:                               ;
                CALL    OUT_CHR         ; No, send character to output
                JMP     TMPL_LP         ; Continue processing characters
;
; \ Force out next character.
;
TMPL_FORCE:                             ;
                CALL    GET_CHR         ; Get character
                JNZ     TMPL_PUT        ; If none, error.  Otherwise put it
;
; Bad template file format.
;
TMPL_ERROR:                             ;
                MOV     CX,L_TBAD       ;
                MOV     DX,OFFSET TMPL_MSG ;
                JMP     ERROR_DSP       ;
;
; < Switch output files.
;
TMPL_FILE:                              ;
                CALL    DTB             ; Get file number
                CMP     AL,'>'          ;
                JNE     TMPL_ERROR      ; Not correct closing symbol
                DEC     DX              ;
                MOV     SI,DX           ;
                SHL     SI,1            ;
                CMP     DX,NUM_OUT      ; Legal value?
                JAE     TMPL_ERROR      ;
                CALL    WR_OUT          ; Spill rest of buffer for current file
                MOV     OUT_INDX,0      ;
                MOV     AX,OUT_HNDL[SI] ; Get handle
                MOV     OUTFILE,AX      ;
                JMP     TMPL_LP         ;
;
; [ Look for pattern type.
;
TMPL_SENT:                              ;
;
; Check for an input area reference.
;
                CALL    DTB             ; Convert value to binary
                OR      DX,DX           ; Input number?
                JNZ     DO_INP          ;
                JMP     CHK_PAT         ; No, check if time/date pattern
;
; We have an input area to copy.  Check to see if the number is good.
;
DO_INP:                                 ;
                DEC     DX              ;
                SHL     DX,1            ; Convert to area index
                SHL     DX,1            ;
                CMP     DX,EDIT_NUM     ; Legal area?
                JAE     TMPL_ERROR      ; No, error
;
; Get area to copy.
; We will not copy trailing blanks from the input area.
; Input will be treated as a single character string with no CR,LFs inserted.
; The template may indicate formatting by following the number with any of
; the following pattern characters:
; ]     =       All remaining input characters
; ?     =       Next input character
; =     =       All input characters up to end of screen line
; |     =       Ignore rest of template if no more input
; \     =       Include next template character in output
; All other characters in the template are copied to the output.
;
                MOV     DI,DX           ; Input area index
                MOV     DX,EDIT_AREA[DI] ; Start of input area
                MOV     DI,EDIT_AREA[DI+2] ; End of input area
;
; Find last non-blank.
;
                CALL    TRIM            ; Find start of trailing spaces
                XCHG    DX,DI           ; DX=End, DI=Start
;
; Scan template doing the replacement.
;
                CMP     EOPT,L_EDIT     ; Editing a screen file?
                JNE     REPL_FIRST      ; Already have first character
                JMP     COPY_SCRN       ; Yes
REPL_INP:                               ;
                CALL    GET_CHR         ; Get next template character
                JZ      TMPL_ERROR      ;
REPL_FIRST:                             ;
                MOV     CX,L_RCHR       ;
                MOV     SI,OFFSET REPL_CHR ;
                CALL    TBL_SRCH        ; Get character type
                CALL    REPL_VECT[SI]   ; Switch on character type
                JMP     REPL_INP        ;
;
; All non-pattern characters.
;
REPL_PUT:                               ;
                CALL    OUT_CHR         ; Just send the character to output
REPL_OK:                                ;
                RET                     ; Continue replacing
GO_STOP:                                ;
                JMP     COPY_STOP       ;
;
; \ Copy the next template character.
;
COPY_FORCE:                             ;
                CALL    GET_CHR         ; Get the character
                JZ      TMPL_ERROR      ;
                JMP     REPL_PUT        ;
;
; ? Copy the next input character.
;
COPY_ONE:                               ;
                CMP     DI,DX           ; Any left?
                JAE     REPL_OK         ; No, nothing to copy
                MOV     AL,[DI]         ;
                INC     DI              ;
                INC     DI              ;
                JMP     REPL_PUT        ;
;
; Copy to file in screen file format.  Generate ANSI sequences as needed.
; For start and end of defined input fields insert [ and ].
; For start and end of constant fields insert { and }.
; Put a \ in front of screen file control characters that appear as data.
;
COPY_SCRN:                              ;
                CMP     DX,SCRN_END     ; Past user defined end?
                JBE     COPY_SOK        ; No
                MOV     DX,SCRN_END     ;
                INC     DX              ;
                INC     DX              ;
COPY_SOK:                               ;
                CALL    COPY_LINE       ; Copy one line
                MOV     AL,CR_CHR       ;
                CALL    OUT_CHR         ;
                MOV     AL,LF_CHR       ; Put end of line
                CALL    OUT_CHR         ;
                CMP     DI,DX           ; Any input left?
                JB      COPY_SOK        ; Yes
                JMP     WF_DONE         ; No
;
; = Copy all input characters up to the end of the screen line.
; Do not copy trailing blanks on the screen line but do skip over them.
;
COPY_LINE:                              ;
                PUSH    CX              ;
                PUSH    AX              ;
                MOV     AX,DI           ; Starting location
                SUB     AX,OFFSET EDIT_LOC ; Index
                SHR     AX,1            ; Convert to character index
                MOV     CX,WORD PTR SCR_COL ; Columns per line
                DIV     CL              ;
                SUB     CL,AH           ; Remaining characters to end of line
                MOV     AX,DX           ;
                SUB     AX,DI           ;
                SHR     AX,1            ; Remaining characters in input field
                CMP     AX,CX           ; Use lesser
                JAE     LINE_COPY       ;
                MOV     CX,AX           ;
LINE_COPY:                              ;
                POP     AX              ;
                JCXZ    LINE_DONE       ;
                PUSH    DX              ;
                MOV     DX,DI           ; Start of area
                ADD     DI,CX           ; End of input area to be copied
                ADD     DI,CX           ;
                PUSH    DI              ; Save end
                CALL    TRIM            ; Ignore trailing blanks
                MOV     CX,DI           ; Compute new length
                SUB     CX,DX           ;
                SHR     CX,1            ;
                MOV     DI,DX           ; Start of copy
                JCXZ    LINE_MT         ; All blank line
;
; Now copy the characters checking for start and end of input areas and
; change of attributes.
;
LINE_CP:                                ;
                CMP     EOPT,L_EDIT     ; Generating screen file?
                JNE     LINE_PUT        ; No
                MOV     AL,'['          ; Do start of fields
                CALL    FIELD_GEN       ; Check for field start
                CMP     AH,[DI+1]       ; Attributes match?
                JNE     GEN_ANSI        ; No, generate ANSI sequence
LINE_CHR:                               ;
                MOV     DX,CX           ;
                MOV     AL,[DI]         ; Get next input character
                MOV     CX,L_SCHR       ; See if this is a screen control char
                MOV     SI,OFFSET SCR_CHR ;
                CALL    TBL_SRCH        ;
                MOV     CX,DX           ;
                CMP     SI,0            ; Normal character?
                JZ      LINE_PUT        ; Yes
                MOV     DL,AL           ;
                MOV     AL,'\'          ; Send force character
                CALL    OUT_CHR         ;
                MOV     AL,DL           ;
LINE_PUT:                               ;
                MOV     AL,[DI]         ; Get next input character
                CALL    OUT_CHR         ; Send to output
                INC     DI              ;
                INC     DI              ;
                CMP     EOPT,L_EDIT     ; Screen file editing?
                JNE     LINE_ELP        ; No
                MOV     AL,']'          ; Do end of fields
                CALL    FIELD_GEN       ;
LINE_ELP:                               ;
                LOOP    LINE_CP         ;
LINE_MT:                                ;
                POP     DI              ; Move to end of line
                POP     DX              ;
LINE_DONE:                              ;
                POP     CX              ;
                RET                     ;
;
; Generate ANSI sequence from change in attribute.
;
GEN_ANSI:                               ;
                PUSH    CX              ;
                PUSH    DX              ;
                XOR     CX,CX           ; No values output yet
;
; Put Esc [ start of ANSI sequence.
;
                MOV     AL,ESC_CHR      ; Start ANSI sequence
                CALL    OUT_CHR         ;
                MOV     AL,'['          ;
                CALL    OUT_CHR         ;
;
; Check to see if foreground colors are changed.
;
                MOV     DL,[DI+1]       ; Isolate foregrounds
                MOV     DH,AH           ;
                AND     DX,0F0FH        ;
                CMP     DL,DH           ; Did foreground change?
                JE      NOT_FORE        ; No
                AND     DL,7            ; Check to see if only intensity
                CMP     DL,DH           ;
                JE      SET_INT         ; Only intensity being set
                TEST    DL,5            ; Switch attributes if appropriate
                JPE     NSW_1           ;
                XOR     DL,5            ; Invert attributes
NSW_1:                                  ;
                MOV     AL,'3'          ; Put foreground color
                CALL    OUT_CHR         ;
                MOV     AL,DL           ;
                ADD     AL,'0'          ;
                CALL    OUT_CHR         ;
                INC     CX              ;
;
; Foreground changed, so check to see if we need to set high intensity.
;
                MOV     DL,[DI+1]       ;
                TEST    DL,8            ; High intensity set?
                JZ      NOT_FORE        ; No
                MOV     AL,';'          ;
                CALL    OUT_CHR         ;
SET_INT:                                ;
                MOV     AL,'1'          ;
                CALL    OUT_CHR         ;
                INC     CX              ;
NOT_FORE:                               ;
                MOV     DH,AH           ;
                MOV     DL,[DI+1]       ; Compare backgrounds
                AND     DX,7070H        ;
                CMP     DL,DH           ; Did background change?
                JE      NOT_BKGR        ; No
                TEST    DL,50H          ; Switch attributes if appropriate
                JPE     NSW_2           ;
                XOR     DL,50H          ;
NSW_2:                                  ;
                JCXZ    NO_SEP2         ; No value output yet
                MOV     AL,';'          ; Put separator
                CALL    OUT_CHR         ;
NO_SEP2:                                ;
                MOV     AL,'4'          ;
                CALL    OUT_CHR         ; Put background color
                MOV     AL,DL           ;
                MOV     CL,4            ;
                SHR     AL,CL           ;
                ADD     AL,'0'          ;
                CALL    OUT_CHR         ;
NOT_BKGR:                               ;
                TEST    BYTE PTR [DI+1],80H ; Blink set?
                JZ      NOT_BLI         ; No
                JCXZ    NO_SEP3         ;
                MOV     AL,';'          ; Separator
                CALL    OUT_CHR         ;
NO_SEP3:                                ;
                MOV     AL,'5'          ;
                CALL    OUT_CHR         ;
NOT_BLI:                                ;
                MOV     AL,'m'          ; Put terminator
                CALL    OUT_CHR         ;
                MOV     AH,[DI+1]       ; New attribute
                POP     DX              ;
                POP     CX              ;
                JMP     LINE_CHR        ;
;
; | Stop copying from this template if there is no more input.
;
COPY_STOP:                              ;
                CMP     DI,DX           ; Any more input?
                JAE     COPY_SKIP       ; No
                RET                     ; Yes
COPY_SKIP:                              ;
                CALL    GET_CHR         ; Skip to end of template
                JZ      BAD_TP          ;
                CMP     AL,']'          ; End of template?
                JE      COPY_DONE       ; Yes
                CMP     AL,'\'          ; Force?
                JNE     COPY_SKIP       ; No, keep looking
                CALL    GET_CHR         ; Ignore next character
                JZ      BAD_TP          ;
                JMP     COPY_SKIP       ; Keep looking
;
; ] Copy remaining input characters to output and then start over at top.
;
COPY_REST:                              ;
                CMP     DI,DX           ; Any remaining input characters?
                JAE     COPY_DONE       ; No
                MOV     AL,[DI]         ;
                CALL    OUT_CHR         ; Copy to output
                INC     DI              ;
                INC     DI              ;
                JMP     COPY_REST       ;
BAD_TP:                                 ;
                POP     AX              ;
                JMP     TMPL_ERROR      ;
COPY_DONE:                              ;
                POP     AX              ;
                JMP     TMPL_LP         ; Continue working on template file
;
; Check for time and date patterns.
; \ Force next character
; MM DD CC YY DW MN hh mm ss are all replaced with appropriate values.
;
CHK_PAT:                                ;
                PUSH    AX              ;
CHK_MORE:                               ;
                CMP     AL,']'          ; End sentinel?
                JE      COPY_DONE       ; Yes, look for next template
                MOV     AH,AL           ; Save first pattern character
                CALL    GET_CHR         ; Get second pattern character
                JZ      BAD_TP          ;
PAT_CHK:                                ;
                CMP     AH,'\'          ; Force next character?
                JE      PAT_NEXT        ; Yes
                MOV     SI,OFFSET TD_TAB ; Pattern table
                MOV     CX,L_TD         ;
                CALL    WTBL_SRCH       ; Find matching entry
                CMP     SI,0            ; Do we have a pattern?
                JNZ     REPL_PAT        ; Yes
                XCHG    AL,AH           ; No, output first char and continue
                CALL    OUT_CHR         ; Send character to output
                XCHG    AL,AH           ;
                JMP     CHK_MORE        ; Keep checking for patterns
;
; \ Found a force character.
;
PAT_NEXT:                               ;
                CALL    OUT_CHR         ; Send forced character to output
PAT_GET:                                ;
                CALL    GET_CHR         ;
                JZ      BAD_TP          ;
                JMP     CHK_MORE        ;
;
; Found a pattern to be replaced.
; Check for DW and MN case and handle specially.
;
REPL_PAT:                               ;
                MOV     DI,TD_LOC[SI-2] ; Data loc
                CMP     SI,4            ; DW or MN?
                JE      CK_MON          ; MN
                JB      CK_DOW          ; DW
DO_2:                                   ;
                MOV     AX,[DI]         ; Get data
                CALL    OUT_CHR         ; Put in output
                MOV     AL,AH           ;
                CALL    OUT_CHR         ;
                JMP     PAT_GET         ;
CK_MON:                                 ;
                MOV     AX,OFFSET MON_TAB ; Month abbreviation table
DO_3:                                   ;
                ADD     AX,[DI]         ;
                MOV     DI,AX           ;
                MOV     AL,[DI]         ; Get abbreviation
                CALL    OUT_CHR         ; Put first character
                INC     DI              ;
                JMP     DO_2            ;
CK_DOW:                                 ;
                MOV     AX,OFFSET DOW_TAB ; Day of week abbreviation table
                JMP     DO_3            ;
;
; { Constant area reference.
;
TMPL_CONST:                             ;
                CALL    DTB             ; Convert value to binary
                OR      DX,DX           ; Constant number?
                JNZ     DO_CONST        ;
                CMP     AL,'['          ; Indirect reference?
                JE      DO_INDIR        ; Indirect constant reference
                JMP     TMPL_ERROR      ; Incorrect constant reference
;
; We have {[nnn]}.
;
DO_INDIR:                               ;
                CALL    DTB             ; Get input field reference
                OR      DX,DX           ; Got a number?
                JZ      GO_TERR         ; Yes
                CMP     AL,']'          ; Closed properly?
                JE      INDIR_OK        ;
GO_TERR:                                ;
                JMP     TMPL_ERROR      ;
INDIR_OK:                               ;
                DEC     DX              ;
                SHL     DX,1            ;
                SHL     DX,1            ;
                CMP     DX,EDIT_NUM     ; Legal field reference?
                JAE     GO_TERR         ; No
                MOV     SI,DX           ;
                MOV     CX,EDIT_AREA[SI+2] ; End of area
                MOV     SI,EDIT_AREA[SI] ; Start of input
                XOR     DX,DX           ;
INDIR_VAL:                              ;
                LODSB                   ; Get next character
                CMP     AL,'0'          ; If not a digit, skip
                JB      INDIR_SKIP      ;
                CMP     AL,'9'          ;
                JA      INDIR_SKIP      ;
                SUB     AL,'0'          ; Convert to binary
                XCHG    AL,DL           ;
                MUL     C10             ;
                ADD     DX,AX           ;
INDIR_SKIP:                             ;
                INC     SI              ;
                CMP     SI,CX           ;
                JB      INDIR_VAL       ;
                CALL    GET_CHR         ; Must have closing brace
                JZ      GO_TERR         ;
;
; We have a constant reference.
;
DO_CONST:                               ;
                CMP     AL,'}'          ; Closing brace?
                JNE     GO_TERR         ;
                DEC     DX              ;
                SHL     DX,1            ;
                SHL     DX,1            ;
                CMP     DX,CONST_NUM    ; Legal constant reference?
                JAE     GO_TMPL         ; No, ignore
                MOV     SI,DX           ;
                MOV     DX,CONST_AREA[SI+2] ; End of constant area
                MOV     SI,CONST_AREA[SI] ; Start of constant
CONST_LP:                               ;
                LODSB                   ;
                CALL    OUT_CHR         ; Send next constant character
                INC     SI              ;
                CMP     SI,DX           ; Reached the end?
                JB      CONST_LP        ; No
GO_TMPL:                                ;
                JMP     TMPL_LP         ; Yes, continue processing template
;
; Processing complete.
;
WF_DONE:                                ;
                CALL    WR_OUT          ; Write remaining buffered output
                MOV     CX,NUM_OUT      ; Number of output files
                XOR     SI,SI           ;
CLOSE_LP:                               ;
                MOV     BX,OUT_HNDL[SI] ; Close output file
                MOV     AH,CLOSE$       ;
                INT     DOS$            ;
                INC     SI              ;
                INC     SI              ;
                LOOP    CLOSE_LP        ;
                JMP     SHORT EXIT_DOS  ;
;
; Esc = Quit
;
ESC_SUB:                                ; Esc
                MOV     AX,OFFSET Q_MSG ;
                MOV     CX,L_Q          ;
                CALL    STAT_MSG        ; Display query "Really want to quit?"
Q_SOL:                                  ;
                XOR     AH,AH           ;
                INT     KYBD$           ; Read next character
                AND     AL,NOT 20H      ; Make upper case
                CMP     AL,'Y'          ; Really want to exit?
                JE      DO_QUIT         ; Yes
                CMP     AL,'N'          ; Want to keep going?
                JNE     Q_SOL           ; No, resolicit
                JMP     EDIT_STATUS     ; Keep on going
DO_QUIT:                                ;
                CALL    REST_VID        ; Restore original screen
EXIT_DOS:                               ;
                MOV     AH,EXIT$        ; Return to DOS
                XOR     AL,AL           ; Normal exit
                INT     DOS$            ;
;
; Commnon code for error message display and error exit to DOS.
;
ERROR_DSP:                              ;
                MOV     AH,WRFILE$      ; Display error message
                MOV     BX,ERROUT       ;
                INT     DOS$            ;
                MOV     CX,2            ;
                MOV     DX,OFFSET CRLF  ; Skip a line
                MOV     AH,WRFILE$      ;
                INT     DOS$            ;
                MOV     AH,EXIT$        ;
                MOV     AL,1            ; Error exit
                INT     DOS$            ;
;
;
; PROCEDURE FIND_AREA
;
; FIND_AREA finds the input area containing the current cursor position.
; If the position is not in an area, the next logical area start is
; returned.
;
; INPUT:
;       SI      =       Current cursor location
;
; OUTPUT:
;       BX      =       Pointer to area table entry
;       CF      =       1, Not in the area
;                       0, In the area
;
FIND_AREA       PROC                    ;
                XOR     BX,BX           ; Search input table
FA_LP:                                  ;
                CMP     BX,EDIT_NUM     ; Still in table?
                JA      FA_FIRST        ; No, go to home position
                CMP     SI,EDIT_AREA[BX] ; Within this area?
                JB      FA_NOT          ; No
                CMP     SI,EDIT_AREA[BX+2] ;
                JB      FA_IN           ; Yes
                ADD     BX,4            ; Try next area
                JMP     FA_LP           ;
FA_FIRST:                               ;
                XOR     BX,BX           ;
FA_NOT:                                 ;
                CLC                     ;
FA_IN:                                  ;
                CMC                     ; Set carry appropriately
                RET                     ;
FIND_AREA       ENDP                    ;
;
;
; PROCEDURE: DISP_MNU
;
; DISP_MNU displays a Nx40 menu on the screen.
;
; INPUT:
;       AX      =       Menu location
;       CX      =       Number of lines in menu
;
; OUTPUT:
;       AX, CX contents destroyed.
;
DISP_MNU        PROC                    ;
                PUSH    DX              ;
                PUSH    DI              ;
                PUSH    SI              ;
                XOR     DI,DI           ;
                MOV     DX,CX           ; Number of lines
                MOV     SI,AX           ; Start of menu
DM_LINE:                                ;
                MOV     CX,40           ;
DM_LP:                                  ;
                LODSW                   ;
                CALL    WRITE_SCREEN    ; Display next character
                LOOP    DM_LP           ;
                ADD     DI,WORD PTR SCR_COL ; New line
                ADD     DI,WORD PTR SCR_COL ;
                SUB     DI,80           ;
                DEC     DX              ;
                JNZ     DM_LINE         ;
                POP     SI              ;
                POP     DI              ;
                POP     DX              ;
                RET                     ;
DISP_MNU        ENDP                    ;
;
;
; PROCEDURE STATUS
;
; Display status line.
;
; INPUT:
;       DI      =       Current cursor position
;       SI      =       Current file position
;
; OUTPUT:
;       AX contents are destroyed.
;
STATUS          PROC                    ;
                PUSH    CX              ;
                PUSH    DX              ;
                PUSH    DI              ;
                PUSH    SI              ;
;
; Convert row and column to ASCII and store in status line.
;
                MOV     AX,DI           ;
                SHR     AX,1            ;
                DIV     SCR_COL         ; Get column and row
                MOV     CL,AH           ; Save column
                XOR     AH,AH           ;
                INC     AL              ;
                MOV     DI,OFFSET STAT_ROW ;
                CALL    BTD3            ; Convert and store row
                MOV     AL,CL           ;
                INC     AL              ;
                XOR     AH,AH           ;
                MOV     DI,OFFSET STAT_COL ;
                CALL    BTD3            ;
;
; Convert file line number and put in status.
;
                LEA     AX,[SI-(OFFSET EDIT_LOC - OFFSET MENU_PSP)] ;
                SHR     AX,1            ;
                CWD                     ;
                DIV     WORD PTR SCR_COL ; Get line number
                INC     AX              ; Start at 1
                MOV     DI,OFFSET STAT_LIN ;
                CALL    BTD3            ; Convert high order
;
; Display status line.
;
                MOV     AH,STAT_ATTR    ; Status display attribute
                MOV     SI,OFFSET STATUS_MSG ;
                MOV     DI,DISP_SIZE    ;
                MOV     CX,WORD PTR SCR_COL ;
STAT_LP:                                ;
                LODSB                   ;
                CALL    WRITE_SCREEN    ;
                LOOP    STAT_LP         ;
                POP     SI              ;
                POP     DI              ;
                POP     DX              ;
                POP     CX              ;
                RET                     ;
STATUS          ENDP                    ;
;
;
; PROCEDURE: STAT_MSG
;
; STAT_MSG displays a message in the status field.
;
; INPUT:
;       AX      =       Start of message to display
;       CX      =       Length of message to display
;
; OUTPUT:
;       AX, CX contents destroyed.
;
STAT_MSG        PROC                    ;
                PUSH    DI              ;
                PUSH    SI              ;
                MOV     SI,AX           ; Location of message
                MOV     AX,CX           ; Save count
                MOV     DI,OFFSET STAT_Q ;
        REP     MOVSB                   ; Copy query to status
                MOV     CX,Q_LNG        ; Clear rest of message area
                SUB     CX,AX           ;
                MOV     AL,' '          ;
        REP     STOSB                   ;
                POP     SI              ;
                POP     DI              ;
                CALL    STATUS          ; Display message
                RET                     ;
STAT_MSG        ENDP                    ;
;
;
; PROCEDURE: STAT_CLR
;
; STAT_CLR clears the message area of the status line.
;
; INPUT:
;       None
;
; OUTPUT:
;       None
;
STAT_CLR        PROC                    ;
                PUSH    AX              ;
                PUSH    CX              ;
                PUSH    DI              ;
                MOV     CX,Q_LNG        ;
                MOV     DI,OFFSET STAT_Q ;
                MOV     AL,' '          ;
        REP     STOSB                   ;
                POP     DI              ;
                POP     CX              ;
                POP     AX              ;
                RET                     ;
STAT_CLR        ENDP                    ;
;
;
; PROCEDURE: GET_TIME
;
; GET_TIME gets the current date and time and edits the value to ASCII
; characters and places the values in the date and time variables.
;
; INPUT:
;       None
;
; OUTPUT:
;       MONTH, MON, DOW, DAY, CENTURY, YEAR, HOUR, MINUTE, SECOND updated.
;
GET_TIME        PROC                    ;
;
; Save registers used.
;
                PUSH    AX              ;
                PUSH    CX              ;
                PUSH    DX              ;
;
; Get date and convert to ASCII.
;
                MOV     AH,DATE$        ;
                INT     DOS$            ; Get current date
                MOV     AH,3            ; Convert DOW to an index
                MUL     AH              ;
                MOV     DOW,AX          ;
;
; Convert date to MM DD CC YY format.
; Compute month table index.
;
                MOV     AL,DH           ; Month index
                DEC     AL              ; Start index at 0
                MOV     AH,3            ;
                MUL     AH              ;
                MOV     MON,AX          ;
                MOV     AL,DH           ; Do month
                CALL    BTD             ; Convert to ASCII
                MOV     MONTH,AX        ;
                MOV     AL,DL           ; Day
                CALL    BTD             ;
                MOV     DAY,AX          ;
                MOV     AX,CX           ; Year
                MOV     CL,100          ; Separate century
                DIV     CL              ;
                MOV     CL,AH           ; Save year
                CALL    BTD             ;
                MOV     CENTURY,AX      ; Century
                MOV     AL,CL           ; Year
                CALL    BTD             ;
                MOV     YEAR,AX         ;
;
; Get time and convert to hh mm ss.
;
                MOV     AH,TIME$        ;
                INT     DOS$            ; Get current time
                MOV     AL,CH           ; Hour
                CALL    BTD             ;
                MOV     HOUR,AX         ;
                MOV     AL,CL           ; Minutes
                CALL    BTD             ;
                MOV     MINUTE,AX       ;
                ADD     DX,256-50       ; Round up seconds
                MOV     AL,DH           ; Seconds
                CALL    BTD             ;
                MOV     SECOND,AX       ;
                POP     DX              ;
                POP     CX              ;
                POP     AX              ;
                RET                     ;
GET_TIME        ENDP                    ;
;
;
; PROCEDURE BTD
;
; BTD converts a binary value from 00-99 to its decimal ASCII equivalent.
;
; INPUT:
;       AL      =       Binary value
;
; OUTPUT:
;       AX      =       ASCII value (low order in AH)
;
BTD             PROC                    ;
                XOR     AH,AH           ; Make a whole word
                DIV     C10             ; Separate digits
                ADD     AX,'00'         ; Convert to ASCII
                RET                     ;
BTD             ENDP                    ;
;
;
; PROCEDURE BTD3
;
; BTD3 converts a binary value from 000-999 to its decimal ASCII equivalent
; and stores the result in a specified location.
;
; INPUT:
;       AX      =       Value
;       ES:DI   =       Location to save value
;
; OUTPUT:
;       AX,DI contents destroyed.
;
BTD3            PROC                    ;
                DIV     C100            ; Isolate 100's digit
                ADD     AL,"0"          ; Convert high order digit
                STOSB                   ;
                MOV     AL,AH           ;
                CALL    BTD             ; Convert low order digits
                STOSW                   ;
                RET                     ;
BTD3            ENDP                    ;
;
;
; PROCEDURE DTB
;
; DTB converts a sequence of decimal characters to their binary equivalent.
; The resulting value must be <65536.
;
; INPUT:
;       BX, CX, SI set up for GET_CHR
;
; OUTPUT:
;       AL      =       Terminator character
;       DX      =       Result value
;
DTB             PROC                    ;
                XOR     DX,DX           ; Zero accumulator
DTB_LP:                                 ;
                CALL    GET_CHR         ; Get next character
                JZ      DTB_END         ; No more characters
                CMP     AL,'0'          ; Decimal character?
                JB      DTB_END         ; No, end of conversion
                CMP     AL,'9'          ;
                JA      DTB_END         ;
                SUB     AL,'0'          ; Convert to binary
                PUSH    AX              ;
                MOV     AX,DX           ;
                MUL     WORD PTR C10    ; Multiply previous value
                MOV     DX,AX           ;
                POP     AX              ;
                ADD     DL,AL           ;
                ADC     DH,0            ;
                JMP     DTB_LP          ; Keep accumulating value
DTB_END:                                ;
                RET                     ;
DTB             ENDP                    ;
;
;
; PROCEDURE GET_CHR
;
; GET_CHR returns the next character of the current file.  This may be either
; the SCREEN file or the TEMPLATE file.  Once one file is read, it must be
; read through to the end without referencing any other file.  The end of the
; file will be signalled and the file closed when no more characters remain
; in the file or the end-of-file sentinel character(26=1AH) is reached.
;
; INPUT:
;       BX      =       File handle(SCREENFILE or TEMPLFILE)
;       IN_CNT  =       Remaining characters in buffer
;       IN_INDX =       Pointer to next character
;
; OUTPUT:
;       AL      =       Character
;       IN_CNT, IN_INDX updated
;       ZF      =       1, end of file
;
GET_CHR         PROC                    ;
;
; Check to see if any more characters remain
;
                CMP     IN_CNT,0        ; Any more in buffer
                JZ      READ_MORE       ; No
;
; Get next character and return it.
;
                XCHG    SI,IN_INDX      ;
                LODSB                   ;
                XCHG    SI,IN_INDX      ;
                DEC     IN_CNT          ; Decrement character count
                CMP     AL,EOF_CHR      ; Is it end of file?
                JE      FILE_DONE       ; Yes
                RET                     ;
;
; Read next buffer full of file.
;
READ_MORE:                              ;
                PUSH    AX              ;
                PUSH    CX              ;
                PUSH    DX              ;
                MOV     AH,RDFILE$      ;
                MOV     CX,L_INB        ; Read all that buffer will hold
                MOV     DX,OFFSET CHR_LOC ;
                MOV     IN_INDX,DX      ;
                INT     DOS$            ; Read file
                MOV     IN_CNT,AX       ; Save amount read
                POP     DX              ;
                POP     CX              ;
                POP     AX              ;
                JC      SCRN_ERROR      ; Error reading file
                CMP     IN_CNT,0        ; Any data read?
                JNZ     GET_CHR         ; Read is OK, so return the character
FILE_DONE:                              ;
                PUSHF                   ; Preserve carry flag
                PUSH    AX              ;
                MOV     AH,CLOSE$       ; Close the file
                INT     DOS$            ;
                POP     AX              ;
                POPF                    ;
                MOV     IN_CNT,1        ; Continue to signal EOF
                MOV     IN_INDX,OFFSET CHR_LOC ;
                MOV     CHR_LOC,EOF_CHR ;
                XOR     AL,AL           ; Indicate end of file
                RET                     ;
;
; Error reading screen file.
;
SCRN_ERROR:                             ;
                CMP     BX,SCREENFILE   ; Screen file?
                JNE     TMPL_FAIL       ; No, template file
                MOV     DX,OFFSET SCRN_ERR ;
                MOV     CX,L_SERR       ;
                JMP     ERROR_DSP       ;
;
; Error reading template file.
;
TMPL_FAIL:                              ;
                MOV     CX,L_TMPL       ;
                MOV     DX,OFFSET TMPL_ERR ;
                JMP     ERROR_DSP       ;
GET_CHR         ENDP                    ;
;
;
; PROCEDURE OUT_CHR
;
; OUT_CHR sends one character to the output file.
;
; INPUT:
;       AL      =       Character to send
;
; OUTPUT:
;       None
;
; PROCEDURE WR_OUT
;
; WR_OUT writes any remaining buffered output to the output file.
;
; INPUT:
;       None
;
; OUTPUT:
;       None
;
OUT_CHR         PROC                    ;
;
; Save registers used.
;
                PUSH    BX              ;
;
; Add character to buffer.  If buffer full, write to file.
;
                MOV     BX,OUT_INDX     ;
                MOV     OUT_BUF[BX],AL  ;
                INC     BX              ;
                MOV     OUT_INDX,BX     ;
                POP     BX              ;
                CMP     BX,L_OUTB       ; Buffer full?
                JAE     WR_OUT          ; Yes
                RET                     ;
;
; Write data to out file.
;
WR_OUT:                                 ;
                PUSH    AX              ;
                PUSH    BX              ;
                PUSH    CX              ;
                PUSH    DX              ;
                MOV     CX,OUT_INDX     ; Amount to write
                JCXZ    NO_OUT          ;
                MOV     AH,WRFILE$      ;
                MOV     BX,OUTFILE      ;
                MOV     DX,OFFSET OUT_BUF ;
                INT     DOS$            ; Write file
                JC      OUT_ERR         ;
NO_OUT:                                 ;
                POP     DX              ;
                POP     CX              ;
                POP     BX              ;
                POP     AX              ;
                RET                     ;
OUT_ERR:                                ;
                MOV     CX,L_OUT        ;
                MOV     DX,OFFSET OUT_MSG ;
                JMP     ERROR_DSP       ;
OUT_CHR         ENDP                    ;
;
;
; PROCEDURE DISP_SCREEN
;
; DISP_SCREEN redisplays the screen.
; SCR_LIN*SCR_COL characters are displayed.
;
; If the show fields flag is set, new field definitions are indicated by
; blinking []'s (║ if a single position) and constant area definitions
; are indicated by blinking {}'s (╬ if a single position).
;
; INPUT:
;       DI      =       Screen cursor position
;       SI      =       Edit buffer cursor position
;
; OUTPUT:
;       None
;
DISP_SCREEN     PROC                    ;
;
; Save registers used.
; Save current cursor position.
;
                PUSH    AX              ;
                PUSH    DI              ;
                PUSH    SI              ;
;
; Display characters until end of screen or buffer.
;
                SUB     SI,DI           ; Start of screen in edit buffer
                XOR     DI,DI           ;
DISP_LP:                                ;
                MOV     AL," "          ; Default space
                MOV     AH,SCR_ATTR     ; Empty attribute
                CMP     SI,EDIT_LAST    ; End of characters?
                JA      NO_CHR          ; Yes
                LODSW                   ; Get next char
NO_CHR:                                 ;
                CALL    WRITE_SCREEN    ;
                CMP     DI,DISP_SIZE    ; End of screen
                JB      DISP_LP         ; No
;
; If the show fields flag is set, insert field indicator characters.
;
                CMP     SHOW,0          ; Showing definitions?
                JZ      NO_SHOW         ; No
                PUSH    BX              ;
                XOR     BX,BX           ;
                SUB     SI,DI           ; Back to start of screen
SHDF_LP:                                ; Do all input and constant definitions
                MOV     AL,'['          ; Input definitions
                MOV     DI,DEF_AREA[BX] ;
                CALL    SHOW_IT         ; Start
                MOV     AL,']'          ;
                MOV     DI,DEF_AREA[BX+2] ;
                DEC     DI              ;
                DEC     DI              ;
                CMP     DI,DEF_AREA[BX] ; Start and end the same?
                JNE     NOT_DBL1        ;
                MOV     AL,'║'          ;
NOT_DBL1:                               ;
                CALL    SHOW_IT         ; End
                MOV     AL,'{'          ; Constant definitions
                MOV     DI,CONST_AREA[BX] ;
                CALL    SHOW_IT         ; Start
                MOV     AL,'}'          ;
                MOV     DI,CONST_AREA[BX+2] ;
                DEC     DI              ;
                DEC     DI              ;
                CMP     DI,CONST_AREA[BX] ; Start and end the same?
                JNE     NOT_DBL2        ;
                MOV     AL,'╬'          ;
NOT_DBL2:                               ;
                CALL    SHOW_IT         ; End
                ADD     BX,4            ;
                CMP     BX,EDIT_FLD*4   ;
                JB      SHDF_LP         ;
NO_CNDF:                                ;
                POP     BX              ;
NO_SHOW:                                ;
                POP     SI              ;
                POP     DI              ;
                POP     AX              ;
                RET                     ;
;
; Check if on this screen and display if so.
;
SHOW_IT:                                ;
                CMP     DI,0            ; Definition present?
                JZ      NOT_SH          ;
                MOV     AH,[DI+1]       ; Get attribute
                CMP     DI,SI           ; On this screen?
                JB      NOT_SH          ; No
                SUB     DI,SI           ; Offset to this one
                CMP     DI,DISP_SIZE    ; On screen?
                JAE     NOT_SH          ; No
                OR      AH,80H          ; Set blink
                CALL    WRITE_SCREEN    ; Display it
NOT_SH:                                 ;
                RET                     ;
DISP_SCREEN     ENDP                    ;
;
;
; PROCEDURE REST_VID
;
; REST_VID restores the screen as it was prior to doing the menu editing.
;
; INPUT:
;       None
;
; OUTPUT:
;       None
;
REST_VID        PROC                    ;
;
; Save registers used.
;
                PUSH    AX              ;
                PUSH    BX              ;
                PUSH    DX              ;
                PUSH    DI              ;
                PUSH    SI              ;
;
; Restore video modes and screen.
;
                MOV     SI,DISP_LOC     ;
                CMP     SI,0            ; Did we save the screen?
                JZ      NO_REST         ; No
                MOV     AX,WORD PTR SCR_COL ;
                SHL     AX,1            ;
                ADD     DISP_SIZE,AX    ; Include the last line
                MOV     EDIT_LAST,-1    ; Make sure area considered in display
                XOR     DI,DI           ; At top of screen
                CALL    DISP_SCREEN     ; Redisplay the screen
NO_REST:                                ;
                MOV     BH,SAVEPAGE     ;
                MOV     AL,BH           ;
                MOV     AH,PAGE$        ;
                INT     VIDEO$          ;
                MOV     DX,SAVECRS      ;
                MOV     AH,CRS$         ;
                INT     VIDEO$          ; Restore cursor position
                POP     SI              ;
                POP     DI              ;
                POP     DX              ;
                POP     BX              ;
                POP     AX              ;
                RET                     ;
REST_VID        ENDP                    ;
;
;
; PROCEDURE: WRITE_SCREEN
;
; WRITE_SCREEN writes a character directly to the video refresh memory.
;
; INPUT:
;       AX      =       Character and attribute to write
;       DI      =       Location at which to write character
;
; OUTPUT:
;       DI      =       DI + 2
;
WRITE_SCREEN    PROC                    ;
;
; Compute video memory offset.
;
                PUSH    BX              ;
                PUSH    DX              ;
                PUSH    ES              ;
                MOV     ES,VIDEO_SEG    ; Screen segment
                ASSUME  ES:NOTHING      ;
                CMP     FAST,0          ; Fast screen update?
                JNZ     FAST_WRITE      ;
                MOV     BX,AX           ;
                MOV     DX,STATUS_REG   ; Retrieve status register.
HORZ_RET:                               ;
                IN      AL,DX           ; Get status from adaptor
                TEST    AL,1            ; Is it low?
                JNZ     HORZ_RET        ; If not, wait until it is.
                CLI                     ; No more interrupts.
WAIT_RET:                               ;
                IN      AL,DX           ; Get status.
                TEST    AL,1            ; Is it high?
                JZ      WAIT_RET        ; If no, wait until it is.
                MOV     AX,BX           ; Retrieve character; now it's OK
FAST_WRITE:                             ; Move the following code up
                STOSW                   ; to write to screen buffer.
                STI                     ; Interrupts back on.
                POP     ES              ;
                ASSUME  ES:CODE         ;
                POP     DX              ;
                POP     BX              ;
                RET                     ;
WRITE_SCREEN    ENDP                    ;
;
;
; PROCEDURE: TRIM
;
; TRIM determines the last non-blank character in an area on the screen.
; Any location that contains the start or end of a defined input field is
; also considered to be non-blank.
; TRIM also considers a change in attribute value the equivalent of a non-blank.
;
; INPUT:
;       DX      =       Start of area
;       DI      =       End of area+2(i.e., location after end)
;       AH      =       Attribute of preceding location
;
; OUTPUT:
;       DI      =       Last non-blank(or attribute change)+2
;       DI      =       DX, entire area blank
;
TRIM            PROC                    ;
                PUSH    AX              ;
                PUSH    BX              ;
                PUSH    CX              ;
                MOV     AL,[DI-1]       ; Last attribute
TRIM_IT:                                ;
                CMP     DX,DI           ; At start of area?
                JE      TRIM_DONE       ; Yes, nothing to do
                CMP     BYTE PTR [DI-2],' ' ; Blank?
                JNE     TRIM_DONE       ; No, found end
                LEA     CX,[DI-2]       ;
                XOR     BX,BX           ;
TRIM_LP:                                ;
                CMP     BX,DEF_NUM      ; Any more input fields?
                JAE     TRIM_DF         ; No
                CMP     CX,DEF_AREA[BX] ; Start of defined field?
                JE      TRIM_DONE       ; Yes, in next position
                CMP     DI,DEF_AREA[BX+2] ; End of defined field?
                JE      TRIM_DONE       ; Yes
                ADD     BX,4            ; Next definition
                JMP     TRIM_LP         ;
TRIM_DF:                                ;
                CMP     CX,DX           ; At first position?
                JA      TRIM_CK         ; No
                CMP     AH,[DI-1]       ; Is attribute different than entry?
                JNE     TRIM_DONE       ; Yes
                JMP     SHORT TRIM_NA   ;
TRIM_CK:                                ;
                CMP     AL,[DI-3]       ; Does attribute change?
                JNE     TRIM_DONE       ; Yes
TRIM_NA:                                ;
                DEC     DI              ; Back up one
                DEC     DI              ;
                JMP     TRIM_IT         ;
TRIM_DONE:                              ;
                POP     CX              ;
                POP     BX              ;
                POP     AX              ;
                RET                     ;
TRIM            ENDP                    ;
;
;
; PROCEDURE: TBL_SRCH
;
; TBL_SRCH searches a list of characters and returns the index*2 of any match
; or 0 for no match.  The result value may be used to index a corresponding
; table of word size values with the 0th entry being the no-match entry.
;
; INPUT:
;       AL      =       Character to be matched
;       CX      =       Length of table to search
;       SI      =       Location of table to search
;
; OUTPUT:
;       SI      =       0 if no match or index*2 of match
;       CX contents are destroyed
;
TBL_SRCH        PROC                    ;
                PUSH    DI              ;
                MOV     DI,SI           ; Table base
        REPNE   SCASB                   ; Search the table
                JE      TBL_FND         ; If we found a match
                MOV     DI,SI           ; So we generate a 0 result
TBL_FND:                                ;
                SUB     SI,DI           ; Form index
                NEG     SI              ;
                SHL     SI,1            ; Double for word index
                POP     DI              ;
                RET                     ;
TBL_SRCH        ENDP                    ;
;
;
; PROCEDURE: WTBL_SRCH
;
; WTBL_SRCH searches a list of words and returns the index of any match
; or 0 for no match.  The result value may be used to index a corresponding
; table of word size values with the 0th entry being the no-match entry.
;
; INPUT:
;       AX      =       Word to be matched
;       CX      =       Length of table to search
;       SI      =       Location of table to search
;
; OUTPUT:
;       SI      =       0 if no match or index of match
;       CX contents are destroyed
;
WTBL_SRCH       PROC                    ;
                PUSH    DI              ;
                MOV     DI,SI           ; Table base
        REPNE   SCASW                   ; Search the table
                JE      WTBL_FND        ; If we found a match
                MOV     DI,SI           ; So we generate a 0 result
WTBL_FND:                               ;
                SUB     SI,DI           ; Form index
                NEG     SI              ;
                POP     DI              ;
                RET                     ;
WTBL_SRCH       ENDP                    ;
;
;
; PROCEDURE: FIELD_GEN
;
; FIELD_GEN is called when a screen file has been edited and is being output
; to check for input and constant fields and insert the bracketing characters
; as appropriate.
;
; INPUT:
;       AL      =       '[' or ']' for start or end check
;       DI      =       File index to check
;
; OUTPUT:
;       None
;
FIELD_GEN       PROC                    ;
                PUSH    BX              ;
;
; Loop through field definitions.
;
                XOR     BX,BX           ; Check for start of input
                CMP     AL,'['          ; Field start?
                JE      FG_LP           ; Yes
                INC     BX              ; Use end of field
                INC     BX              ;
FG_LP:                                  ;
                CMP     BX,DEF_NUM      ; Any more input fields?
                JAE     NO_DEF          ; No
                CMP     DI,DEF_AREA[BX] ; Start of field?
                JNE     NO_DEF          ; Yes
                CALL    OUT_CHR         ; Put start of input
NO_DEF:                                 ;
                CMP     BX,CONST_NUM    ; Any more constant fields?
                JAE     NO_CONST        ; No
                CMP     DI,CONST_AREA[BX] ; Start of constant?
                JNE     NO_CONST        ;
                OR      AL,20H          ; Put start of constant
                CALL    OUT_CHR         ;
                AND     AL,NOT 20H      ;
NO_CONST:                               ;
                ADD     BX,4            ;
                CMP     BX,EDIT_FLD*4   ; Processed all possible fields?
                JB      FG_LP           ;
                POP     BX              ;
                RET                     ;
FIELD_GEN       ENDP                    ;
;
; Location of reusable space.
;
CHR_LOC         LABEL   BYTE            ; Input buffer
OUT_BUF         EQU     CHR_LOC+L_INB   ; Output I/O buffer
;
; Initialization data.
;
ERROR_MSG       LABEL   BYTE            ;
                DB      "MENU [/D/E/N] screen template out" ;
L_ERROR         EQU     $-ERROR_MSG     ;
OPT_CHR         LABEL   BYTE            ;
                DB      'NDE'           ; Option letters
L_OPT           EQU     $-OPT_CHR       ;
OPT_VECT        LABEL   WORD            ;
                DW      BAD_OPT         ;
                DW      N_OPT           ;
                DW      D_OPT           ;
                DW      E_OPT           ;
;
;
; INIT performs video and file initialization for MENU.  It is placed at the
; end of the program area so that its space may be reused.
;
INIT:                                   ;
;
; Find input file name and open it.
;
                MOV     SI,OFFSET P_CNT ;
                LODSB                   ; Get count
                XOR     AH,AH           ;
                MOV     CX,AX           ;
                XOR     AL,AL           ; Open for input only
                XOR     BX,BX           ; Default to standard input or none
                CALL    OPEN_FILE       ; Parse file name and open file
                JC      ERROR_FILE      ; Error getting file
                MOV     SCREENFILE,AX   ; Save handle
                JMP     GET_TMPL        ;
;
; File does not exist.
;
ERROR_FILE:                             ;
                MOV     CX,L_ERROR      ;
                MOV     DX,OFFSET ERROR_MSG ;
                JMP     ERROR_DSP       ;
;
; Find template file name.
;
GET_TMPL:                               ;
                XOR     BX,BX           ;
                XOR     AL,AL           ;
                CALL    OPEN_FILE       ; Get template file
                JC      ERROR_FILE      ;
                MOV     TEMPLFILE,AX    ; Template handle
;
; Get output file name.
;
                MOV     BX,STDOUT       ; Default to standard output
                MOV     AL,1            ; Open file for writing
                CALL    OPEN_FILE       ;
                JC      ERROR_FILE      ;
                MOV     OUTFILE,AX      ; Output file handle
;
; See if multiple output files specified.
;
                MOV     DI,OFFSET OUT_HNDL ;
OUT_LP:                                 ;
                STOSW                   ; Save handle in output table
                INC     NUM_OUT         ; Increment count of output files
OUT_SKP:                                ;
                JCXZ    CHECK_VOID      ; No more files, check void
                MOV     AL,1            ; Create the file
                CALL    OPEN_FILE       ;
                JC      ERROR_FILE      ;
                CMP     AX,STDOUT       ; Any file name there?
                JNE     OUT_LP          ; Yes
                JMP     OUT_SKP         ; No, skip saving handle
CHECK_VOID:                             ;
                CMP     DOPT,L_DOPT     ; Only /D is legal
                JE      CHECK_VID       ;
                CMP     SCREENFILE,0    ; Any parameters?
                JE      ERROR_FILE      ; No, error
;
; Determine current video modes and save them.
;
CHECK_VID:                              ;
                MOV     AX,BIOS_DATA    ; Point to the ROM BIOS data area
                MOV     DS,AX           ; and get base address of active
                ASSUME  DS:BIOS_DATA    ;
                MOV     AX,ADDR_6845    ; display card.
                PUSH    CS              ; Restore DS
                POP     DS              ;
                ASSUME  DS:CODE         ;
                ADD     AX,6            ; Add six to get status register
                MOV     STATUS_REG,AX   ; Store status register.
                CMP     AX,3BAH         ; Status port of MONO card is 3BAH.
                JE      MONO            ; If that's what we got, it's MONO
                MOV     VIDEO_SEG,COLOR_SEG ; else COLOR so use that seg
                MOV     AH,ALTSEL$      ; Try to select the EGA/VGA
                MOV     BL,10H          ; Ask EGA/VGA for status
                INT     VIDEO$          ;
                CMP     BL,10H          ; EGA/VGA present?
                JE      CGA             ; No, assume old CGA
                MOV     AX,BIOS_DATA    ; There is an EGA/VGA.  Check if it is
                MOV     DS,AX           ; the one we are using.
                ASSUME  DS:BIOS_DATA    ;
                MOV     AL,EGA_INFO     ; Get EGA/VGA status byte
                PUSH    CS              ;
                POP     DS              ;
                ASSUME  DS:CODE         ;
                TEST    AL,EGA_MASK     ; Is EGA/VGA active?
                JNZ     CGA             ; No, do CGA initialization only
;
; Since it is an EGA/VGA, there may be other than 25 lines per screen.  Get the
; actual number and change MENU to use it.
;
                XOR     BH,BH           ;
                MOV     AX,EGASTAT$*256+48 ;
                INT     VIDEO$          ; EGA/VGA get status
                PUSH    CS              ;
                POP     ES              ; Reset ES changed by VIDEO$
                INC     DL              ; Number of lines
                MOV     SCR_LIN,DL      ;
;
; For monochrome and EGA/VGA controllers, we do not need to wait for horizontal
; retrace before putting a character on the display.
;
MONO:                                   ; MDA or EGA/VGA
                MOV     FAST,1          ; Set fast video flag
CGA:                                    ;
                MOV     AH,VIDSTAT$     ; Get video state
                INT     VIDEO$          ;
                MOV     SCR_COL,AH      ; Save number of columns
                MOV     SAVEPAGE,BH     ; Save current video page
                CMP     AL,3            ; Check for legal video modes
                JBE     VID_OK          ; OK
                CMP     AL,7            ;
                JE      VID_OK          ;
                MOV     AH,SMODE$       ;
                MOV     AL,3            ; Switch to standard text mode
                INT     VIDEO$          ;
                MOV     DISP_LOC,0      ; Don't try to save cleared screen
                JMP     CHECK_VID       ; Get state again
VID_OK:                                 ;
                MOV     AH,RDCRS$       ;
                INT     VIDEO$          ; Save cursor position
                MOV     SAVECRS,DX      ;
                MOV     AL,SCR_LIN      ; Compute display area size
                DEC     AL              ;
                MUL     SCR_COL         ;
                MOV     DISP_SIZE,AX    ;
                SHL     DISP_SIZE,1     ; Room for attributes
                ADD     AX,WORD PTR SCR_COL ;
                INC     AX              ; Don't run right up to end
                SHL     AX,1            ; Words
                NEG     AX              ;
                CMP     DISP_LOC,0      ; Are we saving the screen?
                JNZ     SV_SCRN         ; Yes
                XOR     AX,AX           ; No size required
SV_SCRN:                                ;
                ADD     AX,SP           ; Subtract from size of program
                SUB     AX,100H         ; Leave room for stack
                CMP     DISP_LOC,0      ; Save screen?
                JZ      NO_SVSCR        ; No
                MOV     DISP_LOC,AX     ; Location of screen save area
NO_SVSCR:                               ;
;
; Initialize edit area to contain blanks.
;
                MOV     DI,OFFSET EDIT_LOC ;
                SUB     AX,DI           ; Length of file area
                SUB     AX,WORD PTR SCR_COL ; Leave pad for last line spaces
                SHR     AX,1            ;
                CWD                     ;
                DIV     WORD PTR SCR_COL ; Make multiple of line length
                MUL     WORD PTR SCR_COL ;
                SHL     AX,1            ;
                MOV     CX,AX           ;
                ADD     AX,OFFSET EDIT_LOC-2 ;
                MOV     EDIT_LAST,AX    ;
                SHR     CX,1            ; Number of words
                MOV     AL," "          ;
                MOV     AH,SCR_ATTR     ;
        REP     STOSW                   ;
;
; Save current screen.
;
                CMP     DISP_LOC,0      ; Are we saving the screen?
                JZ      NO_SCRN         ; No
                XOR     AL,AL           ;
                MOV     AH,PAGE$        ; Force page 0
                INT     VIDEO$          ;
                MOV     DI,DISP_LOC     ;
                MOV     CX,DISP_SIZE    ;
                SHR     CX,1            ;
                ADD     CX,WORD PTR SCR_COL ; Full screen size
                XOR     SI,SI           ; Start at top of screen
SAV_LP:                                 ;
                CALL    READ_SCREEN     ;
                STOSW                   ; Save
                LOOP    SAV_LP          ;
NO_SCRN:                                ;
                JMP     BEGIN           ; Start processing edit file
;
;
; PROCEDURE: READ_SCREEN
;
; READ_SCREEN writes a character directly from the video refresh memory.
;
; INPUT:
;       SI      =       Location in video refresh memory to read
;
; OUTPUT:
;       AX      =       Character and attribute
;       SI      =       SI + 2
;       DX contents are destroyed.
;
READ_SCREEN     PROC                    ;
                PUSH    ES              ;
                MOV     ES,VIDEO_SEG    ; Screen segment
                ASSUME  ES:NOTHING      ;
                CMP     FAST,0          ; Fast screen?
                JNZ     FAST_READ       ; Yes
;
; Wait for horizontal retrace before reading.
;
                MOV     DX,STATUS_REG   ; Retrieve status register.
HORZ_RET2:                              ;
                IN      AL,DX           ; Get status from adaptor
                TEST    AL,1            ; Is it low?
                JNZ     HORZ_RET2       ; If not, wait until it is.
                CLI                     ; No more interrupts.
WAIT_RET2:                              ;
                IN      AL,DX           ; Get status.
                TEST    AL,1            ; Is it high?
                JZ      WAIT_RET2       ; If no, wait until it is.
FAST_READ:                              ;
                LODS    WORD PTR ES:[SI] ; to read to screen buffer.
                STI                     ; Interrupts back on.
                POP     ES              ;
                ASSUME  ES:CODE         ;
                RET                     ;
READ_SCREEN     ENDP                    ;
;
;
; PROCEDURE OPEN_FILE
;
; OPEN_FILE parses the next file name in the parameter area and opens
; the file.  If the file is opened for output only, the file is created.
;
; INPUT:
;       AL      =       Open mode to use (0=read, 1=create, 2=update)
;       BX      =       File handle to use if field missing
;       CX      =       Remaining input character count
;       SI      =       Next input character
;
; OUTPUT:
;       AX      =       Return from OPEN (handle or error status)
;       CF      =       1, error
;       CX,SI   Updated
;
OPEN_FILE       PROC                    ;
;
; Save registers used.
;
                PUSH    DX              ;
                PUSH    AX              ;
;
; Find start of file name by skipping any white space characters.
;
FIND_AGAIN:                             ;
                JCXZ    MIS_FIL         ;
FIND_STRT:                              ;
                LODSB                   ; Get next character
                CMP     AL," "          ; Skip leading blanks/tabs
                JE      SKIP_SEP        ;
                CMP     AL,TAB_CHR      ;
                JNE     STRT_FND        ; Found start of filename
SKIP_SEP:                               ;
                LOOP    FIND_STRT       ;
MIS_FIL:                                ; Use default file handle
                POP     AX              ;
                POP     DX              ;
                MOV     AX,BX           ;
                CLC                     ;
                RET                     ;
;
; Found start of file name.  Now find end.
;
STRT_FND:                               ;
                CMP     AL,'/'          ; Option?
                JE      DO_OPT          ; Yes
                LEA     DX,[SI-1]       ; Start of file name
                JMP     SHORT CHK_FIL   ;
FIL_LP:                                 ;
                LODSB                   ;
CHK_FIL:                                ;
                CMP     AL," "          ;
                JE      END_FIL         ; Found end of it
                CMP     AL,","          ;
                JE      END_FIL         ;
                CMP     AL,TAB_CHR      ;
                JE      END_FIL         ;
                CMP     AL,CR_CHR       ;
                JE      END_FIL         ;
                LOOP    FIL_LP          ; Continue scanning
                INC     SI              ;
                INC     CX              ;
;
; Found end of file name.  Open file.
;
END_FIL:                                ;
                DEC     CX              ;
                LEA     AX,[SI-1]       ; Ending location
                CMP     AX,DX           ; No file?
                JE      MIS_FIL         ; Yes, use default
                MOV     BYTE PTR [SI-1],0 ; Make ASCIIZ
                POP     AX              ; Open type
                CMP     AL,1            ; Open for output only?
                JE      CRE_FILE        ; Yes
                MOV     AH,OPEN$        ;
                INT     DOS$            ;
                POP     DX              ;
                RET                     ;
;
; Create a new file.
;
CRE_FILE:                               ;
                PUSH    CX              ;
                XOR     CX,CX           ; Normal file type
                MOV     AH,CREATE$      ; Create the file
                INT     DOS$            ;
                POP     CX              ;
                POP     DX              ;
                RET                     ;
;
; Check for /N = No_save_screen.
;        or /D = Display only (Only ANSI sequences processed.)
;        or /E = Edit screen file
;
DO_OPT:                                 ;
                DEC     CX              ;
                JCXZ    MIS_FIL         ;
                LODSB                   ; Get option letter
                DEC     CX              ;
                AND     AL,NOT 20H      ; Force to upper case
                PUSH    CX              ;
                PUSH    SI              ;
                MOV     CX,L_OPT        ;
                MOV     SI,OFFSET OPT_CHR ;
                CALL    TBL_SRCH        ;
                JMP     OPT_VECT[SI]    ; Switch on option
N_OPT:                                  ;
                MOV     DISP_LOC,0      ; No screen save required
OK_OPT:                                 ;
                POP     SI              ;
                POP     CX              ;
                JMP     FIND_AGAIN      ; Continue scanning
D_OPT:                                  ;
                MOV     DOPT,L_DOPT     ; Display only, do not process
                JMP     OK_OPT          ; control characters
E_OPT:                                  ;
                MOV     EOPT,L_EDIT     ; Set edit flag
                JMP     OK_OPT          ; Force display mode
BAD_OPT:                                ;
                POP     SI              ;
                POP     CX              ;
                POP     AX              ;
                POP     DX              ;
                STC                     ; Error signal
                RET                     ;
OPEN_FILE       ENDP                    ;
MENU            ENDP                    ;
EDIT_LOC        EQU     OUT_BUF+L_OUTB  ; Buffer for editing
CODE            ENDS                    ;
                END     MENU            ;
[ RETURN TO DIRECTORY ]