;          Dirnotes.asm
;  FORMAT: DIRNOTES [d][path][directory]

; *A  10-15-89 WCL ... Chg'd Home,End key defs to ^Home,^End respectively
;     rev 1.10         Added new Home,End,Tab,Sh-Tab,Ins,Del,^T,^Left,^
;                      Right KEY DEFS
;                      Reassembled with TASM 1.0, Linked with TLINK 2.0.

CODE SEGMENT                           ;*************************;
ASSUME CS:CODE,DS:CODE                 ;*                       *;
ORG 100H                               ;*  REMEMBER TO EXE2BIN  *;
                                       ;*                       *;
START:         JMP    BEGINNING        ;*************************;

;              DATA AREA
;              ---------
COPYRIGHT      DB  'Copyright 1987 Ziff-Davis Publishing Co. - Rev 1.10',10,13
PROGRAMMER     DB  'Michael J. Mefford - Updated 10-15-89 by WCL',1AH
DIRNOTES       DB  'DIRN----.DAT',0
STAR_DOT_STAR  DB  '*.*',0

CURRENT_DISK   DB  ?
STATUS_REG     DW  ?
VIDEO_SEG      DW  0B000H
NORMAL         DB  07H
INVERSE        DB  70H

CURSOR_ORG     DW  ?  ;*A    CH & CL registers: cursor height
CURS_POS       DW  ?  ; row,col
CUR_OFFSET     DW  OFFSET BUFFER
END_OFFSET     DW  ?
PAGE_END       DW  403+21*160
COUNT          DW  1
LINE           DW  403

EOF_FLAG       DB  0
UPDATE_FLAG    DB  0
INS_FLAG       DB  1   ;*A    1 = insert mode

NOT_ENOUGH     DB  'Not enough memory$'
INVALID        DB  'Invalid directory$'
TOO_MANY       DB  'Too many files$'
LOADING        DB  'Loading and sorting directory notes.',0
DIRECTORY      DB  'Directory of ',0
FILES          DB  '    Files',0
STATUS_LINE    DB  'Press Esc to exit',0
SAVING         DB  'Saving ',0

;----------------------------------------------------------------------------;
; Some housekeeping first. Since we will be changing the default drive       ;
; and directory to the requested drive and directory, we need to save the    ;
; current defaults, so they can be restored.  If less than 64K, exit.        ;
;----------------------------------------------------------------------------;

;              CODE AREA
;              ---------
BEGINNING:     CLD
               MOV    AH,19H                 ;Get current drive.
               INT    21H
               MOV    CURRENT_DISK,AL        ;And save.

               MOV    SI,OFFSET CURRENT_DIR  ;Get current directory.
               CALL   GET_DIR

               CMP    SP,65500               ;Do we have 64K?
               MOV    DX,OFFSET NOT_ENOUGH
               JA     PARSE
               JMP    ERROR_EXIT             ;If no, exit.

;-----------------------------------------------------------;
; Parse the command line for parameters and append the      ;
; DIRNOTES filename with the characters of directory name.  ;
;-----------------------------------------------------------;

PARSE:         CMP    BYTE PTR DS:[80H],0    ;Any parameters?
               JZ     APPEND                 ;If no, skip parse.

               MOV    SI,81H                 ;Else, point to first character.
NEXT_PARSE:    LODSB
               CMP    AL,13                  ;Carriage return?
               JZ     APPEND                 ;If yes, done here.
               CMP    AL,32                  ;Leading space?
               JBE    NEXT_PARSE             ;If yes, get next byte.
               PUSH   SI                     ;Save start.

NEXT_PARA:     LODSB
               CMP    AL,13                  ;End of parameter?
               JZ     END_PARA               ;If yes, done here.
               CMP    AL,':'                 ;Drive request?
               JNZ    NEXT_PARA              ;If no, get next byte.
               MOV    DL,BYTE PTR [SI-2]     ;Else, retrieve request.
               AND    DL,5FH                 ;Capitalize.
               SUB    DL,'A'                 ;Convert to DOS format.
               MOV    AH,0EH                 ;And change drive.
               INT    21H
               JMP    SHORT NEXT_PARA        ;Find end of parameter.

END_PARA:      MOV    BYTE PTR DS:[SI-1],0   ;Convert parameter to ASCIIZ.
               POP    DX                     ;Retrieve start.
               DEC    DX                     ;Adjust.
               MOV    AH,3BH                 ;Change directory.
               INT    21H
               MOV    DX,OFFSET INVALID      ;Exit if invalid parameter.
               JNC    APPEND
               JMP    ERROR_EXIT

APPEND:        MOV    SI,80H                 ;Get default directory.
               CALL   GET_DIR
               CLD
NEXT_END:      LODSB                         ;Find end.
               CMP    AL,0
               JNZ    NEXT_END
               DEC    SI
               STD                           ;Reverse direction.
NEXT_START:    AND    BYTE PTR [SI],5FH      ;Capitalize.
               LODSB
               CMP    AL,'\'                 ;Look for last path.
               JNZ    NEXT_START
               CLD                           ;Back to forward direction.
               INC    SI
               INC    SI
               MOV    DI,OFFSET DIRNOTES+5   ;Insert three characters of path
               MOV    CX,3                   ; into filename, DIRN----.DAT
STORE_PATH:    CMP    BYTE PTR [SI],0
               JZ     DISPLAYI
               MOVSB
               LOOP   STORE_PATH

;---------------------------------------------------------------------;
; More housekeeping. We will be writing directly to the screen buffer ;
; so we need the display card address and the status register.        ;
;---------------------------------------------------------------------;

DISPLAYI:      MOV    AX,40H           ;Point to the ROM BIOS data area
               MOV    DS,AX            ; and get base address of active
               MOV    AX,DS:[63H]      ; display card.
               ADD    AX,6             ;Add six to get status register
               PUSH   CS               ;Done there, so restore data segment.
               POP    DS
               MOV    STATUS_REG,AX    ;Store status register.
               CMP    AX,3BAH          ;Status port of MONO card is 3BAh.
               JZ     MESSAGE          ;If that's what we got, it's MONO
               MOV    VIDEO_SEG,0B800H       ; else COLOR so add 800h.
               XOR    BH,BH                  ;Get current attribute
               MOV    AH,8                   ; of display page zero.
               INT    10H
               MOV    NORMAL,AH              ;Store it.
               XOR    AH,1110111B            ;Flip color bits.
               MOV    INVERSE,AH             ;Save it.

MESSAGE:       CALL   CLS
               MOV    SI,OFFSET LOADING      ;Display loading message.
               MOV    DX,0C15H
               CALL   DISPLAY_TEXT

               MOV    DI,OFFSET BUFFER       ;Fill buffer with spaces.
               MOV    CX,30000
               MOV    AX,2020H
               REP    STOSW

;------------------------------------------------------------------;
; Read all the directory filenames and store as records in buffer. ;
;------------------------------------------------------------------;

READ_DIR:      MOV    DX,OFFSET STAR_DOT_STAR
               MOV    CX,6
               MOV    AH,4EH                 ;Find first matching.
               INT    21H
               JNC    STORE_NAME
               JMP    EXIT                   ;If empty directory, exit.

STORE_NAME:    MOV    DI,OFFSET BUFFER       ;Set up pointers and store
               MOV    BP,60000               ; first filename.
               CALL   BUFFER_NAME

FIND_NEXT:     MOV    AH,4FH                 ;Find next matching.
               INT    21H
               JC     STORE_COUNT            ;If carry, no more names.
               INC    COUNT                  ;Inc count of names.
               CALL   BUFFER_NAME
               CMP    DI,BP                  ;Are we encroaching stack?
               JB     FIND_NEXT              ;If no, find next.
               MOV    DX,OFFSET TOO_MANY     ;Else, exit with message.
               JMP    ERROR_EXIT

;---------------------------------------------;
; Store buffer end address and page end,      ;
; number of files then alphabetize filenames. ;
;---------------------------------------------;

STORE_COUNT:   MOV    END_OFFSET,DI          ;Store ending offset.
               MOV    BX,COUNT

               MOV    AX,BX
               MOV    CL,10                  ;Convert to decimal.
               STD                           ;Reverse direction.
               MOV    DI,OFFSET FILES+2      ;Point to storage.
NEXT_COUNT:    DIV    CL
               XCHG   AL,AH
               ADD    AL,'0'                 ;Convert to ASCII.
               STOSB                         ;Store the remainder.
               XCHG   AL,AH
               XOR    AH,AH
               CMP    AX,0                   ;Are we done?
               JNZ    NEXT_COUNT

               CLD                           ;Back to forward direction.
               CMP    BX,20                  ;Enough to file one page?
               JA     SORT                   ;If yes, use default setting.
               MOV    AX,160                 ;Else, calculate last record.
               MUL    BL
               ADD    AX,403                 ;Add bar offset.
               MOV    PAGE_END,AX
               CMP    BX,1                   ;Skip sort if only one filename.
               JZ     OPEN_FILE

SORT:          MOV    DX,END_OFFSET          ;End of filenames in DX.
               SUB    DX,81
NEXT_PASS:     MOV    BP,0
               MOV    BX,OFFSET BUFFER       ;Point to start of buffer.

NEXT_SORT:     MOV    SI,BX                  ;Put in source and destination
               MOV    DI,BX                  ; registers.
               ADD    DI,81
               MOV    CX,12
               REPZ   CMPSB                  ;Compare filenames.
               JBE    END_SORT               ;If already in order, skip.

               MOV    SI,BX                  ;Else, recover pointers.
               MOV    DI,BX
               ADD    DI,81
               MOV    CX,40                  ;Exchange the records.
NEXT_SWAP:     MOV    AX,[DI]
               MOVSW
               MOV    [SI-2],AX
               LOOP   NEXT_SWAP
               MOV    BP,1                   ;Flag that exchange was made.

END_SORT:      ADD    BX,81                  ;Point to next record.
               CMP    BX,DX                  ;End of top?
               JB     NEXT_SORT              ;If no, bubble sort next.
               CMP    BP,0                   ;Was there exchange made?
               JZ     OPEN_FILE              ;If no, done here.
               SUB    DX,81                  ;Else, move top down one record.
               JMP    SHORT NEXT_PASS

;-------------------------------;
; Attempt to read old dirnotes. ;
;-------------------------------;

OPEN_FILE:     MOV    DX,OFFSET DIRNOTES     ;Open DIRNOTES
               MOV    AX,3D00H               ; for reading.
               INT    21H
               JC     READY                  ;If not found, skip to display.

               MOV    BX,AX
               PUSH   BX                     ;Save filehandle.
READ_FILE:     POP    BX
               PUSH   BX
               MOV    DX,60000               ;Point above directory listing.
               MOV    CX,37*81               ;Read up to 37 records at a time.
               MOV    AH,3FH
               INT    21H

               CMP    AX,0                   ;End of file?
               JZ     CLOSE_FILE             ;If yes, done here.
               ADD    DX,AX
               MOV    DI,DX                  ;Else, point to end and tack
               MOV    BYTE PTR [DI],1AH      ; on Ctrl Z as end signature.
               CMP    AX,37*81               ;Was it a full read?
               JZ     COMPARE                ;If yes, compare records.
               MOV    EOF_FLAG,1             ;Else, flag as end of file.

;-------------------------------------------------------;
; Here we will match old DIRNOTES with new directory    ;
; listing. Notes for deleted files will not find match. ;
;-------------------------------------------------------;

COMPARE:       MOV    BP,OFFSET BUFFER       ;Point to first record.
NEXT_FILE:     MOV    BX,60000               ;Point to read buffer.
NEXT_MATCH:    MOV    SI,BX                  ;Set up source and destination.
               MOV    DI,BP
               MOV    CX,6                   ;Filename with extension.
               REPZ   CMPSW                  ;Compare all 12 characters.
               JNZ    END_NOTE               ;Skip if no match.
               ADD    SI,28                  ; else point to note.
               ADD    DI,28
               MOV    CX,20
               REP    MOVSW                  ;Store note.

               SUB    SI,67
               SUB    DI,67
               MOV    CX,13
               REPZ   CMPSW                  ;Has size or date changed?
               JNZ    END_MATCH
               MOV    BYTE PTR [BP+39],32    ;If yes, remove "U".
               JMP    SHORT END_MATCH        ;Skip rest and go to next record.

END_NOTE:      ADD    BX,81                  ;Point to next record.
               CMP    BYTE PTR DS:[BX],1AH   ;Are we at the end?
               JNZ    NEXT_MATCH             ;If no, compare.
END_MATCH:     ADD    BP,81                  ;Point to next record.
               CMP    BYTE PTR DS:[BP],32    ;End of directory listing?
               JNZ    NEXT_FILE              ;If no, check for matches.
               CMP    EOF_FLAG,1             ;Else, end of file?
               JNZ    READ_FILE              ;If no, read more.

CLOSE_FILE:    POP    BX
               MOV    AH,3EH                 ;Close file.
               INT    21H

;--------------------------------------------;
; Now, we are ready to initialize the screen ;
;--------------------------------------------;

READY:         MOV    AX,VIDEO_SEG           ;Initialize video segment.
               MOV    ES,AX
               MOV    DX,4                   ;Row 0; column 3.
               MOV    SI,OFFSET DIRECTORY    ;Display "Directory ".
               CALL   DISPLAY_TEXT
               MOV    AH,19H
               INT    21H                    ;Get drive.
               ADD    AL,'A'                 ;Convert to ASCII.
               CALL   WRITE_TEXT             ;Display it.
               MOV    AL,':'                 ;Add colon.
               CALL   WRITE_TEXT
               MOV    SI,80H                 ;Get directory.
               CALL   GET_DIR
               DEC    SI
               CALL   GET_TEXT               ;Write it as well.
               MOV    DX,180EH               ;Row 24; column 13.
               MOV    SI,OFFSET FILES        ;Display file count.
               CALL   DISPLAY_TEXT
               MOV    DX,1833H               ;Row 24; column 50.
               MOV    SI,OFFSET STATUS_LINE  ;Display "Press Esc to exit".
               CALL   DISPLAY_TEXT
               MOV    BL,INVERSE             ;Put up cursor bar.
               CALL   BAR
               CALL   UPDATE_SCREEN          ;Display directory listing.
               MOV    CURS_POS,229H          ;Initialize cursor position.
               MOV    AH,03                  ;*A
               MOV    BH,0                   ;*A
               INT    10H                    ;*A Get original cursor size
               MOV    CURSOR_ORG,CX          ;*A
               CALL   SET_CUR_SIZE           ;*A Set cursor size

;-----------------------------------------;
; We are ready for business now. We will  ;
; loop here, waiting for user keystrokes. ;
;-----------------------------------------;

GET_KEY:       CALL   SET_CURSOR             ;Update cursor position.
               MOV    AH,0                   ;Wait for
               INT    16H                    ;keystroke.

ASCII:         CMP    AL,32                  ;Is it space or above?
               JB     CR                     ;If no, skip.
               CMP    BYTE PTR CURS_POS,79   ;End of line? Line:42-79 physical
               JZ     GET_KEY                ;If yes, skip.     41-78 logical
               CMP    INS_FLAG, 1            ;Insert mode on?          *A
               JNZ    WRT_ASCII              ;Jmp if not               *A
               CALL   PUSHEM_OUT             ;Else push out characters *A
WRT_ASCII:     CALL   STORE_CHAR             ;Else, store the ASCII character.
               INC    BYTE PTR CURS_POS      ;Update cursor (pnts to next loc)
               JMP    SHORT GET_KEY

CR:            CMP    AH,1CH                 ;Is it carriage return?
               JNZ    BS
               MOV    BYTE PTR CURS_POS,41   ;Cursor to beginning of line.
               JMP    SCROLL_DOWN      ; and scroll down.

BS:            CMP    AH,0EH                 ;Backspace?
               JNZ    TAB
               CMP    BYTE PTR CURS_POS,41   ;Are we already at beginning?
               JZ     GET_KEY                ;If yes, skip.
               DEC    BYTE PTR CURS_POS      ;Else, cursor left one.
               MOV    AL,32                  ;And replace with space.
               CALL   STORE_CHAR
               JMP    SHORT GET_KEY

TAB:           CMP    AH,0FH                 ;Tab?  *A
               JNZ    LEFT_ARROW
               CMP    AL,00H
               JZ     SHFT_TAB
               CMP    BYTE PTR CURS_POS,79   ;Are we already end of line?
               JZ     GET_KEY
               ADD    BYTE PTR CURS_POS,5    ;Cursor right.
               CMP    BYTE PTR CURS_POS,79   ;Are we past end of line?
               JBE    EXIT_TAB
               MOV    BYTE PTR CURS_POS,79
EXIT_TAB:      JMP    SHORT GET_KEY

SHFT_TAB:                                    ;Sh-Tab?  *A
               CMP    BYTE PTR CURS_POS,41   ;Are we already start of line?
               JZ     GET_KEY
               SUB    BYTE PTR CURS_POS,5    ;Cursor left.
               CMP    BYTE PTR CURS_POS,41   ;Are we before start of line?
               JAE    EXIT_SH_TAB            ;Jump if not
               MOV    BYTE PTR CURS_POS, 41
EXIT_SH_TAB:   JMP    GET_KEY

LEFT_ARROW:    CMP    AH,4BH                 ;Left arrow?
               JNZ    RIGHT_ARROW
               CMP    BYTE PTR CURS_POS,41   ;Are we already home position?
               JZ     LEFT_EXIT
               DEC    BYTE PTR CURS_POS      ;If no, back cursor up one.
LEFT_EXIT:     JMP    GET_KEY

RIGHT_ARROW:   CMP    AH,4DH                 ;Right arrow?
               JNZ    CTL_LFT_ARR
               CMP    BYTE PTR CURS_POS,79   ;Are we already end of line?
               JZ     SKIP_CHK
               INC    BYTE PTR CURS_POS      ;Cursor right one.
SKIP_CHK:      JMP    GET_KEY

CTL_LFT_ARR:   CMP    AH,73H                 ;^Left arrow?
               JNZ    CTL_RT_ARR
               INC    BYTE PTR CURS_POS
CHAR_L:        CALL   LOOPER_LEFT
               JAE    CHAR_L
NON_CHAR_L:    CALL   LOOPER_LEFT
               JB     NON_CHAR_L
CHAR_L2:       CALL   LOOPER_LEFT
               JAE    CHAR_L2
               INC    BYTE PTR CURS_POS
EXIT_CTL_L:    JMP    GET_KEY

LOOPER_LEFT:
               CMP    BYTE PTR CURS_POS,41
               JZ     EXIT_CTL_L
               DEC    BYTE PTR CURS_POS
               CALL   GET_CHAR
               CMP    BL,48
               RET

CTL_RT_ARR:    CMP    AH,74H                 ;^Right arrow? *A
               JNZ    UP_ARROW
               DEC    BYTE PTR CURS_POS
CHAR_R:        CALL   LOOPER_RT
               JAE    CHAR_R
               MOV    BH,BYTE PTR CURS_POS   ;Location of last non_char(space)
NON_CHAR_R:    CALL   LOOPER_RT
               JB     NON_CHAR_R
CHK_END:       CMP    BYTE PTR CURS_POS,79
               JB     EXIT_CTL_R1
               DEC    BYTE PTR CURS_POS      ;Point to last logical char
               CALL   GET_CHAR
               CMP    BL,32
               JNZ    EXIT_CTL_R0
               INC    BH
               MOV    BYTE PTR CURS_POS,BH
FIND_CHAR:     CALL   LOOPIT_LEFT
               JZ     FIND_CHAR
EXIT_CTL_R0:   INC    BYTE PTR CURS_POS
EXIT_CTL_R1:   JMP    GET_KEY

LOOPER_RT:     CMP    BYTE PTR CURS_POS,79
               JZ     CHK_END
               INC    BYTE PTR CURS_POS
               CALL   GET_CHAR
               CMP    BL,48
               RET

LOOPIT_LEFT:
               CMP    BYTE PTR CURS_POS,41
               JZ     EXIT_CTL_R1
               DEC    BYTE PTR CURS_POS
               CALL   GET_CHAR
               CMP    BL,32
               RET

UP_ARROW:      CMP    AH,48H                 ;Up arrow?
               JNZ    DN_ARROW
               MOV    BP,-160                ;If yes, move bar up one line.
               MOV    DX,0FF00H              ;And also the cursor.
               CALL   SCROLL_BAR
               JMP    GET_KEY

DN_ARROW:      CMP    AH,50H                 ;Down arrow?
               JNZ    HOME
SCROLL_DOWN:   MOV    BP,160                 ;If yes, move cursor and bar down
               MOV    DX,100H
               CALL   SCROLL_BAR
               JMP    GET_KEY

HOME:          CMP    AH,47H                 ;Home key?  *A
               JNZ    END_KEY
SET_MIN_POS:   MOV    BYTE PTR CURS_POS, 41
               JMP    GET_KEY

END_KEY:       CMP    AH,4FH                 ;End key?   *A
               JNZ    PG_UP
SET_END_POS:   MOV    BYTE PTR CURS_POS, 79
LOOP_END_POS:  CMP    BYTE PTR CURS_POS, 41
               JZ     EXIT_END_POS
               DEC    BYTE PTR CURS_POS
               CALL   GET_CHAR
               CMP    BL,32
               JZ     LOOP_END_POS
               INC    BYTE PTR CURS_POS
EXIT_END_POS:  JMP    GET_KEY

PG_UP:         CMP    AH,49H                 ;Page up?
               JNZ    PG_DN
               MOV    BP,-81*21              ;If yes, move up 21 lines.
               CALL   SCROLL
               JMP    SHORT BOTTOM_BAR       ;And move bar to bottom.

PG_DN:         CMP    AH,51H                 ;Page down?
               JNZ    CTRL_PG_UP
               MOV    BP,81*21               ;If yes, move down 21 lines.
               CALL   SCROLL
               JMP    SHORT TOP_BAR          ;And move bar to top.

CTRL_PG_UP:    CMP    AH,84H                 ;Ctrl PgUp?
               JNZ    CTRL_PG_DN
TOP_BAR:       MOV    SI,403                 ;If yes, move bar to top.
               MOV    CURS_POS,229H
               JMP    SHORT UPDATE_BAR

CTRL_PG_DN:    CMP    AH,76H                 ;Ctrl PgDn?
               JNZ    CTRL_HOME
BOTTOM_BAR:    MOV    SI,PAGE_END            ;If yes, move bar to bottom.
               MOV    AX,SI                  ;Divide page end by 160
               SUB    SI,160                 ; to get cursor row position.
               SUB    AX,403-160
               XOR    DX,DX
               MOV    BX,160
               DIV    BX
               MOV    DH,AL
               MOV    DL,29H
               MOV    CURS_POS,DX
UPDATE_BAR:    CALL   MOVE_BAR               ;Display updates.
               CALL   SET_CURSOR
               CALL   UPDATE_SCREEN
               JMP    NEXT_KEY

CTRL_HOME:     CMP    AH,77H                     ;^Home?  *A
               JNZ    CTRL_END
               MOV    CUR_OFFSET,OFFSET BUFFER   ;If yes, move listing and
               JMP    SHORT TOP_BAR              ; bar to top.

CTRL_END:      CMP    AH,75H                 ;^End? *A
               JNZ    INS_KEY
               MOV    BX,END_OFFSET          ;If yes, move listing and
               SUB    BX,81*21               ; bar to bottom.
               CMP    BX,OFFSET BUFFER
               JBE    BOTTOM_BAR
               MOV    CUR_OFFSET,BX
               JMP    SHORT BOTTOM_BAR

INS_KEY:       CMP    AH,52H                 ;Insert?  *A
               JNZ    DEL
               XOR    BYTE PTR INS_FLAG,1
               CALL   SET_CUR_SIZE
               JMP    GET_KEY

DEL:           CMP    AH,53H                 ;Del?  *A
               JNZ    CTRL_T
               CALL   DEL_CHAR
               JMP    GET_KEY

CTRL_T:        CMP    AH,14H                 ;Ctrl T? *A
               JNZ    CTRL_Y
               MOV    BH,80
               SUB    BH,BYTE PTR CURS_POS
               CALL   GET_CHAR
               CMP    BL,32
               JZ     DEL_A_SPACE
DEL_A_CHAR:    DEC    BH
               CMP    BH,0
               JZ     EXIT_T
               CALL   DEL_CHAR
               CALL   GET_CHAR
               CMP    BL,32
               JNZ    DEL_A_CHAR
DEL_A_SPACE:   DEC    BH
               CMP    BH,0
               JZ     EXIT_T
               CALL   DEL_CHAR
               CALL   GET_CHAR
               CMP    BL,32
               JZ     DEL_A_SPACE
EXIT_T:        JMP    GET_KEY

CTRL_Y:        CMP    AH,15H                 ;Ctrl-Y?  *A
               JNZ    IS_ESC
               MOV    BYTE PTR CURS_POS,41
               MOV    BH, 39
EXIT_DEL_CHR:  CALL   DEL_CHAR
               DEC    BH
               CMP    BH,0
               JNZ    EXIT_DEL_CHR
               JMP    GET_KEY

IS_ESC:        CMP    AH,1                   ;Esc?
               JNZ    NEXT_KEY
               JMP    EXIT                   ;If yes, exit.

NEXT_KEY:      JMP    GET_KEY

                ;*************;
                ; SUBROUTINES ;
                ;*************;

;--------------------------------------;
; This subroutine sets the cursor size ;
;--------------------------------------;

SET_CUR_SIZE:
               CMP    BYTE PTR INS_FLAG,1
               JZ     INSERT_ON
               MOV    CX,CURSOR_ORG
               JMP    SHORT DO_TEN
INSERT_ON:     MOV    CX,080CH
DO_TEN:        MOV    AH,01H
               INT    10H
               RET

;-------------------------------------;
; This subroutine scrolls the screen. ;
;-------------------------------------;

SCROLL:        MOV    SI,CUR_OFFSET          ;Get current offset.
               ADD    SI,BP                  ;Add requested direction.
               JNS    CK_LOWER               ;If signed and PgUp request
               CMP    BP,-81*21              ; then below start.
               JZ     LOWER_LIMIT
CK_LOWER:      CMP    SI,OFFSET BUFFER       ;If above start check upper limit
               JAE    UPPER_LIMIT
LOWER_LIMIT:   MOV    CUR_OFFSET,OFFSET BUFFER    ;Else, make it start.
               JMP    SHORT UPDATE                ;And update screen.

UPPER_LIMIT:   MOV    BX,END_OFFSET               ;See if beyond end of
               CMP    BX,OFFSET BUFFER+21*81      ; directory listing as well.
               JA     CK_UPPER
               MOV    CUR_OFFSET,OFFSET BUFFER
               JMP    SHORT UPDATE

CK_UPPER:      SUB    BX,21*81
               CMP    SI,BX
               JBE    END_SCROLL
               MOV    SI,BX

END_SCROLL:    MOV    CUR_OFFSET,SI          ;Update current offset.
UPDATE:        CALL   UPDATE_SCREEN
               RET

;--------------------------------------------------;
; This subroutine scrolls the bar if between start ;
; and end of page. Otherwise the page is scrolled. ;
;--------------------------------------------------;

SCROLL_BAR:    MOV    SI,LINE                ;Get current line.
               ADD    SI,BP                  ;Add requested line.
               MOV    BP,-81                 ;Assume below beginning.
               CMP    SI,403                 ;Is it?
               JB     SCROLL_PAGE            ;If yes, scroll page instead.
               MOV    BP,81                  ;Do the same for end of page.
               CMP    SI,PAGE_END
               JAE    SCROLL_PAGE
               ADD    CURS_POS,DX            ;If in range, update cursor
               CALL   MOVE_BAR               ; and bar position.
               RET

SCROLL_PAGE:   CALL   SCROLL
               RET

;----------------------------------------------------;
; This subroutine does the actual moving of the bar. ;
;----------------------------------------------------;

MOVE_BAR:      MOV    BL,NORMAL              ;Remove old bar.
               CALL   BAR
               MOV    LINE,SI                ;And move bar to new line.
               MOV    BL,INVERSE
               CALL   BAR
               RET

BAR:           MOV    DI,LINE                ;Retrieve line.
               MOV    BH,38                  ;Bar length 39.
               MOV    DX,STATUS_REG
NEXT_BAR:      MOV    CX,1                   ;Write one character at a time.
               CALL   HORZ_RET
               DEC    BH
               JNZ    NEXT_BAR
               RET

;----------------------------------------------;
; This subroutine stores the ASCII characters. ;
;    AL = character to store                   ;
;----------------------------------------------;

STORE_CHAR:
               CALL   GET_CHAR_LOC           ;*A
               MOV    [SI],AL                ;Store it.
               MOV    CX,1                   ;Write one character
               CALL   WRITE_SCREEN           ;Display by updating screen.
               MOV    UPDATE_FLAG,1          ;Flag as updated so file will
               RET                           ; be written upon Esc.

;------------------------------------------------;
; This subroutine retrieves the ASCII character. ;
;    BL = character to retrieve                  ;
;------------------------------------------------;

GET_CHAR:      CALL   GET_CHAR_LOC           ;*A
               MOV    BL,DS:[SI]             ;Retrieve it.
               RET                           ;

;------------------------------------------------;
; This subroutine deletes an ASCII character.    ; *A
;------------------------------------------------;

DEL_CHAR:
               CMP    BYTE PTR CURS_POS,79       ; Past last char?
               JAE    EXIT_DEL_CHAR              ; Skip del if so, else
               MOV    AX, CURS_POS               ;
               PUSH   AX                         ; Save cursor position
NEXTDEL:       INC    BYTE PTR CURS_POS          ; Point to next position
               CMP    BYTE PTR CURS_POS,79       ; Is it past the last char?
               JB     GETIT                      ; Jump if not
               MOV    AL,32                      ; else fill last char with
                                                 ;    a space
               JMP    SHORT MOVIT
GETIT:         CALL   GET_CHAR                   ; Get char at next location
               MOV    AL,BL                      ; mov it into AL
MOVIT:         DEC    BYTE PTR CURS_POS          ; Point to current del char
               CALL   STORE_CHAR                 ; Put new char there, show it
               INC    BYTE PTR CURS_POS          ; Point to next del position
               CMP    BYTE PTR CURS_POS,79       ; Past the last char yet?
               JB     NEXTDEL                    ; No, continue processing
               POP    AX                         ; Yes, restore old crsr pos
               MOV    CURS_POS,AX
EXIT_DEL_CHAR: RET

;------------------------------------------------;
; This subroutine push out trailing characters   ; *A
;------------------------------------------------;

PUSHEM_OUT:
               CMP    BYTE PTR CURS_POS,78       ; At or past last valid char?
               JAE    PUSH_RET                   ; Yes, Can't push any further
               PUSH   AX                         ; Save current charcter
               MOV    AX, CURS_POS               ;
               PUSH   AX                         ; Save cursor position
               MOV    BYTE PTR CURS_POS,78       ;
NEXTADD:
               POP    AX                         ; Restore cursor postion
               DEC    BYTE PTR CURS_POS          ; Backup one character
               CMP    BYTE PTR CURS_POS,AL       ; Is it prior to start loc?
               PUSH   AX
               JB     PUSH_RESTORE               ; Jump if not
               CALL   GET_CHAR                   ; and get char at that location
               MOV    AL,BL                      ; mov it into AL
               INC    BYTE PTR CURS_POS          ; Point to next char
               CALL   STORE_CHAR                 ; Put previous char there and
                                                 ;      show it
               DEC    BYTE PTR CURS_POS          ; Backup one character
               JMP    NEXTADD
PUSH_RESTORE:
               POP    AX
               MOV    CURS_POS,AX
               POP    AX
PUSH_RET:      RET


;----------------------------------------------------------------; *A
; This subroutine causes [SI] to point to the current character  ;
;   Destroyed: CX,DL                                             ;
;----------------------------------------------------------------;

GET_CHAR_LOC:                                ;
               PUSH   AX                     ;Save AX.
               MOV    SI,CUR_OFFSET          ;Retrieve current starting offset
               MOV    AX,CURS_POS            ;Retrieve cursor position.
               MOV    CX,AX                  ;Save it.
               MOV    AL,AH
               XOR    AH,AH                  ;Isolate row.
               DEC    AX                     ;Adjust for offset.
               DEC    AX
               ADD    SI,AX                  ;Add to source.
               MOV    DL,80                  ;Multiply by 80.
               MUL    DL
               ADD    SI,AX                  ;Add to source.
               MOV    DI,AX                  ;DI is screen offset.
               SHL    DI,1                   ;Adjust for attribute.
               ADD    DI,2*160               ;Adjust for starting in row 3.
               XOR    CH,CH                  ;Isolate column.
               ADD    SI,CX                  ;Add column to offset.
               SHL    CX,1                   ;Double for screen offset.
               ADD    DI,CX                  ;Add it.  = screen offset
               POP    AX                     ;Retrieve AX.
               RET


;---------------------------------------------------;
; This subroutine writes the listing to the screen. ;
;---------------------------------------------------;

UPDATE_SCREEN: MOV    SI,CUR_OFFSET          ;Retrieve starting offset.
               MOV    DI,2*160               ;Point to row three of screen.
               MOV    BH,21                  ;21 lines to write.
NEXT_WRITE:    MOV    CX,79                  ;79 characters per line.
               CALL   WRITE_SCREEN           ;Write them.
               ADD    SI,2                   ;Bump pointer past cr/lf.
               ADD    DI,2                   ;Bump pointer to next line.
               DEC    BH                     ;Do all 21 lines.
               JNZ    NEXT_WRITE
               RET

;------------------------------------------------------------;
; This subroutine displays the directory by writing directly ;
; to the screen buffer. To avoid screen noise (snow) on the  ;
; color card, the horizontal retrace has to be monitored.    ;
;   CX = number of characters to write (79 max)              ;
;   DI = screen position                                     ;
;  Destroyed: BL, AL, DI                                     ;
;------------------------------------------------------------;

WRITE_SCREEN:
               MOV    DX,STATUS_REG          ;Get status register.
NEXT_BYTE:     LODSB                         ;Get a byte.
               MOV    BL,AL                  ;Save it in BL.

HORZ_RET:      IN     AL,DX                  ;Get status.
               TEST   AL,1                   ;Is it low?
               JNZ    HORZ_RET               ;If not, wait until it
               CLI                           ;No more interrupts.

DO_WAIT:       IN     AL,DX                  ;Get status.
               TEST   AL,1                   ;Is it high?
               JZ     DO_WAIT                ;If no, wait until it is.
               MOV    AL,BL                  ;Retrieve character; now it's OK
               STOSB                         ; to write to screen buffer.
               STI                           ;Interrupts back on.
               INC    DI                     ;Bump pointer past attribute.
               LOOP   NEXT_BYTE              ;Get next byte.
               RET                           ;Return

;------------------------------------;
; This subroutine clears the screen. ;
;------------------------------------;

CLS:           MOV    BH,NORMAL              ;Clear with original attribute.
               XOR    CX,CX
               MOV    DX,184FH               ;Entire screen.
               MOV    AX,600H                ;Scroll active page.
               INT    10H
               RET                           ;Return.

;-----------------------------------------;
; These subroutines display the messages. ;
;-----------------------------------------;

DISPLAY_TEXT:  MOV    CURS_POS,DX            ;Store requested cursor position.
               CALL   SET_CURSOR             ;Move cursor.
GET_TEXT:      LODSB
               CMP    AL,0                   ;Zero marks end of string.
               JZ     END_TEXT
               CALL   WRITE_TEXT
               JMP    SHORT GET_TEXT
END_TEXT:      RET

WRITE_TEXT:    PUSH   SI                     ;BIOS does not save SI.
               MOV    AH,0EH                 ;Write teletype
               INT    10H
               POP    SI
               RET

;----------------------------------------------------------------------;
; These two subroutines move the cursor and get the current directory. ;
;----------------------------------------------------------------------;

SET_CURSOR:    PUSH   SI                     ;Save SI pointer; BIOS doesn't.
               MOV    DX,CURS_POS            ;Get requested cursor position.
               XOR    BH,BH                  ;Page zero.
               MOV    AH,2
               INT    10H
               POP    SI
               RET

GET_DIR:       MOV    BYTE PTR [SI],'\'      ;DOS doesn't preface directory
               INC    SI                     ; with slash so we must.
               XOR    DL,DL
               MOV    AH,47H                 ;Retrieve default directory.
               INT    21H
               RET

;--------------------------------------------------;
; This long subroutine stores the filename in DIR  ;
; format. That is, filename, bytes, date and time. ;
;--------------------------------------------------;

BUFFER_NAME:   MOV    SI,158                 ;Point to filename.
               MOV    CX,12                  ;Store 12 bytes of filename.
NEXT_STORE:    LODSB                         ;Get a byte.
               CMP    AL,0                   ;End of filename?
               JZ     END_STORE              ;If yes, finish with blanks.
               CMP    AL,'.'                 ;Is it the period?
               JNZ    STORE_BYTE             ;If no, store.
               SUB    CX,3                   ;Else store 3 spaces.
               MOV    AL,32
               REP    STOSB
               ADD    CX,3
               JMP    SHORT NEXT_STORE       ;Get next byte.

STORE_BYTE:    STOSB                         ;Store byte.
               LOOP   NEXT_STORE             ;Get next byte.
END_STORE:     MOV    AL,32                  ;Pad balance with spaces.
               REP    STOSB

FILE_SIZE:     PUSH   DI                     ;Save pointer.
               ADD    DI,8                   ;Move to end of bytes field.
               MOV    DX,DS:[154]            ;Retrieve high and low words
               MOV    AX,DS:[156]            ; of bytes.
               MOV    BX,10                  ;Convert to decimal; divide by 10
               STD                           ;Reverse direction.

NEXT_SIZE:     MOV    CX,DX                  ;Low word in CX.
               XOR    DX,DX                  ;Zero in high half.
               DIV    BX                     ;Convert to decimal.
               XCHG   AX,CX                  ;Retrieve low word.
               DIV    BX
               XCHG   AX,DX                  ;Retrieve remainder.
               ADD    AL,'0'                 ;Convert to ASCII.
               STOSB                         ;Store it.
               MOV    AX,CX                  ;Are we done?
               OR     CX,DX
               JNZ    NEXT_SIZE              ;If no, divide again.

               CLD                           ;Back to forward direction.
               POP    DI                     ;Retrieve pointer.
               ADD    DI,11                  ;Move to date field.
DATE:          MOV    DX,DS:[152]            ;Retrieve date.
               MOV    AX,DX
               MOV    CL,5                   ;Shift to lowest bits.
               ROR    AX,CL
               AND    AX,0FH                 ;Mask off all but month.
               MOV    CL,0FFH                ;Flag as no leading zeros.
               MOV    CH,'-'                 ;Delimiting character.
               CALL   STORE_WORD             ;Store it.

               MOV    AX,DX                  ;Retrieve date.
               AND    AX,1FH                 ;Mask off all but day.
               MOV    CL,0                   ;Flag include leading zeros.
               MOV    CH,'-'
               CALL   STORE_WORD             ;Store it.

               MOV    AX,DX                  ;Retrieve date for last time.
               MOV    CL,9
               ROR    AX,CL
               AND    AX,7FH                 ;Mask off all but year.
               ADD    AX,80                  ;Adjust to ASCII.
               CMP    AX,100                 ;Past year 2000?
               JB     DISPLAY_DATE           ;If no, display. Else, adjust for
               SUB    AX,100                 ; next century. (Planning ahead!)
DISPLAY_DATE:  MOV    CL,0                   ;Display leading zeros.
               MOV    CH,32
               CALL   STORE_WORD             ;Store it.

TIME:          INC    DI                     ;Move to time field.
               MOV    DX,DS:[150]            ;Retrieve time.
               MOV    AX,DX
               MOV    CL,11                  ;Shift to hours bits.
               ROR    AX,CL
               AND    AX,1FH                 ;Mask off all but hours.
               PUSH   AX
               CMP    AX,12                  ;Past noon?
               JBE    MERIDIAN
               SUB    AX,12                  ;If yes, adjust.
MERIDIAN:      CMP    AX,0                   ;Midnight?
               JNZ    NOT_MIDNIGHT
               MOV    AX,12                  ;If yes, adjust.
NOT_MIDNIGHT:  MOV    CL,0FFH                ;Suppress leading zeros.
               MOV    CH,':'
               CALL   STORE_WORD             ;Store it.

               MOV    AX,DX                  ;Retrieve time.
               MOV    CL,5                   ;Shift to minutes bits.
               ROR    AX,CL
               AND    AX,3FH                 ;Mask off all but minutes.
               MOV    CL,0
               POP    DX                     ;Retrieve hours.
               MOV    CH,'p'                 ;Assume PM.
               CMP    DX,12                  ;Is it PM?
               JAE    PM
               MOV    CH,'a'                 ;If no, AM.

PM:            CALL   STORE_WORD             ;Store it.
               MOV    BYTE PTR [DI],'U'      ;Assume for now updated.
               MOV    BYTE PTR [DI+40],13    ;Tack on carriage return linefeed
               MOV    BYTE PTR [DI+41],10
               ADD    DI,42                  ;Move pointer past note field
               RET                           ; to start of next record.

STORE_WORD:    DIV    BL                     ;Divide by ten.
               ADD    AX,'00'                ;Convert to ASCII.
               CMP    CL,0                   ;Are we to display leading zero?
               JZ     STORE_IT               ;If yes, store as is.
               CMP    AL,'0'                 ;Is it a leading zero?
               JNZ    STORE_IT               ;If no, store it.
               MOV    AL,32                  ;Else, store a space.
STORE_IT:      STOSW
               MOV    AL,CH                  ;Store delimiter character also.
               STOSB
               RET

;-----------------------------------------------------------------;
; This is the exit routines. Check if notes have been updated.    ;
; If yes, write the file. Return to original drive and directory. ;
;-----------------------------------------------------------------;

ERROR_EXIT:    MOV    AH,9                   ;Display error message.
               INT    21H
               CALL   RESTORE_PATH           ;Restore path.
               INT    20H                    ;Exit.

EXIT:          MOV    CURS_POS,1700H         ;Row 22; column 0.
               CALL   SET_CURSOR
               MOV    CX,CURSOR_ORG          ;*A
               MOV    AH,01H                 ;*A
               INT    10H                    ;*A
               CMP    BYTE PTR UPDATE_FLAG,1 ;Did we update notes?
               JNZ    NO_WRITE               ;If no, skip write
               MOV    SI,OFFSET SAVING       ;Display "Saving DIRN----.DAT.
               CALL   GET_TEXT
               MOV    SI,OFFSET DIRNOTES
               CALL   GET_TEXT
               MOV    DX,OFFSET DIRNOTES     ; else point to DIRNOTES
               MOV    CX,20H                 ; create the file.
               MOV    AH,3CH
               INT    21H
               MOV    BX,AX                  ;Filehandle.
               MOV    DX,OFFSET BUFFER       ;Point to the buffer
               MOV    CX,END_OFFSET
               SUB    CX,DX                  ;File size.
               MOV    AH,40H                 ;Write it.
               INT    21H

NO_WRITE:      CALL   RESTORE_PATH           ;Restore default directory.
               MOV    CURS_POS,0             ;Home the cursor.
               CALL   SET_CURSOR
               CALL   CLS                    ;Clear the screen.
               INT    20H                    ; and exit.

RESTORE_PATH:  MOV    DL,CURRENT_DISK        ;Reset the drive.
               MOV    AH,0EH
               INT    21H
               MOV    DX,OFFSET CURRENT_DIR  ;Reset the directory.
               MOV    AH,3BH
               INT    21H
               RET

;-------------------------------------------------;
; Approximate 700 filename buffer at end of code. ;
;-------------------------------------------------;

CURRENT_DIR:
BUFFER         EQU    CURRENT_DIR+66

CODE ENDS
END  START
